简介
Gameplay技能系统能够帮助我们实现任何RPG或MOBA中设计的技能。可以便利的进行这些动作的
-
冷却倒计时
-
资源消耗
-
更改技能等级
-
技能效果配置(粒子系统、音效等等)
系统配置
由于Gameplay技能系统是一个插件,需要先弃用才能使用。可以通过2步来启用它。
-
编辑=》插件=》窗口中启用Gameplay技能系统插件
-
配置项目的Build.cs文件的`PublicDependencyModuleNames`
PublicDependencyModuleNames.AddRange(new string[] { "GameplayAbilities", "GameplayTags", "GameplayTasks", "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
子模块综述
从实现层面来说可以分为以下4个模块
-
Gameplay技能
实为UGameplayAbility类的C++或蓝图子项。能够处理技能的复制和实例化行为。
-
定义了游戏中技能的效果、使用技能付出的代价,以及何时或在何情况下可以使用等。
-
可以作为异步运行的实例化对象存在。
-
可以在整个网络中自我复制,运行在客户端或服务器计算机上(包括客户端预测支持),甚至还能同步变量和执行远程过程调用(RPC)。
-
使引擎可在游戏会话期间灵活实现游戏技能,例如提供的扩展功能可用于实现冷却和使用消耗、玩家输入、使用动画蒙太奇的动画,以及对给予Actor的技能本身做出反应。
-
技能任务
衍生自抽象类UAbilityTask类,以C++编写。能够定义技能的逻辑过程,并异步执行。
-
Gameplay属性和Gameplay效果。
Gameplay属性是存储在FGameplayAttribute
结构中的"浮点"值,将对游戏或Actor产生影响;其通常为生命值、体力、跳跃高度、攻击速度等值。Gameplay效果可即时或随时间改变Gameplay属性(通常称为"增益和减益")。例如,施魔法时减少魔法值,激活"冲刺"技能后提升移动速度,或在治疗药物的效力周期内逐渐恢复生命值。
-
技能系统组件
它是游戏中的角色访问Gameplay技能系统的主要接口,能够管理Gameplay属性、Gameplay事件、存储Gameplay技能甚至能够处理玩家输入到Gameplay技能激活、确认及取消命令的绑定。任何要与Gameplay技能系统交互的Actor都应具有技能系统组件
-
玩法技能
-
授予与撤销技能
授予
在Actor可以使用某项技能之前,必须向其技能系统组件授予该技能。技能系统组件的以下函数可以授予对某项技能的访问
GiveAbility
使用FGameplayAbilitySpec指定要添加的技能,并返回FGameplayAbilitySpecHandle。
GiveAbilityAndActivateOnce
授予后若授予成功则直接接货技能必须实例化,并且必须能够在服务器上运行。尝试在服务器上运行技能后,将返回 FGameplayAbilitySpecHandle。如果技能没有满足所需条件,或者无法执行,返回值将无效,并且技能系统组件将不会被授予该技能。
撤销
以下函数可以利用授予技能后返回的 FGameplayAbilitySpecHandle,来撤销对技能系统组件中该技能的访问。
ClearAbility
从技能系统组件中移除指定技能。
SetRemoveAbilityOnEnd
当该技能执行完毕时,将该技能从技能系统组件中移除。如果未执行该技能,将立即移除它。如果正在执行该技能,将立即清除其输入,这样玩家就无法重新激活它或与它互动。
ClearAllAbilities
从技能系统组件中移除所有技能。此函数是唯一不需要 FGameplayAbilitySpecHandle 的函数。
-
基本用法
-
使用`CanActivateAbility`判断调用者是否能使用此技能
-
使用CallActivateAbility执行技能相关游戏代码,但是不会检查节能是否可用
-
开发者覆盖名为`ActivateAbility`C++函数或Activate Ability蓝图事件。
-
与Actor和组件不同,玩法技能不会使用"tick"函数完成主要工作,而是在激活过程中启动技能任务,异步完成大部分工作,然后连接代理(在C++中)以处理这些任务的输出,或者连接节点以输出执行引脚(在蓝图中)。
-
如果从"激活"中调用`CommitAbility`函数,它将应用执行技能的消耗,例如从玩法属性中减去资源(例如"魔法值"、"体力值"或游戏系统所用的任何其他资源)和应用冷却。
-
`CancelAbility`提供了取消技能的机制,不过技能的`CanBeCanceled`函数可以拒绝请求。与`CommitAbility`不同,该函数可供技能外调用者使用。成功取消先播放给On Gameplay Ability Cancelled,然后通过标准代码路径结束技能,让技能可运行特殊的清理代码,否则取消时的行为将与自行结束时的行为不同。
-
TryActivateAbility 是执行技能的典型方式。该函数调用 CanActivateAbility 来确定技能是否可以立即运行,如果可以,则继续调用 CallActivateAbility
-
EndAbility(C++)或End Ability节点(蓝图)会在技能执行完毕后将其关闭。
如果技能被取消,UGameplayAbility类会将其作为取消流程的一部分自动处理,但其他情况下,开发者都必须调用C++函数或在技能的蓝图图表中添加节点。
如果未能正常结束技能,将导致玩法技能系统认为技能仍在运行,从而带来一些影响,例如禁止将来再使用该技能或任何被该技能阻止的技能。例如,
如果游戏的"喝生命药剂"玩法技能没有正常结束,那么使用该技能的角色就无法执行任何在喝血量药剂时无法执行的操作(例如喝其他药剂、快跑、爬梯子等)。这种阻碍会一直存在,因为玩法技能系统会认为角色还在喝药剂。
-
标记
Cancel Abilities With Tag |
如果任何已在执行的技能带有与执行此技能时提供的列表匹配的标记,则取消那些技能。 |
Block Abilities With Tag |
在执行此技能时,阻止执行具有匹配标记的任何其他技能。 |
Activation Owned Tags |
在执行此技能时,技能的所有者将被给予这组标记。 |
Activation Required Tags |
只有激活的Actor或组件具有所有这些标记时,技能才会被激活。 |
Activation Blocked Tags |
只有激活的Actor或组件没有任何这些标记时,技能才会被激活 |
Target Required Tags |
只有目标Actor或组件具有所有这些标记时,技能才会被激活。 |
Target Blocked Tags |
只有目标Actor或组件没有任何这些标记时,技能才会被激活。 |
-
复制
玩法技能复制策略(Gameplay Ability Replication Policy)
本地预测(Local Predicted)
此选项有助于在响应能力和准确性之间实现良好的平衡。在本地客户端发出命令后,技能将立即在客户端上运行,但服务器起着决定性作用,并且可以根据技能的实际影响来覆盖客户端。客户端实际上是从服务器请求执行"技能"的权限,但也在本地进行处理,就像服务器需要同意客户端对结果的看法一样。因为客户端在本地预测技能的行为,所以只要客户端的预测与服务器不矛盾,它就会非常顺利地运行且无滞后。
仅限本地(Local Only)
客户端仅在本地运行技能。如果使用服务器的客户端是主机(在物理服务器计算机上播放)或者是在单人游戏中,尽管技能将在服务器上运行,也不会对服务器应用复制。这种情况不适用于专用服务器游戏,因为在专用服务器游戏中客户端永远不会在服务器计算机上运行。客户端通过该技能带来的任何影响都将继续遵循常规复制协议,包括可能从服务器接收更正。
服务器启动(Server Initiated)
在服务器上启动的技能将传播到客户端。从客户端的角度来看,这可以更准确地复制服务器上实际发生的情况,但使用技能的客户端会因缺少本地预测而发生延迟。虽然这种延迟非常短,但某些类型的技能(特别是在压力情况下快速执行的操作)将不会像在本地预测模式中那样顺畅。
仅限服务器(Server Only)
仅限服务器"技能将在服务器上运行,不会复制到客户端。被这些技能修改的任何变量都将像往常一样进行复制,这意味着该技能能够影响服务器的权威数据,并会将影响传递给客户端。以这种方式,技能仍然可以影响客户端的观察,尽管技能本身只在服务器上运行。
-
实例化策略
在执行玩法技能时,通常会产生一个(技能类型的)新对象,用于跟踪正在进行的技能。由于在某些情况下可能会非常频繁地执行技能,例如在大逃杀、MOBA、MMO或RTS游戏中一百个或更多玩家与AI角色之间的战斗,可能会出现快速创建技能对象对性能产生负面影响的情况。为了解决这个问题,技能可以选择三种不同的实例化策略,以在效率和功能之间达到平衡。支持的三种实例化类型:
-
按执行实例化:(Instanced per Execution:)
每次技能运行时,都会产生技能对象的副本。
优点:是可以自由使用蓝图图表和成员变量,并且所有内容都将在执行开始时初始化为默认值。
缺点:开销较大。
该策略更适合不会频繁运行的技能。
建议可以用于类似大招并且只有少数几个角色可以使用的节能
-
按Actor实例化:(Instanced per Actor:)
在技能首次执行时,每个Actor会生成该技能的一个实例,该对象会在以后的执行中重复使用。这就要求在两次技能执行之间清理成员变量,同时也使保存多个执行的信息成为可能。按Actor是较为理想的复制方法,因为技能具有可处理变量和RPC的复制对象,而不是浪费网络带宽和CPU时间,在每次运行时产生新对象。
建议该策略适用于大规模的情况,因为大量使用技能的Actor(例如在大型战斗中)只会在第一次使用技能时产生对象。
-
非实例化:(Non-Instanced:)
这是所有类别中最高效的实例化策略。在运行时,技能不会生成任何对象,而是使用类默认对象。
但是,在高效的同时也伴随着一些限制。首先,该策略特别要求技能完全用C ++编写,因为创建蓝图图表需要对象实例。你可以创建非实例化技能的蓝图类,但这只能更改已公开属性的默认值。此外,在执行技能期间,即使在本机C ++代码中,技能也不能更改成员变量,不能绑定代理,也不能复制变量或处理RPC。
该策略仅适用于不需要内部变量存储(尽管可以针对技能用户设置属性)并且不需要复制任何数据的技能。它尤其适合频繁运行且被许多角色使用的技能,例如大型RTS或MOBA作品中部队使用的基本攻击。
-
触发玩法事件
GameEvents 是可以出传递的数据结构,能够直接触发玩法技能,无需通过正常通过,即可根据情境发送数据有效负载。
触发方式:
-
调用Send Gameplay Event To Actor 并提供实施`IAbilitySystemInterface`接口的Actor和玩法事件所需的情境信息。
-
直接在技能系统组件上调用Handle Gameplay Event。因为这不是调用玩法技能的正常方式,所以技能可能需要的情境信息将通过`FGameplayEventData`数据结构传递。该结构是一种通用结构,不会针对任何特定的玩法事件或技能进行扩展,但应该能够满足任何用例的要求。
-
`ContextHandle`字段会根据需要提供其他信息。
-
游戏属性及游戏效果
-
游戏属性
介绍
包括可以通过单浮点数值描述的Actor当前状态的任何数值度量,例如,生命值、体力、移动速度和魔法抗性等等。属性作为`FGameplayAttribute`类型的UProperty在 属性集 中声明,属性集包含属性并监视对它们的任何修改尝试。
Tip:必须在本地代码中创建属性和属性集——它们不能在蓝图中创建。
创建属性集
要创建属性集,应从`UAttributeSet`继承,然后添加使用`UPROPERTY`标记的游戏性属性数据成员。例如,仅包含"生命值(health)"属性的属性集将类似于以下所示:
UCLASS()
class USimpleAttributeSet : public UAttributeSet
{
GENERATED_BODY() public: /** 设置默认值。例如,"生命值(Health)"应设置为正数 */ USimpleAttributeSet(); /** 它衡量在死亡之前可以承受的伤害量。*/ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attributes") FGameplayAttributeData Health;
};
仅包含单个属性的最小属性集。
创建好属性集之后,必须向技能系统组件注册它。可以将属性集添加为拥有技能系统组件的Actor的子对象,或者将它传递给技能系统组件的`GetOrCreateAttributeSubobject`函数。
-
游戏效果
游戏性效果是游戏性技能系统更改属性的方法。其中包括:
-
指示对属性的底数值进行更改,例如,当某个Actor受到伤害时,减小其生命值。
-
临时更改(通常称为"增益"或"减益"),例如,使移动速度提升几秒。
-
随时间推移而应用的永久性更改,例如在几秒钟的时间段内(或无限期地)每秒重新生成特定数量的魔法值。
游戏性效果实现为可与技能系统组件交互且适当时可在它们处于激活状态时在其中存储的仅数据蓝图(属于`UGameplayEffect`基类)。
与大多数游戏性技能系统的其他部分不同,无论在本机还是蓝图代码中,游戏性效果通常不覆盖基类`UGameplayEffect`。相反,游戏性效果被设计成完全通过变量来配置。以下是部分可以调整的游戏性效果的
主要属性
-
时长:游戏性效果可立即应用(例如,受到攻击时生命值减少),在有限期间内应用(例如,持续时间为几秒的移动速度提升),或无限期地应用(例如,随着时间的推移,某个角色自然地恢复法力值)。另外,具有非瞬间时长的效果也可以按不同的间隔应用自身。这不仅对于更改效果在Gameplay方面产生作用的方式非常有用,对于设置重复音频或视觉效果等的时机方面也非常有用。
-
修饰符和执行(Execution):修饰符会确定游戏性效果与属性交互的方式。其中包括与属性自身的数学上的交互,例如,"将防御力提升5%",以及执行效果的游戏性标记要求。当需要让某个游戏性效果产生超出修饰符支持范围的影响时,需要用到"执行(Execution)"。"执行(Execution)"使用`UGameplayEffectExecutionCalculation`来定义游戏性效果执行时它具有的自定义行为。定义修饰符无法充分覆盖的复杂方程式时,它们特别有用。
-
应用要求:应用要求包括游戏性效果应用时必须存在(或被禁止)的多组游戏性标记以及游戏性效果不应用的随机概率。如果这些要求无法满足游戏的需求,可以从`UGameplayEffectCustomApplicationRequirement`基类派生数据对象,在其中你可以编写可任意定义复杂应用规则的本地代码。
-
授予技能:应用时,游戏性效果不仅可以授予游戏性标记,还可以授予技能。当与"执行(Execution)"配合使用时,可将它们用于设置高度特殊的游戏性组合。例如,某个Actor具有指示该Actor浸在油中的游戏性标记或属性,当它被以火为主题的游戏性效果击中时,它就可以获得"着火"技能,从而被动地烧毁附近的Actor并在接下来的十秒钟之内产生具有粒子和动态光照的视觉效果。
-
堆叠:"堆叠"指的是处理对已具有增益或减益(或者游戏性效果,在本示例中就是如此)的目标再次应用增益或减益,以及处理所谓的"溢出"情况的策略,溢出是指在原Gameplay效果的影响下已完全饱和的目标被应用了新的游戏性效果(例如,不断累积的毒药计时条只有在溢出后才会产生持续伤害效果)。系统支持各种各样的堆叠行为,例如,不断累积直至超出阈值,维护在每次应用后增加直至达到最大限制的"堆叠量",在限时效果的影响下重置或增补时间,或独立于各个计时器应用该效果的多个实例。
-
游戏性Cue显示: 游戏性Cue 是可通过游戏性技能系统控制的管理装饰效果(例如,粒子或音效)的方法,它可以节约网络资源。游戏性技能和游戏性效果可以触发它们,它们通过四个可在本地或蓝图代码中覆盖的主函数来产生作用:On Active、While Active、Removed及Executed(仅由游戏性效果使用)。所有游戏性Cue必须与"GameplayCue"开头的游戏性标记相关联,例如"GameplayCue.ElectricalSparks"或"GameplayCue.WaterSplash.Big"。
游戏性Cue管理器 执行游戏性Cue。Actor可通过实现`IGameplayCueInterface`并具有名称与游戏性Cue标记相匹配的函数来对游戏性Cue作出反应。独立的 游戏性Cue通知 蓝图也可以对游戏性Cue作出反应。
-
对效果和属性间的交互进行编程
属性集可覆盖多个函数以处理在某个游戏性效果尝试修改属性时它作出反应的方式。例如,样本`USimpleAttributeSet`中的"生命值"属性可以存储浮点值,而且该值可由游戏性技能系统访问或更改,但是当生命值降到零时实际上什么也没有发生,而且也没有什么可以阻止它降到零以下。要使"生命值"属性具有我们想要的行为方式,属性集自身可以进行干预,方法是覆盖多个处理对其任何属性的尝试修改的虚拟函数。以下函数通常会被属性集覆盖:
PreAttributeChange / PreAttributeBaseChange |
这些函数会在属性被修改前受到调用。它们用于强制实施与属性的值有关的规则,例如"生命值必须介于0和最大生命值之间",而且不应触发针对属性更改的游戏中反应。 | |
PreGameplayEffectExecute |
在修改属性的值之前,此函数可拒绝或改变提议的修改。 | |
PostGameplayEffectExecute |
在修改属性的值之后,此函数可立即对更改作出反应。这通常包括限制属性的最终值,或者触发针对新值的游戏中反应,例如,当"生命值"属性降到零时死去。
|
-
技能任务
"EndTask"函数支持两种终止方式
-
自行终止
-
等待运行它的游戏性技能结束,此时它会自动终止。
可以防止幻影技能任务运行和泄漏CPU周期和内存。能够保证技能不冲突,同一时间内优先保证主要技能结束后才进行下个技能的执行