
Ability System Component(이하 ASC)는 GAS의 핵심 컴포넌트입니다. ASC는 GAS의 GameplayTag, GameplayAbilities, Attributes등 다양한 기능을 이용하기 해당 기능이 필요한 Actor에 반드시 존재해야하기 때문입니다. 위와같은 기능과 객체들은 모두 ASC 내부에 존재하며 ASC에 의해 관리되고 replicated(복제)되며 예외로 Attributes의 경우는 Attribute Set을 통해 replicated(복제)됩니다.
단어가 생소해 복잡하다면 Actor(게임캐릭터, 적 몬스터 등등)가 GAS의 기능을 이용하기 위해서는 ASC가 부착되어 있어야하며 Attributes라는 기능을 위해 추가적으로 Attribute Set이라는 것도 필요하다 라고 이해만 해도 무방합니다.

ASC가 부착된 Actor를 OwnerActor라고 부르며 ASC를 표현하는 게임상에서 실제적으로 표현되는 Actor를 AvatarActor라고 부릅니다. 대부분의 경우 OwnerActor와 AvatarActor가 동일한 대상이지만, 서로 다른 대상일수도 있습니다. 이해를 돕기위해서 가장 유명한 MOBA게임인 LeagueOfLeagend(이하 LOL)를 예로 들어보겠습니다. LOL에서 등장하는 미니언의 경우 OwnerActor와 AvatarActor가 같은 대상인 경우이고 플레이어가 직접 조종하는 챔피언의 경우 다른 대상인 경우입니다. 챔피언의 경우 어째서 두 Actor가 별도로 존재하느냐? 
위 그림을 보면 왼쪽의 경우 Actor가 소멸하면 해당 Actor가 소유하고 있던 AttributeSet과 ASC가 모두 같이 소멸하게 됩니다. (예를들어 챔피언이 죽으면 가지고있던 아이템, 레벨등 이 함께 소멸하는 상황입니다.) 때문에 Actor가 리스폰되면 이전의 정보는 모두 소멸하고 기본값과 기본능력으로 다시 시작하게 됩니다. 때문에 Actor의 소멸과 관계없이 정보를 지속적으로 유지하고싶다면 APlayerState 클래스를 만들어 ASC와 AttributeSet를 가지고 있도록 하고APlayerState(Owner Actor) 클래스에 Pawn(Avatar Actor)을 연결시키는 방식으로 구현하면 Attribute와 ASC값을 계속 유지시킬수있습니다.
멀티플레이어 게임의 경우 서버와 클라이언트간의 데이터의 일관성 유지가 중요합니다. 이를 위해 서버와 클라이언트간의 데이터 복제는 필수적인 요소입니다.
AbilitySystemComponent::SetIsReplicated()는 ASC 클래스에서 해당 컴포넌트가 네트워크를 통해 복제가 가능한지 불가능한지 여부를 결정하는 함수입니다. SetIsReplicated(true)를 호출하면 컴포넌트의 복제가 가능한 상태가 되고 SetIsReplicated(false)를 호출하면 복제가 비활성화되는 형식입니다. AbilitySystemComponent::SetReplicationMode() 함수는 ASC의 복제 모드를 설정하는 함수입니다. 설정된 모드에 따라서 해당 ASC는 클라이언트와 서버간에 어떤 데이터를 복제할지 결정됩니다.
Replication Mode
복제모드의 경우 열거형으로 정의되어 있는데 세가지의 옵션이있습니다.
- EGameplayEffectReplicationMode::Minimal
GameplayTags와GameplayCues가 모두에게 복제되고GameplayEffects는 복제되지않습니다. 멀티플레이 환경에서 AI Actors(미니언)에게 권장됩니다.
- EGameplayEffectReplicationMode::Mixed
최소한의 데이터GameplayTags와GameplayCues가 모두에게 복제되고GameplayEffects는 소유한 클라이언트에게만 복제됩니다. 멀티플레이 환경에서 플레이어가 제어하는 Actor에게 권장됩니다.
- EGameplayEffectReplicationMode::Full
모든 정보를 복제합니다 모든GameplayeEffect가 모든 클라이언트에게 복제됩니다. 싱글플레이 환경에서 권장됩니다.
GameplayEffect,GameplayTags,GameplayCues 의 자세한 정보는 글의 내용상 다른 포스트에서 서술하겠지만, 어떤 상황에서 어떤 Mode를 사용하는지만 간략하게 파악했다면 무방합니다.
PlayerState를 사용하는 경우 NetUpdateFrequency값을 조정해 주어야 합니다. APlayerState의 NetUpdateFrequency값은 1로 설정되어있는데 이는 초당 1번의 업데이트를 한다는 뜻으로 보통의 경우 딜레이가 발생하게 됩니다. 이에 반해 AActor,APawn,APlayerController클래스들은 NetUpdateFrequency값이 100으로 높게 설정되어있습니다. 이 때문에 PlayerState에 저장되어있는 Attribute(HP,MP 등)정보들은 업데이트 될 때 클라이언트에서 지연현상이 발생하게됩니다. 반면 Player State에 연결된 Controller나 Character에 직접적으로 저장하게 되면 지연현상은 발생하지 않게될것입니다. 하지만 우리는 Attribute의 정보를 통해 상호작용하기 때문에 Player State의 NetUpdateFrequency을 조정해야합니다.
// TestPlayerState.cpp
ATestPlayerState::ATestPlayerState()
{
NetUpdateFrequency = 100.f;
}
위와같은 방법으로 간단히 조정이 가능합니다. 하지만 이 방법의 경우 게임에 소수의 플레이어만 있는 경우 문제가 되지않지만 많은 플레이어가 연결되어있는 경우 좋지않은 방법일수있습니다. PlayerState의 경우 서버의 모든 플레이어에게 복제되며 불필요한 네트워크 트래픽을 발생시킬수있기때문입니다. 이에대해 알아보는 글은 다음에 적도록 하겠습니다.
ASC를 소유하고싶은 클래스를 선언합니다. 이때 해당 클래스는 AActor class 에게 상속받거나 또는 하위클래스 (APawn class ,ACharacter class 등등)로부터 상속받아야합니다. 또한 IAbilitySystemInterface을 상속받아야 하는데 이는 필수사항은 아니지만 강력히 권장됩니다.//AGDPlayerState.h
class AGDPlayerState: public APlayerState, public IAbilitySystemInterface
CreateDefaultSubobject 함수를 통해 AblitySystemComponent를 구성하고 SetIsReplicated 값을 true로 설정합니다.AGDPlayerState::AGDPlayerState()
{
// PlayerState에 ASC를 생성, 복제활성화 , 모드설정
AbilitySystemComponent = CreateDefaultSubobject<UGDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent-> SetIsReplicated(true);
AbilitySystemComponent-> SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
// PlayerState에 AttributeSet(이하AS) AS의 경우 아직 설명은 하지 못했지만 Attribute는 AS를 통해 관리되기 때문에 AS또한 생성해주어야 합니다.
AttributeSet = CreateDefaultCreateDefaultSubobject<UGDAttributeSet>("AttributeSet");
NetUpdateFrequency = 100.f;
//...
}
GetAbilitySystemComponent()라는 오버라이드 해야하는 함수가 있는데, 이 함수를 AbilitySystemComponent를 반환하도록 해당 Actor 내에서 오버라이드 해줍니다.//AGDPlayerState.h
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
UPROPERTY()
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
UPROPERTY()
TObjectPtr<UAttributeSet> AttributeSet;
//AGDPlayerState.cpp
//GetAbilitySystemComponent() 정의
UAbilitySystemComponent* AGDPlayerState::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
4.이제 OwnerActor와 AvatarActor를 설정해주어야 하는 단계입니다. 아래의 경우 플레이어가 직접 제어하는 Character의 초기화 모습입니다.
//서버
void AGDHeroCharacter::PossessedBy(AController * NewController)
{
//NewContoroller가 AGDHeroCharacter클래스를 제어를 시작할떄
Super::PossessedBy(NewController);
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
check(PS)
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
//ASC의 OwnerActor를 PS(AGDPlayerState)로 AvatarActor를 this(AGDHeroCharacter)로 설정
PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this);
//...
}
// 클라이언트
void AGDHeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
check(PS)
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
AbilitySystemComponent->InitAbilityActorInfo(PS, this);
// ...
}
```
5.AI 캐릭터일 경우 매우 간단합니다. OwnerActor와 AvatarActor가 자기자신이기 때문에 this로 설정해주면 됩니다.
```cpp
void AEnemy::Beginplay()
{
Super::Beginplay();
AbilitySystemComponent->InitAbilityActorInfo(this,this);
}
https://www.udemy.com/course/unreal-engine-5-gas-top-down-rpg/?couponCode=OF83024D
https://dev.epicgames.com/documentation/ko-kr/unreal-engine/unreal-engine-5-4-documentation