基于 Unreal Lyra 的 GAS 属性扩展和应用

Viewed 5

说明

  • 本文主要目的在于,以给游戏新增蓝属性(以下简称 Mana)并作为消耗应用到技能Dash为例,记录和分享基于 Unreal 的 Lyra 项目进行 Gameplay Ability System (以下简称 GAS )属性扩展的一种方案。
  • 本文所述方法也适用于其他属性,如 SP 等。
  • 本文所述方案只是当前可行方案的一种,不代表唯一方案或最佳实践。
  • 本文基于 Unreal Engine 5.2,其中的 Lyra 为引擎自带示例游戏工程。工程中的资源(主要是工程和插件 Content 目录中的资源)从虚幻商城免费工程 Lyra 中得来。引擎(含工程)源码地址见文末相关链接。
  • 本文主要介绍扩展步骤,会展示相关源码并做必要解释。但不会对底层原理做过多说明。

效果

效果展示


ezgif.com-optimize.gif

说明

  • Mana 有状态条且表现正常
  • 使用 Dash 后可以扣除 Mana
  • Mana 不足时无法使用 Dash
  • 在联网下也有效,玩家之间看到的对方的 Mana 也是正常更新的。
  • 其他玩家 Dash 多次后剩余 Mana


63ar1ltluf0q2hur3qc2o1msdi.png

  • 该玩家再次 Dash 后的剩余 Mana


76iidcb6cbm7lcr1u5aim4t04t.png

修改

相关文件


6hlu1ram42i97o620hdph1sm61.png

修改说明

  • Mana 属性,顾名思义,是属于玩家的一种属性,作为属性,跟 HP (或者说 Health)性质相同。区别在于,HP 是受到伤害等时会变化,而 Mana 则是在使用技能时变化,即 Mana 一般作为一种开销(即 Cost)存在。我们这里也主要参考作为 HP 的 Health 来支持 Mana。
  • 首先需要定义 Mana 这种属性(即 ATTRIBUTE),可以参考 HP ,在 LyraHealthSet 中定义 Mana 。
  • LyraHealthSet.h
  • 通过 ATTRIBUTE_ACCESSORS 定义和生成 Mana 和 maxMana 属性以及配套函数。


0afshm0op72jqpd93kaibvcvsb.png

  • ATTRIBUTE_ACCESSORS 是 Lyra 对 GAS 的进一步封装,主要作用是定义一组用于访问和初始化属性的辅助函数。GAMEPLAYATTRIBUTE_PROPERTY_GETTER 、GAMEPLAYATTRIBUTE_VALUE_GETTER、GAMEPLAYATTRIBUTE_VALUE_SETTER、GAMEPLAYATTRIBUTE_VALUE_INITTER等。


6hrgqkh8r2gkl45056fk0sv66m.png

  • 定义 Mana 和 maxMana 属性,并设置同步回调函数。


1ih6lsgrp4a3ensuu05mq83q85.png

3n65u4o53st86m2l03r0gags3u.png

  • LyraHealthSet.cpp
  • ULyraHealthSet::ULyraHealthSet
  • 在构造函数中初始化属性值(其实最好通过读取配置等方式对属性的初始化值进行初始化和设置)


50m849o3mqp8s2gs6ret0cfq7a.png

  • ULyraHealthSet::GetLifetimeReplicatedProps
  • 添加属性的网络同步的基础支持


6hvj189rekjrp89dc9d1t1nbeu.png

  • ULyraHealthSet::OnRep_Mana、ULyraHealthSet::OnRep_MaxMana
  • 重新属性同步回调函数,使用辅助宏来设置属性。


7qhof1glun76mfk00hg691nha6.png

  • ULyraHealthSet::PostGameplayEffectExecute
  • 在执行 GameplayEffect 之前属性基础值的修改。主要就是修改和微调、修正属性值,或派发 Mana 耗尽的通知。


4uc5lfdferk77rir9poa8l6ib4.png

1i2uhei675656lt7fn97gj0k5q.png

6vhd7du5ds8mtbh1nah83s7bpl.png

  • ULyraHealthSet::PostAttributeChange
  • 属性修改完毕之后的处理,如属性如果不和谐,则修正。或者检验最终的属性值是否合理。


58bm3i6cqvls90ltlv0e02acdu.png

67u72hnododj5tpfdm522fr1v8.png

  • ULyraHealthSet::ClampAttribute
  • 辅助函数,主要用于对属性进行修正。


6oi39g8fl2rbj4nnmg26n33qsm.png

  • (可选)根据 LyraHealthSet 里的逻辑,在 LyraGameplayTags 里新增无限 Mana 的 tag
  • LyraGameplayTags.h
  • 声明在 cpp 中使用 UE_DEFINE_GAMEPLAY_TAG 定义的 native gameplay tag ,以允许其他模块或代码使用创建的 tag 变量。


4iiv0aajqgk7okrs54ikaiu9qb.png

  • LyraGameplayTags.cpp
  • 使用在标头中外部声明的注释定义 native gameplay Tag ,以允许其他模块或代码使用创建的 Tag 变量。


2i7f7pv9a1eb14ufdnk5ifg3b1.png

  • 外部通过 LyraHealthComponent 来获取 Mana 属性,即监听其变化。
  • LyraHealthComponent.h
  • 定义属性获取函数(bp可用)以及属性变化通知回调函数(状态条变化等需要)


7rmvcgs62gk21pnp0ga49f0lpu.png

4j2ntmgcsekidtsq01ljc123rs.png

0diiugqkv73886vefobpmf1skd.png

  • LyraHealthComponent.cpp
  • 监听属性变化回调、设置初始值为最大值并执行一次属性变化回调通知(这样可以初始化状态条)。


7l7ph8qvvk26ki65foitvakmmb.png

  • 移除回调


5qqmhas3phu2ep189set0nmub3.png

  • 在 Component 中通过 AttributeSet 获取属性,以及归一化的属性(状态条的进度是基于百分比的)


3mtuh0le827kuu305uqpcoak6n.png

  • 处理和派发属性变化通知。


0k3p2n67pag0lqi0772cmfeov0.png

  • Mana 主要作为 Cost 来开销,可以参考 ULyraAbilityCost_ItemTagStack 定义一个 Cost
  • LyraAbilityCost_Mana.h
  • 跟 ULyraAbilityCost_ItemTagStack 等差不多,主要是属性换成我们需要的作为需要扣除的 Mana 的 Cost。


50nnbais8ve471um0f5kalbd6p.png

  • LyraAbilityCost_Mana.cpp
  • ULyraAbilityCost_Mana::CheckCost
  • 检测身上的 Mana 是否足够这次扣除


2d2pq1gtkveh4e5uapvglppjsr.png

  • ULyraAbilityCost_Mana::ApplyCost
  • 修改 Mana,减去这次的 Cost 。


7hdom28epl0m5q7hgio7c019ek.png

  • 关于用 SetNumericAttributeBase 还是 ApplyModToAttribute
  • UE5中,SetNumericAttributeBase和ApplyModToAttribute都是用于修改CharacterAttributeSet属性集中的属性值的函数,但它们的具体作用和使用场景略有不同:
    1. SetNumericAttributeBase:这个函数是用来设置属性的基础值的,即直接设置属性的数值。由于这个函数直接设置属性的数值,因此需要注意函数入参的数值范围,避免设置超出属性范围的数值。如果需要将属性的数值增加或减少一定数值,则可以使用AddNumericAttribute()函数。
    1. ApplyModToAttribute:这个函数是用来对属性值进行修改的,它可以对属性的基础值进行加、减、乘、除等数学运算,从而得到新的属性值。这个函数会根据不同的数学运算符,对属性的基础值进行相应的数学操作,然后返回新的属性值。这个函数通常用于技能、装备等属性的计算中。 因此,两个函数的区别在于:SetNumericAttributeBase是直接设置属性的数值,而ApplyModToAttribute是对属性的基础值进行数学运算,得到新的属性值。根据实际应用场景的需要,选择合适的函数进行使用。
  • 直接看 ApplyModToAttribute 的源码,可以看到其底层也是调用 SetNumericAttributeBase 的,只是在调用之前目标值是经公式计算得来的。


2k3qjns2dfr6ui7k55ikkgogv2.png

  • 为什么不用 ULyraAbilityCost_ItemTagStack 或 ULyraAbilityCost_PlayerTagStack 等已经有的 ULyraAbilityCost 而要新建一个专门的 Mana ULyraAbilityCost?
  • 因为已有的不支持蓝的扣除。
  • 为什么不直接在 GA 上使用 Cost GE?
  • 主要是因为如果要用 Cost GE 的话,得为所有需要消耗蓝的技能都创建自己的 Cost GE BP(即便部分可以共用),而代码的话只要写一个 Cost 的类就好,GA 里可以直接配置消耗的蓝的数值。
  • 另一方面,在具体消耗数值都可以在 GA 或 GE 里调节的情况下,代码比BP更有利于差异对比和维护。
  • 修改 Dash 技能,添加 Mana Cost
  • GA_Hero_Dash.uasset
  • 在 Costs -> additional Costs 里选刚才添加的 Mana Cost,数值填 25(或其他,这里的正数表示要扣多少)


4impbsntv1orqpn9ruj8le9uuu.png

  • 修改状态条,扩展 Mana 支持
  • W_Healthbar.uasset
  • designer视图中,添加 Mana 条,并跟 HP 条组成纵向布局(非必须)。


3hm7cfj6k0or8g1dfa9796q53h.png

  • Initialize Bar Visuals 的时候更新 Mana 数字和进度。


3q0c14hkor2vut89hevu5ik8pf.png

  • OnPosessedPawnChanged 的时候,给 Mana 绑定变化事件,并更新 Mana 数字和进度。


1djb4oa4i31icefhf4ep7po8gk.png

  • 创建名为 ManaChanged 的事件,添加 Mana 变化时的逻辑:Mana数字改变,Mana 条进度更新。


19g5cbdct7tlsenpcfl1h7busi.png

相关链接

**声明:**本文来自公众号:GameDevLearning),转载请附上原文链接(https://mp.weixin.qq.com/s/tlEVQJ03yeBuuiD3jRS4EA)及本声明。

0 Answers