Gameplay Ability System (GAS)

  • 캐릭터의 능력이나 속성 등을 관리할 수 있게 도와주는 프레임워크
  • 캐릭터의 능력 또는 스킬의 구현
    • Gameplay Ability
  • 액터의 수치화된 속성 관리
    • Atttibute
  • 액터에 특정한 효과(Effect) 적용
    • Gameplay Effect
  • 액터에 상태(state)를 Tag로 관리
    • Gameplay Tag
  • 시각/음향 효과를 생성
    • Gameplay Cue
  • 위 요소들의 Replication 및 Local Prediction

GAS를 사용하는 이유

  • 멀티플레이 게임 개발 시, 동기화 이슈를 커버할 수 있다
  • 버프 / 디버프, 캐릭터들의 attribute 관리, 상태 관리 등 게임 피쳐 기능
  • 캐릭터의 행동 단위로 적절
  • 컴포넌트가 주렁주렁일때보단 DA로 관리 가능
  • 언리얼 공식 지원 플러그인

Gameplay Ability System Component (ASC)

  • 액터가 GAS Framework와 연동하기 위해서 반드시 attach 하고 있어야 하는 Component

OwnerActor vs. Avatar Actor

  • OwnerActor는 실제로 ASC가 attach 된 owner actor
  • AvatarActor는 ASC의 물리적인 Representation
  • 이 둘은 같은 액터를 가리킬 수도, 다른 액터를 가리킬 수도 있다
    • 플레이어의 경우 OwnerActor가 PlayerState인 경우가 있을 수 있다고 한다
// 1. ASC가 Pawn에 부착된 경우
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);
	}

	//...
}

// 2. 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);
	}

	// ...
}

Replication

  • ASC는 GameplayEffect, GameplayTag, GameplayCue의 복제를 담당한다
  • Attribute는 AttributeSet에 의해 복제된다
    • Full
      • 싱글플레이어에서 사용
      • 모든 이펙트가 모든 클라에 복제
    • Mixed
      • 멀티플레이어, 플레이어에 의해 컨트롤되는 액터에서 사용
      • 이펙트는 owning client에게만 복제되며, 태그와 큐는 모두에게 복제
      • 이 때 OwnerActor의 Owner가 컨트롤러여야 한다
    • Minimal
      • 멀티플레이어, AI에 의해 컨트롤되는 액터에서 사용
      • 이펙트는 복제되지 않고 태그와 큐는 모두에게 복제
    Net Execution Policy 설명
    Local Only GameplayAbility는 소유한 클라이언트에서만 실행됩니다. 로컬에서만 시각적 효과를 변경하는 Ability에 유용할 수 있습니다. 싱글 플레이어 게임에서는 Server Only를 사용해야 합니다.
    Local Predicted Local Predicted GameplayAbility는 먼저 소유한 클라이언트에서 활성화되고, 그 후 서버에서 실행됩니다. 서버는 클라이언트가 예측한 내용을 수정합니다. 예측에 대한 자세한 내용은 Prediction을 참조해주세요.
    Server Only GameplayAbility는 오직 서버에서만 실행됩니다. Passive GameplayAbility는 보통 Server Only입니다. 싱글 플레이어 게임에서는 이 방식을 사용해야 합니다.
    Server Initiated Server Initiated GameplayAbility는 먼저 서버에서 활성화되고, 그 후 소유한 클라이언트에서 실행됩니다. 개인적으로는 이 방법은 많이 사용하지 않았습니다.

Gameplay Tag

  • GameplayTagManager에 등록되며, 계층구조를 가진다
  • 사실 GAS와는 독립적으로 사용 가능한 기능
// 태그 레퍼런스 얻기
FGameplayTag::RequestGameplayTag(FName("Your.GameplayTag.Name"))

Attribute

  • Smart floating point numbers
  • Current + Base values
    • Base Value는 Effect가 끝나고 나면 복구 용도의 기준 수치
    • Current Value는 Effect가 적용된 용도로 사용하는 현재 수치

Attribute Set

  • 하나의 ASC는 여러 개의 Attribute Set을 가질 수 있따
    • 굉장히 낮은 오버헤드로 부담없이 사용 가능
    • 다만 같은 종류의 Attribute Set을 여러 개 넣는 것은 불가능
// Current value의 변경이 일어나기 전 반응할 수 있는 함수
PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue);

//  BaseValue가 변경된 이후 트리거
PostGameplayEffectExecute(const FGameplayEffectModCallbackData & Data);

Gameplay Effect

  • Attribute의 수치 변경 혹은 Tag 적용 등 가능
  • Duration Policy에 따라 적용되는 Value가 다르다
    • Instance -> BaseValue
    • Duration / Infinite -> CurrentValue

post_thumbnail

Gameplay Cue

  • FX 효과들의 Wrapper 역할
  • 보통은 Client Only의 audio, visual 효과를 실행한다

Local Gameplay Cue

  • Gameplay Cue의 트리거 함수는 기본적으로 멀티캐스트 RPC
  • 많은 RPC 호출을 예방하기 위해 Local Gameplay Cue를 사용할 수 있다
  • 주로 프로젝타일 효과, 근접 충돌 효과, animattion montage 출력 등의 상황에서 사용된다
UFUNCTION(BlueprintCallable, Category = "GameplayCue", Meta = (AutoCreateRefTerm = "GameplayCueParameters", GameplayTagFilter = "GameplayCue"))
void ExecuteGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

UFUNCTION(BlueprintCallable, Category = "GameplayCue", Meta = (AutoCreateRefTerm = "GameplayCueParameters", GameplayTagFilter = "GameplayCue"))
void AddGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

UFUNCTION(BlueprintCallable, Category = "GameplayCue", Meta = (AutoCreateRefTerm = "GameplayCueParameters", GameplayTagFilter = "GameplayCue"))
void RemoveGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

void UPAAbilitySystemComponent::ExecuteGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters & GameplayCueParameters)
{
	UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::Executed, 
    
    GameplayCueParameters);
}

void UPAAbilitySystemComponent::AddGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters & GameplayCueParameters)
{
	UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::OnActive, GameplayCueParameters);
	UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::WhileActive, GameplayCueParameters);
}

void UPAAbilitySystemComponent::RemoveGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters & GameplayCueParameters)
{
	UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::Removed, GameplayCueParameters);
}
  • GameplayCue가 Local로 추가된다면, 반드시 Local에서 Remove 처리도 되어야 한다

출처