icon안동민 개발노트

애니메이션 블루프린트 기초


 애니메이션 블루프린트는 언리얼 엔진에서 캐릭터의 복잡한 애니메이션 로직을 시각적으로 구현할 수 있게 해주는 강력한 도구입니다.

 이 절에서는 애니메이션 블루프린트의 기본 구조와 C++에서 이를 제어하고 확장하는 방법을 살펴보겠습니다.

애니메이션 블루프린트의 기본 구조

 애니메이션 블루프린트는 크게 애님 그래프, 이벤트 그래프, 그리고 스테이트 머신으로 구성됩니다.

 애님 그래프

 애님 그래프는 포즈를 계산하고 블렌딩하는 주요 부분입니다.

 C++에서 애님 그래프의 결과에 접근하려면 다음과 같이 할 수 있습니다.

UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    virtual void NativeUpdateAnimation(float DeltaSeconds) override;
 
private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation", meta = (AllowPrivateAccess = "true"))
    float Speed;
};
 
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);
 
    APawn* Pawn = TryGetPawnOwner();
    if (Pawn)
    {
        Speed = Pawn->GetVelocity().Size();
    }
}

 스테이트 머신

 스테이트 머신은 캐릭터의 다양한 상태를 관리합니다.

 C++에서 현재 스테이트를 확인하거나 변경하려면,

UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Animation")
    void SetCurrentState(FName NewState);
 
    UFUNCTION(BlueprintPure, Category = "Animation")
    FName GetCurrentState() const;
 
private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation", meta = (AllowPrivateAccess = "true"))
    FName CurrentState;
};
 
void UMyAnimInstance::SetCurrentState(FName NewState)
{
    CurrentState = NewState;
}
 
FName UMyAnimInstance::GetCurrentState() const
{
    return CurrentState;
}

 블렌드 스페이스

 블렌드 스페이스는 여러 애니메이션을 부드럽게 혼합합니다.

 C++에서 블렌드 스페이스 파라미터를 조정하려면,

UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    virtual void NativeUpdateAnimation(float DeltaSeconds) override;
 
private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation", meta = (AllowPrivateAccess = "true"))
    float DirectionAngle;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation", meta = (AllowPrivateAccess = "true"))
    float Speed;
};
 
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);
 
    APawn* Pawn = TryGetPawnOwner();
    if (Pawn)
    {
        FVector Velocity = Pawn->GetVelocity();
        Speed = Velocity.Size();
        DirectionAngle = FMath::Atan2(Velocity.Y, Velocity.X);
    }
}

C++에서 커스텀 애니메이션 노드 생성

 커스텀 애니메이션 노드를 생성하여 애니메이션 블루프린트의 기능을 확장할 수 있습니다.

UCLASS()
class MYGAME_API UAnimNode_MyCustomNode : public UAnimNode_Base
{
    GENERATED_BODY()
 
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Links)
    FPoseLink BasePose;
 
    virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
    virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
    virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
    virtual void Evaluate_AnyThread(FPoseContext& Output) override;
 
private:
    // 커스텀 로직을 위한 변수들
};
 
void UAnimNode_MyCustomNode::Evaluate_AnyThread(FPoseContext& Output)
{
    BasePose.Evaluate(Output);
    // 여기에 포즈 수정 로직 추가
}

인스턴스 접근 및 변수 조작

 게임플레이 코드에서 애니메이션 블루프린트 인스턴스에 접근하고 변수를 조작하는 방법

UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()
 
public:
    void UpdateAnimationState();
 
private:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Animation", meta = (AllowPrivateAccess = "true"))
    UMyAnimInstance* AnimInstance;
};
 
void AMyCharacter::UpdateAnimationState()
{
    AnimInstance = Cast<UMyAnimInstance>(GetMesh()->GetAnimInstance());
    if (AnimInstance)
    {
        AnimInstance->SetCurrentState(FName("Running"));
        // 다른 변수들도 여기서 업데이트
    }
}

게임플레이 코드와의 효과적인 연동

  1. 이벤트 기반 통신
UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAnimationEventSignature);
 
    UPROPERTY(BlueprintAssignable, Category = "Animation")
    FOnAnimationEventSignature OnJumpStart;
 
    UFUNCTION(BlueprintCallable, Category = "Animation")
    void TriggerJumpStart();
};
 
void UMyAnimInstance::TriggerJumpStart()
{
    OnJumpStart.Broadcast();
}
  1. 인터페이스를 통한 통신
UINTERFACE(MinimalAPI, Blueprintable)
class UAnimationCommunicationInterface : public UInterface
{
    GENERATED_BODY()
};
 
class IAnimationCommunicationInterface
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Animation")
    void OnAnimationEvent(FName EventName);
};

애니메이션 시스템의 성능 최적화 기법

  1. LOD (Level of Detail) 시스템 활용
UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    virtual void NativeUpdateAnimation(float DeltaSeconds) override;
 
private:
    void UpdateLOD();
};
 
void UMyAnimInstance::UpdateLOD()
{
    USkeletalMeshComponent* MeshComponent = GetOwningComponent();
    if (MeshComponent)
    {
        int32 LODLevel = MeshComponent->GetPredictedLODLevel();
        // LOD 레벨에 따라 애니메이션 복잡도 조절
    }
}
  1. 애니메이션 압축 설정
void AMyCharacter::SetupAnimationCompression()
{
    UAnimSequence* AnimSequence = LoadObject<UAnimSequence>(nullptr, TEXT("/Game/Animations/MyAnimation"));
    if (AnimSequence)
    {
        AnimSequence->CompressionSettings.TranslationCompressionFormat = ACF_Float96NoW;
        AnimSequence->CompressionSettings.RotationCompressionFormat = ACF_Float96NoW;
        AnimSequence->CompressionSettings.ScaleCompressionFormat = ACF_Float96NoW;
        AnimSequence->RequestSyncAnimRecompression();
    }
}

복잡한 애니메이션 로직의 디버깅 방법

  1. 로깅 활용
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);
 
    UE_LOG(LogAnimation, Verbose, TEXT("Current State: %s, Speed: %f"), *GetCurrentState().ToString(), Speed);
}
  1. 비주얼 디버깅
void UMyAnimInstance::DrawDebug(UCanvas* Canvas, APlayerController* Controller)
{
    Super::DrawDebug(Canvas, Controller);
 
    FVector2D ScreenPosition(50.0f, 50.0f);
    FString DebugString = FString::Printf(TEXT("Current State: %s"), *GetCurrentState().ToString());
    FCanvasTextItem TextItem(ScreenPosition, FText::FromString(DebugString), GEngine->GetSmallFont(), FLinearColor::White);
    Canvas->DrawItem(TextItem);
}

프로젝트에서의 애니메이션 관리 전략

 1. 모듈화

 애니메이션 로직을 재사용 가능한 작은 단위로 분리합니다.

UCLASS()
class UAnimationModule_Locomotion : public UObject
{
    GENERATED_BODY()
 
public:
    void UpdateLocomotion(UMyAnimInstance* AnimInstance);
};
 
void UAnimationModule_Locomotion::UpdateLocomotion(UMyAnimInstance* AnimInstance)
{
    // 이동 관련 애니메이션 로직
}

 2. 데이터 주도 설계

 애니메이션 설정을 데이터 에셋으로 관리합니다.

UCLASS()
class UAnimationConfig : public UDataAsset
{
    GENERATED_BODY()
 
public:
    UPROPERTY(EditAnywhere, Category = "Animation")
    float WalkSpeed;
 
    UPROPERTY(EditAnywhere, Category = "Animation")
    float RunSpeed;
 
    UPROPERTY(EditAnywhere, Category = "Animation")
    UBlendSpace* LocomotionBlendSpace;
};

 3. 버전 관리 및 에셋 종속성 추적

 애니메이션 에셋의 버전을 관리하고 종속성을 추적합니다.

UCLASS()
class UAnimationVersionManager : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Animation")
    bool IsAnimationVersionCompatible(UAnimSequence* Animation, int32 RequiredVersion);
 
    UFUNCTION(BlueprintCallable, Category = "Animation")
    TArray<UAnimSequence*> GetDependentAnimations(UAnimSequence* Animation);
};

 애니메이션 블루프린트는 언리얼 엔진의 강력한 기능 중 하나로, 복잡한 애니메이션 로직을 효과적으로 구현할 수 있게 해줍니다.

 C++와의 연동을 통해 더욱 강력하고 유연한 애니메이션 시스템을 구축할 수 있습니다.