PART 1 - 강의 2/3

어빌리티 활성화 흐름

TryActivateAbility부터 EndAbility까지의 전체 시퀀스

01

활성화 시퀀스 개요

클라이언트 예측 어빌리티의 전체 흐름

클라이언트 예측 어빌리티 활성화 시퀀스 1. 클라이언트: TryActivateAbility() 호출 | 2. InternalTryActivateAbility() 호출 | 3. CanActivateAbility() 검사 - 태그 요구사항 확인 - 비용(Cost) 확인 - 쿨다운 확인 - 활성 인스턴스 확인 | 4. Prediction Key 생성 후 CallServerTryActivateAbility() 호출 | 5. 서버 응답 전에 CallActivateAbility() 호출 (예측 실행) | 6. PreActivate() -> ActivateAbility() 순차 호출 | 7. 서버 검증 결과에 따라: - 성공: 계속 진행 - 실패: ClientActivateAbilityFailed() 호출 -> 롤백
중요: 클라이언트 예측

LocalPredicted 어빌리티는 서버 응답을 기다리지 않고 즉시 실행합니다. 서버가 거부하면 모든 예측된 변경사항이 롤백됩니다.

02

CanActivateAbility 검사

어빌리티 활성화 전 검증 단계

태그 요구사항

ActivationRequiredTags 보유, ActivationBlockedTags 미보유, 소스/타겟 태그 조건 확인

비용 검사

CostGameplayEffect로 정의된 어트리뷰트 비용 충족 여부 (마나, 스태미나 등)

쿨다운 검사

CooldownGameplayEffect로 부여된 쿨다운 태그의 활성 여부 확인

인스턴스 검사

Instancing Policy에 따른 동시 활성화 가능 여부, 이미 활성 상태인지 확인

태그 요구사항 구조 // GameplayAbility에서 설정 UPROPERTY(EditDefaultsOnly, Category = "Tags") FGameplayTagContainer ActivationOwnedTags; // 활성화 시 부여할 태그 UPROPERTY(EditDefaultsOnly, Category = "Tags") FGameplayTagContainer ActivationRequiredTags; // 필수 태그 (모두 있어야 함) UPROPERTY(EditDefaultsOnly, Category = "Tags") FGameplayTagContainer ActivationBlockedTags; // 차단 태그 (하나라도 있으면 불가) UPROPERTY(EditDefaultsOnly, Category = "Tags") FGameplayTagContainer CancelAbilitiesWithTag; // 이 태그 가진 어빌리티 취소 UPROPERTY(EditDefaultsOnly, Category = "Tags") FGameplayTagContainer BlockAbilitiesWithTag; // 이 태그 가진 어빌리티 차단
03

ActivateAbility 구현

어빌리티 로직의 핵심 진입점

ActivateAbility 구현 예제 void UMyGameplayAbility::ActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) { // 권한 또는 예측 키가 있는지 확인 if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)) { // 비용과 쿨다운 커밋 if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) { EndAbility(Handle, ActorInfo, ActivationInfo, true, true); return; } // 어빌리티 로직 실행 PerformAbilityAction(); } // 중요: EndAbility 호출은 개발자 책임! }
EndAbility 호출 필수

모든 어빌리티는 반드시 EndAbility()를 호출해야 합니다. 호출하지 않으면 어빌리티가 영원히 활성 상태로 남아 리소스 누수와 버그를 유발합니다.

04

CommitAbility 분해

비용과 쿨다운을 적용하는 핵심 함수

CommitAbility 내부 동작 // CommitAbility() = CommitCost() + CommitCooldown() bool UGameplayAbility::CommitAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, FGameplayTagContainer* OptionalRelevantTags) { // 1. 비용 적용 (마나 소모 등) if (!CommitAbilityCost(Handle, ActorInfo, ActivationInfo, OptionalRelevantTags)) { return false; } // 2. 쿨다운 적용 if (!CommitAbilityCooldown(Handle, ActorInfo, ActivationInfo, false, OptionalRelevantTags)) { return false; } return true; } // 비용만 따로 적용하고 싶을 때 bool CommitAbilityCost(...); // 쿨다운만 따로 적용하고 싶을 때 bool CommitAbilityCooldown(...); // 적용 없이 검사만 하고 싶을 때 bool CheckCost(...); bool CheckCooldown(...);
분리 호출 사용 사례
  • CheckCost -> PerformAction -> CommitCost: 액션 성공 시에만 비용 소모
  • CommitCost -> ... -> CommitCooldown: 차징 완료 시 쿨다운 시작
  • CommitAbility 생략: 패시브 어빌리티 (비용/쿨다운 없음)
05

EndAbility 처리

어빌리티 정리와 종료 플래그

EndAbility 구현 void UMyGameplayAbility::EndAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) { // 정리 로직 // - 활성 태스크 종료 // - 임시 GameplayEffect 제거 // - 델리게이트 언바인드 // 부모 호출 필수! Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); } // 종료 파라미터: // bReplicateEndAbility: true면 네트워크로 종료 복제 // bWasCancelled: true면 외부에서 취소됨 (vs 정상 완료)
종료 방식 bReplicateEndAbility bWasCancelled
정상 완료 true false
외부 취소 (다른 어빌리티에 의해) true true
로컬 전용 종료 false false
서버 거부로 인한 실패 true true
SUMMARY

핵심 요약

  • 활성화 시퀀스: TryActivate -> CanActivate -> PreActivate -> ActivateAbility
  • CanActivateAbility: 태그, 비용, 쿨다운, 인스턴스 조건 검사
  • CommitAbility = CommitCost + CommitCooldown (분리 호출 가능)
  • EndAbility 호출 필수 - 미호출 시 리소스 누수
  • 클라이언트 예측 시 서버 거부 -> 롤백 처리
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: TryActivateAbility 호출 흐름

GiveAbility()로 어빌리티를 부여하고 TryActivateAbilityByClass()로 활성화하세요. CanActivateAbility(), ActivateAbility(), EndAbility()의 호출 순서를 로그로 확인하세요.

실습 2: GameplayTag 기반 활성화

AbilityTags, BlockAbilitiesWithTag, ActivationBlockedTags를 설정하여 특정 상태에서 어빌리티가 차단되는 시스템을 구현하세요.

심화 과제

undefined