UE5 AnimBlueprint C++ 연동 가이드
개요 — AnimBlueprint와 C++의 경계
Section titled “개요 — AnimBlueprint와 C++의 경계”Animation Blueprint(AnimBP)는 블루프린트 비주얼 스크립팅으로 애니메이션 로직을 구현하지만, 데이터 소스와 이벤트 처리는 C++로 분리하는 것이 유지보수성과 성능 모두에 유리합니다.
C++ 연동의 핵심은 UAnimInstance 서브클래스입니다. AnimBP가 이를 상속하도록 설정하면, C++에서 선언한 변수와 함수를 AnimBP 그래프에서 직접 사용할 수 있습니다.
1. UAnimInstance 서브클래싱
Section titled “1. UAnimInstance 서브클래싱”1.1 C++ 클래스 선언
Section titled “1.1 C++ 클래스 선언”#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;};1.2 구현
Section titled “1.2 구현”#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.3 AnimBP에서 C++ 클래스 연결
Section titled “1.3 AnimBP에서 C++ 클래스 연결”- AnimBP 에디터 → Class Settings → Parent Class 를
UMyAnimInstance로 변경 - 이후 AnimBP 그래프에서
Speed,bIsInAir등의 변수가 노출됩니다
2. C++에서 AnimInstance 접근
Section titled “2. C++에서 AnimInstance 접근”// 캐릭터에서 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 — 단일 시점 이벤트”#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");};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); } }}3.2 UAnimNotifyState — 구간 이벤트
Section titled “3.2 UAnimNotifyState — 구간 이벤트”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;};4. Blend Space 파라미터 C++ 제어
Section titled “4. Blend Space 파라미터 C++ 제어”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; }}5. 최적화 전략
Section titled “5. 최적화 전략”5.1 Animation Fast Path
Section titled “5.1 Animation Fast Path”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 깨짐5.2 Update Rate Optimization
Section titled “5.2 Update Rate Optimization”// 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 설정 |