83. UE5 RPG 实现属性值的设置

news2024/9/22 13:44:45

在前面,我们实现了角色升级相关的功能,在PlayerState上记录了角色的等级和经验值,并在变动时,通过委托广播的形式向外广播,然后在UI上,通过监听委托的变动,进行修改等级和经验值。
在这一篇里,我们实现一下对属性值的操作,原理和等的差不多,但是编写死不一样。属性值是在属性面板上的属性,它有一个专用的属性面板控制器。
我们的控制器的基类为RPGWidgetController.h,从基类分了两个子类,OverlayWidgetController.h一直显示在战斗界面的覆层的控制器,用于更新血量蓝量,技能,经验等级等。AttributeMenuWidgetController.h为专门为属性面板使用的控制器,我们在属性里设置了一个数组,用于对应属性标签和属性的对应,并在属性修改后,对其进行广播,用于属性面板的更新,虽然此内容与本篇无关,作为记录。
我们接下来,会在此控制器增加属性点变动的委托。

在PlayerState增加属性点记录

和等级相同,我们先增加对属性点的参数。

	UPROPERTY(VisibleAnywhere, ReplicatedUsing=OnRep_AttributePoints)
	int32 AttributePoints = 0;

以及属性点的变动委托

FOnPlayerStateChanged OnAttributePointsChangedDelegate; //属性点数变动委托

从服务器到客户端的同步函数

	UFUNCTION()
	void OnRep_AttributePoints(int32 OldAttributePoints) const; //服务器出现更改自动同步到本地函数 属性点

实现

void ARPGPlayerState::OnRep_AttributePoints(int32 OldAttributePoints) const
{
	OnAttributePointsChangedDelegate.Broadcast(AttributePoints);
}

接着增加属性点的相关函数

	//属性点
	FORCEINLINE int32 GetAttributePoints() const {return AttributePoints;} //获取角色当前属性点
	void AddToAttributePoints(int32 InAttributePoints); //增加属性点
	void SetAttributePoints(int32 InAttributePoints); //设置当前属性点

在设置后广播委托

void ARPGPlayerState::AddToAttributePoints(const int32 InAttributePoints)
{
	AttributePoints += InAttributePoints;
	OnAttributePointsChangedDelegate.Broadcast(AttributePoints);
}

void ARPGPlayerState::SetAttributePoints(const int32 InAttributePoints)
{
	AttributePoints = InAttributePoints;
	OnAttributePointsChangedDelegate.Broadcast(AttributePoints);
}

控制器添加委托

为了方便增加玩家状态的委托,我们将之前放在OverlayWidgetController.h覆层控制器里面的委托,移到基类RPGWidgetController.h,这样它的子类都可以使用这个委托类型,防止重复定义。

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerStateChangedSignature, int32, NewValue); //当玩家状态该表回调类型

接下来,我们就可以在属性面板控制器AttributeMenuWidgetController.h里面声明属性点委托

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnPlayerStateChangedSignature AttributePointsChangedDelegate; //监听属性点的变化委托

在绑定委托这里,我们实现对PlayerState里的委托绑定匿名函数,用于UI使用

	ARPGPlayerState* RPGPlayerState = CastChecked<ARPGPlayerState>(PlayerState);
	//绑定PlayerState的属性点委托
	RPGPlayerState->OnAttributePointsChangedDelegate.AddLambda([this](const int32 Points)
	{
		AttributePointsChangedDelegate.Broadcast(Points);
	});

在初始化这里,我们也需要广播一次,在面板打开时,显示正确的属性点

	const ARPGPlayerState* RPGPlayerState = CastChecked<ARPGPlayerState>(PlayerState);
	AttributePointsChangedDelegate.Broadcast(RPGPlayerState->GetAttributePoints());

修改一次升多个等级

我们还未对一次提升两级或者三级时对角色的属性点奖励和技能点奖励进行设置,实现起来简单,我们只需要对提升的等级进行遍历,将所有的奖励应用即可。
在AS里面,我们在升级时增加遍历,将已经升到的等级的奖励应用

//获取获得经验后的新等级
			const int32 NewLevel = IPlayerInterface::Execute_FindLevelForXP(Props.SourceCharacter, CurrentXP + LocalIncomingXP);
			const int32 NumLevelUps = NewLevel - CurrentLevel; //查看等级是否有变化
			if(NumLevelUps > 0)
			{
				//如果连升多级,我们通过for循环获取每个等级的奖励
				for(int32 i = CurrentLevel; i < NewLevel; i++)
				{
					//获取升级提供的技能点和属性点
					const int32 AttributePointsReward = IPlayerInterface::Execute_GetAttributePointsReward(Props.SourceCharacter, i);
					const int32 SpellPointsReward = IPlayerInterface::Execute_GetSpellPointsReward(Props.SourceCharacter, i);

					//增加角色技能点和属性点
					IPlayerInterface::Execute_AddToAttributePoints(Props.SourceCharacter, AttributePointsReward);
					IPlayerInterface::Execute_AddToSpellPoints(Props.SourceCharacter, SpellPointsReward);
				}

				//提升等级
				IPlayerInterface::Execute_AddToPlayerLevel(Props.SourceCharacter, NumLevelUps);

				//播放升级效果
				IPlayerInterface::Execute_LevelUp(Props.SourceCharacter);

				//将血量和蓝量填充满
				SetHealth(GetMaxHealth());
				SetMana(GetMaxMana());
			}

在UI实现对属性点的监听

我们之前创建的属性面板,以及对应的部件无法实现当前功能
在这里插入图片描述
所以,我们要为属性点创建一个专用的控制面板
在这里插入图片描述
在控件里面,我们需要通过蓝图修改属性名称,直接写上即可
在这里插入图片描述
然后修改监听的委托,修改为监听属性点的变动委托
在这里插入图片描述
现在我们有了属性点的值,接着要在玩家角色类里,将之前未完成的函数编写完成

void ARPGHero::AddToAttributePoints_Implementation(int32 InAttributePoints)
{
	ARPGPlayerState* PlayerStateBase = GetPlayerState<ARPGPlayerState>();
	check(PlayerStateBase); //检测是否有效,无限会暂停游戏
	PlayerStateBase->AddToAttributePoints(InAttributePoints);
}

测试,在角色等级提升时,角色属性点是否会自动修改。
在这里插入图片描述

设置加点按钮的开启关闭

接下来,我们实现一下对加点按钮的开启和关闭的设置。我们想在有可分配的属性点时,加点按钮可以点击,而在可分配属性点数为0时,我们将其设置为不可点击。
我们在WBP_TextValueButtonRow里增加一个函数,用于设置按钮点击是否开启
在这里插入图片描述
然后在WBP_AttributeMenu里增加一个函数,传入当前可分配的属性点数,根据属性点数设置是个按钮是否需要开启
在这里插入图片描述
接着,我们重写事件,将获取到的将控制器存储到变量,并监听属性点变动回调,调用设置按钮函数,并在最后初始化。
在这里插入图片描述
这个获取方式是我们写的蓝图函数库的方法,它们设置的位置是我们自定义的HUD类上,并存储一份单例
在这里插入图片描述
接着运行测试,在可分配属性点为0时,无法点击
在这里插入图片描述
当拥有了可分配属性点,增加按钮变为可点击状态
在这里插入图片描述

实现属性加点功能

有了可分配属性,接下来我们将实现属性加点的功能。
我们将通过点击按钮触发事件,然后调用控制器内的函数,将加点属性的标签传递,接着在ASC里,通过发送事件,将需要增加的属性和之前增长经验的方式增加,并在内部处理可分配的属性值。
首先,我们在AttributeMenuWidgetController.h增加一个函数,用于增加事件触发后的调用函数

	UFUNCTION(BlueprintCallable, Category="GAS|Attributes")
	void UpgradeAttribute(const FGameplayTag& AttributeTag); //升级属性

然后在自定义的ASC中增加两个函数,它们其实是为了实现一个功能,但是对于属性操作只需要在服务器去做修改即可,所以,我们额外增加了一个只有服务器运行的函数

	void UpgradeAttribute(const FGameplayTag& AttributeTag); //升级属性

	UFUNCTION(Server, Reliable)
	void ServerUpgradeAttribute(const FGameplayTag& AttributeTag); //服务器升级属性函数

我们需要能够在ASC里面去获取当前角色是否拥有可分配的属性点数,在角色接口里增加一个获取的函数

	UFUNCTION(BlueprintNativeEvent)
	int32 GetAttributePoints() const; //获取可分配属性点数

在角色基类里覆写它

virtual int32 GetSpellPoints_Implementation() const override;

实现此函数,从PlayerState里获取即可

int32 ARPGHero::GetAttributePoints_Implementation() const
{
	const ARPGPlayerState* PlayerStateBase = GetPlayerState<ARPGPlayerState>();
	check(PlayerStateBase); //检测是否有效,无限会暂停游戏
	return PlayerStateBase->GetAttributePoints();
}

有了属性点数的获取,我们就可以实现ASC中创建的升级属性的函数,如果角色拥有可分配的点数,那么我们将让服务器执行发送事件,我们之前创建了被动技能接收属性相关的标签事件,我们将需要提升的属性标签和提升数量传递过去,并通过接口,减少相应的数量。

void URPGAbilitySystemComponent::UpgradeAttribute(const FGameplayTag& AttributeTag)
{
	//判断Avatar是否基础角色接口
	if(GetAvatarActor()->Implements<UPlayerInterface>())
	{
		//判断是否用于可分配点数
		if(IPlayerInterface::Execute_GetAttributePoints(GetAvatarActor()) > 0)
		{
			ServerUpgradeAttribute(AttributeTag); //调用服务器升级属性
		}
	}
}

void URPGAbilitySystemComponent::ServerUpgradeAttribute_Implementation(const FGameplayTag& AttributeTag)
{
	FGameplayEventData Payload; //创建一个事件数据
	Payload.EventTag = AttributeTag;
	Payload.EventMagnitude = 1.f;

	//向自身发送事件,通过被动技能接收属性加点
	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetAvatarActor(), AttributeTag, Payload);

	//判断Avatar是否基础角色接口
	if(GetAvatarActor()->Implements<UPlayerInterface>())
	{
		IPlayerInterface::Execute_AddToAttributePoints(GetAvatarActor(), -1); //减少一点可分配属性点
	}
}

最后,我们在控制器里,调用ASC对应的函数

void UAttributeMenuWidgetController::UpgradeAttribute(const FGameplayTag& AttributeTag)
{
	URPGAbilitySystemComponent* ASC = CastChecked<URPGAbilitySystemComponent>(AbilitySystemComponent);
	ASC->UpgradeAttribute(AttributeTag);
}

接着编译代码,打开被动技能应用的GE,我们之前在里面增加了经验的设置
在这里插入图片描述
由于设置对应的属性是通过标签设置的,我们接着增加四个主要属性的修改,如果没有设置对应的属性,那么传递的值将默认为0
在这里插入图片描述
在这里插入图片描述
只要事件传递过来,带有标签和数值,GE会通过SetbyCaller被设置。

实现按钮的点击事件

最后,我们想给点击事件绑定回调,在回调里调用控制器的升级属性函数,并将属性标签传递,我们在里面已经监听对应标签的属性变动的回调了,标签是设置在UI上的,已经有了标签,后续就是实现点击回调调用函数即可。
在这里插入图片描述
我们首先在WBP_TextValueRow蓝图,将控制器切换并设置为变量,这样方便后续和子蓝图的使用
在这里插入图片描述
然后在WBP_AttributePointsRow里,对按钮的点击回调进行绑定,调用属性面板控制器的升级属性
在这里插入图片描述
这样,我们实现了整个逻辑,现在就可以去测试

在这里插入图片描述

修复无法满血的bug

出现这个bug的原因是因为在我们设置满血的时候,最大血量或者最大蓝量的属性还没有应用。解决这个bug需要在属性变动的回调里面修改,我们可以根据比例将属性值进行提升,或者设置一个变量,在属性变动时,进行修改。
如果按比例修改,我们可以按照官方的RPG进行修改,这里设置一下设置变量修改。
我们创建两个变量,在升级时设置将其设置为true

//将血量和蓝量填充满, 我们将设置变量
bFillHealth = true;
bFillMana = true;

覆写PostAttributeChange,它会在属性变动时触发

virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override; //属性变动后回调函数

在里面如果最大血量或者蓝量变动,并且变量为true,那么我们将蓝量或者血量填满

void URPGAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
	Super::PostAttributeChange(Attribute, OldValue, NewValue);

	if(Attribute == GetMaxHealthAttribute() && bFillHealth)
	{
		SetHealth(GetMaxHealth());
		bFillHealth = false;
	}

	if(Attribute == GetMaxManaAttribute() && bFillMana)
	{
		SetMana(GetMaxMana());
		bFillMana = false;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1943301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GoogleCTF2023 Writeup

GoogleCTF2023 Writeup Misc NPC Crypto LEAST COMMON GENOMINATOR? Web UNDER-CONSTRUCTION NPC A friend handed me this map and told me that it will lead me to the flag. It is confusing me and I don’t know how to read it, can you help me out? Attach…

Unity 批处理详讲(含URP)

咱们在项目中&#xff0c;优化性能最重要的一个环节就是合批处理&#xff0c;&#xff0c;在早期Unity中&#xff0c;对于合批的处理手段主要有三种&#xff1a; Static Batching Dynamic Batching GPU Instancing 如今Unity 为了提升合批范围与效率&#xff0c;提供了…

昇思 25 天学习打卡营第 15 天 | mindspore 实现 VisionTransformer 图像分类

1. 背景&#xff1a; 使用 mindspore 学习神经网络&#xff0c;打卡第 15 天&#xff1b;主要内容也依据 mindspore 的学习记录。 2. Vision Transformer 介绍&#xff1a; mindspore 实现 VisionTransformer 图像分类&#xff1b;VisionTransformer 论文地址 VisionTransfo…

掌握Python:三本不可错过的经典书籍

强烈推荐Python初学者用这三本书入门! Python3剑客 一、《Python编程从入门到实践》 这本书适合零基础的Python读者&#xff0c;旨在帮助他们快速入门Python编程&#xff0c;并达到初级开发者的水平。书中深入浅出地介绍了Python的基础概念&#xff0c;如变量、循环、函数等…

华清数据结构day4 24-7-19

链表的相关操作 linklist.h #ifndef LINKLIST_H #define LINKLIST_H #include <myhead.h> typedef int datatype; typedef struct Node {union{int len;datatype data;};struct Node *next; } Node, *NodePtr;NodePtr list_create(); NodePtr apply_node(datatype e); …

开源XDR-SIEM一体化平台 Wazuh (1)基础架构

简介 Wazuh平台提供了XDR和SIEM功能&#xff0c;保护云、容器和服务器工作负载。这些功能包括日志数据分析、入侵和恶意软件检测、文件完整性监控、配置评估、漏洞检测以及对法规遵从性的支持。详细信息可以参考Wazuh - Open Source XDR. Open Source SIEM.官方网站 Wazuh解决…

秒懂C++之string类(上)

目录 一.string类的常用接口说明 二.不太常用接口&#xff08;了解接口&#xff09; 三.string类的遍历访问 3.1 迭代器iterator 3.2 反向迭代器 四.string的其他功能 4.1 reserve(扩容&#xff09; 4.2 resize 4.3 at 4.4 append 4.5 4.6 insert 一.string类的常用…

VS2015加断点(红色),修改过后,断点变为白色不能命中

实际这个问题是因为&#xff1a;源文件和原始版本不同。解决方法有二&#xff1a; 一&#xff0c;在断点上右键&#xff0c;选择“位置”》勾选”允许源代码与原始版本不同&#xff1b; 二&#xff0c;点击菜单栏“调试”》“选项和设置”》“常规”》去掉“要求源文件与原始…

外卖霸王餐运营规划,系统该怎么选择?

在当今的外卖市场中&#xff0c;竞争日益激烈&#xff0c;如何吸引并留住消费者成为了每个餐饮商家关注的焦点。霸王餐作为一种创新的营销策略&#xff0c;以其独特的魅力&#xff0c;吸引了大量消费者的目光。然而&#xff0c;如何有效地运营霸王餐活动&#xff0c;选择合适的…

浅谈断言之XML Schema断言

浅谈断言之XML Schema断言 “XML Schema断言”是一种专门用于验证基于XML的响应是否遵循特定XML Schema定义的标准和结构的断言类型。下面我们将详细探讨XML Schema断言的各个方面。 XML Schema断言简介 XML Schema断言&#xff08;XML Schema Assertion&#xff09;允许用户…

EXO项目解析:pynvml怎么实现监控的,包括什么参数

目录 pynvml怎么实现监控的,包括什么参数 pynvml实现监控的方式 pynvml包括的主要参数 GPU功耗的组成 举例说明 注意事项 EXO项目解析:https://github.com/exo-explore/exo?tab=readme-ov-file 这段代码是一个使用setuptools库编写的Python包安装脚本,主要用于定义和…

std的时间函数——chrono

参考&#xff1a; C 标准库 分数运算&#xff08;ratio库&#xff09; 再也不被时间束缚&#xff1a;C stdchrono时间库全面解析 C11时间类 c11 chrono全面解析(最高可达纳秒级别的精度) C std::chrono库使用指南 (实现C 获取日期,时间戳,计时等功能) 一、std的分数ratio…

Android 防止重复点击

1.第一种方式&#xff1a; private static final int MIN_DELAY_TIME 2000; // 两次点击间隔不能少于2000ms private static long lastClickTime System.currentTimeMillis(); public static boolean isFastClick() { boolean flag true; long currentClickTime …

JMeter接口测试-3.断言及参数化测试

1. 断言 JMeter官方断言&#xff08;Assertion&#xff09;的定义 用于检查测试中得到的响应数据是否符合预期&#xff0c;用于保证测试过程中的数据交互与预期一致 断言的目的&#xff1a; 一个取样器可以添加多个不同形式的断言&#xff0c;根据你的检查需求来添加相应的…

dou dian滑块captchaBody

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi a…

基于生物地理算法的MLP多层感知机优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 生物地理算法&#xff08;BBO&#xff09;原理 4.2 多层感知机&#xff08;MLP&#xff09; 4.3 BBO优化MLP参数 5.完整程序 1.程序功能描述 基于生物地理算法的MLP多层感知机优化mat…

Git之repo sync -l与repo forall -c git checkout用法区别(四十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Java 集合框架:TreeMap 的介绍、使用、原理与源码解析

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 021 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

SpringBoot原理解析(二)- Spring Bean的生命周期以及后处理器和回调接口

SpringBoot原理解析&#xff08;二&#xff09;- Spring Bean的生命周期以及后处理器和回调接口 文章目录 SpringBoot原理解析&#xff08;二&#xff09;- Spring Bean的生命周期以及后处理器和回调接口1.Bean的实例化阶段1.1.Bean 实例化的基本流程1.2.Bean 实例化图例1.3.实…

leetcode算法题之接雨水

这是一道很经典的题目&#xff0c;问题如下&#xff1a; 题目地址 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 解法1&#xff1a;动态规划 动态规划的核心就是将问题拆分成若干个子问题求解&#…