GAS(Game Ability System)
- 복잡한 스킬과 상태 효과를 쉽게 관리하고 구현할 수 있도록 돕는 프레임워크
- 능력(Ability), 효과(Effect), 속성(Attribute)등의 개념을 중심으로 작동합니다.
- 클라이언트와 서버 간의 동기화 작업을 자동으로 처리해주기 때문에 네트워크 게임 환경에서도 안정적인 성능을 유지할 수 있습니다.
- GAS는 모듈식 구조로 확장성과 재사용성이 뛰어납니다.
- 능력 발동 중 다른 능력이 겹치지 않도록 처리하거나 복잡한 효과를 관리할 수 있습니다.
Ability
- 게임 내 캐릭터가 사용할 수 있는 동작이나 스킬
- 시작, 실행, 종료라는 기본 단계를 가지고 있으며 조건부로 발동될 수 있다.
Attribute
- 캐릭터의 상태를 나타내는 값으로 예를 들어 체력, 마나, 스태미나같은 스탯이 있습니다.
- 속성은 능력이나 효과의 영향을 받으며 게임 내 캐릭터의 능력 발동과 직접적으로 연결됩니다.
Effect
- 능력이 발동될 때 일시적 또는 지속적인 변화를 캐릭터에게 부여하는 요소
- 예를 들어 공격력 증가, 체력 회복, 상태 이상 등
- 효과는 속성에 직접적인 변화를 주어 특정 시간 동안 유지되거나 조건에 따라 종료됩니다.
- 스킬을 Skill_Attack_Fireball 처럼 태그를 이용해서 관리할 수 있습니다.
- 능력 발동 조건이나 제한, 효과 적용 조건 등에 쓰임
- CanAttack, Stunned 같은 태그를 사용하여 특정 능력을 발동 시킬 수 있는지를 관리할 수 있습니다.
GAS를 사용하기 위해 Gameplay Abilites plugin 추가
- Edit -> Plugin
- bulid.cs에 GameplayTas, GameplayTasks, GameplayAbilities 모듈 추가
AbilitySystemComponent를 상속받은 클래스 생성
- 스킬코드를 관리하는 매니저같은 역할
- 꼭 플레이어의 스킬만이 아닌 몬스터 스킬, 버프도 관리 대상이 될 수 있다.
- 해당 컴포넌트를 몬스터와 플레이어의 상위 클래스인 캐릭터 클래스에서 관리하는 것이 좋다.
#include "AbilitySystemInterface"
class My_API AMyCharacter: public Character, public IAbilitySystemInterface
{
public:
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
virtual void InitAbilitySystem();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<class UMyAbilitySystemComponent> AbilitySystemComponent;
}
UAbilitySystemComponent* AR1Character::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
- 게임 어빌리티 시스템 컴포넌트를 들고있다는 힌트를 주기 위해 IAbilitySystemInterface를 상속받는다
- AbilitySystemComponent는 몬스터는 몬스터의 클래스에서 생성해도 되지만 Player는 PlayerState 클래스에서 들고 있도록 하는 것이 좋다
- PlayerState는 네트워크를 통해 동기화되기 때문에 게임 플레이 중에도 정보를 유지할 수 있고, 캐릭터를 바꾸거나 죽더라도 어빌리티 시스템을 지속적으로 유지할 수 있도록 합니다.
몬스터에서 AbilitySystemComponent 만들고 초기화하기
AMyMonster::AMyMonster()
{
GetMesh()->SetRelativeLocationAndRotation(FVector(0.f, 0.f, -88.f), FRotator(0.f, -90.f, 0.f));
AbilitySystemComponent = CreateDefaultSubobject<UMyAbilitySystemComponent>("AbilitySystemComponent");
}
void AMyMonster::BeginPlay()
{
Super::BeginPlay();
InitAbilitySystem();
}
void AMyMonster::InitAbilitySystem()
{
Super::InitAbilitySystem();
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
- InitAbilityActorInfo에는 어빌리티 시스템을 가지고있는 Owner와 실제로 컨트롤 되는 Avatar에 대한 정보를 전달해야한다.
- 몬스터의 경우 실제로 어빌리티 시스템도 가지고 있고 컨트롤 되고 있는 아바타이기 때문에 둘 다 this를 전달하면 된다.
PlayerState Class를 상속받은 클래스 생성
public:
AMyPlayerState(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
public:
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<class UMyAbilitySystemComponent> AbilitySystemComponent;
AMyPlayerState::AMyPlayerState(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
AbilitySystemComponent = CreateDefaultSubobject<UMyAbilitySystemComponent>("AbilitySystemComponent");
}
UAbilitySystemComponent* AMyPlayerState::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
- AbilitySystemComponent의 원본은 PlyaerState에서 만들어서 플레이어에서 참조할 수 있도록 한다.
GameMode에서 만들어준 PlayerState 클래스 적용
- 별도로 Player와 PlayerState를 연결해주지 않아도 된다.
Player에 AbilitySystemComponent 추가
protected:
virtual void PossessedBy(AController* NewController) override;
virtual void InitAbilitySystem() override;
void AMyPlayer::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
InitAbilitySystem();
}
void AMyPlayer::InitAbilitySystem()
{
Super::InitAbilitySystem();
if(AMyPlayerState* PS = GetPlayerState<AMyPlayerState>())
{
AbilitySystemComponent = Cast<UMyAbilitySystemComponent>(PS->GetAbilitySystemComponent());
AbilitySystemComponent->InitAbilityActorInfo(PS, this);
}
}
- 플레이어가 PossessedBy 될 때 생성하는 이유는 플레이어가 소유된 시점에 AbilitySystemComponent가 생성되어 어빌리티와 상태 관련 기능들을 일관되게 처리하고 네트워크 상의 동기화를 보장하기 위함입니다.