Ability System Component
AbilitySystemComponent(ASC)
是GAS的核心,这个组件用于处理与GAS系统的交互,所有使用GameplayAbililty,包含Attribute,接收GameplayEffect的Actor都应该附加与ASC,由ASC来管理并同步这些对象。
ASC
附加的Actor可以作为ASC的OwnerActor,ASC的物理代表Actor可以称为AvatarActor(我这里理解为这个Actor的包含具体的模型)。OwnerActor和AvatarActor可以是同一个Actor,比如MOBA中游戏的小兵,也可以是不同的Actor,比如MOBA中玩家控制的英雄(玩家可以选择不同的英雄,因此AvatarActor会不断变化)。在MOBA游戏中玩家的ASC,OwnerActor是PlayerState,AvatarActor则是英雄的Character类,如果Actor需要重生,并且重生需要持续Attribute的效果或者GamplayEffect的效果,那么OwnerActor的位置应该选取PlayerState。
**NOTE:**如果选取PlayerState作为OwnerActor,我们需要注意提高PlayerState的NetUpdateFrequency
,其默认值很低,因此客户端改变时会有延迟和卡顿,需要启用Adaptive Network Update Frequency。
OwnerActor需要继承并实现IAbilitySystemInterface
,如果AvatarActor和OwnerActor不是相同的Actor,那么AvatarActor也应该继承实现IAbilitySystemInterface
,这个接口有一个必须重写的函数UAbilitySystemComponent* GetAbilitySystemComponent() const
,其返回一个指向ASC的指针,ASC通过该借口函数与GAS系统内部进行交互。
ASC
在FGameplayAbilitySpecContainer ActivatableAbility
中保存GameplayAbility
. 当你想遍历ActivatableAbility.Items
时, 确保在循环体之上添加ABILITYLIST_SCOPE_LOCK();
来锁定列表以防其改变(比如移除一个Ability). 每个域中的ABILITYLIST_SCOPE_LOCK();
会增加AbilityScopeLockCount
, 之后出域时会减量. 不要尝试在ABILITYLIST_SCOPE_LOCK();
域中移除某个Ability(Ability删除函数会在内部检查AbilityScopeLockCount
以防在列表锁定时移除Ability).
同步模式
ASC
定义了三种不同的同步模式用于同步GameplayEffect
, GameplayTag
和GameplayCue
- Full
, Mixed
和Minimal
. Attribute
由其AttributeSet
同步.
同步模式 | 何时使用 | 描述 |
---|---|---|
Full | 单人 | 所有GameplayEffect 都同步到客户端. |
Mixed | 多人, 玩家控制的Actor | GameplayEffect 只同步到其所属客户端, 只有GameplayTag 和GameplayCue 同步到所有客户端. |
Minimal | 多人, AI控制的Actor | GameplayEffect 从不同步到任何客户端, 只有GameplayTag 和GameplayCue 同步到所有客户端. |
设置和初始化
ASC一般在OwnerActor的构造函数中创建,并且声明为Replicated(多人游戏中),在代码中,玩家控制的角色的ASC我们存放于PlayerState中,因此代码如下:
AFusePlayerState::AFusePlayerState()
{
//ASC初始化
FuseAbilitySystemComponent = CreateDefaultSubobject<UFuseAbilitySystemComponent>(TEXT("FuseAbilitySystemComponent"));
FuseAbilitySystemComponent->SetIsReplicated(true);
}
ASC
不仅需要在OwnerActor进行初始化,还需要在AvatarActor中进行初始化,并且在多人游戏中,不仅在服务器上进行初始化,我们还需要在所有客户端同样进行初始化。
假如ASC
的OwnerAcrtor与AvatarActor相同,都是 Pawn中,那么服务器的初始化应该放在Pawn
的PossessBy()
函数中,而客户端则是应该在PlayerController
的AcknowledgePossession()
中初始化,代码如下:
//服务器更新
void AFuseCharacterBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
AFusePlayerState* PlayerState = GetPlayerState<AFusePlayerState>();
if (FuseAbilitySystemComponent)
{
FuseAbilitySystemComponent->InitAbilityActorInfo(this, this);
}
SetOwner(NewController);
}
//客户端更新
void APAPlayerControllerBase::AcknowledgePossession(APawn* P)
{
Super::AcknowledgePossession(P);
APACharacterBase* CharacterBase = Cast<APACharacterBase>(P);
if (CharacterBase)
{
CharacterBase->GetAbilitySystemComponent()->InitAbilityActorInfo(CharacterBase, CharacterBase);
}
//...
}
而如果OwnerActor是PlayerState,AvatarActor是Character,那么服务器初始化应该在Pawn的PossessdBy()
函数,而客户端则是应该在Character的OnRep_PlayerState()函数中初始化。
//由于ASC是存在于PlayerState的,因此我们需要从PlayerState获取ASC,服务端执行
void AFuseCharacterBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
AFusePlayerState* PlayerState = GetPlayerState<AFusePlayerState>();
if (PlayerState)
{
FuseAbilitySystemComponent = Cast<UFuseAbilitySystemComponent>(PlayerState->GetAbilitySystemComponent());
PlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(PlayerState, this);
UE_LOG(LogTemp, Log, TEXT("Server: ASC Init Compeleted!"));
}
}
void AFuseCharacterBase::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
AFusePlayerState* PlayerState = GetPlayerState<AFusePlayerState>();
if (PlayerState)
{
FuseAbilitySystemComponent = Cast<UFuseAbilitySystemComponent>(PlayerState->GetAbilitySystemComponent());
PlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(PlayerState, this);
UE_LOG(LogTemp, Log, TEXT("Client: ASC Init Compeleted!"));
}
}
在游戏界面中,我们需要使用蓝图重载GameMode和PlayerState,并在游戏地图中设置重载的GameMode,然后使用聆听服务器开启两个客户端。
结果如下:
可以看到初始化在服务器进行了一次,在客户端进行了两次,由于是聆听服务器,服务器上开始的客户端还会再初始化一次。因此ASC在多人游戏下能够正常初始化。
角色没有设置摄像机和弹簧臂,所以视角很奇怪= =。