https://github.com/tranek/GASDocumentation#concepts-as
단순히 위의 문서를 보고 적은 것입니다. 차라리 위의 문서를 봐주세요. 저 영어 못 합니다.
Gameplay Ability System를 공부해 보자
GAS
는 어빌리티(액터가 할 수 있는 것으로 액티브 스킬이나 패시브 스킬과 같은 능력)나 어트리뷰트(액터의 능력치)를 만들 수 있는 굉장히 유연한 프레임워크로 RPG나 MOBA류의 게임에서 사용하면 좋다. 어빌리티의 결과로 어트리뷰트를 변경시키거나 버프나 디버프, 쿨다운 타이머 혹은 마나 사용과 같은 자원 비용을 구현할 수 있다. 어빌리티의 파티클이나 사운드도 설정할 수 있다. 점프와 같은 간단한 어빌리티나 우리가 다른 게임에서 보던 복잡한 스킬도 구현이 가능토록 해준다.
GAS는 싱글 플레이와 더불어 멀티 플레이 게임에서도 사용가능하도록 다음 사항의 기본 솔루션을 제공한다.
- 캐릭터의 레벨을 베이스로한 어빌리티나 스킬 구현과 소모되는 자원, 쿨타임 (
GameplayAbilites
) - 액터에 속한 어트리뷰트(능력치)의 수치를 조정 (
Attributes
) - 액터에 상태 효과 적용 (
GameplayEffects
) - 액터에 GameplayTags 적용 (
GameplayTags
) - 파티클과 사운드 적용 (
GameplayCues
) - 위의 사항 들에 대한
Replication
GAS
는 C++
에서 설정해야 하지만 디자이너가 블루프린트
에서 생성할 수 있다.
4.1 Ability System Component
ASC
는 GAS
의 심장이다. ASC
는 UActorComponent
로 시스템으로 GAS
와의 모든 상호작용을 처리한다. GameplayAbilities
를 사용하거나 어트리뷰트
를 가지거나 GameplayEffects
를 적용받는 액터는 반드시 ASC
가 필요하다. ASC
의 안에서 앞의 요소들(GameplayAbilities
, GameplayEffects
)이 존재한다. (어트리뷰트는 예외로 AttributeSet
이 존재한다.)
ASC
가 있는 액터는 ASC
의 OwnerActor
이며 물리적인 표현을 담당하는 ASC
의 액터는 AvatarActor
이다. MOBA게임의 미니언처럼 간단한 경우엔 OwnerActor
와 AvatarActor
는 서로 같을 수도 있다. OwnerActor
는 PlayerState
, AvatarActor
는 Character
클래스로 MOBA의 영웅 캐릭터는 서로 다를 수 있다. 만약 우리 영웅이 죽고 리스폰되기까지 어트리뷰트
나 GameplayEffect
가 필요하다면 ASC
의 이상적인 위치는 PlayerState
이다.
Note: 만약 ASC
가 PlayerState
에 위치한다면 NetUpdateFrequency
를 높이는 것이 좋다. 기본적으로 NetUpdateFrequency
는 매우 낮은 값으로 설정되어 있어 어트리뷰트
나 GamplayTag
와 같은 변경 사항이 발생하기 전에 딜레이나 렉이 발생할 수 있다. 포트나이트가 이렇게 사용한다.
OwnerActor
와 AvatarActor
모두 액터와는 다르다. 모두 IAbilitySystemInterface
를 구현해야 한다. 이 인터페이스는 ASC
에 대한 포인터를 반환하는 GetAbilitySystemComponent()를
오버라이딩 해야한다. ASC
는 이 인터페이스 함수를 찾아서 시스템 내부에서 서로 상호작용한다.
ASC
는 현재 실행중인 GameplayEffects
를 FActiveGameplayEffectsContainer ActiveGameplayEffects
안에 보관한다.
ASC
는 GameplayEffects
에 부여된 Gameplay Abilites
를 FGameplayAbilitySpecContainer ActivatableAbilities
안에 보관한다. ActivatableAbilities.Items
를 반복할 때마다 어빌리티
의 제거로 인해 목록이 삭제되지 않도록 ABILITYLIST_SCOPE_LOCK();
을 루프위에 추가하는 걸 잊지마라. ABILITYLIST_SCOPE_LOCK();
은 AbilityScopeLockCount
를 증가시켰다가 스코프에서 벗어나면 감소한다. ABILITYLIST_SCOPE_LOCK();
의 스코프내에서 어빌리티
를 제거하려 하지 마라. (어빌리티 초기화 함수
는 내부적으로 AbilityScopeLockCount
를 확인하여 목록이 잠긴 경우 어빌리티
가 제거되지 않도록 한다.)
4.1.2 Setup and Initialization
ASC
는 OwnerActor
의 생성자에서 생성되며 명시적으로 Replicate
된다.(PlayerState
) 무조건 C++
에서 사용되어야 한다.
AGDPlayerState::AGDPlayerState()
{
// Create ability system component, and set it to be explicitly replicated
AbilitySystemComponent = CreateDefaultSubobject<UGDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);
//...
}
ASC
는 서버와 클라이언트 모두에서 OwnerActor
및 AvatarActor
를 사용하여 초기화해야한다. 싱글 플레이어 게임은 서버만 걱정하면 된다.
ASC
가 폰에 상주하는 플레이어 제어 캐릭터인 경우 일반적으로 폰의 PossessedBy()
함수의 서버에서 초기화하고 PlayerController
의 AcknowlegePosssession()
함수의 클라이언트에서 초기화한다.
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);
}
//...
}
ASC
가 PlayerState
에 상주하는 플레이어 제어 캐릭터인 경우 일반적으로 폰의 PossessedBy()
함수의 서버에서 초기화하고 폰의의 OnRep_PlayerState()
함수의 클라이언트에서 초기화한다. 이렇게 하면 클라이언트에 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);
}
// ...
}
4.2 Gameplay Tags
FGameplayTags
는 Parent.Child.Grandchild
처럼 계층적인 이름 형식으로 GameplayTagManager
에 등록되어 있다. GameplayTags
는 분류와 오브젝트의 상태를 설명하는데 굉장히 편리하다. 예를 들어 케릭터가 기절했다면 기절 시간동안 캐릭터에 State.Debuff.Stun
과 같은 GameplayTag
를 설정하면 된다.
기존에 booleans
나 enums
을 사용하던 것을 GameplayTags
로 교체하면 더 편하게 사용할 수 있다.
개체에 태그를 지정할 때, 일반적으로 개체에 태그가 있다면 해당 태그를 AbilitySystemComponent
에 추가하여 GameplayAbilitySystem
이 개체와 상호작용할 수 있도록 한다. UAbilitySystemComponent
는 IGameplayTagAssetInterface
를 구현하고 있어 자신(컴포넌트가 있는 액터)의 게임플레이 태그에 액세스할 수 있는 기능을 제공한다.
여러개의 GameplayTags
를 FGameplayTagContainer
에 저장할 수 있다. Tarray<FGameplayTag>
로 저장하는 것보다 FGaemplayTagContainers
가 더 효율적이다. Tags
는 FNames
이지만 FastReplication
이 프로젝트 세팅에서 가능하다면 FGameplayTagContainers
에 패킹하는게 더 효율적이다. Fast Replication
은 서버와 클라이언트 모두 같은 GameplayTags
를 가져야한다. 일반적으로 문제는 되지 않으므로 이 옵션을 사용하는게 좋다. GameplayTagContainers
는 TArray<FGameplayTag>
를 반환할 수 있다.
FGameplayTagCountContainer
에 저장된 GameplayTags
는 GameplayTag
의 인스턴스 수를 가지는 TagMap
을 가진다. FGameplayTagCountCoutainer
는 GameplayTags
를 여전히 가지고 있을 수도 있지만 TagMapCount
는 0이다. ASC
가 여전히 GameplayTag
를 가지고 있다면 우리가 디버깅시 이런 상황을 마주할 수 있을 것이다. HasTag()
또는 HasMatchingTag()
또는 이와 유사한 함수는 TagMapCount
를 확인하고 GameplayTag
가 없거나 TagMapCount
가 0인 경우 false를 반환한다.
GameplayTag
는 DefaultGameplayTags.ini
에서 미리 정의해야 한다. UE5는 개발자가 DefaultGameplayTags.ini
를 수동으로 편집할 필요 없이 GameplayTags
를 관리할 수 있도록 프로젝트 설정에 인터페이스를 제공한다.
GameplayTag
에디터는 GameplayTags
를 만들고 이름을 변경하고 참조하고 삭제할 수 있는 기능을 제공한다.
GameplayTags
는 프로젝트 세팅에서 설정할 수 있다.
GameplayTags
의 이름을 변경하면 기존의 GameplayTag
을 참조하는 에셋이 새로운 GameplayTag
로 리다이렉션된다. 추천하는 방법은 새로운 GameplayTag
를 만들고 기존의 GameplayTag
를 참조하는 에셋을 모두 새로운 GameplayTag
로 변경하고 기존의 GameplayTag
를 삭제하여 리다이렉션을 하지 않도록 하는 것이 좋다.
GameplayTags
에디터에는 Fast Replication
말고도 replicate
된 GameplayTags
를 최적화하는 기능도 있다.
GameplayTags
는 GameplayEffect
에서 추가된 경우 replicate
된다. ASC
는 LooseGameplayTags
를 추가할 수 있도록 해준다. LooseGameplayTag
는 replicate
되지 않고 수동으로 관리해야 한다. 샘플 포르젝트에서는 State.Dead
를 LooseGameplayTag
로 사용하여 체력이 0이 되었을 때, 클라이언트가 즉시 반응할 수 있도록 해준다. 리스폰 시 TagMapCount
가 다시 0으로 돌아간다. LooseGameplayTags
를 사용해야만 TagMapCount
를 수동적으로 조정할 수 있다. TagMapCount
를 수동적으로 조정하려면 UAbilitySystemComponent;:AddLooseGameplayTag()
와 UAbilitySystemComponent::RemoveLooseGameplayTag()
을 사용하는게 좋다.
C++
에서 GameplayTag
에 대한 참조 얻기:
FGameplayTag::RequestGameplayTag(FName("Your.GameplayTag.Name"))
부모 또는 자녀의 GameplayTag
를 얻는 것과 같은 고급 GameplayTag
조작을 위해서는 GameplayTagManager
가 제공하는 기능을 살펴보는게 좋다. GameplayTagManager
에 접근하기 위해서는 GameplayTagManager.h
를 include
하고 UGameplayTagManager::Get().FunctionName
을 호출한다. GameplayTagManager
는 트리의 형태로 (parent, child, etc) GameplayTags
를 저장되어 직접적인 string 조작이나 비교보다 빠르다.
GameplayTag
와 GameplayTagContainers
는 특수한 UPROPERY
를 사용할 수 있다. Meta = (Categories = "GameplayCue")
는 블루프린트에서 GameplayCue
부모 태그를 가진 GameplayTags
만 보일 수 있도록 필터처리를 해준다. GameplayTag
나 GameplayTagContainer
변수가 GameplayCues
에서만 사용되어야 할 때 유용하다.
함수에서 GameplayTag
파라미터 필터를 원한다면 UFUNCTION에서 Meta = (GameplayTagFilter = "GameplayCue")
를 사용하면 된다. 함수에서 GameplayTagContainer
파라미터는 필터를 상요할 수 없다. 사용하고 싶다면 엔진에서 수정하면 된다.
4.2.1 Responding to Changes in Gameplay Tags
ASC
는 GameplayTags
가 추가되거나 제거되었을 경우 델리게이트로 EGameplayTagEventType
을 지원한다. GameplayTag
가 추가되거나 삭제되었을 때 혹은 GameplayTag
의 TagMapCount
가 변경되었을 때 호출된다.
AbilitySystemComponent->RegisterGameplayTagEvent(FGameplayTag::RequestGameplayTag(FName("State.Debuff.Stun")), EGameplayTagEventType::NewOrRemoved).AddUObject(this, &AGDPlayerState::StunTagChanged);
콜백 함수로 GameplayTag
와 TagCount
가 인자로 들어간다.
virtual void StunTagChanged(const FGameplayTag CallbackTag, int32 NewCount);
'Unreal Engine > Gameplay Ability System' 카테고리의 다른 글
[UE] Gameplay Ability System -6- Ability Task (0) | 2023.05.30 |
---|---|
[UE] Gameplay Ability System -5- Gameplay Ability (0) | 2023.05.30 |
[UE] Gameplay Ability System -4- Gameplay Effects (0) | 2023.05.28 |
[UE] Gameplay Ability System -3- Attribute Set (0) | 2023.05.26 |
[UE] Gameplay Ability System -2- Attributes (0) | 2023.05.24 |