AbilitySystemComponent
(ASC
)是整个 GAS 系统中的核心。它本质上是一个 UActorComponent
(UAbilitySystemComponent
),负责处理技能系统中涉及到的所有交互。任意 Actor
,只要它想要使用技能 [GameplayAbilities
],拥有着属性 Attributes
,或者接收效果 GameplayEffects
,都必须附着一个 ASC
组件。所有这些对象都存在于、被管理于以及被复制于 ASC
(其中 Attributes
是个特例,AttributeSet
负责其复制)。开发者们可以自行根据需要拓展 ASC
。
拥有 ASC
的 Actor
也被称为是 ASC
的 OwnerActor
。 ASC
实际上作用的 Actor
被称为是AvatarActor
。OwnerActor
和 AvatarActor
可以是同一个 Actor
,比如 MOBA 游戏中的一个简单的 AI 小兵。它们也可以是不同的 Actor
,比如 MOBA 游戏中玩家控制的英雄角色,其中 OwnerActor
是 PlayerState
而 AvatarActor
则是 Character
类。大部分的 Actors
都会把 ASC
放在他们自己身上。但是某些情况下比如你的Actor
需要使用重生机制,并且在重生后仍然保留死亡之前的 Attributes
或者是 GameplayEffects
(例如 MOBA 游戏中的英雄),那么 ASC
的理想位置就是在 PlayerState
上。
注意: 如果你的 ASC
在 PlayerState
上,那么你可能会需要去提高 PlayerState
的网络更新频率(NetUpdateFrequency
)。原本 PlayerState
里该值在默认情况下是很低的,可能会导致 Attributes
或者 GameplayTags
在客户端上同步的延迟。如果是这样的话,确保激活 Adaptive Network Update Frequency
,这也是Fortnite(堡垒之夜)中的解决办法。
如果 OwnerActor
和 AvatarActor
是不同的 Actor
,那么两者都应该去实现接口IAbilitySystemInterface
。这个接口只有一个需要重写的方法 UAbilitySystemComponent* GetAbilitySystemComponent() const
,会返回一个指针指向它的 ASC
组件。在系统内部,ASC
互相之间就是通过寻找这个接口函数来进行交互。
ASC
存有当前处于激活状态的 GameplayEffects
,具体就位于 FActiveGameplayEffectsContainer ActiveGameplayEffects
。
ASC
存有它所赋予的 Gameplay Abilities
,具体就位于 FGameplayAbilitySpecContainer ActivatableAbilities
。无论何时,当你想要遍历ActivatableAbilities.Items
,请一定在你的循环之前添加 ABILITYLIST_SCOPE_LOCK();
语句,以锁定其中的内容以防对其中内容的修改(意外删除某项技能)。ABILITYLIST_SCOPE_LOCK();
本质上是在作用范围内增加 AbilityScopeLockCount
然后当离开作用范围时相应的减少。不要尝试在 ABILITYLIST_SCOPE_LOCK();
的作用范围内移除某项技能(清除技能的函数会在内部检查AbilityScopeLockCount
,从而防止在内容被锁定的情况下移除技能)。
4.1.1 复制模式 - Replication Mode
ASC
定义了三种不同的复制模式用以复制 GameplayEffects
、GameplayTags
以及 GameplayCues
,分别是Full
、Mixed
以及Minimal
。Attributes
是由他们所在的AttributeSet
来进行复制的。
复制模式 | 使用情景 | 描述 |
---|---|---|
Full | 单人 | GameplayEffect 会被复制到所有客户端 |
Mixed | 多人和玩家控制的 Actors | GameplayEffects 仅被复制到拥有者客户端。只有GameplayTags 和GameplayCues 会被复制到所有客户端 |
Minimal | 多人和AI控制的 Actors | GameplayEffects 不会复制到任何客户端。只有GameplayTags 和GameplayCues 会被复制到所有客户端 |
注意: Mixed
复制模式要求 OwnerActor
的 Owner
必须是 Controller
。PlayerState
的默认 Owner
是 Controller
,但是Character
不是。如果使用 Mixed
复制模式时其 OwnerActor
不是 PlayerState
,那么你需要调用 OwnerActor
上的 SetOwner()
并传递一个有效的Controller
进去。
从4.24版本开始,PossessedBy()
会将 Pawn
的拥有者设置为新的 Controller
。
4.1.2 设置和初始化 - Setup and Initialization
ASC
通常是在 OwnerActor
的构造器中进行构造,并且显式得标记为可复制(replicated)。这一步必须在 C++ 内完成。
AGDPlayerState::AGDPlayerState()
{
// Create ability system component, and set it to be explicitly replicated
AbilitySystemComponent = CreateDefaultSubobject<UGDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);
//...
}
ASC
需要在服务器和客户端都完成初始化,其中两个重要的初始化参数为 OwnerActor
和 AvatarActor
。通常时机是在 Pawn
的Controller
设置之后(在 possession 之后)。单人游戏只需要关心服务器路径。
对于玩家控制的角色( ASC
存在于 Pawn
之上),我通常是在 Pawn
的 PossessedBy()
方法中完成 ASC
在服务器的初始化,在 PlayerController
的 AcknowledgePossession()
方法中完成 ASC
在客户端的初始化。
void APACharacterBase::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
if (AbilitySystemComponent)
{
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
// ASC MixedMode replication requires that the ASC Owner's Owner be the Controller.
SetOwner(NewController);
}
void APAPlayerControllerBase::AcknowledgePossession(APawn* P)
{
Super::AcknowledgePossession(P);
APACharacterBase* CharacterBase = Cast<APACharacterBase>(P);
if (CharacterBase)
{
CharacterBase->GetAbilitySystemComponent()->InitAbilityActorInfo(CharacterBase, CharacterBase);
}
//...
}
对于玩家控制的角色(ASC
存在于 PlayerState
上),我通常是在 Pawn
的 PossessedBy()
方法中完成 ASC
在服务器的初始化,在 Pawn
的 OnRep_PlayerState()
方法中完成 ASC
在客户端的初始化。这确保了 PlayerState
在客户端上已经存在。
// Server only
void AGDHeroCharacter::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
if (PS)
{
// Set the ASC on the Server. Clients do this in OnRep_PlayerState()
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
// AI won't have PlayerControllers so we can init again here just to be sure. No harm in initing twice for heroes that have PlayerControllers.
PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this);
}
//...
}
// Client only
void AGDHeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
if (PS)
{
// Set the ASC for clients. Server does this in PossessedBy.
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
// Init ASC Actor Info for clients. Server will init its ASC when it possesses a new Actor.
AbilitySystemComponent->InitAbilityActorInfo(PS, this);
}
// ...
}
如果你得到如下的错误反馈 LogAbilitySystem: Warning: Can't activate LocalOnly or LocalPredicted ability %s when not local!
,那说明你并没有在客户端上初始化你的 ASC
。