Skip to content

UE5 AnimBlueprint C++ 연동 가이드

Animation Blueprint(AnimBP)는 블루프린트 비주얼 스크립팅으로 애니메이션 로직을 구현하지만, 데이터 소스와 이벤트 처리는 C++로 분리하는 것이 유지보수성과 성능 모두에 유리합니다.

C++ 연동의 핵심은 UAnimInstance 서브클래스입니다. AnimBP가 이를 상속하도록 설정하면, C++에서 선언한 변수와 함수를 AnimBP 그래프에서 직접 사용할 수 있습니다.


MyAnimInstance.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "MyAnimInstance.generated.h"
UCLASS()
class MYGAME_API UMyAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
virtual void NativeInitializeAnimation() override;
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
// AnimBP 그래프에서 읽는 변수 — BlueprintReadOnly로 노출
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Movement")
float Speed = 0.f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Movement")
bool bIsInAir = false;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat")
bool bIsAiming = false;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat")
float AimPitch = 0.f;
private:
// 캐싱 — NativeUpdateAnimation에서 매 프레임 GetOwningPawn() 호출 방지
TWeakObjectPtr<class AMyCharacter> CachedCharacter;
TWeakObjectPtr<class UCharacterMovementComponent> CachedMovement;
};
MyAnimInstance.cpp
#include "MyAnimInstance.h"
#include "MyCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
void UMyAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
// 소유 폰 캐싱 — 초기화 시 한 번만 수행
if (APawn* Pawn = GetOwningPawn())
{
CachedCharacter = Cast<AMyCharacter>(Pawn);
if (CachedCharacter.IsValid())
{
CachedMovement = CachedCharacter->GetCharacterMovement();
}
}
}
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
if (!CachedCharacter.IsValid() || !CachedMovement.IsValid()) return;
// 속도: XY 평면 기준
Speed = CachedMovement->Velocity.Size2D();
bIsInAir = CachedMovement->IsFalling();
bIsAiming = CachedCharacter->IsAiming();
// 에임 피치: 컨트롤러 회전 기반
if (AController* Controller = CachedCharacter->GetController())
{
AimPitch = Controller->GetControlRotation().Pitch;
// Pitch는 0~360 범위 — -90~90으로 정규화
if (AimPitch > 180.f) AimPitch -= 360.f;
}
}

NativeThreadSafeUpdateAnimation은 워커 스레드에서 실행되는 Thread-Safe 업데이트입니다. UObject 접근 없이 순수 계산만 가능하며, 멀티코어 활용으로 성능이 향상됩니다.

  1. AnimBP 에디터 → Class SettingsParent ClassUMyAnimInstance로 변경
  2. 이후 AnimBP 그래프에서 Speed, bIsInAir 등의 변수가 노출됩니다

// 캐릭터에서 AnimInstance 접근
void AMyCharacter::TriggerAttackAnim()
{
if (USkeletalMeshComponent* Mesh = GetMesh())
{
if (UMyAnimInstance* AnimInst = Cast<UMyAnimInstance>(Mesh->GetAnimInstance()))
{
// 직접 함수 호출 또는 변수 설정
AnimInst->PlayAttackMontage();
}
}
}
// 몽타주 재생
void UMyAnimInstance::PlayAttackMontage()
{
if (AttackMontage)
{
Montage_Play(AttackMontage, 1.0f);
}
}
// 몽타주 완료 델리게이트 바인딩
void UMyAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
// 몽타주 종료 이벤트 등록
OnMontageEnded.AddDynamic(this, &UMyAnimInstance::HandleMontageEnded);
}
UFUNCTION()
void UMyAnimInstance::HandleMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
if (Montage == AttackMontage)
{
// 공격 몽타주 종료 처리
bIsAttacking = false;
}
}

3. AnimNotify & AnimNotifyState C++ 구현

Section titled “3. AnimNotify & AnimNotifyState C++ 구현”

3.1 UAnimNotify — 단일 시점 이벤트

Section titled “3.1 UAnimNotify — 단일 시점 이벤트”
AttackHitNotify.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "AttackHitNotify.generated.h"
UCLASS()
class MYGAME_API UAttackHitNotify : public UAnimNotify
{
GENERATED_BODY()
public:
// Notify 발동 시 호출
virtual void Notify(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference) override;
// 에디터 표시 이름
virtual FString GetNotifyName_Implementation() const override
{
return TEXT("AttackHit");
}
UPROPERTY(EditAnywhere, Category = "Hit")
float HitRadius = 50.f;
UPROPERTY(EditAnywhere, Category = "Hit")
FName SocketName = TEXT("WeaponSocket");
};
AttackHitNotify.cpp
void UAttackHitNotify::Notify(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference)
{
Super::Notify(MeshComp, Animation, EventReference);
if (!MeshComp) return;
AActor* Owner = MeshComp->GetOwner();
if (!Owner) return;
// 소켓 위치에서 Sphere Trace로 히트 판정
FVector SocketLocation = MeshComp->GetSocketLocation(SocketName);
TArray<FHitResult> HitResults;
FCollisionQueryParams Params;
Params.AddIgnoredActor(Owner);
Owner->GetWorld()->SweepMultiByChannel(
HitResults, SocketLocation, SocketLocation,
FQuat::Identity, ECC_Pawn,
FCollisionShape::MakeSphere(HitRadius), Params);
for (const FHitResult& Hit : HitResults)
{
if (AActor* HitActor = Hit.GetActor())
{
// 데미지 적용
UGameplayStatics::ApplyDamage(HitActor, 10.f, Owner->GetInstigatorController(), Owner, nullptr);
}
}
}
FootstepTrailNotifyState.h
UCLASS()
class MYGAME_API UFootstepTrailNotifyState : public UAnimNotifyState
{
GENERATED_BODY()
public:
// 구간 시작
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
float TotalDuration,
const FAnimNotifyEventReference& EventReference) override;
// 구간 중 매 프레임
virtual void NotifyTick(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
float FrameDeltaTime,
const FAnimNotifyEventReference& EventReference) override;
// 구간 종료
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp,
UAnimSequenceBase* Animation,
const FAnimNotifyEventReference& EventReference) override;
};

Blend Space의 축 값은 AnimBP의 변수로 전달됩니다. C++에서 변수를 설정하면 Blend Space가 자동으로 보간됩니다.

// AnimInstance에서 Blend Space 파라미터 관리
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Locomotion")
float MoveSpeed = 0.f; // Blend Space X축
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Locomotion")
float MoveDirection = 0.f; // Blend Space Y축 (strafe)
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
if (!CachedCharacter.IsValid()) return;
FVector Velocity = CachedCharacter->GetVelocity();
MoveSpeed = Velocity.Size2D();
// 이동 방향: 캐릭터 전방 기준 각도
if (MoveSpeed > 1.f)
{
FVector Forward = CachedCharacter->GetActorForwardVector();
FVector VelocityDir = Velocity.GetSafeNormal2D();
float Dot = FVector::DotProduct(Forward, VelocityDir);
float Cross = FVector::CrossProduct(Forward, VelocityDir).Z;
MoveDirection = FMath::RadiansToDegrees(FMath::Atan2(Cross, Dot));
}
else
{
MoveDirection = 0.f;
}
}

AnimBP 컴파일러는 C++ UPROPERTY를 직접 읽는 노드를 Fast Path로 최적화합니다. Fast Path는 블루프린트 VM을 거치지 않고 메모리를 직접 참조하므로 성능이 크게 향상됩니다.

Fast Path 조건:

  • Member Variable 노드 (BlueprintReadOnly UPROPERTY)
  • Negated Boolean 노드
  • Integer/Float Comparison 노드
// Fast Path 적용 가능 — 단순 멤버 변수
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Movement")
float Speed;
// Fast Path 불가 — 함수 반환값은 VM 경유
UFUNCTION(BlueprintPure)
float GetSpeed() const { return Speed; } // AnimBP에서 이 함수를 쓰면 Fast Path 깨짐
// SkeletalMesh에서 Update Rate 설정 (멀리 있는 캐릭터 틱 절감)
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (USkeletalMeshComponent* Mesh = GetMesh())
{
// 화면 비율에 따라 자동으로 Tick Rate 조절
Mesh->bEnableUpdateRateOptimizations = true;
Mesh->AnimUpdateRateParams->bSkipAnimationInterpolation = false;
}
}

목적권장 방법
AnimBP에 데이터 공급UAnimInstance 서브클래스 + UPROPERTY
단일 시점 이벤트UAnimNotify::Notify()
구간 이벤트UAnimNotifyState::NotifyBegin/Tick/End()
Blend Space 제어AnimInstance 변수로 축 값 전달
성능 최적화Fast Path (멤버 변수 직접 노출) + Update Rate 설정