找回密码
 立即注册
查看: 606|回复: 11

UE5-GAS插件UE5.3改动

[复制链接]
发表于 2023-10-11 17:50 | 显示全部楼层 |阅读模式
Unreal在前段时间发布5.3版本后的某天,我将本身写着玩的游戏切到了新版本,如往常一样打开了我的项目,创建了一个GE后惊呆了,好家伙,你是谁?你还是我认识的GE嘛?你变了!于是我想这写一篇文章大致过一遍GAS插件5.3的改动,其它部门变化不大,主要是GE进行了大改。
Gameplay Effect

是的,你没有看错,新的GE就长这样,从本来密密麻麻的一排配置项,变成了此刻只有这么一丢丢了,老的代码自动转成了新的配置实现,GE本来功能变成了可配置的component。GE此刻的功能变成了通过配置组件去实现。


看到这个新布局,我第一时间想到的就是Lyra项目中的物品系统,每一个item的属性和功能是通过Fragment组件来决定的。
如下图,Lyra一个手枪的物品的属性是由五个fragment决定的,Equippable Item决定了手枪作为装备的功能,Quick Bar Icon和Pickup Icon决定了手枪的UI表示,Set Stats为手枪的tag信息,Reticle Config应该决定了准心的表示。物品的类型和功能不再像一般的做法通过类的担任和派生实现,装备,消耗品等功能都可以通过一个个Fragment去实现和配置。


5.3中Gameplay Effect的思路也和物品类似,通过组件来决定GE的属性和功能。话不多说,打开源码,看看GE是如何实现的。在GameplayEffect中对应的属性为GEComponents数组.


GE功能组件类名为GameplayEffectComponent,担任于Object。首先看一看注释:
这是5.3推出的新功能,GEComponent在理解GE流程和小心维护回调基础上,避免了通过一大堆的接口去实现期望的功能,可以极大的减少代码时间。
GEComponent依赖于GE,和GE一样,对于多个释放的GE,都具有不异的GEComponent(关系应该类似于GameplayEffect和ActiveGameplayEffect的关系,GE本身是不会实例化的,激活的GE只是存了GE的CDO引用)因此,GEComponent不应该也不能存储数据,当前版本实现数据获取是通过delegate带来数据回调。因此当前仍然有一些数据需要存储在GE(应该是Active GE布局体)中,未来会在GE Spec中存储额外的数据以实现功能的扩展。


以前配置在GE中的功能,比如AssetTags,GrantTags,Conditional Effect等,此刻被划分到了一个个GEComponent傍边,通过配置的方式去添加


下图就是5.3官方提供的GE Component,基本上就是之前GE已有的功能,比如Asset Tags此刻变成了Asset Tags Component。通过这种方式,让我们扩张GE的属性和功能变得比以前便利太多,只需要派生一个本身的GE Component即可。


接下来开始看GEComponent中的函数,GEComponent的功能实现都是通过在子类中,按照需求重写对应的函数,在对应的时机触发需要的逻辑。
函数名触发条件和感化网络同步
CanGameplayEffectApply用于判断GE是否可以成功被Apply,如果返回false,则GE会添加掉败Server, Predict Client
OnActiveGameplayEffectAdded对于持续性的GE生效,当GE被添加至角色身上时生效,如果GE设置了堆叠Stack,则只会在Stack为空的时候才会生效Server, Owning Client
OnGameplayEffectExecuted对于InstantGE生效(Period GE可以被视为周期性的触发Instant GE),当GE Execute时触发Server
OnGameplayEffectApplied对所有的GE城市生效,周期性GE只会在添加时触发一次All
在继续讨论这四个函数具体功能的时候,先解释一下ActiveGameplayEffect,该布局体可以被理解为实例化的GE,也就是实际挂在角色身上的GE数据。我们可以知道GE本身是没有实例化的,所以诸如 暂停/激活,持续时间改变,被移除等等事件,是没有法子直接从GE上回调的,他们实际上发生在ActiveGameplayEffect,由此触发


ActiveGameplayEffect此中一个属性EventSet提供了激活的GE上可绑定的回调事件,GEComponent上使用的也是这里的事件,比如GE Component会在持续时间内赐与方针技能组,我们可以在OnActiveGameplayEffectAdded时调用Grant Ability,那么如何知道GE结束了呢,这个时候就需要绑定EventSet中的OnEffectRemoved,这也是官方代码中AbilitiesGameplayEffectComponent的做法


此中包含了四个Delegate,分袂代表了Active GE被移除,Stack Active GE的仓库发生变化,Active GE的持续事件发生变化,Active GE的激活状态发生改变。


在GE Component中监听GE Remove的方式示例


CanGameplayEffectApply
CanGameplayEffectApply,用于决定GE是否能成功被Apply,一个GE身上的所有components必需都返回true,才能被成功的apply。默认返回true


代码的执行挨次为,ASC调用函数ApplyGameplayEffectSpecToSelf(所有Apply Effect的函数最终城市调用此函数起实际感化),函数中会调用Gameplay Effect的CanApply函数,如果返回false,则GE会添加掉败,返回一个空的ActiveGEHandle


GE中的CanApply函数会遍历GE的GEComponents,调用CanGameplayEffectApply函数,只要有一个返回false,则GE无法被成功Apply


OnActiveGameplayEffectAdded
当GE被添加到ActiveGameplayEffectsContainer中时调用,当GE有持续时间时才会被添加到container中,意味着这个函数只对duration和infinite的GE生效。返回true则意味着GE需要保持激活,如果返回false则会被暂停,暂停并不意味着被移除,它仍然会等待这被激活。这个函数是会被replicate的,意味着GE方针的主控端也会调用此函数。
如果函数返回false,则GE会被遏制,遏制并不意味着被移除,如果条件变为true了,GE会再次生效


执行挨次如下
首先还是ASC的ApplyGameplayEffectSpecToSelf,如果GE有持续时间,或者因为预测需要将instant GE视为一个Infnite GE,会调用ASC的GE存储容器的函数ApplyGameplayEffectSpec


ApplyGameplayEffectSpec最终会生成一个布局体ActiveGameplayEffect并添加并储存起来,GE的效果也都是在这里起效的。


在函数的最后,调用InternalOnActiveGameplayEffectAdded表白GE已经被成功的添加至Active Effect Container,似乎如果GE设置为堆叠,Add就不会起效了。


InternalOnActiveGameplayEffectAdded将会调用GE的OnAddedToActiveContainer


终于,OnAddedToActiveContainer函数中会遍历GE的GEComponents,调用OnActiveGameplayEffectAdded


OnGameplayEffectExecuted
当GE被执行时调用,GE的执行只会在Role_Authority下的Instant GE才会被执行,对于有period的GE来说,它的每一次执行都可以被视为周期性的执行instant GE。


GE的执行是如何触发的呢?首先对于instant GE来说,GE并不会生成Active GE添加到容器中,因此在ASC里会调用ExecuteGameplayEffect函数让GE效果生效。对于周期性的GE来说,由于它具有持续时间,会被添加到Active GE容器中,周期性的调用Container中的函数InternalExecutePeriodicGameplayEffect,然后调用ExecuteGameplayEffect让GE效果生效,和instant GE的流程一致。
FActiveGameplayEffectsContainer::ExecuteActiveEffectsFrom->GE::OnExecuted




GE::OnExecuted -> GEComponent::OnGameplayEffectExecuted


OnGameplayEffectApplied
当GE被释放或者堆叠时调用,duration和instant GE城市触发Apply,对于周期性的GE,该函数不会多次触发,只会在刚生效时触发一次。和Execute和Add分歧,Apply函数是会网络同步的!EPIC也更保举在此函数中实现本身的功能


OnGameplayEffectApplied的调用挨次还是从ASC的ApplyGameplayEffectSpecToSelf开始,调用GE函数OnApplied,在OnApplied中遍历GEComponent调用OnGameplayEffectApplied




GEComponent类及其功能

这里介绍一下当前插件中已经实现的GE Component,基本上都是原有的GE功能,并进行了必然程度的更改和扩展。以前想要扩展GE的功能会斗劲麻烦,但是此刻扩展变得十分简单了,读者如果想要实现一些本身特定的GE功能,可以从找一个类似机制的GE Component参考
AbilitiesGameplayEffectComponent
老版本的GrantAbility,感化是在GE生效期间赐与角色技能


实现方式为重写OnActiveGameplayEffectAdded函数,监听ActiveGE的移除和暂停状态,中止状态为false为GE激活,此时调用GrantAbilities,而当GE被暂停或移除时,则调用RemoveAbilities


AdditionalEffectsGameplayEffectComponent
感化是GE生效时可以触发额外的GE,分袂有四个配置项,分袂代表分歧条件下触发的GE。

  • OnApplicationGameplayEffects:GE Apply时触发的额外GE
  • OnCompleteAlways:GE结束时触发的额外GE,无论是何种条件导致的
  • OnCompleteNormal:GE因为持续时间结束,正常结束时触发的额外GE
  • OnCompletePrematurely:GE因为不测情况被移除时,触发的额外GE


这里会再提一下该GE是如何通过ActiveGameplayEffect中的Delegate触发额外GE的。
对于OnCompleteAlways GEs来说,也就是GE结束时触发的额外GE。在函数OnActiveGameplayEffectAdded中监听了此Active GE被Remove的委托


触发的函数OnActiveGameplayEffectRemoved,则会遍历OnCompleteAlways ,然后Apply


AssetTagsGameplayEffectComponent
替代原有的GE中的配置项Asset Tags


BlockAbilityTagsGameplayEffectComponent
替代原有的Block Ability Tags,可以阻止特定tag的技能激活


生效的机制为配置tag后,会对GE的CachedBlockedAbilityTags进行改削


当GE激活时,会将BlcokTags注册进ASC中


ChanceToApplyGameplayEffectComponent
用于设置GE成功Apply的概率


CustomCanApplyGameplayEffectComponent
这里配置本身实现的GE Apply条件,来决定GE释放是否可以成功




GameplayEffectUIData_TextOnly
用于配置GE的UI信息,这里只有一个简单的TEXT,有需要的可以派生UGameplayEffectUIData类去添加本身需要的数据信息


ImmunityGameplayEffectComponent
配置角色免疫其它GE的条件


RemoveOtherGameplayEffectComponent
配置移除其它GE


TargetTagRequirementsGameplayEffectComponent
配置Tag条件,满足特定的条件GE就能执行对应的阶段。Application决定的是GE能否成功的释放到角色身上,OnGoing决定的是GE是否处于激活状态,即使没有激活,GE仍然是在角色身上的,Remove决定了GE移除的条件,当条件满足时,GE会被移除。


TargetTagsGameplayEffectComponent
替代原有的Grant Tags功能,实现方式和BlockAbilityTagsGameplayEffectComponent类似


测验考试实现Custom Effect Component

这个部门测验考试实现一个自定义功能的Effect Component,功能类似于AdditionalEffectsGameplayEffectComponent,在GE生效后会再Apply一些额外的GE效果,区别在于,额外释放的GE持续时间依赖于源GE,如果GE掉效,则它释放的额外GE也会掉效。我知道这个功能通过现有的配置完全可以实现,但是筹谋会感觉太难太复杂,有时候就是需要额外的再写功能。这里我写的很简单,只是为了做一个示范测验考试。
首先创建在C++中创建UGameplayEffectComponent的子类,重写OnActiveGameplayEffectAdded和OnGameplayEffectApplied两个函数


OnActiveGameplayEffectAdded中绑定一个委托,调用本身实现的函数OnActiveGameplayEffectRemoved,将添加的GE进行移除


OnGameplayEffectApplied中遍历OnGrantEffects,然后ApplyEffect


OnActiveGameplayEffectRemoved的实现也很简单,遍历OnGrantEffects然后移除GE


测试一下,创建一个测试GE,添加刚才实现的Component


可以看到GE_Test生效期间成功释放了一个额外的GE_Test_Grant,15s后持续时间结束,两个GE都被成功的移除。


其它改动

GameplayModMagnitudeCalculation
Bug修复
这个类时用于GE中自定义Modifier中改削某种属性的数值,在5.3中给K2_GetCapturedAttributeMagnitude等功能函数加上了const修饰符,这个bug也是挺搞笑的,在5.0-5.2这个类用于获取属性,Tag的函数全都因为没有const修饰符是无法调用的,此刻使用不需要改削源代码或者本身加函数了

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2023-10-11 17:51 | 显示全部楼层
好活儿!赏!
发表于 2023-10-11 17:51 | 显示全部楼层
我一直觉得这种组织数据的方式很奇怪。比方说仅仅是一个tag的区别,都要创建一个新的GE,不但越来越难以管理还会造成很多内存的浪费。感觉需要那种用DT、JSON等等的方式,再加上DataRegistry来配置数据
发表于 2023-10-11 17:52 | 显示全部楼层
内存浪费?应该不至于吧,dt加载数据是会把表全部的数据都加载的,ge多几个tag而已,数据量忽略不计了。管理的话倒是,不过可以写一个导表工具维护并创建ge
发表于 2023-10-11 17:53 | 显示全部楼层
我问个思路,有一种GE,它效果是造成14-24区间的随机伤害,那请问在联机游戏时怎么同步这个随机数,并让客户端在合适的阶段可以获取到这个数值(用于打印日志)
发表于 2023-10-11 17:53 | 显示全部楼层
伤害在服务器上算,伤害随机的种子可以放在context里发下去,用random from stream,随机结果应该是一样的
发表于 2023-10-11 17:54 | 显示全部楼层
妙哉 谢大佬,那类似暴击这种也大概可以实现。如果是暴击的话还要传递一个触发暴击的同步变量
发表于 2023-10-11 17:55 | 显示全部楼层
可以直接在context里加一组数据存储伤害信息,服务器算出来暴击bool就为true表明此次伤害暴击了
发表于 2023-10-11 17:55 | 显示全部楼层
[思考] 也就是说,Context是服务器生成,打包一网络发送给客户端么?
然后就可以被客户端上的GC拿到用于表现(实际能外面还会包一层ContextSpecHandle)。
如此方便~
发表于 2023-10-11 17:56 | 显示全部楼层
是的,差不多
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-3 17:28 , Processed in 0.107752 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表