언리얼 GAS 입문
**Gameplay Ability System(GAS)**은 언리얼 엔진이 제공하는 고수준 프레임워크로, RPG, 액션, MOBA 등 복잡한 스킬·버프·상태 이상 시스템을 체계적으로 구현할 수 있게 해줍니다. 포트나이트, 파라곤 등 에픽 게임즈 자체 타이틀에서 검증된 프레임워크입니다.
GAS는 처음 접하면 구성 요소가 많아 진입 장벽이 느껴지지만, 핵심 개념 5가지만 이해하면 구조가 명확해집니다.
| 구성 요소 | 약어 | 역할 |
|---|---|---|
| AbilitySystemComponent | ASC | GAS의 중심 컴포넌트, 모든 기능을 관리 |
| GameplayAbility | GA | 스킬·액션의 실제 동작 정의 |
| GameplayEffect | GE | 스탯 변경, 버프/디버프 적용 |
| GameplayAttribute | Attribute | 체력, 마나, 공격력 등 수치 데이터 |
| GameplayTag | Tag | 상태·조건 분류용 계층 태그 |
1. 프로젝트 설정
섹션 제목: “1. 프로젝트 설정”1.1 플러그인 활성화
섹션 제목: “1.1 플러그인 활성화”언리얼 엔진 에디터에서 편집 > 플러그인 메뉴를 열고 Gameplay Abilities를 검색하여 활성화합니다. 활성화 후 에디터를 재시작합니다.
1.2 Build.cs 모듈 추가
섹션 제목: “1.2 Build.cs 모듈 추가”GAS를 사용할 모듈의 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 모듈 }); }}2. AbilitySystemComponent (ASC)
섹션 제목: “2. AbilitySystemComponent (ASC)”2.1 ASC란?
섹션 제목: “2.1 ASC란?”AbilitySystemComponent는 GAS의 중심입니다. 이 컴포넌트를 가진 Actor만 GAS 기능을 사용할 수 있습니다. ASC는 다음을 관리합니다.
- 부여된 어빌리티 목록 및 활성화 상태
- 적용된 GameplayEffect(버프/디버프) 목록
- GameplayAttribute 값
- GameplayTag 현황
2.2 캐릭터에 ASC 붙이기
섹션 제목: “2.2 캐릭터에 ASC 붙이기”일반적으로 플레이어 캐릭터는 Character 또는 PlayerState에 ASC를 붙입니다. AI나 단순 오브젝트는 Character에 바로 붙이는 경우가 많습니다.
#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;};#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를 참조할 수 없습니다.
3. GameplayAttribute
섹션 제목: “3. GameplayAttribute”3.1 AttributeSet 생성
섹션 제목: “3.1 AttributeSet 생성”Attribute는 별도의 UAttributeSet 서브클래스에서 정의합니다. 각 수치는 FGameplayAttributeData 타입으로 선언하고, GAS에서 제공하는 매크로로 접근자를 자동 생성합니다.
#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);};#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);}3.2 AttributeSet을 ASC에 등록
섹션 제목: “3.2 AttributeSet을 ASC에 등록”ASC는 AttributeSet을 자동으로 감지합니다. Character 생성자에서 AttributeSet을 CreateDefaultSubobject로 생성하면 됩니다.
// MyCharacter.cpp - 생성자 내부MyAttributeSet = CreateDefaultSubobject<UMyAttributeSet>(TEXT("MyAttributeSet"));4. GameplayEffect (GE)
섹션 제목: “4. GameplayEffect (GE)”4.1 GE의 역할
섹션 제목: “4.1 GE의 역할”GameplayEffect는 Attribute를 변경하거나 GameplayTag를 부여하는 데이터 에셋입니다. 코드로도 만들 수 있지만, 에디터에서 Blueprint 기반 데이터 에셋으로 만드는 것이 일반적입니다.
GE의 지속 방식은 세 가지입니다.
| Duration Policy | 설명 | 대표 사용처 |
|---|---|---|
| Instant | 즉시 적용 후 종료 | 피해, 즉시 회복 |
| Duration | 지정 시간 동안 유지 | 일시 버프/디버프 |
| Infinite | 수동으로 제거할 때까지 유지 | 장비 스탯, 패시브 |
4.2 코드에서 GE 적용하기
섹션 제목: “4.2 코드에서 GE 적용하기”// 데미지 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()); }}5. GameplayAbility (GA)
섹션 제목: “5. GameplayAbility (GA)”5.1 GA란?
섹션 제목: “5.1 GA란?”GameplayAbility는 스킬 하나의 모든 동작을 정의하는 클래스입니다. 발동 조건, 쿨다운, 마나 비용, 실제 효과 적용까지 모두 GA 안에서 관리합니다.
5.2 기본 GA 구현
섹션 제목: “5.2 기본 GA 구현”#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;};#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);}5.3 GA 부여 및 발동
섹션 제목: “5.3 GA 부여 및 발동”// 캐릭터에게 어빌리티 부여 (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); }}6. GameplayTag
섹션 제목: “6. GameplayTag”6.1 GameplayTag의 역할
섹션 제목: “6.1 GameplayTag의 역할”GameplayTag는 "State.Stunned", "Ability.FireBolt.Active" 처럼 점(.)으로 계층 구조를 표현하는 문자열 식별자입니다. GAS에서 상태, 조건, 이벤트를 분류하는 핵심 수단입니다.
- 어빌리티 발동 조건 (
ActivationBlockedTags,ActivationRequiredTags) - 어빌리티 활성 중 소유 태그 (
ActivationOwnedTags) - GE로 태그 부여/제거
- 태그 존재 여부로 상태 확인
6.2 태그 등록
섹션 제목: “6.2 태그 등록”프로젝트 설정 > 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 데미지 데이터 키")7. 초기 스탯 초기화
섹션 제목: “7. 초기 스탯 초기화”캐릭터의 초기 스탯(체력 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()); }}7.5 ExecutionCalculation — 커스텀 데미지 공식
섹션 제목: “7.5 ExecutionCalculation — 커스텀 데미지 공식”UGameplayEffectExecutionCalculation은 여러 Attribute를 읽어 복잡한 데미지 공식을 GAS 내부에서 처리하는 고급 패턴입니다. GE 에셋의 Execution 항목에 지정하면 적용됩니다.
UCLASS()class UDamageExecCalc : public UGameplayEffectExecutionCalculation{ GENERATED_BODY()public: UDamageExecCalc(); virtual void Execute_Implementation( const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override;};struct FDamageStatics{ DECLARE_ATTRIBUTE_CAPTUREDEF(AttackPower) DECLARE_ATTRIBUTE_CAPTUREDEF(Defense)
FDamageStatics() { DEFINE_ATTRIBUTE_CAPTUREDEF(UMyAttributeSet, AttackPower, Source, true) DEFINE_ATTRIBUTE_CAPTUREDEF(UMyAttributeSet, Defense, Target, false) }};
static const FDamageStatics& DamageStatics(){ static FDamageStatics Statics; return Statics;}
UDamageExecCalc::UDamageExecCalc(){ RelevantAttributesToCapture.Add(DamageStatics().AttackPowerDef); RelevantAttributesToCapture.Add(DamageStatics().DefenseDef);}
void UDamageExecCalc::Execute_Implementation( const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const{ float AttackPower = 0.f, Defense = 0.f; FAggregatorEvaluateParameters EvalParams; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude( DamageStatics().AttackPowerDef, EvalParams, AttackPower); ExecutionParams.AttemptCalculateCapturedAttributeMagnitude( DamageStatics().DefenseDef, EvalParams, Defense);
// SetByCaller로 전달된 기본 데미지 float BaseDamage = ExecutionParams.GetOwningSpec() .GetSetByCallerMagnitude( FGameplayTag::RequestGameplayTag("Data.Damage"), false, 0.f);
// 공격력 반영, 방어력 50% 감산, 최소 1 float FinalDamage = FMath::Max(1.f, BaseDamage + AttackPower - Defense * 0.5f);
OutExecutionOutput.AddOutputModifier( FGameplayModifierEvaluatedData( UMyAttributeSet::GetHealthAttribute(), EGameplayModOp::Additive, -FinalDamage));}7.6 AbilityTask — 비동기 로직 처리
섹션 제목: “7.6 AbilityTask — 비동기 로직 처리”GA 내에서 애니메이션 완료 대기, 타격 판정 타이밍 감지 등 시간 기반 로직은 UAbilityTask로 처리합니다.
void UGA_MeleeAttack::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; }
// 몽타주 재생 + 완료/취소 콜백 UAbilityTask_PlayMontageAndWait* MontageTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy( this, NAME_None, AttackMontage); MontageTask->OnCompleted.AddDynamic(this, &UGA_MeleeAttack::OnMontageCompleted); MontageTask->OnCancelled.AddDynamic(this, &UGA_MeleeAttack::OnMontageCancelled); MontageTask->ReadyForActivation();
// 애님 노티파이에서 보낸 GameplayEvent 수신 (타격 판정 타이밍) UAbilityTask_WaitGameplayEvent* EventTask = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent( this, FGameplayTag::RequestGameplayTag("Event.Melee.HitCollision")); EventTask->EventReceived.AddDynamic(this, &UGA_MeleeAttack::OnHitCollision); EventTask->ReadyForActivation();}
void UGA_MeleeAttack::OnHitCollision(FGameplayEventData Payload){ FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(DamageEffectClass, GetAbilityLevel()); ApplyGameplayEffectSpecToTarget( CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, Spec, FGameplayAbilityTargetDataHandle());}
void UGA_MeleeAttack::OnMontageCompleted(){ EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false);}8. 정리 및 다음 단계
섹션 제목: “8. 정리 및 다음 단계”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으로 복잡한 데미지 공식 구현