본 게시글은 GAS 구조를 공부하던 중 UAbilitySystemComponent 의 주석을 필자가 아는만큼 해석해놓은 글이니 읽으실 때 참고 부탁드립니다.
(필자의 수준은 매우 낮다는 것을 고려)
UAbilitySystemComponent 란, Ability System 의 아래 3가지 요소를 사용함에 있어 조금 더 쉽게 상호작용하기 위해서 존재하는 컴포넌트다.
1. Gameplay Abilities :
- 플레이어나 AI 등의 사용자가 "어빌리티" 를 획득하거나 부여하는 방법을 제공한다.
- "어빌리티" 인스턴스를 관리한다. (무언가가 어빌리티를 갖고 있는 경우 등)
- 리플리케이션 기능을 제공한다.
- "어빌리티"는 반드시 UGameplayAbility 자신에 의해 리플리케이트 되어야 하는데, UAbilitySystemComponent 는 여기에 대해 어빌리티 활성화를 위한 RPC 리플리케이션을 제공한다.
우선, 게임플레이 어빌리티란 게임을 할 때 사용할 수 있는 여러 행동들을 의미한다.
예를 들면 각종 스킬이 있다. "스킬"을 사용하기 위해서는 어떤 비용이 드는지, 어떤 상황에서 언제 사용할 수 있는지가 정의되는데 게임플레이 어빌리티도 마찬가지이다.
이런 어빌리티를 사용하기 위해서는 바로 해당 액터의 Ability System Component 에 Ability 를 부여해야 사용할 수 있게 되는 것이다. 여기서 하나의 궁금증이 풀린다. Ability System Component (이하 ASC) 는 Ability 를 획득하거나 부여할 수 있는 방법을 제공한다는 것. 또, 어빌리티를 획득했다면 획득한 상태임을 알 수 있어야 하는데 이것이 인스턴스 관리의 의미이다.
싱글 플레이라면 크게 상관 없겠지만, 모든 싱글플레이 게임 역시도 멀티플레이를 기반으로 하여 설계해 나가는 것이 좋다. (실제로 에픽 게임즈에서도 그런 방향을 지향하도록 권한다.) 이 때, 우리는 Replicate 를 고려해야만 하는데 이러한 기능들도 ASC에서 제공한다는 것이다.
2. Gameplay Effects :
- 활성화된 Gameplay Effects 를 갖는 FActiveGameplayEffectsContainer 를 제공한다.
- Gameplay Effects 를 자신이나 다른 타겟에게 적용하도록 하는 메소드를 제공한다.
- FActiveGameplayEffectsContainers 데이터 핸들링을 위한 래퍼를 제공한다. (Duration, Magnitude 등)
- GameplayEffects 를 제거하는 메소드 역시 제공한다.
게임플레이 이펙트란 GAS에서 "Attribute" 를 변경하는 방법이다.
"Attribute" 란, float 값으로 정의 가능한 모든 상태를 의미하는데 예를 들면 체력, 마나, 힘, 방어력 등이 있다.
적에게 공격을 당하면 체력이 줄어드는 것은 이제 어느 게임이든 마찬가지이다. 이 경우에도 "공격"을 당함으로써 "체력"이라는 어트리뷰트의 값이 변경되어야 하고, 이에 따라 체력 값을 조정하는 것이 바로 Gameplay Effect 라고 할 수 있다.
즉, 이를 적용하거나 제거하고 핸들링하는 것이 ASC를 통해서 가능하다는 소리이다.
3. Gameplay Attributes :
- Attribute Set 을 할당하거나 초기화하는 메소드를 제공한다.
- AttributeSets 를 가져올 수 있는 메소드를 제공한다.
위에서 설명했듯, Attribute 를 관리할 수 있다는 의미이다.
결국 GAS에서 벌어지는 상호작용을 구현하기 위해서는 ASC가 필수적이라는 것을 알 수 있다.
더 아래를 보면, 위와 같이 어빌리티나 어빌리티 SPEC에 대한 여러 델리게이트를 확인할 수 있다.
어빌리티를 발동했지만 활성화에 실패하거나, 어빌리티가 종료되었을 때 등 여러 상황에 대한 콜백이 존재한다.
또, 앞서 말했듯 Gameplay Effect (이하 GE) 에 대한 리플리케이션 수준도 제공한다.
Replication 은 결국 서버와 클라이언트 사이에 네트워크 통신과 값 복사가 빈번히 일어나는 일인 만큼 규모가 커질 수록 CPU의 부담도 같이 늘어간다. 때문에 최적화가 필요한데, 모든 정보를 전부 Replicate 하기보다는 해당 액터에게 영향을 미치는 부분만 따로 Replicate 하기를 누구나 바랄 것이다.
이런 이유로 제공하는 것이 바로 EGameplayEffectReplicationMode 이다.
Full 모드에서는 모든 게임플레이 정보가 모든 클라이언트에게 복사된다. (오버헤드가 제일 크다.)
- 그렇기 때문에 싱글플레이어에서만 사용한다고 보면 된다.
Mixed 모드에서는 GE가 적용될 액터를 소유한 클라이언트에게만 복제된다. 그 이후에 Gameplay Cue와 Gameplay Tag는 모든 클라이언트에게 복제된다. (오버헤드가 중간 수준이다.)
- 예를 들어, 적이 나의 캐릭터를 공격했다고 가정해 보자. 여기서 공격을 당했기 때문에 어트리뷰트가 수정되어야 하는 플레이어는 '나' 뿐이다. 주변의 다른 플레이어는 그저 적의 공격 모션과 내 캐릭터의 피격 모션, 그리고 이펙트나 소리 등이 동시에 들리면 된다. 이런 경우에 사용하기 위해 GE를 공격당한 클라이언트에만 복제함으로써 부하를 줄일 수 있다.
Minimal 모드에서는 GE가 복제되지 않는다. 오직 Gameplay Cue와 Gameplay Tag 만 모든 클라이언트에게 복제된다. (오버헤드가 제일 작다.)
- 만약 AI가 또 다른 AI에 의해 공격당하는 경우, 이 때는 GE가 적용될 플레이어가 없고 AI에 대한 모든 연산은 서버에서 이루어지므로 결과적으로 Replicate 할 필요가 없다.
이제 이 아래로는 UAbilitySystemComponent 에 대한 몸체들이 나온다.
Attribute의 유효성을 검사하거나, 값을 가져오거나 전체 목록을 가져오기도 하며
그냥 전반적으로 Attribute와 AttributeSet에 대한 관리 메소드들이 주를 이룬다.
조금 중요한 부분은 더 아래에 나오는데, GE를 적용하는 메소드들이 그 예이다.
GE는 ApplyGameplayEffectSpecToTarget, ApplyGameplayEffectSpecToSelf 등으로 적용할 수 있는데
Self 는 지금 이 ASC에 적용하는 것이고, Target 은 이 ASC가 아닌 다른 타겟 ASC에 적용하는 함수이다.
(실제로 UAbilitySystemComponent* Target 이라고 되어 있다.)
근데 주석을 자세히 읽어 보면, Applies a previously created gameplay effect spec 이라고 되어 있다.
GE Spec 이란 대체 뭘까?
GE Spec 은 말 그대로 해당 GE의 스펙 사양, 즉 인스턴스이다.
Gameplay Effect 는 주로 프로그래머보다는 디자이너들이 만들게 되는데, 이렇게 블루프린트 클래스로 작성된 GE를 인스턴스화 해서 ASC에 적용하는 것이 GE Spec의 일이다. 즉, Gameplay Effect Spec 을 만들어서 적용해야 한다.
물론 ASC에서 만드는 방법도 제공한다.
MakeOutgoingSpec 을 확인해 보면 다른 객체들에게 적용될 준비가 된 GE Spec 을 가져온다고 되어 있다.
또 그 인자로 TSubclassOf<UGameplayEffect> 를 받기 때문에 GE 클래스로부터 Spec 을 얻어오는 함수임을 확인할 수 있다.
다만, 몸체를 보면 GE Spec을 만들고 이를 GE Spec Handle 이라는 래퍼 클래스로 래핑하여 반환한다는 것을 알 수 있으니, 결국 우리가 사용하는 것은 핸들인 셈이다.
다시 그 밑으로는 GameplayEffect 에 대한 세부 사양을 관리하는 함수들이 줄지어 있음을 확인할 수 있다.
여기서도 모두 인자로 GE Spec Handle 을 받고 있다. 즉 GAS에서는 GE를 모두 래퍼 클래스로 관리하려고 한다.
쭉쭉 내려보면 아까 말했던 델리게이트도 선언되어있고, 이에 따른 콜백들도 여러 개가 보인다.
여기서 FOnGameplayEffectAppliedDelegate 델리게이트 멤버들에 적힌 주석을 보면 대부분의 Delegate 는 Called on server 라고 되어있다. 즉 Replicate를 위해 서버에서만 델리게이트가 호출된다는 것이다.
Instant 와 Duration GE가 "적용되는" 경우에는 OnGameplayEffectAppliedDelegateToSelf 와 OnGameplayEffectAppliedDelegateToTarget 가,
Periodic GE의 경우에는 "실행될 때마다" OnPeriodicGameplayEffectExecuteDelegateOnSelf 와 OnPeriodicGameplayEffectExecuteDelegateOnTarget 가 호출되는데,
유일하게 OnActiveGameplayEffectAddedDelegateToSelf 이놈만이 Server 와 Client 모두에서 호출되며,
Instant 는 빠진 Duration GE 가 "추가되는" 경우에만! 호출이 된다고 적혀 있다.
뿐만 아니라, GE가 제거되는 경우도 마찬가지로 델리게이트가 존재한다.
OnAnyGameplayEffectRemovedDelegate 와 OnGameplayEffectRemoved_InfoDelegate 이 그것인데,
반환형을 자세히 보면 하나는 FOnGivenActiveGameplayEffectRemoved& 이런식으로 델리게이트를 참조하고,
다른 하나의 경우 FOnActiveGameplayEffectRemoved_Info* 로 ActiveGameplayEffect 에 대한 정보를 가져오므로
조금 다른 식으로 사용할 수 있다.
Gameplay Cue 에 대한 함수들이라던지, Replication 에 대한 함수들이 나온 뒤,
중간부터는 Ability 에 대한 내용이 슬슬 나오기 시작한다.
Gameplay Abilities 에 대한 주석 내용을 확인해 보면,
앞서 언급했듯 ASC에서 Ability 에 대한 어떤 책임을 갖고 있는지가 나온다.
액터 별, 혹은 실행 인스턴스 별로 Ability Instance 에 대한 관리를 한다고 되어 있으며 (인스턴스 추적을 통해)
인스턴스화 되지 않은 어빌리티는 ASC에 없더라도 실행될 수 있어야 한다고 적혀 있는데, 이것이 가능하려면 GameplayAbility ActorInfo + GameplayAbility 가 있어야 가능하다고 하는 것 같다.
어찌 됐건, 어빌리티 인스턴스를 추적 관리하고 부여하거나 없앨 수 있다는 말이다.
또, Gameplay Tag 를 저장하고 관리하는 Gameplay Tag Container 를 검색하여 태그와 매칭되는 모든 어빌리티를 가져오거나 클랫, 태그를 통해 어빌리티를 활성화 할 수도 있다.
이 외에도 Ability Blocking Tag 를 통해 존재하는 어빌리티를 취소하거나 상태를 확인할 수도 있다.
그 아래로는 디버깅과 관련된 함수들도 여럿 지원해 주고 있으며, Anim Montage 에 대한 지원도 있다.
아무래도 어빌리티와 애니메이션은 뗄 수 없는 관계이긴 하다.
그 다음으로는 액터를 관리하고 있는데,
여기서 눈여겨 보아야 할 점은 OwnerActor 와 AvatarActor 로 나누어져 있다는 것이다.
주석을 보면 OwnerActor 는 논리적으로 ASC를 갖고 있는 액터를 말하며
AvatarActor 는 어빌리티를 위해 사용될 물리적 액터를 말한다.
만약 PlayerController 이 Character 를 소유하고, GAS를 사용하고자 한다면
실제로 논리적 소유자는 Character이 아닌 PlayerController 이다. 컨트롤러는 플레이어를 대변하니 말이다.
Character 는 컨트롤러가 빙의되어 움직이고 있는 인형에 불과하다.
(OwnerActor = PlayerController, AvatarActor = Character)
즉, 이렇게 나눠서 관리하며, OnRep 매크로를 통해 리플리케이션을 자체적으로 하고 있다는 것도 알 수 있다.
그리고 위에서 언급했던 정보들 (Movement Component, Mesh Component, Anim Instance 등등) 을 캐싱해놓고 있는
AbilityActorInfo 를 갖고 있다.
그리고 활성화가 가능한 어빌리티를 갖고 있는 배열도 존재한다.
모든 부분을 세세하게 보지는 않았지만, 결국 ASC가 뭐하는 놈인지, GAS가 어떻게 굴러가는지 주석과 코드를 보며
대충은 예상할 수 있다.
모든 프로젝트에서 반드시 GAS를 사용해야 하는 것은 아니지만, 이런 클래스 구조를 알고 있다면 나중에 독자적으로 어빌리티 구조를 만든다고 하더라도 차용할 수 있는 설계 방식이 생기는 것이니 기회가 될 때 다른 프레임워크도 최대한 많이 사용하는 것이 좋다고 생각한다.
'Unreal 5 > 디자인 패턴 및 주요 개념' 카테고리의 다른 글
[Unreal 5] GAS - Gameplay Effect Modifier (0) | 2024.07.11 |
---|---|
[Unreal 5] 매크로 지정자 (1) | 2024.07.08 |
[Unreal 5] ABP Template 생성 (0) | 2024.05.31 |
[Unreal 5] 스마트 포인터 유효성 판단 (0) | 2024.04.05 |
[Unreal 5] Component 에 대해 (0) | 2024.04.01 |