본문 바로가기

언리얼 개발자

[Unreal] AbilityTriggers in GameplayAbility

 GameplayAbility 내부에는 "AbilityTriggers" 라는 변수를 가지고 있고 아래 사진과 같이 에디터 상에서 편집도 가능합니다.

 

GameplayAbility Ability Triggers

Trigger Tag : 어빌리티가 반응할 태그

Trigger Source : Trigger Tag가 어떤 형태로 발생해야 반응할 지 선택

 

Trigger Source 종류를 보면 좀 더 역할이 무엇인지 감이 잡힙니다.

1. GameplayEvent : Triggered from a gameplay event, will come with payload

2. OwnedTagAdded : Triggered if the ability's owner gets a tag added, triggered once whenever it's added

3. OwnedTagPresent : Triggered if the ability's owner gets tag added, removed when the tag is removed

 

구체적으로 더 알아보고 싶었던 것은 Trigger Tag를 발생시켰을 때 어빌리티 시스템은 어떻게 감지하고 특정 어빌리티를 실행시키는지입니다. 특히 GameplayEvent를 중점으로 기존 어빌리티를 직접 실행시키는 방식과 사용법도 많이 다른 지도 살펴보려 합니다.

 

[ AbilitySystemComponent에서 Ability Trigger 정보를 저장하는 방식 ]

 먼저 GameplayEvent에 의해 실행시키려는 GameplayAbility도 AbilitySystemComponent(ASC)에 등록해주어야 합니다.

ASC에 GameplayAbility에 등록하는 과정에서 호출되는 함수 중엔 OnGiveAbility() 가 있습니다.

AbilitySystemComponent::OnGiveAbility()

코드를 보면 추가되는 GameplayAbility의 AbilityTriggers들을 검사하고 적절한 Map에 데이터를 저장하는 것을 볼 수 있습니다. TriggerSource 타입에 따라 GameplayEventTriggeredAbilitiesOwnedTagTriggeredAbilities에 저장됩니다.

Ability Trigger 정보가 저장되는 변수들

당연하게도 GameplayAbility가 제거될 때 호출되는 OnRemoveAbility에서는 Ability Trigger 정보가 제거됩니다.

 

[ Generate GameplayEvent ]

 ASC에는 HandleGameplayEvent() 함수를 제공합니다. 인자로 이벤트를 발생시키는 Tag와 추가적인 정보를 담을 수 있도록 GameplayeEventData 구조체를 포인터로 넘길 수 있게 제공합니다. 만약 GameplayEvent.Death 와 같은 Tag를 통해 Death Ability를 실행시키고 싶다면 아래의 HandleGameplayEvent의 Tag 인자를 통해 넘겨주면 됩니다. Lyra 프로젝트의 LyraHealthComponent 쪽에서 이 방식을 통해 구현했습니다.

AbilitySystemComponent::HandleGameplayEvent()

해당 함수의 일부 코드를 보면 OnGiveAbility() 함수에서 Trigger 정보를 저장해둔 GameplayEventTriggeredAbilities 변수를 이용하고 있습니다. 이벤트로 넘어온 Tag에 반응하는 어빌리티가 있다면 TriggerAbilityFromGameplayEvent() 함수를 호출합니다. 주의할 점은 Event Tag의 Parent까지 돌면서 찾는다는 것입니다.

 

 

[ TriggerAbilityFromGameplayEvent ]

 ASC에는 GameplayEvent에 따라 실행할 GameplayAbility에 대해서는 일반적인 TryActivateAbility() 와는 다른 방식으로 실행합니다. TriggerAbilityFromGameplayEvent() 함수 내부에서는 HasNetworkAuthorityToActivateTriggeredAbility()를 통해서 어빌리티를 실행시키려는 환경이 Authoirty를 갖고 있는지 확인합니다. Authority를 갖고 있는지 여부는 GameplayAbility의 Execution Policy를 통해 판단합니다.

AbilitySystemComponent::TriggerAbilityFromGameplayEvent()

ASC의 GameplayAbility 실행 함수인 TryActivateAbility() 는 내부적으로 Execution Policy를 확인하고 RPC를 통해 적절한 환경에서 호출할 수 있도록 지원합니다. 하지만 GameplayEvent를 통해 실행하는 경우엔 return false를 통해 어빌리티 실행 자체를 막습니다. 그렇기 때문에 Execution Policy에 맞는 환경에서 어빌리티를 실행시켜주어야 합니다.

 어빌리티의 Execution Policy가 LocalPredicted 옵션이라면 Local에서 실행 후 ServerTryActivateAbilityWithEventData() RPC를 통해 서버에 알립니다. 이벤트 데이터 또한 보내주게 되어 있습니다. 반대로 ServerInitiated 옵션에는 서버에서 실행 후에 ClientActivateAbilitySucceedWithEventData() RPC를 통해 클라에 보내줍니다. 역시 이벤트 데이터도 보냅니다. 코드 상으로는 ServerInitiated 라고 명시되어 있진 않습니다.

 위에서 말씀드린 정확한 코드는 UAbilitySystemComponent::InternalTryActivateAbility() 쪽에서 확인할 수 있습니다.