PART 2 · 강의 1/3

AnimGraph 실행 흐름

UAnimInstance, NativeUpdate, FAnimInstanceProxy의 내부 동작과 멀티스레드 평가 메커니즘을 심층 분석합니다.

SECTION 01

UAnimInstance 아키텍처

Animation Blueprint의 C++ 기반 클래스 UAnimInstance의 구조와 라이프사이클

UAnimInstance는 Animation Blueprint의 C++ 백엔드입니다. USkeletalMeshComponent마다 하나의 UAnimInstance가 존재하며, 게임플레이 데이터를 읽어 AnimGraph에 전달하고, 애니메이션 평가 결과를 관리합니다.

UAnimInstance 클래스 계층
UAnimInstance (Engine) ├── UAnimInstance::NativeInitializeAnimation() ← 초기화 ├── UAnimInstance::NativeUpdateAnimation() ← 매 프레임 업데이트 ├── UAnimInstance::NativePostEvaluateAnimation() ← 평가 후 처리 │ ├── FAnimInstanceProxy (내부 프록시) │ ├── PreUpdate() ← 게임 스레드에서 데이터 준비 │ ├── Update() ← 워커 스레드에서 실행 가능 │ └── Evaluate() ← 워커 스레드에서 AnimGraph 평가 │ └── 사용자 AnimBP (Blueprint 확장) ├── EventGraph ← BlueprintUpdateAnimation └── AnimGraph ← 노드 트리
프레임별 실행 순서
NativeUpdateAnimation
BlueprintUpdateAnimation
Proxy.PreUpdate
Proxy.Update
AnimGraph Evaluate
PostEvaluate
C++
// 커스텀 AnimInstance 클래스 UCLASS() class UMyAnimInstance : public UAnimInstance { GENERATED_BODY() public: // 초기화 - 컴포넌트 설정 시 한 번 호출 virtual void NativeInitializeAnimation() override; // 매 프레임 게임 스레드에서 호출 virtual void NativeUpdateAnimation(float DeltaSeconds) override; // AnimGraph 평가 완료 후 게임 스레드에서 호출 virtual void NativePostEvaluateAnimation() override; UPROPERTY(BlueprintReadOnly, Category = "Animation") float Speed = 0.f; UPROPERTY(BlueprintReadOnly, Category = "Animation") float Direction = 0.f; UPROPERTY(BlueprintReadOnly, Category = "Animation") bool bIsInAir = false; };
SECTION 02

FAnimInstanceProxy와 스레드 안전성

AnimGraph 노드가 워커 스레드에서 안전하게 실행되기 위한 프록시 패턴

FAnimInstanceProxy는 UAnimInstance의 데이터를 워커 스레드에서 안전하게 접근하기 위한 프록시 구조체입니다. AnimGraph 노드는 UAnimInstance가 아닌 FAnimInstanceProxy를 통해서만 데이터에 접근합니다.

메서드 스레드 역할
PreUpdate() Game Thread UAnimInstance에서 데이터를 Proxy로 복사
Update(DeltaTime) Worker Thread AnimGraph 노드 Update 호출, Montage 틱
Evaluate(Output) Worker Thread AnimGraph 노드 트리 평가, FCompactPose 생성
PostUpdate() Game Thread Proxy 결과를 UAnimInstance로 복사
스레드 안전성 규칙

AnimGraph 노드의 Update_AnyThreadEvaluate_AnyThread에서는 UAnimInstance에 직접 접근하면 안 됩니다. 이 함수들은 워커 스레드에서 실행될 수 있으며, 게임 스레드의 UAnimInstance 데이터와 경합(race condition)이 발생합니다. 반드시 FAnimInstanceProxy를 통해 데이터에 접근하세요.

C++
// 커스텀 AnimInstanceProxy struct FMyAnimInstanceProxy : public FAnimInstanceProxy { FMyAnimInstanceProxy() = default; FMyAnimInstanceProxy(UAnimInstance* InAnimInstance) : FAnimInstanceProxy(InAnimInstance) {} // 게임 스레드에서 호출 - UAnimInstance 데이터 복사 virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override { Super::PreUpdate(InAnimInstance, DeltaSeconds); UMyAnimInstance* MyAnim = Cast<UMyAnimInstance>(InAnimInstance); CachedSpeed = MyAnim->Speed; CachedDirection = MyAnim->Direction; } // 워커 스레드에서 안전하게 사용 가능한 캐시 데이터 float CachedSpeed = 0.f; float CachedDirection = 0.f; }; // GetProxyOnAnyThread / GetProxyOnGameThread 사용 FAnimInstanceProxy& Proxy = AnimInstance->GetProxyOnGameThread<FMyAnimInstanceProxy>();
SECTION 03

AnimGraph 노드 트리 평가

FAnimNode_Base를 루트로 하는 AnimGraph 노드 트리의 평가 메커니즘

AnimGraph는 FAnimNode_Base를 상속하는 노드들의 트리 구조입니다. 최종 출력에서 시작하여 트리를 역방향으로 순회하며, 각 노드가 순차적으로 포즈를 계산하고 블렌딩합니다.

AnimGraph 평가 흐름 (역방향 순회)
[Output Pose] ← 최종 포즈 출력 ↑ [Layered Blend Per Bone] ↑ ↑ [State Machine] [Slot 'UpperBody'] ↑ ↑ [BlendSpace2D] [Montage] ↑ [Sequence Player] 평가 순서: Sequence Player → BlendSpace2D → State Machine Montage → Slot → Layered Blend → Output

Initialize_AnyThread

노드 초기화. 필요한 리소스 할당, 본 참조 캐시. AnimGraph 로드 시 한 번 호출됩니다.

Update_AnyThread

매 프레임 노드 상태 업데이트. DeltaTime 적용, 웨이트 계산, Blend 진행 등. 워커 스레드에서 실행됩니다.

Evaluate_AnyThread

실제 포즈 계산. 입력 포즈를 받아 처리 후 출력 포즈를 FPoseContext에 기록합니다.

CacheBones_AnyThread

LOD 변경 시 호출. 새 LOD의 RequiredBones에 맞게 본 인덱스를 재매핑합니다.

C++
// 커스텀 AnimNode 구현 예시 USTRUCT(BlueprintInternalUseOnly) struct FAnimNode_ScaleBySpeed : public FAnimNode_Base { GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Links) FPoseLink BasePose; // 입력 포즈 링크 UPROPERTY(EditAnywhere, Category = Settings) float SpeedScale = 1.f; // 노드 업데이트 (워커 스레드 가능) virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override { BasePose.Update(Context); // DeltaTime 기반 로직 } // 포즈 평가 (워커 스레드 가능) virtual void Evaluate_AnyThread(FPoseContext& Output) override { BasePose.Evaluate(Output); // 루트 본 스케일 수정 FCompactPoseBoneIndex RootIndex(0); FTransform& RootTransform = Output.Pose[RootIndex]; RootTransform.ScaleTranslation(SpeedScale); } };
SECTION 04

NativeUpdate와 Blueprint 연동

C++ NativeUpdate와 Blueprint EventGraph의 협력 패턴

실무에서는 C++ NativeUpdateAnimation에서 성능 민감한 데이터 수집을 수행하고, Blueprint EventGraph에서는 시각적 로직이나 간단한 분기 처리를 담당하는 하이브리드 패턴이 일반적입니다.

C++
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds) { Super::NativeUpdateAnimation(DeltaSeconds); APawn* OwnerPawn = TryGetPawnOwner(); if (!OwnerPawn) return; // 속도 계산 (C++에서 효율적으로) FVector Velocity = OwnerPawn->GetVelocity(); Speed = Velocity.Size2D(); // 이동 방향 계산 FRotator ActorRotation = OwnerPawn->GetActorRotation(); Direction = CalculateDirection(Velocity, ActorRotation); // 공중 여부 ACharacter* Character = Cast<ACharacter>(OwnerPawn); if (Character) { bIsInAir = Character->GetCharacterMovement()->IsFalling(); } } // Blueprint에서 접근 가능한 프로퍼티로 노출 // AnimBP EventGraph에서 추가 로직 수행 가능
성능 팁: Property Access 시스템

UE5에서는 Property Access 시스템을 통해 AnimGraph 노드에서 직접 프로퍼티를 바인딩할 수 있습니다. EventGraph를 거치지 않고 AnimGraph 핀에서 바로 Character.Movement.Velocity 같은 경로로 접근하면, Blueprint VM 오버헤드를 줄이고 멀티스레드 평가와의 호환성을 높일 수 있습니다.

UAnimInstance::NeedsImmediateUpdate

이 함수가 false를 반환하면 애니메이션 업데이트가 워커 스레드로 완전히 오프로드됩니다. 게임 스레드에서의 즉시 업데이트가 필요하지 않다면 false를 반환하도록 오버라이드하여 게임 스레드 부하를 줄일 수 있습니다.

SUMMARY

핵심 요약

  • UAnimInstance는 Animation Blueprint의 C++ 백엔드로, NativeUpdate/BlueprintUpdate/PostEvaluate 라이프사이클을 따른다.
  • FAnimInstanceProxy는 워커 스레드에서 AnimGraph를 안전하게 평가하기 위한 프록시 패턴이다.
  • AnimGraph 노드(FAnimNode_Base)는 Initialize/Update/Evaluate/CacheBones 가상 함수로 동작한다.
  • AnimGraph는 출력에서 입력 방향으로 역방향 트리 순회하며 포즈를 계산한다.
  • C++ NativeUpdate에서 데이터 수집, Blueprint에서 시각적 로직을 처리하는 하이브리드 패턴이 권장된다.
PRACTICE

도전 과제

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

실습 1: AnimBP 기본 구성

Animation Blueprint를 생성하고, Event Graph에서 캐릭터의 Velocity를 가져와 Speed 변수에 저장하세요. AnimGraph에서 이 변수를 사용하여 Idle/Walk 애니메이션을 전환하는 기본 그래프를 구성하세요.

실습 2: NativeUpdateAnimation 오버라이드

Blueprint에서 NativeUpdateAnimation 이벤트를 오버라이드하여 매 프레임 캐릭터 상태(Speed, IsFalling, Direction)를 업데이트하세요. Try Get Pawn Owner로 캐릭터를 캐스팅하고, 변수를 AnimGraph에서 참조하세요.

심화 과제

C++에서 UAnimInstance를 상속한 커스텀 AnimInstance 클래스를 만들고, NativeUpdateAnimation에서 FAnimInstanceProxy를 활용하여 워커 스레드 안전한 변수 업데이트를 구현하세요. Property Access 노드를 사용하여 게임 스레드 접근 없이 AnimGraph에서 직접 변수를 읽는 패턴도 적용하세요.