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

在前面,我们实现了角色升级相关的功能,在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;}
}

接下来介绍另一种方式,我是从动作RPG官方案例里得到的灵感,这种方式可以不但可以恢复满血,而且还可以实现如果血上限提高,当前血量按比例提高的效果。如果你制作的游戏有限时的血上限提升的效果,那么,可以使用这种方式。
我们知道设置血量和蓝量时,是当前属性没有设置,导致血量无法填满,但是我们在PostAttributeChange可以获得变动前和变动后的值,我们可以按比例增加血量和蓝量。
所以,我们还是按照之前的方式设置血量和蓝量

//将血量和蓝量填充满, 我们将设置变量
SetHealth(GetMaxHealth());
SetMana(GetMaxMana());

然后修改PostAttributeChange函数,按照比例增加它的血量

void URPGAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{Super::PostAttributeChange(Attribute, OldValue, NewValue);if (!FMath::IsNearlyEqual(OldValue, NewValue)) //判断值是否产生了变化{if (Attribute == GetMaxHealthAttribute()){const float CurrentValue = GetHealth(); //获取当前的实际值const float NewDelta = (OldValue > 0.f) ? (CurrentValue * NewValue / OldValue) - CurrentValue : NewValue; //获取到最大值变动后,按比列修改后的值SetHealth(NewDelta + CurrentValue);}else if (Attribute == GetMaxManaAttribute()){const float CurrentValue = GetMana(); //获取当前的实际值const float NewDelta = (OldValue > 0.f) ? (CurrentValue * NewValue / OldValue) - CurrentValue : NewValue; //获取到最大值变动后,按比列修改后的值SetMana(NewDelta + CurrentValue);}}
}

如果需要是短时间修改血上限,在血量上限buff到时间后,恢复默认时,将血量不再按比例下降,我们可以限制一下值变动的值即可

NewDelta = FMath::Max(NewDelta, 0.f);

修复被动技能应用报错

我们在被动技能应用的GE里面添加了多个SetbyCaller,但是每次只设置一个SetbyCaller,其它未设置的,就会出现报错
在这里插入图片描述
原因:主要是因为在GA里面应用GE时,由于没有默认值造成的,所以我们需要为其设置默认值,那么修改GA的蓝图,在创建时设置默认值即可。
我们设置一个标签,设置所有的需要设置默认值的标签,和GE对应
在这里插入图片描述

设置上当前GE会修改的内容
在这里插入图片描述
我们接着在GA里添加一个函数,用于创建GE,然后遍历数组设置默认值
在这里插入图片描述
在外部,我们还是通过接收事件去覆盖
在这里插入图片描述
这样就完成了报错的修改。

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

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

相关文章

鸿蒙开发仓颉语言【Hyperion: 一个支持自定义编解码器的TCP通信框架】组件

Hyperion: 一个支持自定义编解码器的TCP通信框架 特性 支持自定义编解码器高效的ByteBuffer实现&#xff0c;降低请求处理过程中数据拷贝自带连接池支持&#xff0c;支持连接重建、连接空闲超时易于扩展&#xff0c;可以积木式添加IoFilter处理入栈、出栈消息 组件 hyperio…

Mongodb的通配符索引

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第95篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

代理协议解析:如何根据需求选择HTTP、HTTPS或SOCKS5?

代理IP协议是一种网络代理技术&#xff0c;可以实现隐藏客户端IP地址、加速网站访问、过滤网络内容、访问内网资源等功能。常用的IP代理协议主要有Socks5代理、HTTP代理、HTTPS代理这三种。代理IP协议主要用于分组交换计算机通信网络的互联系统中使用&#xff0c;只负责数据的路…

开局一个启动器:从零开始入坑ComfyUI

前几天刷某乎的时候看到了一位大佬写的好文&#xff0c;可图 IP-Adapter 模型已开源&#xff0c;更多玩法&#xff0c;更强生态&#xff01; - 知乎 (zhihu.com) 久闻ComfyUI大名&#xff0c;决定试一下。这次打算不走寻常路&#xff0c;不下载现成的一键包了&#xff0c;而是…

let、var、const 的区别 --js面试题

作用域 ES5中的作用域有&#xff1a;全局作用域、函数作用域&#xff0c;ES6中新增了块级作用域。块作用域由 { } 包括&#xff0c;if 语句和 for 语句里面的 { } 也属于块作用域。 var 1.没有块级作用域的概念&#xff0c;但具有函数全局作用域、函数作用域的概念 {var a …

别再只知道埋头苦学python了!!学了python后月入1w不在话下,不准你还不知道!!!

在Python接单的过程中&#xff0c;掌握一些技巧、注意相关事项以及选择合适的接单平台是非常重要的 一、Python接单要注意哪些 报酬问题&#xff1a;在接单前&#xff0c;务必明确客户所说的报酬是税前还是税后&#xff0c;以避免后期产生纠纷。时间管理&#xff1a;不要与客户…

Nginx 如何处理 WebSocket 连接?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 Nginx 如何处理 WebSocket 连接&#xff1f;一、WebSocket 连接简介二、Nginx 处理 WebSocket 连接的基本原理三、配置 Nginx 支持 WebSocket 连接四、Nginx 中的…

【启明智显分享】甲醛检测仪HMI方案:ESP32-S3方案4.3寸触摸串口屏,RS485、WIFI/蓝牙可选

今年&#xff0c;“串串房”一词频繁引发广大网友关注。“串串房”&#xff0c;也被称为“陷阱房”“贩子房”——炒房客以低价收购旧房子或者毛坯房&#xff0c;用极度节省成本的方式对房子进行装修&#xff0c;之后作为精修房高价租售&#xff0c;因甲醛等有害物质含量极高&a…

“从爱好者到职业画师:一位AI绘画践行者的赚钱实战秘籍“

&#x1f3a8; 【引子&#xff1a;AI绘画&#xff1a;艺术与科技的交汇】 在数字化浪潮席卷全球的今天&#xff0c;人工智能技术以其颠覆性的力量&#xff0c;正悄然改写着艺术创作的传统版图。当AI与绘画碰撞交融&#xff0c;诞生出一种全新的艺术形式——AI绘画。它不仅是科…

别只盯着苹果了,华为Mate70也有AI技术,听说效果让人直接惊呼

随着人工智能技术的不断进步&#xff0c;智能手机行业也迎来了前所未有的变革。苹果、三星等国际知名手机厂商纷纷在新品发布会上重点展示其手机的AI技术&#xff0c;而华为作为中国科技的领军企业&#xff0c;其在AI领域的成就同样不容小觑。 华为Mate系列作为其旗舰系列&…

科研绘图系列:R语言组合热图和散点图

介绍 热图展示参与者的属性,散点图表示样本的时间跨度。 加载R包 library(tidyverse) library(ComplexHeatmap) library(circlize) library(cowplot)导入数据 数据可从以下链接下载(画图所需要的所有数据): 百度云盘链接: https://pan.baidu.com/s/1iEE9seTLdrrC3WDHJy…

计算机网络基础:3.DNS服务器、域名分类

一、DNS服务器 DNS服务器在网络中的作用类似于餐厅中的“顾客座位对照表”&#xff0c;它帮助前台&#xff08;路由器&#xff09;将顾客&#xff08;用户&#xff09;的请求转发到正确的餐桌&#xff08;目标设备&#xff09;。 (1)概念与原理 DNS的基本概念 DNS&…

Gson的基本使用:解析Json格式数据 序列化与反序列化

目录 一&#xff0c;Gson和Json 1&#xff0c;Gson 2&#xff0c;Json 3&#xff0c;Gson处理对象的几个重要点 4&#xff0c;序列化和反序列化 二&#xff0c;Gson的使用 1&#xff0c;Gson的创建 2&#xff0c;简单对象序列化 3&#xff0c;对象序列化&#xff0c;格…

Wordpress安装到win10(2024年7月)

目录 1.wordpress介绍 2下载应用 2.1.wordpress 2.2XAMPP 2.3 PHPmyadmin 3.配置应用 3.1XAMPP进程 3.2 文件配置 3.3 phpmyadmin配置 4.配置网页 4.1 数据库创建 4.2 安装wordpress 5.进入面板 6.总结 1.wordpress介绍 WordPress是一个开源内容管理系统&#xff0…

Java台球厅助教教练预约上门到店系统源码

&#x1f3b1;一杆在手&#xff0c;天下我有&#xff01;台球助教教练预约系统&#xff0c;让球技飙升不是梦&#x1f680; &#x1f3af;【开篇&#xff1a;台球爱好者的福音来啦&#xff01;】&#x1f3af; 还在为找不到合适的台球教练而烦恼吗&#xff1f;或是想要在家就…

社交圈子聊天交友系统搭建社交app开发:陌生交友发布动态圈子单聊打招呼群聊app介绍

系统概述 社交圈子部天交友系统是一个集成即时通讯、社区互动、用户管理等功能的在线社交平台。它支持用户创建个人资料&#xff0c;加入兴趣围子&#xff0c;通过文字、图片、语音、视频等多种方式进行交流&#xff0c;满足用户在不同场景下的社交需求 核心功能 -&#xff0c;…

Matlab编程资源库(1)选择结构

一、if语句 在 MATLAB 中&#xff0c; if 语句有 3 种格式。 (1) 单分支 if 语句&#xff1a; if 条件 语句组 end 当条件成立时&#xff0c;则执行语句组&#xff0c;执行完之后&#xff0c; 继续执行 if 语句的后继语句&#xff0c;若条件不成 立&#xff0c;则直接执…

Qt源码交叉编译带openssl的Qt版本

一.背景 近期项目由于对接的后台服务是https的&#xff0c;之前交叉编译的Qt是不带openssl的&#xff0c;为了能支持https&#xff0c;必须要重新编译Qt。 二.环境 环境准备&#xff1a; Ubuntu版本 &#xff1a;18.04&#xff1b; openssl 版本&#xff1a;1.1.1.g&#xff1b…

C/C++的堆栈内存分配详解

在C/C编程中&#xff0c;内存管理是至关重要的一个方面。理解内存的分配方式有助于编写高效、可靠的程序&#xff0c;C/C主要使用两种内存分配方式&#xff1a;堆&#xff08;heap&#xff09;和栈&#xff08;stack&#xff09;。这两者在管理方式、性能和使用场景上都有显著区…

python计算PMF、PDF、CDF、PPF、KDE介绍

目录 1. 总括1.1 绘图示例1.2 概念介绍1. PMF (Probability Mass Function)2. PDF (Probability Density Function)3. CDF (Cumulative Distribution Function)4. PPF (Percent Point Function)5. KDE (Kernel Density Estimation)1. 总括 1.1 绘图示例 上图展示了PMF、PDF、C…