作为 GAS 系统中的核心元素之一 GameplayAbility 代表一个可大可小的 “能力”
这个能力能干啥,基本全靠你自己定义的边界,大到一个复杂的技能,小到一个动作,都可以作为一个“能力”
可以说 GA 的目的是基于一系列的行为控制来生成 GameplayEffect 改变 GameplayAttribue, 创建 GameplayCue 来处理显示和声音效果
这次我们基于 UE 版本 4.26 来简析一下 UGameplayAbility 类
基本信息
类型继承 UObject,并实现 IGameplayTaskOwnerInterface 接口
实现 IGameplayTaskOwnerInterface 的目的是为了可以在其中执行异步的 UGameplayTask
而 GA 使用的 GameplayTasksComponent 是 GA 的当前释放者的 AbilitySystemComponent (UAbilitySystemComponent 也是继承自 UGameplayTasksComponent 的)
重要的函数
如源码中的注释所写的,GA 里面有几个重要的函数
CanActivateAbility
函数定义
1 | virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const; |
函数功能是检查是否可以激活此 Ability
GAS 中在 UAbilitySystemComponent::TryActivateAbility 中调用
- Handle 参数:AbilitySpcHandle,这个代表一个 Ability 的实例和数据载体的 Handle
- ActorInfo 参数:当前技能的释放者信息,类型 FGameplayAbilityActorInfo 包括技能所有者,技能 Avatar Actor (代表物理上释放技能的 Actor,可以没有)和 PC ,骨架 Mesh ,是否本地 Actor 等信息
- SourceTags:表示释放者拥有的 Tags
- TargetTags:表示目标拥有的 Tags
- OptionalRelevantTags:用来存放一些额外的信息,本体里面用来放技能释放失败的原因 Tags
函数默认流程
函数是 virtual 的,你可以自己重写
默认流程如下
- 判断是否符合执行策略(只能在服务器执行,只能在客户端发起之类的)
- 判断 ActorInfo 中是否含有 AbilitySystemComponent ,没有则不可以释放
- 判断用户是否禁止释放技能(AbilitySystemComponent.UserAbilityActivationInhibited,例如菜单状态下不能释放之类的)
- 判断技能冷却是否完成
冷却是由 TSubclassOf
CooldownGameplayEffectClass 来控制的,在技能成功被 commit 的时候,会触发生成这个 GE,判断是否冷却了就是判断是否有这个 GE - 判断技能消费是否够
同样的消费是由 TSubclassOf
CostGameplayEffectClass 来控制的,这个 GE 是懒加载的,这里会调用 AbilitySystemComponent->CanApplyAttributeModifiers 来检查是否有资源释放技能 - 检查当前 Tags 是否满足释放条件,包括需要的 Tag,Block 的 Tag 等
- 获得技能实例的技能信息(FGameplayAbilitySpec)
- 如果绑定了输入,检查是否为不允许输入的技能(AbilitySystemComponent->IsAbilityInputBlocked)
- 检查蓝图覆盖的 K2_CanActivateAbility 是否可以释放
CallActivateAbility
函数定义
1 | void CallActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate = nullptr, const FGameplayEventData* TriggerEventData = nullptr); |
函数用来激活 Ability
参数除了上面的以外还有
- ActivationInfo:FGameplayAbilityActivationInfo 类型,存储一些技能的激活信息,包括 PK 和 ActivationMode (激活的模式, Authority 或者 Client 等)
- OnGameplayAbilityEndedDelegate: Ability Ended 回调
- TriggerEventData:FGameplayEventData 类型,包括技能发起者,目标,目标数据,增幅,Tag容器等,还可以传入两个 Custom 的 UObject 用来传递数据
函数主要流程
- 执行 PreActivate(Virtual 可覆盖)
a. 刷新对齐 ServerMovement
b. 如果是实例化的 Activity,设置 bIsActive 为激活状态,设置为 bIsBlockingOtherAbilities 为 True
c. 设置 Activity 的当前 ActorInfo 和 ActivationInfo
d. 加入需要激活的 GameplayTags
e. 加入 AbilityEnded 回调
f. 发送 AbilityActivatedCallbacks 事件
g. 更新 Ability 的 Blocking 和 Cancel Tags - 执行 ActivateAbility(Virtual 可覆盖)
a. 如果有蓝图实现,则使用蓝图实现 K2_ActivateAbility 激活任务
b. 如果有蓝图的 TriggerEvent 实现,则执行 K2_ActivateAbilityFromEvent(*TriggerEventData) 激活任务,灭有 TriggerData 则 End Ability
c. 如果没有蓝图实现,则直接 Commit Ability
CommitAbility
函数定义
1 | virtual bool CommitAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr); |
函数是确认 Ability
默认流程
- 调用 CommitCheck 检查是否可以 Commit (和 CanActivateAbility 差不多,也可以做额外的检查)
- 设置冷却,执行消耗资源 (ApplyCooldown 和 ApplyCost)
- 执行蓝图 K2_CommitExecute
- 发送 AbilityCommittedCallbacks 广播
CancelAbility
函数定义
1 | virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility); |
函数用来取消 Ability
默认流程
- 检查 Ability 是否可以被 Cancel (如果是非实例化的,则可以被 Cancel,如果是实例化的,检查是否可以被 Cancel 的设置)
- 检查当前的 Ability 是否正在 ApplyGameplayEffectSpecToTarget (ScopeLockCount),如果正在被操作,加入 WaitingToExecute 中,并会在执行完成后,执行本次 Cancel 操作
- 如果需要,执行 ReplicateEndOrCancelAbility 执行客户端或者服务器的 CancelAbility 或者 EndAbility 的 RPC
- 广播 OnGameplayAbilityCancelled 事件
- 执行 EndAbility
EndAbility
函数定义
1 | virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled); |
函数用来结束 Ability
默认流程
- 检查 Ability 是否可以被 End
a. 不是 Active 的实例化 Acitivity 不可以被 End
b. 检查 Handle 的 ActivitySpec 是否 Active - 查当前的 Ability 是否正在 ApplyGameplayEffectSpecToTarget (ScopeLockCount),如果正在被操作,加入 WaitingToExecute 中,并会在执行完成后,执行本次 EndAbility 操作
- 执行蓝图 K2_OnEndAbility
- 清除这个 Ability 在 World 中的所有 timer 和 latent actions
- 广播 OnGameplayAbilityEnded 事件,并移除回调
- 广播 OnGameplayAbilityEndedWithData 事件,并移除回调
- 对于实例化 Ability 设置 bIsActive 为非激活
- 结束所有挂在 Ability 上的 GameplayTask,并 Reset 列表来移除内存占用
- 如果需要执行 ReplicateEndOrCancelAbility
- 移除 ActivationTags
- 移除技能添加的 GameplayCue
- 移除 AbilityComponent 上的 Blocking 和 Cancel Tags
- 移除 PK 缓存
- 移除 AbilityComponent 上的 AnimatingAbility
- 播出 AbilityComponent 上的 AbilityEndedCallbacks , OnAbilityEnded 事件
- 需要同步的 Ability ,移除 Spec 的 ReplicatedInstance(服务器)
- 不需要同步的 Ability ,移除 Spec 的 NonReplicatedInstances
- 服务器上如果没有活动的 Spec 则,且设置为在 Activation 后就从 Component 中移除 Ability 则 ClearAbility,否则 MarkAbilitySpecDirty
这里的 ClearAbility,和 GiveAbility 对应,是从 AbilityComponent 中移除这个 Ability 的 Handle 而 MarkAbilitySpecDirty 是如字面意思表示 AbilitySpec 已经被修改过了
配置项
- ReplicationPolicy:同步策略
- InstancingPolicy:实例化策略,可以设置为非实例化,每个 Actor 实例化,或者每次执行实例化,非实例化的 Ability 只是用其 CDO 来做逻辑
- bServerRespectsRemoteAbilityCancellation : 是否可以从客户端来 Cancel 服务器上的 Ability
- bRetriggerInstancedAbility:是否在激活一个实例化的 Ability 的时候, 先 End 再重新激活
- NetExecutionPolicy:网络同步策略,包括本地预测,服务器 only 等
- NetSecurityPolicy:网络安全政策,设置 Ability 的执行权限
- CostGameplayEffectClass:消费的 GE
- AbilityTriggers:可以激活 Ability 的 Tags
- CooldownGameplayEffectClass:冷却的 GE
- CancelAbilitiesWithTag:执行的时候,会 Cancel 含有这些 Tag 的其他 Ability
- BlockAbilitiesWithTag:执行的时候,会 Block 含有这些 Tag 的其他 Ability
- ActivationOwnedTags:激活的时候附加到 Owner 的 Tags
- ActivationRequiredTags:激活的时候需要的 Tags
- ActivationBlockedTags:激活的时候,如果有这些 Tags 会阻止激活
- SourceRequiredTags:Source Actor 有这些 Tags 的时候才能激活
- SourceBlockedTags:Source Actor 有这些 Tags 的时候不能激活
- TargetRequiredTags:Target Actor 有这些 Tags 的时候才能激活
- TargetBlockedTags:Target Actor 有这些 Tags 的时候不能激活
- bReplicateInputDirectly: 是否直接把输入事件传到服务器
- AbilityTags:拥有的 Tags,会同其他的 Ability 的 CancelAbilitiesWithTag 和 CancelAbilitiesWithTag 协同作用
小结
- 非实例化的 Ability ,总是可以被 Cancel,也总是会 Block 其他的 Ability,而不需要设置
- 对于每个实例会不同的 Ability,需要设置为实例化,否则不要设置为实例化,以节省资源,Ability 所需的实例数据事实上存在 AbilitySpec 中