Unreal: Gameplay Ability System(GAS) 기초
Gameplay Ability System (GAS)
Gameplay Ability System (GAS)는 언리얼 엔진에서 제공하는 플러그인 기반의 프레임워크로, RPG나 MOBA같은 장르의 복잡한 캐릭터 스탯, 스킬, 아이템, 상호작용등을 데이터 중심으로 손쉽게 정의/관리하기 위해 도입됬다. 가령 여러 속성의 공격 방식을 구현한다고 했을 때, 일반공격, 불 공격, 얼음공격등에 대해 서로 다른 데미지 판정이나 로직이 들어가야 한다면, 거기에 젖어있거나 독에 중독된것같은 상태들에 따른 특수한 판정까지 겹쳐진다면 이를 감당하기 위해 코드는 무한정 비대해질 것이다. GAS는 이를 컴포넌트화 시키고 Tag로 관리하는 방식을 통해 관리 가능한 프로젝트로 재구성해준다.
몇개의 컴포넌트로 구성되어 있으며 기초적으론 GA를 중추로 삼아 캐릭터의 기능을 컴포넌트화 시킨다. 이상적으로 구성됬다면, 모든 기능들은 GAS를 바탕으로 각 컴포넌트에서 정의, 실행될 것이고, 캐릭터는 거의 그 컴포넌트를 묶어놓는 래퍼 클래스 수준에 가깝게 변할 것이다. 이는 캐릭터 클래스의 비대화를 막을 뿐 더러 유연한 추가, 수정, 재구성등을 가능하게 만든다.
사용하기 위해서는 먼저 Gameplay Abilities 플러그인을 프로젝트에 포함시켜주어야 한다. 기본 기능으로 편입되지는 않았어서 수동으로 추가해주어야 한다. 그리고 Build.cs에서 3개의 모듈을 추가해주게되면 사용할 준비가 완료된다.
1
2
3
4
//build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "Niagara",
//GAS를 위해 추가해야 되는 모듈 3개
"GameplayAbilities", "GameplayTags", "GameplayTasks" });
추가가 끝나면 GAS의 핵심 컴포넌트인 AbilitySystemComponent를 사용할 캐릭터에 추가해줘야 한다. 후에 나올 Ability 부여, Attribute관리, GameplayEffect 적용, GameplayTag 관리등의 기능을 하는 컴포넌트이다. 먼저 AbilitySystemInterface를 캐릭터에 상속하고, UAbilitySystemComponent를 반환할 GetAbilitySystemComponent()를 구현한다. PossessedBy()에서는 InitAbilityActorInfo()를 통해 컴포넌트의 정보를 업데이트 한다. PossessedBy() 대신 BeginPlay()나 다른 시작에 해당하는 생명 주기 이벤트에서도 가능하며, 이 시점과 업데이트 하는 정보는 싱글플레이/멀티플레이, 게임 구조와 환경에 따라 달라질 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//TestCharacterBase.h
#include "AbilitySystemInterface.h"
#include "AbilitySystemComponent.h"
//...
UCLASS()
class TESTPROJECT_API ATestCharacterBase : public ACharacter, public IAbilitySystemInterface
{
//...
public:
ATestCharacterBase();
//AbilitySystemComponent 접근
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
protected:
//AbilitySystemComponent 를 담을 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
virtual void PossessedBy(AController* NewController) override;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//TestCharacterBase.cpp
//Sets default values
ATestCharacterBase::ATestCharacterBase()
{
PrimaryActorTick.bCanEverTick = true;
//선언은 다른 컴포넌트처럼
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
}
void ATestCharacterBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
//누가 소유자고 누가 캐릭터인지
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
Gameplay Ability (GA)
가장 기본이 되는 부분으로, 게임의 스킬/기술에 해당하는 클래스이다. 스킬의 실질적인 기능을 여기서 정의하게 된다. GameplayAbility 클래스를 상속받아 구현할 수 있다. ActorInfo같은 주어지는 매개변수에서 호출시 필요한 정보들을 불러와 활용할 수 있으며, Abilities/Tasks/ 경로에 AbilityTask_로 시작하는 많은 기능들을 불러와 활용할 수 있다. BP의 그래프에서 스킬의 기능을 구현해도 되지만, 스킬의 기능은 C++에서 구현하고, 태그나 설정의 관리만 블루프린트에서 하는 것을 추천한다. 기능은 코드로 구현되어있어야 충돌, 수정에 용이하고, 태그를 에디터에서 설정하니 컴파일/빌드에 소모되는 시간을 줄여줄 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
//GA_Flash.h
#include "Abilities/GameplayAbility.h"
#include "GA_Flash.generated.h"
UCLASS()
class TESTPROJECT_API UGA_Flash : public UGameplayAbility
{
GENERATED_BODY()
public:
//어빌리티 실행시 동작 내용
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
};
1
2
3
4
5
6
//GA_Flash.cpp
void UGA_Flash::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
//구현 내용 적기
}
Gameplay Ability를 상속하는 BP를 만들면 Tags에서 여러 태그를 관리할 수 있다. 
- AssetTags
- GA를 나타낼 태그이다.
- Cancel Abilities with Tag
- 이 GA가 실행될 때 실행을 중단할 태그들이다.
- ex) 달리기 도중 워프를 시전하면 달리기를 멈추고 워프를 시전한다.
- Block Abilities with Tag
- 이 GA가 실행될 때 실행되는 동안 막을 태그들이다. 동시실행이 안되도록 막는다.
- ex) 마법을 위한 캐스팅 중에 다른 기술을 사용할 수 없다.
- Activation Owned Tags
- 이 GA가 실행되는 동안만 지니다 끝나면서 사라질 태그이다. GA의 실행 상태를 나타내는데 사용될 수 있다.
- Activation Requried Tags
- GA가 실행되기 위해 사전에 가져야 할 태그이다.
- Activation Blocked Tags
- 있으면 GA를 실행시킬 수 없는 태그이다.
- ex) 스턴, 슬로우같은 CC기 상태에선 조작을 할 수 없다.
- Source Required Tags
- 실행되기 위해서 GA 사용 주체가 가져야하는 태그이다.
- 무기, 아이템등 출처를 검사하는 용도로 보다 많이 쓰인다는 점에서 캐릭터나 아바타등의 호출 주체를 검사하는 Activation Requried Tags와는 다르다.
- 다만 그 구현방식 자체는 꼭 Source Required Tags를 사용하지 않는 형태로도 가능한 경우들은 있다.
- ex) 무기에 따라 공격이 달라지는 구조, 무기에 모듈/파츠를 이용해 강화되는 특수 스킬, 아이템에 의한 패시브 스킬 적용
- 다만 경우에 따라 별도의 GA로 분리하거나 AttributeSet/GameplayEffect를 쓰는 등 다른 방법이 더 좋을 수 있다.
- 실행되기 위해서 GA 사용 주체가 가져야하는 태그이다.
- Source Blocked Tags
- GA 사용 주체가 가지면 실행을 막는 태그이다.
- Target Requried Tags
- 실행되기 위해서 GA 대상이 가져야하는 태그이다. 항상 대상이 있는 것은 아니기에 공격같이 대상이 있을 때 유효한 설정이다.
- Target Blocked Tags
- GA 대상이 가지면 실행을 막는 태그이다.
고급설정에 추가적인 설정도 있다.
- Replication Policy
- 복제할지의 여부로, 멀티플레이어 게임을 만들 때의 Replicated와 유사한 Gameplay Ability의 정책이다.
- Instancing Policy
- GA를 인스턴스화 해서 반복해 사용하게 할지의 여부. 각 액터마다 혹은 각 실행마다 반복해서 사용하게 하거나 하지 않을 수도 있다.
- NonInstanced의 경우 재사용을 통해 실행을 보다 가볍게 만들어주는 효과가 있지만, 네트워크상의 위험이나 문제 탓에 권장되진 않는다.
- ex) 함수마다 카운트/스택을 공유해야 하는경우, 혹은 반대로 서로간의 공유/간섭이 없어야 하는 경우 명시적인 지정의 역할
- Net Execution Policy
- GA가 클라이언트와 서버중 어디에서 시작/승인될지를 결정한다.
- 로컬만 실행할지
- 서버만 실행할지
- 서버에서 실행하되 로컬에도 있으면 같이 실행할지
- 로컬에서 실행하되 서버에도 있으면 같이 실행할지
- GA가 클라이언트와 서버중 어디에서 시작/승인될지를 결정한다.
- Net Security Policy
- 서버가 GA실행에 어디까지 제어할지 범위를 결정한다.
- 상관없는지
- 실행만 신경쓸지
- 종료만 신경쓸지
- 둘 다 서버에서 제어할지
- 서버가 GA실행에 어디까지 제어할지 범위를 결정한다.