언리얼 엔진

[언리얼 엔진] GAS Cost, Cooldown, GameplayCue 정리

dhlee-dev 2026. 6. 23. 22:32

앞서 작성한 GAS 정리글에서는 AttributeSet과 GameplayEffect 흐름을 정리했다.

Attribute 값은 AttributeSet에 저장되고 값의 변경은 보통 GameplayEffect를 통해 일어나며,
변경 결과는 RepNotify나 Attribute Change Delegate를 통해 UI 갱신으로 이어질 수 있었다.

이번 글에서도 GameplayEffect가 다시 등장한다.

다만 이번에는 데미지나 회복처럼 Attribute 값을 변경하는 관점이 아니라,
Ability의 Cost와 Cooldown을 처리하는 관점에서 정리해보려고 한다.

또 지금까지 간단히만 보고 넘어갔던 GameplayCue도 함께 정리하려고 한다.

GameplayCue는 데미지 계산이나 상태 변경 자체보다는,
파티클, 사운드, 카메라 흔들림, 피격 몽타주 같은 연출을 GAS 흐름에서 분리하는 데 사용할 수 있었다.

이번 글의 핵심은 Ability가 실행될 때 Cost와 Cooldown이 GAS 안에서 어떻게 검사되고 적용되는지,
그리고 피격 연출을 GameplayCue로 어떻게 분리할 수 있는지를 이해하는 것이다.


Cost와 Cooldown도 GameplayEffect로 처리한다

GAS에서는 Ability의 자원 소모와 쿨타임도 GameplayEffectGameplayTag로 처리할 수 있다.

Ability 실행
-> Cost GameplayEffect로 자원 소모
-> Cooldown GameplayEffect로 Cooldown 태그 부여
-> Cooldown 태그가 유지되는 동안 Ability 재사용 제한

즉, GameplayEffect는 단순히 데미지나 회복만 처리하는 것이 아니라,
Ability를 사용할 때 필요한 비용과 쿨타임도 처리할 수 있다.

이번 실습에서는 보조 공격 Ability인 GA_Secondary에 Mana Cost와 Cooldown을 적용했다.


Cost GameplayEffect

Cost는 Ability를 사용할 때 소모되는 자원이다.
예를 들어 Mana를 사용하는 보조 공격이라면 Ability 사용 시 Mana가 일정량 감소해야 한다.

이를 위해 실습에서는 GE_Cost_Secondary라는 GameplayEffect를 만들고,
Mana Attribute를 감소시키도록 Modifier를 설정했다.

개념적으로 보면 다음과 같다.

GE_Cost_Secondary
-> Mana Attribute 감소

그리고 GA_Secondary의 Class Defaults에서 Costs 항목에 GE_Cost_Secondary를 등록했다.

이렇게 하면 GAS는 Ability를 활성화할 수 있는지 검사할 때 현재 Mana로 Cost를 지불할 수 있는지 확인할 수 있다.

흐름은 다음과 같다.

1. GA_Secondary에 Cost GameplayEffect 등록
2. Cost GE는 Mana를 감소시키는 Modifier를 가짐
3. Ability 실행 시 GAS가 Cost 지불 가능 여부를 검사
4. Mana가 부족하면 Ability 활성화 실패
5. Mana가 충분하면 Ability 실행 가능

여기서 중요한 점은 Cost GameplayEffect를 등록했다고 해서 Mana가 자동으로 바로 감소하는 것은 아니라는 점이다.
Cost가 실제로 적용되는 시점은 CommitAbilityCost 또는 CommitAbility를 호출했을 때이다.


Cooldown GameplayEffect

Cooldown도 GameplayEffect로 처리할 수 있다.

실습에서는 GA_Secondary에 쿨타임을 적용하기 위해 CCTags.Cooldown.Secondary 태그를 만들었다.
그리고 GE_Cooldown_Secondary GameplayEffect를 생성했다.

이 GameplayEffect는 Duration을 가지고,
적용되는 동안 ASC에 Cooldown 태그를 부여하는 역할을 한다.

GE_Cooldown_Secondary
-> Duration을 가진 GameplayEffect
-> 일정 시간 동안 Cooldown.Secondary 태그 부여

설정 흐름은 다음과 같다.

1. GE_Cooldown_Secondary 생성
2. Duration Policy를 Has Duration으로 설정
3. Duration을 원하는 쿨다운 시간으로 설정
4. 대상 ASC에 Cooldown.Secondary 태그를 부여하도록 설정
5. GA_Secondary의 Cooldowns 항목에 GE_Cooldown_Secondary 등록

이렇게 하면 CommitAbility가 호출될 때 Cooldown GameplayEffect가 적용된다.
그리고 Cooldown GameplayEffect가 유지되는 동안 ASC에는 Cooldown.Secondary 태그가 부여된다.

이 태그가 있는 동안에는 해당 Ability를 다시 사용할 수 없다.


Cooldown Tag가 중요한 이유

Cooldown은 단순히 시간을 재는 것만으로 끝나지 않는다.
GAS에서는 Cooldown 상태를 GameplayTag로 표현할 수 있다.

Ability 사용
-> Cooldown GameplayEffect 적용
-> ASC에 Cooldown.Secondary 태그 부여
-> 태그가 유지되는 동안 Ability 재사용 제한
-> Duration 종료
-> Cooldown 태그 제거
-> Ability 재사용 가능

여기서 중요한 점은 Cooldown GameplayEffect가 부여하는 태그와
Ability가 Cooldown으로 판단하는 태그가 일관되게 맞아야 한다는 것이다.

이번 실습에서는 다음 관계가 맞아야 했다.

GA_Secondary
-> Cooldowns에 GE_Cooldown_Secondary 등록

GE_Cooldown_Secondary
-> Cooldown.Secondary 태그 부여

ASC
-> Cooldown.Secondary 태그가 있는 동안 GA_Secondary 재사용 제한

즉, Cooldown GameplayEffect는 일정 시간 동안 Cooldown 태그를 부여하고,
Ability는 그 태그를 통해 현재 쿨타임 중인지 판단한다고 볼 수 있다.


CommitAbilityCost, CommitAbilityCooldown, CommitAbility

Cost와 Cooldown은 GameplayEffect로 등록해두었다고 해서 항상 자동으로 실제 적용되는 것은 아니다.
실제로 Cost나 Cooldown을 적용하려면 Commit 계열 함수를 호출해야 한다.

대표적으로 다음과 같이 정리할 수 있다.

함수 역할
CommitAbilityCost Cost만 실제 적용
CommitAbilityCooldown Cooldown만 실제 적용
CommitAbility Cost와 Cooldown을 함께 검사하고 적용

처음에는 GA_Secondary에서 Mana Cost만 적용하면 됐기 때문에 CommitAbilityCost를 사용할 수 있었다.
하지만 이후 Cooldown까지 함께 적용해야 했기 때문에 CommitAbility를 사용하도록 변경했다.

CommitAbility를 호출하면 Ability에 등록된 Cost GameplayEffect와
Cooldown GameplayEffect를 검사한 뒤 적용한다.

CommitAbility
-> Cost 적용
-> Cooldown 적용

따라서 Cost만 따로 적용하고 싶다면 CommitAbilityCost,
Cooldown만 따로 적용하고 싶다면 CommitAbilityCooldown,
일반적으로 Ability 사용을 확정하면서 Cost와 Cooldown을 함께 처리하고 싶다면
CommitAbility를 사용하면 된다.

다만 CommitAbility 계열 함수들은 성공 여부를 반환하므로,
실패했다면 이후 공격 처리나 추가 동작을 진행하지 않도록 확인하는 것이 안전하다.


CommitAbility를 호출하는 시점

Cost와 Cooldown은 언제 적용할지도 중요하다.
Ability가 시작되자마자 CommitAbility를 호출하면 입력한 시점에 바로 Cost와 Cooldown이 적용된다.

ActivateAbility 초반
-> CommitAbility 호출
-> 입력 즉시 Cost / Cooldown 적용

반대로 실습에서는 보조 공격 몽타주 중간에 GameplayEvent를 받은 뒤 CommitAbility를 호출했다.

즉, 입력 시점이 아니라 실제 공격 판정이 발생하는 시점에 Cost와 Cooldown을 적용한 것이다.

GA_Secondary 활성화
-> 몽타주 재생
-> Wait Gameplay Event로 공격 타이밍 대기
-> AnimNotify에서 GameplayEvent 전송
-> Ability가 이벤트 수신
-> CommitAbility 호출
-> Cost / Cooldown 적용
-> 이후 공격 처리

두 방식 중 어느 것이 무조건 맞다고 보기는 어렵다.

입력한 순간 Ability를 사용한 것으로 볼 것인지,
실제 공격 판정이 발생한 순간 Ability를 사용한 것으로 볼 것인지에 따라
CommitAbility를 호출하는 위치가 달라질 수 있다.

이번 실습에서는 보조 공격의 실제 판정 타이밍을 기준으로 Cost와 Cooldown을 적용하는 구조를 사용했다.


Mana Pickup으로 Mana 회복 예시

Cost를 적용했기 때문에 Mana를 회복하는 흐름도 필요했다.
실습에서는 BP_Mana_Pickup 액터를 만들어 플레이어가 획득했을 때 Mana를 회복하도록 했다.

BP_Mana_Pickup은 Overlap이 발생하면 겹친 Actor의 ASC를 가져오고,
서버 권한에서 GE_AddMana를 적용한 뒤 자신을 Destroy한다.

GE_AddMana는 Mana를 즉시 한 번만 회복시키는 Effect가 아니라,
일정 시간 동안 주기적으로 Mana를 회복시키는 GameplayEffect로 만들었다.

설정은 다음과 같다.

Duration Policy
-> Has Duration

Duration Magnitude
-> 4.5초

Period
-> 0.5초

Execute Periodic Effect on Application
-> true

Modifier
-> Mana Add 5

Execute Periodic Effect on Application 옵션이 켜져 있으면 적용 시점에도 한 번 즉시 실행된다.
따라서 총 회복량을 맞추려면 첫 즉시 실행까지 포함해서 Duration과 Period를 계산해야 했다.


GameplayCue란?

GameplayCue는 GAS에서 연출을 처리하기 위한 시스템이다.
예를 들어 다음과 같은 연출을 GameplayCue로 분리할 수 있다.

  • 파티클
  • 사운드
  • 카메라 흔들림
  • 피격 몽타주
  • Impact 이펙트

GAS 흐름에서 실행되기 때문에
멀티플레이 환경에서 필요한 연출을 클라이언트 쪽에 재생시키는 용도로도 활용할 수 있다.

데미지 계산이나 Attribute 변경은 GameplayEffect가 담당하고,
그 결과를 어떻게 보여줄지는 GameplayCue가 담당하도록 나눌 수 있다.

GameplayEffect
-> Health 감소
-> 상태 변화

GameplayCue
-> 피격 파티클 재생
-> 카메라 흔들림
-> 피격 몽타주 재생

처음에는 Ability 안에서 직접 파티클을 Spawn하고 몽타주를 재생해도 된다고 생각할 수 있다.
하지만 Ability에 데미지 처리, 상태 변경, 연출 재생이 모두 들어가면 Ability가 점점 복잡해진다.

GameplayCue를 사용하면 Ability는 이벤트를 받고 Cue를 실행하는 흐름만 담당하고,
실제 연출 처리는 Cue 쪽으로 분리할 수 있다.


GameplayCue Notify Actor와 Static

GameplayCue는 구현 방식에 따라
GameplayCue Notify ActorGameplayCue Notify Static으로 나누어 생각할 수 있다.

구분 특징 사용 예시
GameplayCue Notify Actor 인스턴스가 생성되고 상태를 가질 수 있음 지속 시간이 있거나 상태 관리가 필요한 연출
GameplayCue Notify Static 별도 상태를 오래 유지하지 않는 단발성 처리에 적합 한 번 재생하고 끝나는 파티클, 사운드

단발성 Impact 파티클처럼 한 번 재생하고 끝나는 연출이라면 Static 형태가 잘 어울린다.
반대로 지속적으로 유지되거나 상태를 가지고 관리해야 하는 연출이라면 Actor 형태가 더 적합할 수 있다.

이번 실습에서는 피격 파티클처럼 한 번 실행하고 끝나는 연출에 GameplayCue Notify Static을 사용했다.


GameplayCue 검색 경로 설정

GameplayCue를 사용하다 보면 에디터 로그에 GameplayCue 검색 경로와 관련된 메시지가 표시될 수 있다.

기본 설정을 하지 않으면 프로젝트 전체 폴더에서 GameplayCue를 찾을 수 있는데,
프로젝트 규모가 커지면 비효율적일 수 있다.

그래서 DefaultGame.ini에 GameplayCue가 들어있는 폴더 경로를 지정해두면
GameplayCue를 더 빠르게 찾을 수 있다.

이 부분은 기능 구현 자체와 직접 관련된 핵심은 아니지만,
GameplayCue를 많이 사용할 프로젝트라면 설정해두는 편이 좋다.


GameplayCue로 피격 연출 분리하기

실습에서는 플레이어가 공격받을 때
피격 몽타주, 카메라 흔들림, Impact 파티클을 재생하기 위해 GameplayCue를 사용했다.

전체 흐름은 다음과 같다.

적 공격
-> 플레이어에게 데미지 이벤트 전송
-> 플레이어의 리스너 Ability가 이벤트 수신
-> GameplayCueParameters 생성
-> GameplayCue 실행
-> Cue에서 피격 연출 재생

여기서 중요한 점은 데미지 처리와 연출 처리를 분리했다는 것이다.

데미지 자체는 GameplayEffect를 통해 Health Attribute를 변경한다.

반면 피격자가 어떤 방향으로 피격 몽타주를 재생할지, 어디에 Impact 파티클을 Spawn할지,
카메라 흔들림을 줄지는 GameplayCue 쪽에서 처리한다.

데미지 처리
-> GameplayEffect

피격 연출
-> GameplayCue

이렇게 나누면 공격 Ability가 피격 연출의 세부 구현을 모두 알 필요가 줄어든다.


GameplayCueParameters

GameplayCue를 실행할 때는 필요한 정보를 GameplayCueParameters에 담아 전달할 수 있다.
어떤 정보가 필요한지는 Cue가 담당하는 연출에 따라 달라진다.

단순히 사운드나 파티클만 재생하는 Cue라면 위치 정보만 필요할 수도 있고,
피격 방향 계산이나 Impact 위치가 필요한 Cue라면 공격자, 피격자, HitResult 같은 정보가 필요할 수 있다.

이번 실습의 피격 연출에서는 다음과 같은 정보가 필요했다.

역할
Instigator 공격자
Target 피격자
EffectContext / ContextHandle HitResult 정보 전달
SourceObject 또는 OptionalObject 사용할 파티클 시스템 전달

GC_HitReact에서는 이 정보를 사용해 공격자와 피격자의 위치를 기준으로 피격 방향을 계산했다.
그리고 방향에 맞는 HitReact 몽타주 섹션을 재생했다.

Instigator 위치
-> Target 위치
-> 공격 방향 계산
-> Front / Back / Left / Right 결정
-> 해당 HitReact 몽타주 섹션 재생

HitResult의 ImpactPoint 위치에 전달받은 파티클 시스템을 Spawn해서 Impact 이펙트를 재생했다.

즉, GameplayCueParameters는 Cue 실행에 필요한 정보를 전달하는 용도이고,
어떤 값을 담을지는 Cue가 어떤 연출을 담당하는지에 따라 달라진다.


정리

이번 글에서는 GAS의 Cost, Cooldown, GameplayCue 흐름을 정리했다.

이전 GAS 정리글에서 GameplayEffect를 Attribute 값을 변경하는 용도로 정리했다면,
이번 글에서는 Ability의 자원 소모와 쿨타임을 처리하는 용도로 다시 살펴봤다.

간단히 정리하면 다음과 같다.

개념 역할
Cost GameplayEffect Ability 사용 자원 소모
Cooldown GameplayEffect 일정 시간 Cooldown 태그 부여
CommitAbilityCost Cost만 실제 적용
CommitAbilityCooldown Cooldown만 실제 적용
CommitAbility Cost와 Cooldown을 함께 검사하고 적용
GameplayCue 파티클, 사운드, 카메라 흔들림, 몽타주 같은 연출 처리
GameplayCue Notify Actor 상태를 가질 수 있는 Cue
GameplayCue Notify Static 단발성 연출에 적합한 Cue

결국 GAS에서는 Ability 실행 자체뿐만 아니라,
자원 소모, 쿨타임, 상태 태그, 연출까지 하나의 흐름 안에서 연결할 수 있었다.

Ability
-> Cost / Cooldown은 GameplayEffect로 처리
-> 상태 구분은 GameplayTag로 처리
-> 연출은 GameplayCue로 처리

이렇게 역할을 나누면 처음에는 구조가 복잡해 보이지만,
Ability가 늘어나고 연출이 다양해질수록 각 요소를 따로 수정하고 재사용하기 쉬워질 것 같다.


마무리

이번 글을 끝으로 강의를 들으면서 정리한 GAS 기본 흐름을 어느 정도 마무리하게 되었다.

처음에는 GAS가 어렵고 복잡하게 느껴졌다.

이전에는 체력값이나 스태미너값을 직접 수정하고,
공격 실행 조건, 파티클, 애니메이션 같은 처리도 필요한 곳에서 직접 연결하는 방식으로 구현했다.

작은 기능에서는 이런 방식도 충분히 가능하지만,
전투 시스템이 커질수록 각 기능이 서로 얽혀 관리하기 어려워질 수 있다고 느꼈다.

GAS를 공부해보니 배우는 과정은 쉽지 않았지만,
역할을 나누어 사용하면 오히려 복잡한 전투 시스템을 더 체계적으로 관리할 수 있을 것 같았다.

아직은 강의 실습을 따라가며 익힌 단계지만,
앞으로 전투 시스템을 설계할 때는 GAS를 적용해보면서
Ability, Effect, Attribute, Tag, Cue 흐름을 더 깔끔하게 관리해보고 싶다.