ASC (Ability System Component)는 어떤 액터에도 붙어서 구현될 수 있다. 따라서 폰 또는 캐릭터 클래스에 이를 구현할지, 또는 PlayerState, PlayerController 등에 이를 구현할지는 개발자의 마음이라고 할 수 있다.
플레이어가 조종하는 캐릭터의 경우 폰의 사망 및 파괴 시에도 ASC가 유지되도록 구현하는 편이 낫기 때문에 Character 액터보다는 PlayerState에 ASC 및 AttributeSet을 추가하는 것이 좋을 것이다. PlayerController도 하나의 후보가 될 수 있지만 PlayerController는 다른 클라이언트에게 복제되지 않는다는 특징이 있다. 한편 PlayerState는 다른 모든 클라이언트에 복제되기 때문에, ASC를 구현하기 좋은 장소가 된다.
AI 컨트롤 캐릭터의 경우에는, 플레이어가 직접 조종할 필요도 없고 부활 등을 신경쓰지 않아도 되기 때문에 단순히 Character 클래스에 구현해도 문제가 없다.
// AuraPlayerState.h
class AURA_API AAuraPlayerState : public APlayerState
{
GENERATED_BODY()
protected:
UPROPERTY()
TObjectPtr<class UAbilitySystemComponent> AbilitySystemComponent;
UPROPERTY()
TObjectPtr<class UAttributeSet> AttributeSet;
};
...
// AuraPlayerState.cpp
AAuraPlayerState::AAuraPlayerState()
{
NetUpdateFrequency = 100.f;
AbilitySystemComponent = CreateDefaultSubobject<UAuraAbilitySystemComponent>("AbilitySystemComponent");
AbilitySystemComponent->SetIsReplicated(true);
AttributeSet = CreateDefaultSubobject<UAuraAttributeSet>("AttributeSet");
}
protected 영역에 ASC와 AS의 TObjectPtr
을 만들고 생성자에서 CreateDefaultSubobject
를 통해 컴포넌트를 생성한다.
Gameplay effect를 아직 자세히 다루진 않지만 이것에 대한 replication에 관한 고려 역시 필요하다.
위 그림과 같이 세 가지 종류의 replication mode가 존재한다.
플레이어가 컨트롤하는 캐릭터의 경우 Mixed
모드를 선택하여 owning client, 즉 자신에게 replicated 되게 하는 것이 좋다. 한편 AI 컨트롤 캐릭터의 경우 Minimal
모드를 선택해도 문제가 없는 것이, AI의 GameplayEffect는 다른 클라이언트에서 얻을 필요가 없기 때문이다.
따라서 우리는 플레이어 컨트롤 캐릭터는 Mixed
, AI 컨트롤 캐릭터는 Minimal
로 구현하기로 하고, 각각 PlayerState 클래스와 Character 클래스에 AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
와 같이 지정해 줄 수 있다.
Mixed
replication mode를 사용할 때 중요한 것은 OwnerActor
의 owner는 반드시 Controller여야 한다는 것이다. 폰의 경우 PossessedBy()
함수에서 자동으로 세팅되며, PlayerState의 경우에도 마찬가지로 State의 owner가 자동으로 controller로 설정된다.
다만 OwnerActor가 PlayerState가 아닌 경우에 Mixed replication 모드를 사용한다면, OwnerActor에서 직접 SetOwner()
를 호출해서 직접 컨트롤러를 세팅해 주어야 한다.
ASC는 이 컴포넌트가 구현된 주인 액터, 즉 Owner가 누군지 항상 알 수 있어야 한다. 또한 AvatarActor
라는 개념 또한 존재한다. Avatar Actor란 이 ASC가 구현되어 실질적으로 represent 되어 눈에 보이는 액터를 의미한다.
AI 컨트롤 캐릭터의 경우 OwnerActor와 AvatarActor 모두 캐릭터 그 자체가 되는 단순한 구조를 가지고 있다. 한편 플레이어 컨트롤의 경우 우리는 PlayerState에 ASC를 구현하기로 했다. 이 경우에는 OwnerActor는 PlayerState 액터가 되겠지만 그것이 실제로 구현되어 월드에 존재하는 액터는 Character 액터이므로 이 경우 AvatarActor는 Character 액터가 된다.
게임이 진행되기 전 AvatarActor와 OwnerActor를 지정하는 과정이 필요하다. 이 작업은 UAbilitySystemComponent::InitAbilityActorInfo(AActor* OwnerActor, AActor* AvatarActor)
함수에서 진행할 수 있다.
그렇다면 이제 논점은 어디서 이 함수를 호출해야 하는가이다.
InitAbilityActorInfo
는 possession 이후에 호출되어야 한다. 달리 말하면 폰의 컨트롤러가 세팅된 이후에 호출되어야 한다.
PossessedBy
함수는 컨트롤러가 폰을 점유한 이후에 실행되므로 적절하다. 그러나 이 함수는 서버에서만 동작한다. 그래서 클라이언트에서는 다른 함수를 사용해야 한다. AcknowledgePossession
함수는 possession이 성공적으로 이루어졌고, 즉 폰이 valid controller를 가졌음을 확인할 수 있기 때문에 클라이언트에서는 이 함수에서 호출하는 것이 좋다.
서버에서는 마찬가지로 PossessedBy
함수에서 구현할 수 있다. 클라이언트에서는 PlayerState가 변경될 때에 OnRep_PlayerState
가 자동으로 호출되므로 이 RepNotify 함수에서 호출할 수 있다.
AI 컨트롤 캐릭터의 경우에는 캐릭터 클래스에 ASC를 구현하므로, 서버와 클라이언트 모두에서 BeginPlay
를 호출할 수 있고, InitAbilityActorInfo
함수를 호출하기에 충분히 적당한 장소가 된다.