오늘은 GAS을 활용하여 캐릭터의 입력 처리를 구현해 보려고 합니다.
순서는 다음과 같습니다.
1.플레이어 캐릭터의 GAS 설정 방법
2. GAS에 관련된 입력을 범용적으로 처리하는 방식
3. GA에서 AT(Ability Task)를 필요한 이유 및 사용방법
4. GAS의 디버깅 방법
구현 결과

플레이어 캐릭터의 GAS 설정 방법
GAS를 통해 플레이어에게 GA(Gameplay Ability)를 부여하려면, 먼저 Ability System Component(ASC)를 설치해야 한다.
ASC를 어느 클래스에 배치할지는 게임의 구조에 따라 달라진다.
일반적으로 Character에 넣겠지만, 나의 경우 플레이어 캐릭터가 도중에 교체되는 게임 구조를 목표로 하고 있기 때문에,
캐릭터가 교체되면 기존 Character Instance는 소멸하기 때문에, 그 안에 ASC를 두면 어빌리티와 어트리뷰트 정보가 함께 사라지는 문제가 발생한다.
이를 방지하기 위해 PlayerState에 ASC를 배치하기로 결정했다.
PlayerState는 캐릭터가 교체되더라도 유지되므로, 플레이어의 능력 정보를 안정적으로 보존할 수 있다.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "AbilitySystemInterface.h"
#include "ABGASPlayerState.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLEGAS_API AABGASPlayerState :
public APlayerState,
public IAbilitySystemInterface
{
GENERATED_BODY()
public:
AABGASPlayerState();
// IAbilitySystemInterface을(를) 통해 상속됨
UAbilitySystemComponent* GetAbilitySystemComponent() const override;
protected:
UPROPERTY(EditAnywhere, Category = GAS)
TObjectPtr<class UAbilitySystemComponent> ASC;
};
해당 코드는 PlayerState.h 내용으로
AbilitySystemInterface의 Interface를 상속하여 ASC의 Get 함수를 구현해주었다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "Player/ABGASPlayerState.h"
#include "GameplayAbilitySpec.h"
#include "AbilitySystemComponent.h"
AABGASPlayerState::AABGASPlayerState()
{
ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC"));
// ASC->SetIsReplicated(true); 멀티플레이라면,
}
UAbilitySystemComponent* AABGASPlayerState::GetAbilitySystemComponent() const
{
return ASC;
}
해당 코드는 PlayerState.cpp 내용으로 생성자에서 ASC를 생성해주고 Get 함수에서는 ASC를 Return 하도록 구현하였다.
다음으로는 Player Character에 해당 ASC를 넘겨주는 작업이다.
void AABGASCharacterPlayer::PossessedBy(AController* NewController) // 서버에서만 호출됨
{
Super::PossessedBy(NewController);
AABGASPlayerState* GASPS = GetPlayerState<AABGASPlayerState>(); // 블루프린트에서 설정해줌
if(GASPS != NULL)
{
ASC = GASPS->GetAbilitySystemComponent();
ASC->InitAbilityActorInfo(GASPS, this);
/*for (const auto& StartAbility : StartAbilities)
{
FGameplayAbilitySpec StartSpec(StartAbility);
ASC->GiveAbility(StartSpec);
}*/
for (const auto& StartInputAbility : StartInputAbilities)
{
FGameplayAbilitySpec StartSpec(StartInputAbility.Value);
StartSpec.InputID = StartInputAbility.Key;
ASC->GiveAbility(StartSpec);
}
SetupGASInputComponent();
APlayerController* PlayerController = CastChecked<APlayerController>(NewController);
PlayerController->ConsoleCommand(TEXT("showdebug abilitysystem"));
}
}
플레이어 클래스의 PoossedBy 부분에서 GameMode에서 설정한 Playerstate를 가져와 주었다.
왜 PoossedBy 에서 설정해주었냐면 OnPossedBy가 호출되는 시점은 바로 이런 순서를 가지기 때문이다.
PlayerState 생성 -> Controller가 Character를 Possess -> OnPossessedBy() 호출 ( 이 시점에는 이미 PlayerState가 존재)
하기 때문이다.
여기까지가 플레이어에게 GAS 시스템을 설정해 준 과정이다.
GAS에 관련된 입력을 범용적으로 처리하는 방식
다음으로는 GAS를 이용한 입력을 구현해보겠습니다.

일반적으로 캐릭터에 InputSystem에 InputAction을 맵핑하는거와 크게 다를게 없지만,
여기서 주의깊게 보아야할 점으로는 바로 뒤에 있는 매개변수 InputID 입니다.
해당 매개변수를 통해

해당 Ability가 어떤 Ability인지 구분해 낼 수 있습니다.
앞서 설명한 InputID 기반 구조로 어떻게 동작하는지 확인하기 위해, 해당 캐릭터 클래스를 부모 클래스로 하는 Blueprint를 생성하여 어빌리티와 입력을 직접 바인딩해보면 다음과 같습니다.

여기까지가 GAS에 관련된 입력을 범용적으로 처리하는 방식입니다.
GA에서 AT(Ability Task)를 필요한 이유 및 사용방법
다음으로는 AT(Ability Task)를 활용하여 GA를 다루는 작업을 구현해보겠습니다.
우선 구현에 앞서 "AT를 왜 사용해야 하는가?"에 대해 먼저 고민해보았습니다.
이를 이해하기 좋은 상황이 있습니다. 바로 "플레이어가 공격하는 동안 이동을 막고, 공격이 끝나면 다시 이동을 허용하고 싶은 경우" 입니다.
GA만으로 이를 구현한다면, GA가 실행될 때 ActivateAbility 함수가 호출되어 공격 몽타주를 재생하면서 동시에 CharacterMovementComponent를 Move_None으로 설정하여 이동을 막을 수 있습니다.
문제는 그 다음입니다. 몽타주가 끝나는 시점을 GA가 스스로 알 수 없습니다. GAS 입장에서 몽타주는 단순히 애니메이션 시스템의 영역이기 때문에, 몽타주가 종료되었다고 해서 EndAbility가 자동으로 호출되지 않습니다.
이것이 바로 AT(Ability Task)가 필요한 이유입니다. AT가 몽타주를 감시하다가 종료 이벤트를 감지하면, 콜백을 통해 EndAbility를 명시적으로 호출해주는 구조가 필요합니다. 결국 AT는 GA가 스스로 알수 없는 비동기 타이밍을 감지하여 GA에게 알려주는 역할을 담당합니다.
이러한 구조를 가지고 있으면 해당 구조를 고려해서 구현한다면 다음과 같습니다.
void UABGA_Attack::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
AABCharacterBase* ABCharacter = CastChecked<AABCharacterBase>(ActorInfo->AvatarActor.Get());
ABCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
UAbilityTask_PlayMontageAndWait* PlayAttackTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, TEXT("PlayAttack"), ABCharacter->GetComboActionMontage());
PlayAttackTask->OnCompleted.AddDynamic(this, &UABGA_Attack::OnCompleteCallback);
PlayAttackTask->OnInterrupted.AddDynamic(this, &UABGA_Attack::OnInterruptedCallback);
PlayAttackTask->ReadyForActivation();
}
다음과 같이 AT에 몽타주가 끝났을 때는 OnCompleted에 바인딩된 OnCompleteCallBack()함수를 호출하며
몽타주가 중간에 강제 중단된다면 OnInterrupted에 바인딩된 OnInterruptedCallback()함수를 호출하여 상황을 알려줍니다.
ReadyForActivation() 함수는 AT를 실제로 실행 시작하는 함수로 AT는 Create해도 바로 실행되지 않기 때문에 반드시 이 함수를 사용해야 합니다.
이렇게 생성과 동시에 실행이 되지 않도록 분리한 이유는 만약 생성과 동시에 실행된다면, 콜백을 등록하기 전에 AT가 끝나버리는 경우가 생길 수 있기 때문입니다. 따라서 콜백을 모두 등록한 뒤 실행을 보장하귀 위해 명시적으로 시작 지점을 분리 한 것입니다.

void UABGA_Attack::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
AABCharacterBase* ABCharacter = CastChecked<AABCharacterBase>(ActorInfo->AvatarActor.Get());
ABCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
}
다음과 같이 몽타주 재생이 끝났다면 EndAbility를 호출하여 공격 중에는 이동하지 못하게 설정한 CharacterMovementComponent를 다시 MOVE_Walking으로 수정하여 공격 몽타주가 끝난 후에는 움직일 수 있게 처리합니다.
GAS의 디버깅 방법


Activation Owned Tags는 GA가 활성화 되어 있는 동안 ASC에 자동으로 부여되는 태그입니다.
동장 방식은 다음과 같습니다.
GA 활성화 -> Activation Owned Tags에 등록된 태그들이 ASC 에 자동 추가
추가된 Tag는 게임 플레이 중 "showdebug abilitysystem"을 입력하여 현재 Owned Tag 값을 확인하면서 디버깅이 가능합니다.

또는 다음과 같이 Debug 명령어를 매 게임플레이 순간마다 작성하기 힘드시다면 다음과 같은 방법 또한 있습니다.

지금까지 GAS을 활용한 캐릭터의 입력 처리 구현에 대해 살펴보았습니다.
'Unreal Project > GAS' 카테고리의 다른 글
| GAS( Game Ability System) 이란? ( 공부 일기 ) (0) | 2026.04.29 |
|---|