PART 3 · 강의 2/3

Anim Notify와 Notify State

UAnimNotify, UAnimNotifyState의 구조, NotifyTrigger 메커니즘, 커스텀 Notify 구현을 심층 분석합니다.

SECTION 01

Anim Notify 아키텍처

UAnimNotify의 실행 모델과 트리거 타이밍

UAnimNotify는 애니메이션의 특정 시간에 이벤트를 발생시키는 메커니즘입니다. 사운드 재생, 이펙트 스폰, 게임플레이 로직 트리거 등 애니메이션과 동기화된 이벤트 처리에 사용됩니다.

Notify 타입 기반 클래스 특징 사용 사례
Anim Notify UAnimNotify 단일 시점 이벤트, Notify 함수 한 번 호출 사운드, 파티클 스폰, 히트 판정
Anim Notify State UAnimNotifyState 시작-틱-종료 구간 이벤트 트레일 이펙트, 무적 구간, 콤보 윈도우
Blueprint Notify AnimBP EventGraph AnimBP에서 직접 처리하는 경량 이벤트 간단한 프로퍼티 변경, 디버그
C++
// 커스텀 AnimNotify 구현 UCLASS() class UAnimNotify_PlayFootstep : public UAnimNotify { GENERATED_BODY() public: UPROPERTY(EditAnywhere, Category = "Footstep") USoundBase* FootstepSound; UPROPERTY(EditAnywhere, Category = "Footstep") FName FootBoneName = FName("foot_l"); virtual void Notify( USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override { if (!MeshComp || !FootstepSound) return; // 발 위치에서 사운드 재생 FVector FootLocation = MeshComp->GetBoneLocation(FootBoneName); UGameplayStatics::PlaySoundAtLocation( MeshComp->GetWorld(), FootstepSound, FootLocation); // 표면 타입에 따른 다른 사운드 선택 FHitResult Hit; FVector TraceStart = FootLocation; FVector TraceEnd = TraceStart - FVector(0, 0, 50.f); // LineTrace로 바닥 머티리얼 확인... } };
SECTION 02

Anim Notify State

구간 기반 NotifyState의 Begin/Tick/End 라이프사이클

UAnimNotifyState는 시작과 끝이 있는 구간 이벤트입니다. NotifyBegin에서 시작, NotifyTick에서 매 프레임 업데이트, NotifyEnd에서 정리를 수행합니다.

NotifyState 라이프사이클
NotifyBegin
NotifyTick (매 프레임)
NotifyTick ...
NotifyEnd
C++
// 커스텀 AnimNotifyState: 무기 트레일 이펙트 UCLASS() class UAnimNotifyState_WeaponTrail : public UAnimNotifyState { GENERATED_BODY() public: UPROPERTY(EditAnywhere, Category = "Trail") UNiagaraSystem* TrailEffect; UPROPERTY(EditAnywhere, Category = "Trail") FName StartSocket = FName("weapon_start"); UPROPERTY(EditAnywhere, Category = "Trail") FName EndSocket = FName("weapon_end"); virtual void NotifyBegin( USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override { // 트레일 이펙트 시작 if (TrailEffect && MeshComp) { SpawnedComponent = UNiagaraFunctionLibrary::SpawnSystemAttached( TrailEffect, MeshComp, StartSocket, FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::SnapToTarget, true); } } virtual void NotifyTick( USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime, const FAnimNotifyEventReference& EventReference) override { // 매 프레임 트레일 포인트 업데이트 } virtual void NotifyEnd( USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override { // 트레일 이펙트 정리 if (SpawnedComponent) { SpawnedComponent->DestroyComponent(); } } private: UPROPERTY() UNiagaraComponent* SpawnedComponent = nullptr; };
주의: NotifyEnd 보장

Montage가 중단되거나 블렌드 아웃되면 NotifyEnd가 호출되지 않을 수 있습니다. 리소스 정리가 필요한 NotifyState에서는 bIsNativeBranchingPoint = true 설정으로 확실한 End 콜백을 보장하거나, 별도의 정리 메커니즘을 마련해야 합니다.

SECTION 03

Notify Trigger 메커니즘

Notify가 트리거되는 조건과 블렌딩 시 동작 규칙

설정 설명
Notify Filter Type No Filtering: 항상 트리거. LOD: 특정 LOD에서만 트리거
Montage Tick Type Queued: 다음 틱 실행. Branching Point: 즉시 실행
Trigger Weight Threshold 블렌딩 시 애니메이션 웨이트가 이 값 이상일 때만 트리거 (기본 0.0)
bTriggerOnDedicatedServer 데디케이티드 서버에서 트리거 여부 (사운드/이펙트는 false)
Notify Color 에디터에서 Notify 트랙의 시각적 색상 (기능 영향 없음)
BlendSpace와 Notify

BlendSpace 내 여러 애니메이션에 Notify가 있을 때, 가장 높은 웨이트를 가진 애니메이션의 Notify만 트리거됩니다 (Highest Weighted Anim 설정). 웨이트 기반 필터링으로 불필요한 이중 트리거를 방지합니다.

SECTION 04

실전 Notify 패턴

히트 판정, 이펙트 동기화, GAS 연동 패턴

히트 판정 콤보

NotifyState로 "AttackWindow" 구간을 설정. NotifyBegin에서 콜리전 활성화, NotifyEnd에서 비활성화. Tick에서 Sweep을 수행하여 다단 히트를 판정합니다.

발소리 시스템

Notify에서 LineTrace로 바닥 Physical Material을 확인하고, 재질별 다른 사운드를 재생합니다. MetaSound와 연동하면 동적 변조도 가능합니다.

GAS GameplayEvent

AnimNotify에서 UAbilitySystemBlueprintLibrary::SendGameplayEventToActor로 GameplayEvent를 발송하여 Ability를 활성화합니다.

카메라 셰이크

강한 공격이나 착지 시 Notify에서 PlayerCameraManager->StartCameraShake를 호출하여 화면 흔들림을 연출합니다.

C++
// GAS 연동 AnimNotify UCLASS() class UAnimNotify_SendGameplayEvent : public UAnimNotify { GENERATED_BODY() public: UPROPERTY(EditAnywhere, Category = "GameplayAbility") FGameplayTag EventTag; virtual void Notify( USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override { AActor* Owner = MeshComp->GetOwner(); if (!Owner) return; FGameplayEventData EventData; EventData.EventTag = EventTag; EventData.Instigator = Owner; UAbilitySystemBlueprintLibrary::SendGameplayEventToActor( Owner, EventTag, EventData); } };
SUMMARY

핵심 요약

  • UAnimNotify는 단일 시점, UAnimNotifyState는 Begin/Tick/End 구간 이벤트를 제공한다.
  • Branching Point 타입은 서브프레임 정밀도로 즉시 실행되어, 콤보 입력이나 히트 판정에 적합하다.
  • Trigger Weight Threshold로 블렌딩 시 낮은 웨이트 애니메이션의 Notify 트리거를 방지할 수 있다.
  • NotifyState의 NotifyEnd는 Montage 중단 시 호출이 보장되지 않으므로 별도 정리 메커니즘이 필요하다.
  • GAS와 연동하여 AnimNotify에서 GameplayEvent를 발송하는 것이 실전 패턴이다.
PRACTICE

도전 과제

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

실습 1: 기본 Anim Notify 활용

공격 Montage의 타격 타이밍에 Play Sound Notify와 Play Particle Effect Notify를 추가하세요. 발걸음 애니메이션에도 Footstep Notify를 배치하고, AnimBP의 이벤트 그래프에서 AnimNotify_Footstep 이벤트를 처리하세요.

실습 2: 커스텀 Notify State 구현

AnimNotifyState를 상속하는 커스텀 Notify State를 블루프린트로 만드세요. NotifyBegin에서 무기 콜리전을 활성화하고, NotifyEnd에서 비활성화하는 히트 판정 윈도우 시스템을 구현하세요. NotifyTick에서 스윕 체크를 수행하세요.

심화 과제

C++에서 UAnimNotify를 상속한 커스텀 Notify 클래스를 만들어, 에디터에서 설정 가능한 프로퍼티(데미지 배율, 이펙트 종류)를 노출하세요. Branching Point Notify로 정밀 타이밍이 필요한 콤보 분기 시스템을 구현하세요.