20. UE5 RPG创建次级属性并实现设置

news/2024/11/29 12:41:39/

在游戏中,游戏的角色属性会根据游戏玩法也不同,当前制作的RPG中,主要分为两种属性,一种是主要属性另一种次级属性。这两种类型属性的区分主要数值的计算是否需要依托于其它数值
举个例子,你的体力值主要在于角色的个人成长增加以及装备等的增加,它不依托于其它属性,所以是主要属性。而你的血量上限需要依托于体力数值,根据体力进行一定的比例设置血量上线,它需要依托于体力属性,所以是次级属性。
接下来,我们首先将属性设置完整,这个属性一般需要策划进行设计,然后程序进行实现,如果自己学习,那么你就是策划,也是程序,所以,接下来我们自己设计一下属性。

经过了长达几分钟的思考,主要属性为一下几项:

  1. Strength 力量
  2. Intelligence 智力
  3. Resilience 韧性
  4. Vigor 体力

而次要属性里面我们设计了多项数值,依托于上面的主要属性,主要用于战斗中

  1. MaxHealth 血量上限,基于Vigor 体力属性计算
  2. MaxMana 蓝量上限,基于Intelligence 智力属性
  3. Armor 防御,基于Resilience 韧性属性计算, 降低所受伤害
  4. ArmorPenetration 护甲穿透,基于Resilience 韧性属性计算,降低敌人的防御,增加暴击率
  5. BlockChance 格挡率 ,基于Armor 防御属性计算,增加格挡伤害概率,触发时,降低一半所受伤害
  6. CriticalHitChance 暴击率,基于ArmorPenetration 护甲穿透属性计算,增加触发暴击伤害的概率
  7. CriticalHitDamage 暴击伤害,基于ArmorPenetration 护甲穿透属性计算,触发暴击时基于增加的伤害量
  8. CriticalHitResistance 暴击抵抗,基于Armor 防御属性计算,降低敌人的暴击概率
  9. HealthRegeneration 血量自动恢复,基于Vigor 体力属性计算,每秒自动恢复一定血量
  10. ManaRegeneration 蓝量自动恢复,基于Intelligence 智力属性,每秒自动恢复蓝量

在AS增加新的属性

我们需要将上面提到的所有属性添加到AS里面,虽然之前的文章说了很多遍如何添加了,这里再复习一遍加强记忆。
在AttributeSet类的上面,我们先定义了一个宏,在调用这个宏的时候,会给我们生成属性对应的定义:

// Uses macros from AttributeSet.h
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

这个宏定义生成四个调用的函数使用。

接下里就是定义一个属性的步骤:

  1. 首先创建一个属性,定义好蓝图可读,定义和服务器的同步函数,以及分类
	UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Strength, Category="Primary Attributes")FGameplayAttributeData Strength; //力量
  1. 设置宏,生成对属性的操作函数
	UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Strength, Category="Primary Attributes")FGameplayAttributeData Strength; //力量ATTRIBUTE_ACCESSORS(UAttributeSetBase, Strength);

ATTRIBUTE_ACCESSORS宏调用后,最终会生成四个函数,那Strength举例,会生成四个函数分别是:GetStrengthAttribute() 获取属性引用 GetStrength() 获取属性值 SetStrength(float NewValue) 设置属性值 InitStrength(float NewValue) 初始化属性值

  1. 定义OnRep_Strength,这个函数会在服务器上的属性值发生变化时,将服务器的数值同步到本地。
	UFUNCTION()void OnRep_Strength(const FGameplayAttributeData& OldStrength) const;

在cpp中的实现

void UAttributeSetBase::OnRep_Strength(const FGameplayAttributeData& OldStrength) const
{GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, Strength, OldStrength);
}

这个宏会生成对应的函数回调,在服务器值修改时,同步到本地。
https://docs.unrealengine.com/5.3/zh-CN/conditional-property-replication-in-unreal-engine/
4. 最后一步就是在GetLifetimeReplicatedProps回调函数中注册此属性提交服务器同步,在本地属性发生改变时,将此值提交给服务器。

void UAttributeSetBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{Super::GetLifetimeReplicatedProps(OutLifetimeProps);//主要属性DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Strength, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Intelligence, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Resilience, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Vigor, COND_None, REPNOTIFY_Always);//次级属性DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Health, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, MaxHealth, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Mana, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, MaxMana, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Armor, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, ArmorPenetration, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, BlockChance, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, CriticalHitChance, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, CriticalHitDamage, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, CriticalHitResistance, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, HealthRegeneration, COND_None, REPNOTIFY_Always);DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, ManaRegeneration, COND_None, REPNOTIFY_Always);
}

通过以上四个步骤,我们将所有的属性都注册一下。编译运行一下,showdebug abilitysystem 看一下属性
在这里插入图片描述

实现次级属性的设置

上面,我们把属性都创建了出来,而次级属性需要在主要属性变动的时候,实时变动,这里也不卖关子了,要实现这种效果,我们使用GE的Infinite,在主要属性变动,实时OverWrite覆写值即可。
接下来,我们将实现此功能,在上一章中,我们在角色的基础类中实现了一个方法,实现了主要属性数值的添加

void ACharacterBase::InitializePrimaryAttributes() const
{check(IsValid(GetAbilitySystemComponent()));check(DefaultPrimaryAttributes);const FGameplayEffectContextHandle ContextHandle = GetAbilitySystemComponent()->MakeEffectContext();const FGameplayEffectSpecHandle SpecHandle = GetAbilitySystemComponent()->MakeOutgoingSpec(DefaultPrimaryAttributes, 1.0f, ContextHandle);GetAbilitySystemComponent()->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), GetAbilitySystemComponent());
}

接下来,我们要修改这个函数可以复用,因为我们将需要添加主要属性和次要属性,这两个属性需要分开添加,我们将这个函数增加两个参数,一个是类作为参数传入,另一个则是玩家等级(后续可以根据等级设置不同的属性)

void ACharacterBase::ApplyEffectToSelf(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level) const
{check(IsValid(GetAbilitySystemComponent()));check(GameplayEffectClass);const FGameplayEffectContextHandle ContextHandle = GetAbilitySystemComponent()->MakeEffectContext();const FGameplayEffectSpecHandle SpecHandle = GetAbilitySystemComponent()->MakeOutgoingSpec(GameplayEffectClass, Level, ContextHandle);GetAbilitySystemComponent()->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), GetAbilitySystemComponent());
}

增加一个GE属性,用于在角色身上设置次级属性的GE

	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="Attributes")TSubclassOf<UGameplayEffect> DefaultSecondaryAttributes;

增加一个函数,用于初始化两个GE,调用ApplyEffectToSelf函数

void ACharacterBase::InitializeDefaultAttributes() const
{ApplyEffectToSelf(DefaultPrimaryAttributes, 1.f);ApplyEffectToSelf(DefaultSecondaryAttributes, 1.f);
}

最后,在英雄角色初始化数据的地方,修改为调用InitializeDefaultAttributes()即可
编译打开UE,在角色属性查看是否可以设置两个类
在这里插入图片描述
后面,我们只需要实现次级属性的GE应用即可

实现次级属性的GE

前面提到了,我们要实现次级属性的设置,需要使用GameplayEffect去实现,所以,我们创建一个专门设置次级属性的GE
在这里插入图片描述
设置应用给角色
在这里插入图片描述
设置持续时间为无限
在这里插入图片描述
先增加一个属性进行测试,这里我们使用对Maxhealth进行修改,比例和体力一比一,就是多少体力就有多少最大血量,运算模式记得修改为覆盖(覆写)
在这里插入图片描述
运行查看,发现体力和最大血量是相同的
在这里插入图片描述
接下来我们修改碰撞盒子,在碰撞事件中,增加体力十点,查看最大血量是否跟着改变
在这里插入图片描述
结果显而易见,通过利用GE我们简简单单就实现了对属性的实时修改。

接下来就是对大批量的属性进行修改,这个工作主要是策划进行设计,作为独立游戏,没有策划,你就是策划,我们就随意写一套用作测试所有属性好了。
这里,我将所有的次级属性都进行了设置
在这里插入图片描述
下面对每个属性设置的参数一一截图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行项目,showdebug abilitysystem 发现属性都正常设置
在这里插入图片描述
如果属性过多,后面使用debug查看会很麻烦,所以一般情况,都有一个属性面板查看角色的属性,后面,我们将制作一个属性面板进行属性查看


http://www.ppmy.cn/news/1387336.html

相关文章

C语言——详解字符函数和字符串函数(一)

Hi,铁子们好呀&#xff01;今天博主来给大家更一篇C语言的字符函数和字符串函数~ 具体讲的内容如下&#xff1a; 文章目录 &#x1f386;1.字符分类函数&#x1f4af;&#x1f4af;⏩1.1 什么是字符分类函数的&#xff1f;&#x1f4af;&#x1f4af;⏩1.2 字符函数的类型有哪…

移动端实现一个日历带提示

移动端实现一个日历带提示的效果&#xff1a; 功能&#xff1a;超过当前12点不能选明天的&#xff0c;只能选后天的日期。 使用组件&#xff1a;vant-Calendar const formatter (day: CalendarDayItem) > {// console.log("day", day);const currentTime new …

yocto系列之针对tarball编写recipes

回顾 针对借助yocto构建linux 镜像我们已经讲述了6部分&#xff0c; 简单回顾如下&#xff1a; Yocto: 第1部分 - yocto系列之yocto是个什么东东 https://mp.csdn.net/mp_blog/creation/editor/136742286 Yocto: 第2部分 - yocto系列之配置ubuntu主机 https://mp.csdn.net…

油烟净化器:餐饮卫生环保检测的必备之选

我最近分析了餐饮市场的油烟净化器等产品报告&#xff0c;解决了餐饮业厨房油腻的难题&#xff0c;更加方便了在餐饮业和商业场所有需求的小伙伴们。 在如今日益关注卫生环保的时代背景下&#xff0c;油烟净化器成为餐饮业者必备的环保设备&#xff0c;不仅能够保持餐饮环境清…

Elasticsearch:调整近似 kNN 搜索

在我之前的文章 “Elasticsearch&#xff1a;调整搜索速度”&#xff0c;我详细地描述了如何调整正常的 BM25 的搜索速度。在今天的文章里&#xff0c;我们来进一步探讨如何提高近似 kNN 的搜索速度。希望对广大的向量搜索开发者有一些启示。 Elasticsearch 支持近似 k 最近邻…

C++ 11:基于范围的 for 循环

基于范围的for 循环&#xff08;Range-based for loop&#xff09;是 C11 标准引入的一项特性&#xff0c;它提供了一种更简洁、更安全的遍历容器&#xff08;如数组、向量等&#xff09;的方式。 与传统的 for 循环相比&#xff0c;基于范围的 for 循环自动处理迭代&#xff…

axios 请求 url 地址,判断网络地址是否存在

使用 axios 来判断一个 URL 地址是否可连接&#xff0c;通常涉及到发送一个 HTTP 请求到这个地址&#xff0c;并检查响应的状态码。如果请求成功&#xff0c;并且返回了 200 OK 状态码&#xff0c;那么通常可以认为这个地址是可连接的。 以下是一个简单的示例&#xff0c;使用…

Leetcode 1. 两数之和

心路历程&#xff1a; 很简单的题&#xff0c;双层暴力就可以&#xff0c;用双指针的话快一点。暴力时间复杂度O( n 2 n^2 n2)&#xff0c;双指针时间复杂度O(nlogn) O(n) O(n) O(nlogn)。 注意的点&#xff1a; 1、题目需要返回原数组的索引&#xff0c;所以排序后还需要…