02 - Ability System Component
本文主要说明了UE5中Gameplay技能系统(Gameplay Ability System, GAS)中有关 Ability System Component(ASC)的相关内容。
概述
技能系统组件(UAbilitySystemComponent,简称ASC)是Actor和GAS之间的桥梁。Actor需要拥有自己的技能系统组件,或访问PlayerState或Pawn上的技能系统组件,才能与GAS进行互动。
要让Actor参与到GAS中,需要让它重写实现IAbilitySystemInterface接口中的GetAbilitySystemComponent()函数。这是因为在部分情况下会使用其他Actor的ASC,如果不实现该函数就会让获取流程变复杂。
基础知识
Owner Actor和Avatar Actor
ASC的所有者有Owner Actor和Avatar Actor之分:
- Owner Actor:是ASC的逻辑所有者,也就是实际拥有该ASC的Actor;
- Avatar Actor:是ASC的物理表现载体,也就是实际执行Gameplay Ability等特性的Actor;
这两个Actor将ASC的所有权和表现层进行分离,方便开发者进行相关开发。
通过执行InitAbilityActorInfo()函数以初始化这两个Actor,一般来说,执行该函数的时机如下:
- 对于玩家控制的角色,在设置好Controller的所有权后:
- ASC的Owner Actor是Pawn:服务端在
PossessedBy()中执行;客户端在AcknowledgePossession()中执行; - ASC的Owner Actor是PlayerState:服务端在
PossessedBy()中执行;客户端在OnRep_PlayerState()中执行;
- ASC的Owner Actor是Pawn:服务端在
- 对于AI控制的角色,如果ASC的Owner Actor是Pawn,则在
BeginPlay()中执行即可;
例如在Aura项目中,玩家的ASC在AAuraPlayerState类中被初始化,在AAuraCharacter::InitAbilitySystem()中通过IAbilitySystemInterface接口中的GetAbilitySystemComponent()函数被获取,并设置这两个Actor:
AuraCharacter.cpp点击展开/折叠代码
void AAuraCharacter::InitAbilitySystem()
{
AAuraPlayerState* AuraPlayerState = GetPlayerState<AAuraPlayerState>();
checkf(AuraPlayerState, TEXT("Can't get AuraPlayerState !!!"));
AuraPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(AuraPlayerState, this);
// ...
}
网络同步
要想启用ASC的网络同步,需要在初始化ASC时执行SetIsReplicated(true)。
Replication Mode
执行SetIsReplicated(true)后,还需要执行SetReplicationMode()以设置ASC的网络同步模式。ASC的网络同步模式主要有如下三种:
- Full:适用于极少数玩家场景(单机/局域网), Gameplay Effects会被同步到所有客户端中;
- Mixed:适用于多人游戏, 玩家控制的Actor, Gameplay Effects只会被同步到玩家拥有的客户端中,Gameplay Cues 和 Gameplay Tags 会被同步到所有客户端中;
- Minimal:适用于多人游戏, AI控制的Actor, Gameplay Effects不会被同步, Gameplay Cues 和 Gameplay Tags 会被同步到所有客户端中;
此外,对于Mixed模式,它的OwnerActor必须是Controller。
常用操作
以下内容是关于ASC的常用操作,笔者会根据自己使用情况进行更新。
GE相关
OnGameplayEffectAppliedDelegate
ASC通过定义若干委托以允许我们绑定GE施加时的回调函数:
AbilitySystemComponent.h点击展开/折叠代码
/** Delegate for when an effect is applied */
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);
/** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
/** Called on server whenever a GE is applied to someone else. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget;
/** Called on both client and server whenever a duration based GE is added (E.g., instant GEs do not trigger this). */
FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf;
/** Called on server whenever a periodic GE executes on self */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf;
/** Called on server whenever a periodic GE executes on target */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget;
我们需要根据情况和GE的类型选择对应的委托,然后创建相关回调函数并绑定。
例如在Aura项目中,玩家拾取道具时会触发GE,此时GE会通过它绑定的GameplayTag通知UI弹出对应效果提示框,因此我们需要绑定OnGameplayEffectAppliedDelegateToSelf委托:
AuraAbilitySystemComponent.cpp点击展开/折叠代码
void UAuraAbilitySystemComponent::OnAbilityActorInfoSet()
{
OnGameplayEffectAppliedDelegateToSelf.AddUObject(this, &UAuraAbilitySystemComponent::OnGEApplied);
}
// 这里有后缀是因为该函数为Client RPC
void UAuraAbilitySystemComponent::OnGEApplied_Implementation(UAbilitySystemComponent* InASC, const FGameplayEffectSpec& InGESpec,
FActiveGameplayEffectHandle ActiveGEHandle)
{
FGameplayTagContainer TagContainer;
InGESpec.GetAllAssetTags(TagContainer);
// 广播获取的Tag到UIWidgetController
OnGetGEAssetTag.Broadcast(TagContainer);
}
GameplayTag相关
RegisterGameplayTagEvent
ASC通过RegisterGameplayTagEvent()函数以允许我们绑定GameplayTag变化时的回调函数,该函数的定义如下:
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnGameplayEffectTagCountChanged, const FGameplayTag, int32);
/** Allow events to be registered for specific gameplay tags being added or removed */
FOnGameplayEffectTagCountChanged& RegisterGameplayTagEvent(FGameplayTag Tag, EGameplayTagEventType::Type EventType=EGameplayTagEventType::NewOrRemoved);
其中,各参数的含义如下:
- Tag:需要监听的GameplayTag;
- EventType:需要监听的事件类型,主要有以下几种:
- NewOrRemoved:当GameplayTag被添加或移除时触发;
- AnyCountChange:当GameplayTag的计数发生变化时触发;
例如在Aura项目中,敌人受击时会被添加一个GameplayTag,通过该回调可以实现敌人受击相关逻辑:
AuraEnemy.cpp点击展开/折叠代码
void AAuraEnemy::BeginPlay()
{
// ...
// 绑定特定GameplayTag被添加/移除的回调
AuraAbilitySystemComponent->RegisterGameplayTagEvent(AuraGameplayTags::GE::HitReact.GetTag(), EGameplayTagEventType::NewOrRemoved)
.AddUObject(this, &AAuraEnemy::OnGEHitReactTagChanged);
// ...
}
void AAuraEnemy::OnGEHitReactTagChanged(const FGameplayTag CallbackTag, int32 NewCount)
{
bDoHitReacting = NewCount > 0;
GetCharacterMovement()->MaxWalkSpeed = bDoHitReacting ? 0.f : DefaultWalkSpeed;
}