4.1 Ability System Component
AbilitySystemComponent
(ASC
)是GAS的核心。它是一个UActorComponent
(UAbilitySystemComponent
),负责处理与系统的所有交互。任何希望使用GameplayAbilities
、拥有Attributes
或接收GameplayEffects
的Actor
都必须附加一个ASC
。这些对象都存在于ASC
中,并由其管理和同步(除了Attributes
,它们由其AttributeSet
同步)。开发者通常会对其进行子类化,但这不是必须的。
附加了ASC
的Actor
被称为ASC
的OwnerActor
。ASC
的物理表现Actor
被称为AvatarActor
。OwnerActor
和AvatarActor
可以是同一个Actor
,例如在MOBA游戏中的简单AI小兵中。它们也可以是不同的Actor
,例如在MOBA游戏中由玩家控制的英雄,其中OwnerActor
是PlayerState
,而AvatarActor
是英雄的Character
类。大多数Actor
会在自身上附加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
在FActiveGameplayEffectsContainer ActiveGameplayEffects
中保存其当前活动的GameplayEffects
。
ASC
在FGameplayAbilitySpecContainer ActivatableAbilities
中保存其授予的Gameplay Abilities
。任何时候你计划迭代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
的Owner
设置为新的Controller
。
4.1.2 设置和初始化(Setup and Initialization)
ASCs
通常在OwnerActor
的构造函数中构建,并显式标记为可Replicated。这必须在C++中完成。
AGDPlayerState::AGDPlayerState()
{
// 创建能力系统组件,并设置为显式Replicated
AbilitySystemComponent = CreateDefaultSubobject<UGDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);
//...
}
ASC
需要在服务器和客户端上用其OwnerActor
和AvatarActor
进行初始化。你希望在Pawn
的Controller
设置之后(在占有之后)进行初始化。单人游戏只需关注服务器路径。
对于ASC
位于Pawn
上的玩家控制角色,我通常在服务器上在Pawn
的PossessedBy()
函数中初始化,并在客户端上在PlayerController
的AcknowledgePossession()
函数中初始化。
void APACharacterBase::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
if (AbilitySystemComponent)
{
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
// ASC MixedMode同步要求ASC Owner的Owner是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()
函数中初始化,并在客户端上在Pawn
的OnRep_PlayerState()
函数中初始化。这确保了客户端上存在PlayerState
。
// 仅服务器
void AGDHeroCharacter::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
if (PS)
{
// 在服务器上设置ASC。客户端在OnRep_PlayerState()中执行此操作。
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
// AI没有PlayerControllers,所以我们可以在这里再次初始化。对于有PlayerControllers的英雄,初始化两次没有害处。
PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this);
}
//...
}
// 仅客户端
void AGDHeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
if (PS)
{
// 为客户端设置ASC。服务器在PossessedBy中执行此操作。
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
// 为客户端初始化ASC Actor Info。服务器将在占有新Actor时初始化其ASC。
AbilitySystemComponent->InitAbilityActorInfo(PS, this);
}
// ...
}
如果你收到错误信息LogAbilitySystem: Warning: Can't activate LocalOnly or LocalPredicted ability %s when not local!
,那么你没有在客户端初始化你的ASC
。