icon안동민 개발노트

블렌드 스페이스와 몽타주 활용


 블렌드 스페이스와 애니메이션 몽타주는 언리얼 엔진에서 복잡한 애니메이션 시스템을 구현하는 데 필수적인 도구입니다.

 이 절에서는 C++ 관점에서 이들의 고급 활용법을 살펴보겠습니다.

블렌드 스페이스

 블렌드 스페이스 생성 및 파라미터 제어

 블렌드 스페이스를 C++에서 생성하고 제어하는 방법은 다음과 같습니다.

UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    virtual void NativeUpdateAnimation(float DeltaSeconds) override;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
    UBlendSpace* LocomotionBlendSpace;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
    float Speed;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
    float Direction;
};
 
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);
 
    APawn* Pawn = TryGetPawnOwner();
    if (Pawn)
    {
        Speed = Pawn->GetVelocity().Size();
        Direction = CalculateDirection(Pawn->GetVelocity(), Pawn->GetActorRotation());
    }
 
    if (LocomotionBlendSpace)
    {
        FVector BlendInput(Speed, Direction, 0.f);
        LocomotionBlendSpace->GetBlendPose(BlendInput, PoseInstanceCache);
    }
}

 1D 및 2D 블렌드 스페이스

 1D 블렌드 스페이스는 단일 파라미터를, 2D 블렌드 스페이스는 두 개의 파라미터를 사용합니다.

// 1D 블렌드 스페이스 예시 (예 : 걷기 속도에 따른 블렌딩)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
UBlendSpace1D* WalkBlendSpace;
 
// 2D 블렌드 스페이스 예시 (예 : 이동 속도와 방향에 따른 블렌딩)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")
UBlendSpace* LocomotionBlendSpace;

 동적 블렌드 스페이스 조작

 런타임에 블렌드 스페이스를 동적으로 조작할 수 있습니다.

void UMyAnimInstance::UpdateBlendSpace(float NewSpeed, float NewDirection)
{
    if (LocomotionBlendSpace)
    {
        FVector BlendInput(NewSpeed, NewDirection, 0.f);
        FAnimationUpdateContext UpdateContext(this, GetDeltaTime());
        LocomotionBlendSpace->Update(UpdateContext, BlendInput);
    }
}

애니메이션 몽타주

 몽타주 구조 및 재생

 몽타주는 여러 애니메이션 섹션을 포함하는 복합 애니메이션입니다.

UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Animation")
    void PlayAttackMontage();
 
private:
    UPROPERTY(EditDefaultsOnly, Category = "Animation")
    UAnimMontage* AttackMontage;
};
 
void AMyCharacter::PlayAttackMontage()
{
    if (AttackMontage)
    {
        PlayAnimMontage(AttackMontage);
    }
}

 몽타주 섹션 관리

 몽타주의 특정 섹션을 재생하거나 점프할 수 있습니다.

void AMyCharacter::PlayMontageSection(FName SectionName)
{
    if (AttackMontage)
    {
        PlayAnimMontage(AttackMontage, 1.0f, SectionName);
    }
}
 
void AMyCharacter::JumpToMontageSection(FName SectionName)
{
    if (AttackMontage)
    {
        int32 SectionIndex = AttackMontage->GetSectionIndex(SectionName);
        if (SectionIndex != INDEX_NONE)
        {
            AttackMontage->JumpToSection(SectionName);
        }
    }
}

 복잡한 애니메이션 시퀀스 구현

 몽타주를 사용하여 복잡한 애니메이션 시퀀스를 구현할 수 있습니다.

UCLASS()
class UMyAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Animation")
    void PerformComboAttack(int32 ComboCount);
 
private:
    UPROPERTY(EditDefaultsOnly, Category = "Animation")
    UAnimMontage* ComboMontage;
};
 
void UMyAnimInstance::PerformComboAttack(int32 ComboCount)
{
    if (ComboMontage)
    {
        FName SectionName = FName(*FString::Printf(TEXT("Combo%d"), ComboCount));
        Montage_Play(ComboMontage);
        Montage_JumpToSection(SectionName, ComboMontage);
    }
}

블렌드 스페이스와 몽타주 조합

 블렌드 스페이스와 몽타주를 조합하여 자연스러운 캐릭터 동작을 구현할 수 있습니다.

UCLASS()
class UMyAdvancedAnimInstance : public UAnimInstance
{
    GENERATED_BODY()
 
public:
    virtual void NativeUpdateAnimation(float DeltaSeconds) override;
 
    UFUNCTION(BlueprintCallable, Category = "Animation")
    void PerformAction(EActionType ActionType);
 
private:
    UPROPERTY(EditDefaultsOnly, Category = "Animation")
    UBlendSpace* LocomotionBlendSpace;
 
    UPROPERTY(EditDefaultsOnly, Category = "Animation")
    TMap<EActionType, UAnimMontage*> ActionMontages;
 
    float Speed;
    float Direction;
};
 
void UMyAdvancedAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);
 
    APawn* Pawn = TryGetPawnOwner();
    if (Pawn)
    {
        Speed = Pawn->GetVelocity().Size();
        Direction = CalculateDirection(Pawn->GetVelocity(), Pawn->GetActorRotation());
    }
 
    if (LocomotionBlendSpace)
    {
        FVector BlendInput(Speed, Direction, 0.f);
        LocomotionBlendSpace->GetBlendPose(BlendInput, PoseInstanceCache);
    }
}
 
void UMyAdvancedAnimInstance::PerformAction(EActionType ActionType)
{
    if (UAnimMontage* ActionMontage = ActionMontages.FindRef(ActionType))
    {
        Montage_Play(ActionMontage);
    }
}

성능 최적화 기법

  1. 블렌드 스페이스 샘플링 최적화
void UMyAnimInstance::OptimizeBlendSpaceSampling()
{
    if (LocomotionBlendSpace)
    {
        LocomotionBlendSpace->SetBorderSampleDimensionDivisor(1.0f);
        LocomotionBlendSpace->SetInterpolationMode(EBlendSpaceInterpolationMode::Linear);
    }
}
  1. 몽타주 메모리 사용 최적화
void UMyAnimInstance::OptimizeMontageMemoryUsage()
{
    if (ComboMontage)
    {
        ComboMontage->CompressRawAnimData();
        ComboMontage->SetPreviewMesh(nullptr);
    }
}

네트워크 게임에서의 애니메이션 동기화

 네트워크 게임에서 애니메이션을 동기화하려면 다음과 같은 방법을 사용할 수 있습니다.

UCLASS()
class AMyNetworkedCharacter : public ACharacter
{
    GENERATED_BODY()
 
public:
    UFUNCTION(Server, Reliable, WithValidation)
    void ServerPerformAction(EActionType ActionType);
 
    UFUNCTION(NetMulticast, Reliable)
    void MulticastPerformAction(EActionType ActionType);
 
private:
    UPROPERTY(ReplicatedUsing = OnRep_CurrentAction)
    EActionType CurrentAction;
 
    UFUNCTION()
    void OnRep_CurrentAction();
};
 
void AMyNetworkedCharacter::ServerPerformAction_Implementation(EActionType ActionType)
{
    CurrentAction = ActionType;
    MulticastPerformAction(ActionType);
}
 
bool AMyNetworkedCharacter::ServerPerformAction_Validate(EActionType ActionType)
{
    return true; // Implement proper validation logic
}
 
void AMyNetworkedCharacter::MulticastPerformAction_Implementation(EActionType ActionType)
{
    // Perform the action on all clients
    UMyAdvancedAnimInstance* AnimInstance = Cast<UMyAdvancedAnimInstance>(GetMesh()->GetAnimInstance());
    if (AnimInstance)
    {
        AnimInstance->PerformAction(ActionType);
    }
}
 
void AMyNetworkedCharacter::OnRep_CurrentAction()
{
    // Update visual state based on the replicated action
    UMyAdvancedAnimInstance* AnimInstance = Cast<UMyAdvancedAnimInstance>(GetMesh()->GetAnimInstance());
    if (AnimInstance)
    {
        AnimInstance->PerformAction(CurrentAction);
    }
}

 블렌드 스페이스와 애니메이션 몽타주는 언리얼 엔진에서 복잡하고 자연스러운 캐릭터 애니메이션을 구현하는 데 필수적인 도구입니다.

 C++를 통해 이들을 세밀하게 제어하고 최적화함으로써, 고성능의 유연한 애니메이션 시스템을 구축할 수 있습니다.

 블렌드 스페이스를 사용하여 연속적인 애니메이션 전환을 구현하고, 몽타주를 통해 복잡한 애니메이션 시퀀스를 관리할 수 있습니다.

 두 기술을 적절히 조합하면, 캐릭터의 기본 움직임부터 복잡한 액션 시퀀스까지 다양한 동작을 효과적으로 구현할 수 있습니다.