Skip to content

언리얼 GAS 입문 가이드

**Gameplay Ability System(GAS)**은 언리얼 엔진이 제공하는 고수준 프레임워크로, RPG, 액션, MOBA 등 복잡한 스킬·버프·상태 이상 시스템을 체계적으로 구현할 수 있게 해줍니다. 포트나이트, 파라곤 등 에픽 게임즈 자체 타이틀에서 검증된 프레임워크입니다.

GAS는 처음 접하면 구성 요소가 많아 진입 장벽이 느껴지지만, 핵심 개념 5가지만 이해하면 구조가 명확해집니다.

구성 요소약어역할
AbilitySystemComponentASCGAS의 중심 컴포넌트, 모든 기능을 관리
GameplayAbilityGA스킬·액션의 실제 동작 정의
GameplayEffectGE스탯 변경, 버프/디버프 적용
GameplayAttributeAttribute체력, 마나, 공격력 등 수치 데이터
GameplayTagTag상태·조건 분류용 계층 태그

언리얼 엔진 에디터에서 편집 > 플러그인 메뉴를 열고 Gameplay Abilities를 검색하여 활성화합니다. 활성화 후 에디터를 재시작합니다.

GAS를 사용할 모듈의 Build.cs 파일에 의존성을 추가합니다.

YourProject.Build.cs
public class YourProject : ModuleRules
{
public YourProject(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"GameplayAbilities", // GAS 핵심 모듈
"GameplayTags", // GameplayTag 모듈
"GameplayTasks" // GameplayTask 모듈
});
}
}

AbilitySystemComponent는 GAS의 중심입니다. 이 컴포넌트를 가진 Actor만 GAS 기능을 사용할 수 있습니다. ASC는 다음을 관리합니다.

  • 부여된 어빌리티 목록 및 활성화 상태
  • 적용된 GameplayEffect(버프/디버프) 목록
  • GameplayAttribute 값
  • GameplayTag 현황

일반적으로 플레이어 캐릭터는 Character 또는 PlayerState에 ASC를 붙입니다. AI나 단순 오브젝트는 Character에 바로 붙이는 경우가 많습니다.

MyCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h" // IAbilitySystemInterface 구현 필수
#include "MyCharacter.generated.h"
UCLASS()
class YOURPROJECT_API AMyCharacter : public ACharacter, public IAbilitySystemInterface
{
GENERATED_BODY()
public:
AMyCharacter();
// IAbilitySystemInterface 순수 가상 함수 구현
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GAS")
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
};
MyCharacter.cpp
#include "MyCharacter.h"
#include "AbilitySystemComponent.h"
AMyCharacter::AMyCharacter()
{
// ASC 생성 - 기본 UAbilitySystemComponent 사용 가능, 서브클래스도 OK
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true); // 멀티플레이어라면 반드시 설정
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
}
UAbilitySystemComponent* AMyCharacter::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}

IAbilitySystemInterface를 반드시 구현해야 하는 이유: GAS 내부에서 Actor가 ASC를 가지고 있는지 확인할 때 이 인터페이스를 통해 ASC를 가져옵니다. 구현하지 않으면 다른 Actor가 이 캐릭터의 ASC를 참조할 수 없습니다.


Attribute는 별도의 UAttributeSet 서브클래스에서 정의합니다. 각 수치는 FGameplayAttributeData 타입으로 선언하고, GAS에서 제공하는 매크로로 접근자를 자동 생성합니다.

MyAttributeSet.h
#pragma once
#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "MyAttributeSet.generated.h"
// GAS 접근자 매크로 - Getter, Setter, InitMetaAttribute 자동 생성
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
UCLASS()
class YOURPROJECT_API UMyAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
UMyAttributeSet();
// 최대 체력 (MaxHealth를 먼저 선언하여 Health 초기화 시 참조 가능하게)
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_MaxHealth)
FGameplayAttributeData MaxHealth;
ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxHealth)
// 현재 체력
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Health)
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(UMyAttributeSet, Health)
// 마나
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Mana)
FGameplayAttributeData Mana;
ATTRIBUTE_ACCESSORS(UMyAttributeSet, Mana)
// 공격력
UPROPERTY(BlueprintReadOnly, Category = "Attributes")
FGameplayAttributeData AttackPower;
ATTRIBUTE_ACCESSORS(UMyAttributeSet, AttackPower)
// Attribute 변경 전 처리 (클램핑 등)
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
// GameplayEffect 적용 후 처리
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
UFUNCTION()
virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
UFUNCTION()
virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
UFUNCTION()
virtual void OnRep_Mana(const FGameplayAttributeData& OldMana);
};
MyAttributeSet.cpp
#include "MyAttributeSet.h"
#include "Net/UnrealNetwork.h"
#include "GameplayEffectExtension.h"
UMyAttributeSet::UMyAttributeSet()
{
// 기본값은 GameplayEffect(InitialStats GE)로 초기화하는 것을 권장
}
void UMyAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
// 체력은 0 ~ MaxHealth 범위로 클램핑
if (Attribute == GetHealthAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
}
if (Attribute == GetManaAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, 100.f);
}
}
void UMyAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
// 체력이 0 이하가 되면 사망 처리
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
if (GetHealth() <= 0.f)
{
// 사망 이벤트 발생 - Character에서 처리
// (예: AbilitySystemComponent->AddLooseGameplayTag(DeadTag))
}
}
}
void UMyAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Health, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Mana, COND_None, REPNOTIFY_Always);
}
void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth);
}
void UMyAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, MaxHealth, OldMaxHealth);
}
void UMyAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Mana, OldMana);
}

ASC는 AttributeSet을 자동으로 감지합니다. Character 생성자에서 AttributeSet을 CreateDefaultSubobject로 생성하면 됩니다.

// MyCharacter.cpp - 생성자 내부
MyAttributeSet = CreateDefaultSubobject<UMyAttributeSet>(TEXT("MyAttributeSet"));

GameplayEffect는 Attribute를 변경하거나 GameplayTag를 부여하는 데이터 에셋입니다. 코드로도 만들 수 있지만, 에디터에서 Blueprint 기반 데이터 에셋으로 만드는 것이 일반적입니다.

GE의 지속 방식은 세 가지입니다.

Duration Policy설명대표 사용처
Instant즉시 적용 후 종료피해, 즉시 회복
Duration지정 시간 동안 유지일시 버프/디버프
Infinite수동으로 제거할 때까지 유지장비 스탯, 패시브
// 데미지 GE 적용 예시
void AMyCharacter::ApplyDamage(AMyCharacter* Target, float DamageAmount)
{
if (!Target || !Target->GetAbilitySystemComponent())
{
return;
}
// GE 스펙 생성 - 에디터에서 만든 GE 클래스를 참조
FGameplayEffectSpecHandle SpecHandle = AbilitySystemComponent->MakeOutgoingSpec(
DamageEffectClass, // TSubclassOf<UGameplayEffect>
1.f, // Level
AbilitySystemComponent->MakeEffectContext()
);
if (SpecHandle.IsValid())
{
// SetByCaller로 동적 수치 전달 (GE 에셋에서 SetByCaller 설정 필요)
SpecHandle.Data->SetSetByCallerMagnitude(
FGameplayTag::RequestGameplayTag(FName("Data.Damage")),
DamageAmount
);
// 대상 ASC에 GE 적용
Target->GetAbilitySystemComponent()->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get());
}
}

GameplayAbility는 스킬 하나의 모든 동작을 정의하는 클래스입니다. 발동 조건, 쿨다운, 마나 비용, 실제 효과 적용까지 모두 GA 안에서 관리합니다.

GA_FireBolt.h
#pragma once
#include "CoreMinimal.h"
#include "Abilities/GameplayAbility.h"
#include "GA_FireBolt.generated.h"
UCLASS()
class YOURPROJECT_API UGA_FireBolt : public UGameplayAbility
{
GENERATED_BODY()
public:
UGA_FireBolt();
// 어빌리티 발동 시 호출
virtual void ActivateAbility(
const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
const FGameplayEventData* TriggerEventData
) override;
// 어빌리티 종료 시 호출 (반드시 EndAbility 호출 필요)
virtual void EndAbility(
const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
bool bReplicateEndAbility,
bool bWasCancelled
) override;
protected:
// 발사할 프로젝타일 클래스 (에디터에서 설정)
UPROPERTY(EditDefaultsOnly, Category = "FireBolt")
TSubclassOf<AActor> ProjectileClass;
// 피해 GameplayEffect 클래스 (에디터에서 설정)
UPROPERTY(EditDefaultsOnly, Category = "FireBolt")
TSubclassOf<UGameplayEffect> DamageEffectClass;
};
GA_FireBolt.cpp
#include "GA_FireBolt.h"
#include "AbilitySystemComponent.h"
#include "GameFramework/Character.h"
UGA_FireBolt::UGA_FireBolt()
{
// 서버에서만 인스턴스 생성 (기본값, 단순 어빌리티에 적합)
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerExecution;
// 이 어빌리티가 활성화되어 있을 때 부여될 태그 (중복 발동 방지 등에 활용)
ActivationOwnedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.FireBolt.Active")));
// 이 태그를 가진 상태에서는 발동 불가 (예: 스턴 상태)
ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("State.Stunned")));
}
void UGA_FireBolt::ActivateAbility(
const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
const FGameplayEventData* TriggerEventData)
{
// 비용(마나) 및 쿨다운 커밋 - 실패 시 어빌리티 취소
if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
{
EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
return;
}
// 발동 캐릭터 가져오기
ACharacter* Character = Cast<ACharacter>(ActorInfo->AvatarActor.Get());
if (!Character)
{
EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
return;
}
// 프로젝타일 스폰
if (ProjectileClass)
{
FVector SpawnLocation = Character->GetActorLocation() + Character->GetActorForwardVector() * 100.f;
FRotator SpawnRotation = Character->GetControlRotation();
FActorSpawnParameters SpawnParams;
SpawnParams.Instigator = Character;
SpawnParams.Owner = Character;
Character->GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnLocation, SpawnRotation, SpawnParams);
}
// 어빌리티 종료
EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
}
void UGA_FireBolt::EndAbility(
const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo,
bool bReplicateEndAbility,
bool bWasCancelled)
{
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
// 캐릭터에게 어빌리티 부여 (BeginPlay 또는 초기화 시점에 호출)
void AMyCharacter::GiveDefaultAbilities()
{
if (!HasAuthority() || !AbilitySystemComponent)
{
return;
}
for (TSubclassOf<UGameplayAbility>& AbilityClass : DefaultAbilities)
{
AbilitySystemComponent->GiveAbility(
FGameplayAbilitySpec(AbilityClass, 1, INDEX_NONE, this)
);
}
}
// 특정 키 입력으로 어빌리티 발동
void AMyCharacter::UseAbility_Q()
{
if (AbilitySystemComponent)
{
// Tag로 어빌리티 발동
FGameplayTagContainer AbilityTags;
AbilityTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.FireBolt")));
AbilitySystemComponent->TryActivateAbilitiesByTag(AbilityTags);
}
}

GameplayTag"State.Stunned", "Ability.FireBolt.Active" 처럼 점(.)으로 계층 구조를 표현하는 문자열 식별자입니다. GAS에서 상태, 조건, 이벤트를 분류하는 핵심 수단입니다.

  • 어빌리티 발동 조건 (ActivationBlockedTags, ActivationRequiredTags)
  • 어빌리티 활성 중 소유 태그 (ActivationOwnedTags)
  • GE로 태그 부여/제거
  • 태그 존재 여부로 상태 확인

프로젝트 설정 > GameplayTags > GameplayTag Table 또는 에디터 내 GameplayTag Manager에서 태그를 등록합니다. Config/DefaultGameplayTags.ini에 직접 추가할 수도 있습니다.

; Config/DefaultGameplayTags.ini
[/Script/GameplayTags.GameplayTagsSettings]
+GameplayTagList=(Tag="State.Stunned",DevComment="스턴 상태")
+GameplayTagList=(Tag="State.Dead",DevComment="사망 상태")
+GameplayTagList=(Tag="Ability.FireBolt",DevComment="파이어볼트 어빌리티")
+GameplayTagList=(Tag="Ability.FireBolt.Active",DevComment="파이어볼트 활성 중")
+GameplayTagList=(Tag="Data.Damage",DevComment="SetByCaller 데미지 데이터 키")

캐릭터의 초기 스탯(체력 100, 마나 50 등)은 Instant GE로 초기화하는 것이 GAS 권장 패턴입니다.

// ASC 초기화 시 초기 스탯 GE 적용
void AMyCharacter::InitializeAttributes()
{
if (!AbilitySystemComponent || !DefaultAttributeEffect)
{
return;
}
FGameplayEffectContextHandle EffectContext = AbilitySystemComponent->MakeEffectContext();
EffectContext.AddSourceObject(this);
FGameplayEffectSpecHandle SpecHandle = AbilitySystemComponent->MakeOutgoingSpec(
DefaultAttributeEffect, // Instant GE - Curve Table로 초기값 설정
1.f,
EffectContext
);
if (SpecHandle.IsValid())
{
AbilitySystemComponent->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get());
}
}

GAS의 핵심 흐름을 정리하면 다음과 같습니다.

캐릭터 (IAbilitySystemInterface 구현)
└─ AbilitySystemComponent
├─ AttributeSet (수치 데이터)
│ └─ Health, Mana, AttackPower ...
├─ GameplayAbility (스킬 로직)
│ └─ 발동 → CommitAbility(비용+쿨다운) → 효과 적용 → EndAbility
├─ GameplayEffect (수치 변경)
│ └─ Instant / Duration / Infinite
└─ GameplayTag (상태 분류)
└─ 발동 조건 체크, 상태 표현

이 가이드에서 다룬 내용만으로 간단한 스킬 시스템을 구현할 수 있습니다. 다음 단계로 아래 주제를 학습하면 GAS를 실전에서 더욱 효과적으로 활용할 수 있습니다.

  • AbilityTask: 비동기 동작(애니메이션 대기, 타격 판정 타이밍 등) 처리
  • GameplayCue: GAS와 연동된 시각/사운드 이펙트 재생
  • Prediction(클라이언트 예측): 멀티플레이어 환경에서 지연 없는 반응성 구현
  • Modular Gameplay: GE의 Execution Calculation으로 복잡한 데미지 공식 구현