跳到主要内容
信息
· 文章中可能会出现一些错误,希望大佬们可以在评论区指出;

06 - Gameplay Ability

本文主要说明了UE5中Gameplay技能系统(Gameplay Ability System, GAS)中Gameplay Ability (GA) 的相关内容。

概述

Gameplay Ability(下文简称GA),继承自UGameplayAbility,定义了游戏内技能的效果、使用技能付出的代价(如果有),以及使用的条件和冷却等。GA支持异步运行多个任务,包括角色动画、粒子声效,基于分支的用户输入和交互等。GA可以通过网络同步实现客户端预测支持、变量复制同步和执行远程过程调用RPC。

GA-1

下图内容是GA的总结:

  • GA是用于定义一个技能或能力的类;
  • GA必须被授予和激活才能使用,其中授予过程在服务端上进行,然后将GASpec同步到拥有该GA的客户端上;
  • GA可以拥有启用条件,例如花费和冷却时间;
  • GA可以异步执行一些任务,这需要Ability Task的支持;

GA-2

配置项

Tags

可通过Gameplay Tags帮助决定GA间的交互方式,每个GA均拥有一组可用来标记和分类的Gameplay Tags,这些Tags会影响它的行为。具体配置项详见下表:

Gameplay Tag 类型描述
Ability Tags该GA拥有的Tag
Cancel Abilities With Tag该GA执行后,拥有这些Tag的GA会被取消
Block Abilities With Tag该GA激活时,拥有这些Tag的GA会被阻止激活
Activation Owned Tags该GA激活时,将这些Tag给予GA所有者;
如果AbilitySystemGlobals中的ReplicateActivationOwnedTags启用,这些Tag会被网络同步
Activation Required Tags如果GA所有者的Actor/Component拥有全部这些Tag,该GA才能被激活
Activation Blocked Tags如果GA所有者的Actor/Component拥有任一这些Tag,该GA会被阻止激活
Source Required Tags如果GA施加者的Actor/Component拥有全部这些Tag,该GA才能被激活
Source Blocked Tags如果GA施加者的Actor/Component拥有任一这些Tag,该GA会被阻止激活
Target Required Tags如果GA承受者的Actor/Component拥有全部这些Tag,该GA才能被激活
Target Blocked Tags如果GA承受者的Actor/Component拥有任一这些Tag,该GA会被阻止激活

Input

  • Replicate Input Directly:如果选中该项,该GA将总会向服务器同步输入按下/松开的事件。

Advanced

GA的高级设置部分,分为网络和实例化两大类。

网络

  • Replication Policy:决定GA是否会复制自身实例、更新状态或跨网络传输Gameplay事件。但该配置项不会影响复制GA的激活与结束

  • Server Respects Remote Ability Cancellation:当本地客户端的GA结束时,结束服务器的对应GA。

  • Net Execution Policy:对于启用复制GA的多人游戏,有如下处理网络复制的策略:

配置项描述
Local Predicted在客户端激活GA后执行并进行预测,根据服务器执行GA的结果确认GA造成的效果是否该回滚改正。这样做可以在数据准确和游戏延迟间取得较好的平衡。
Local Only只在客户端执行GA,服务器不会执行GA。
Server Initiated先在服务器执行GA,然后在客户端执行。这样做虽然准确,但会产生一定的延迟。
Server Only只在服务器执行GA。
  • Net Security Policy:该配置项将决定客户端和服务器对GA的激活/结束权限:
配置项描述
Client Or Server客户端和服务器均能激活/结束GA
Server Only Execution只有服务器被允许激活GA
Server Only Termination只有服务器被允许结束GA
Server Only只有服务器被允许激活/结束GA

实例化

  • Instancing Policy:该项控制GA实例化时的策略,这会在限制它在代码实现中的行为。具体配置项如下表所示:
配置项描述
Instanced Per Execution每次激活GA时都会创建新实例。该实例化策略可以自由使用蓝图或成员变量,并且这些内容都会在执行前被初始化为默认值,因此不建议存储一些持久性数据。由于开销较大,该策略更适合不会频繁运行的技能,例如MOBA游戏中的英雄大招;
Instanced Per Actor首次激活GA时为Actor创建一个实例,之后激活时会重复使用此示例。该实例化策略的变量必须每次要手动重置,因此能存储一些持久化数据。该策略适用于大规模的情况,因为大量使用技能的Actor(例如在大型战斗中)只会在第一次使用技能时产生对象;
Non-Instanced是性能最高的实例化逻辑,仅使用该GA的类默认对象(Class Default Object,CDO)。该实例化策略不能存储状态、不能向Ability Task绑定委托,只能实现纯C++代码逻辑。该策略适合频繁运行且被许多角色使用的GA,例如大型RTS或MOBA作品中部队使用的基本攻击;
  • Retrigger Instanced Ability:如果选中该项,并且激活一个已经激活的GA实例,那么该GA实例就会被结束并重新激活。

Costs

  • Cost Gameplay Effect Class:提供一个GE,GA需要花费对应的代价才能被激活。

Triggers

  • Ability Triggers:根据特定事件触发GA。

Cooldowns

  • Cooldown Gameplay Effect Class:提供一个GE,GA需要等待对应的冷却时间才能被激活。

常用操作

授予和移除GA

要想让Actor使用GA,需要向它的Ability System Component(下文简称ASC)授予GA,并且只能在服务端上授予或移除GA。ASC提供如下函数进行授予或移除GA的操作:

函数说明
GiveAbility()使用 FGameplayAbilitySpec 指定要添加的GA,并返回 FGameplayAbilitySpecHandle
GiveAbilityAndActivateOnce()使用 FGameplayAbilitySpec 指定要添加的GA,并返回 FGameplayAbilitySpecHandle。该GASpec必须实例化,并且必须能够在服务器上运行。如果该GA没有满足所需条件,或者无法执行,返回值将无效,并且ASC将不会被赋予该GA。
ClearAbility()从ASC中移除指定GA。
SetRemoveAbilityOnEnd()在GA结束执行后将其从ASC种移除。如果此时GA未执行,立即移除;如果GA正在执行,则清除输入,让玩家无法重新激活或与它交互。
ClearAllAbilities()从ASC中移除所有GA。

GA的生命周期

GA执行生命周期的相关函数如下:

  1. CanActivateAbility():即使Actor没有尝试执行特定GA,该函数也可让Actor知道它是否能执行特定GA。
  2. CallActivateAbility():执行GA的相关逻辑,但不会检查该GA是否可用。需要注意的是:
    • GA的自定义逻辑需要在ActivateAbility()C++函数或Activate Ability蓝图事件中实现。
    • GA的主要工作不在Tick函数中完成,而是通过激活Ability Task来异步完成相关逻辑。执行完这些Ability Task后需要通过C++的委托或蓝图的输出引脚来处理后续逻辑。
    • 如果在GA激活期间调用CommitAbility()函数,它将会应用执行GA的消耗或冷却,例如减少Attribute资源等。
    • CancelAbility()函数可以在GA逻辑外调用,它可以取消当前执行的GA,尽管CanBeCanceled()可以拒绝这个请求。成功取消GA后,将会广播OnGameplayAbilityCancelled委托,让该GA可以运行一些被取消时的逻辑。
  3. TryActivateAbility():执行GA的典型方式,是上面两个函数的结合。通过CanActivateAbility()判断是否能执行GA,如果能的话就调用CallActivateAbility()
  4. EndAbility()函数或End Ability蓝图节点:是GA逻辑正常结束后必须执行的C++函数/蓝图节点,不执行的话会让GAS系统认为该GA仍在运行。

激活执行GA

ASC提供了很多激活执行GA的函数:

TryActivateAbility()

AbilitySystemComponent.h点击展开/折叠代码
/** 
* Attempts to activate the given ability, will check costs and requirements before doing so.
* Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
* If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
*/
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool bAllowRemoteActivation = true);

尝试激活给定的GA,会先检查成本和需求。如果认为成功激活了,会返回true,但由于激活过程中后期失败,可能会返回false。如果bAllowRemoteActivation为true,它将远程激活本地/服务器GA;如果为false,它将仅尝试本地激活GA。

在Aura项目中,该函数用于激活大部分基于玩家输入的GA:

AuraAbilitySystemComponent.cpp点击展开/折叠代码
void UAuraAbilitySystemComponent::AbilityInputTagOnHeld(const FGameplayTag& InputTag)
{
if (!InputTag.IsValid())
{
return;
}

for (TArray<FGameplayAbilitySpec> ActivatableGASpecs = GetActivatableAbilities();
FGameplayAbilitySpec& GASpec : ActivatableGASpecs)
{
// 寻找该按键输入能触发的GA
if (GASpec.DynamicAbilityTags.HasTagExact(InputTag))
{
// 标识该GA的输入触发为: 已按下
AbilitySpecInputPressed(GASpec);
// 如果该GA仍未激活, 激活它
if (!GASpec.IsActive())
{
TryActivateAbility(GASpec.Handle);
}
}
}
}

TryActivateAbilitiesByTag()

AbilitySystemComponent.h点击展开/折叠代码
/** 
* Attempts to activate every gameplay ability that matches the given tag and DoesAbilitySatisfyTagRequirements().
* Returns true if anything attempts to activate. Can activate more than one ability and the ability may fail later.
* If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate abilities.
*/
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation = true);

尝试激活每个符合给定GameplayTag和DoesAbilitySatisfyTagRequirements()的GA。如果任何尝试激活,则返回 true。可以激活多个GA,并且可能在稍后失败。如果bAllowRemoteActivation为true,它将远程激活本地/服务器GA;如果为false,它将仅尝试本地激活GA。

在Aura项目中,角色受到伤害时会通过此函数激活带有相应GameplayTag的GA,用于实现受击逻辑:

AuraAttributeSet.cpp点击展开/折叠代码
// void UAuraAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)
// ...
FGameplayTagContainer TagContainer;
TagContainer.AddTag(AuraGameplayTags::GE::HitReact.GetTag());
EffectProperties.TargetASC->TryActivateAbilitiesByTag(TagContainer);
// ...

参考资料