캐릭터 컨트롤 및 기본 애니메이션 구현하기(2)

주상돈·2025년 1월 31일

TIL

목록 보기
21/53

Character 클래스에 액션 바인딩 추가하기


1️⃣ 캐릭터 클래스에서 입력 액션 연결의 개념

  • PlayerController는 "키나 마우스 입력"을 감지하고, 각 입력 액션 (IA)을 활성화해줄 뿐이다. 그다음 캐릭터 클래스(SpartaCharacter)에서 “어떤 함수가 호출될지”를 바인딩해주어야 최종적으로 동작이 이루어진다.
  • 정리하자면
    1. PlayerControllerSpartaInputMappingContext(IMC)를 활성화한다.
    2. 해당 IMC에는 IA_Move, IA_Jump 등의 UInputAction들이 키보드/마우스와 맵핑되어 있다.
    3. 캐릭터(SpartaCharacter)가 SetupPlayerInputComponent() 함수를 통해 “각 액션이 발생했을 때 어떤 함수를 실행할지”를 등록해준다.
    4. 이렇게 등록한 함수들이, 실제로 움직이거나 점프하는 등 캐릭터 동작을 수행한다.

2️⃣ 캐릭터에 액션 바인딩 추가하기

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpartaCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
// Enhanced Input에서 액션 값을 받을 때 사용하는 구조체
struct FInputActionValue;

UCLASS()
class SPARTAPROJECT_API ASpartaCharacter : public ACharacter
{
		GENERATED_BODY()

public:
		ASpartaCharacter();

protected:
		UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
		USpringArmComponent* SpringArmComp;
		UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
		UCameraComponent* CameraComp;
	
		// 입력 바인딩을 처리할 함수
		virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
		
		// IA_Move와 IA_Jump 등을 처리할 함수 원형
    // Enhanced Input에서 액션 값은 FInputActionValue로 전달됩니다.
	  UFUNCTION()
		void Move(const FInputActionValue& value);
		UFUNCTION()
		void StartJump(const FInputActionValue& value);
		UFUNCTION()
		void StopJump(const FInputActionValue& value);
		UFUNCTION()
		void Look(const FInputActionValue& value);
		UFUNCTION()
		void StartSprint(const FInputActionValue& value);
		UFUNCTION()
		void StopSprint(const FInputActionValue& value);
};

Character.h

#include "SpartaCharacter.h"
#include "SpartaPlayerController.h"
#include "EnhancedInputComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

ASpartaCharacter::ASpartaCharacter()
{
		PrimaryActorTick.bCanEverTick = false;

    SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    SpringArmComp->SetupAttachment(RootComponent);
    SpringArmComp->TargetArmLength = 300.0f;
    SpringArmComp->bUsePawnControlRotation = true;

    CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
    CameraComp->bUsePawnControlRotation = false;
}

void ASpartaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    // Enhanced InputComponent로 캐스팅
    if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent))
    {
        // IA를 가져오기 위해 현재 소유 중인 Controller를 ASpartaPlayerController로 캐스팅
        if (ASpartaPlayerController* PlayerController = Cast<ASpartaPlayerController>(GetController()))
        {
            if (PlayerController->MoveAction)
            {
                // IA_Move 액션 키를 "키를 누르고 있는 동안" Move() 호출
                EnhancedInput->BindAction(
                    PlayerController->MoveAction,
                    ETriggerEvent::Triggered,
                    this,
                    &ASpartaCharacter::Move
                );
            }
            
            if (PlayerController->JumpAction)
            {
                // IA_Jump 액션 키를 "키를 누르고 있는 동안" StartJump() 호출
                EnhancedInput->BindAction(
                    PlayerController->JumpAction,
                    ETriggerEvent::Triggered,
                    this,
                    &ASpartaCharacter::StartJump
                );
                
                // IA_Jump 액션 키에서 "손을 뗀 순간" StopJump() 호출
                EnhancedInput->BindAction(
                    PlayerController->JumpAction,
                    ETriggerEvent::Completed,
                    this,
                    &ASpartaCharacter::StopJump
                );
            }
            
            if (PlayerController->LookAction)
            {
                // IA_Look 액션 마우스가 "움직일 때" Look() 호출
                EnhancedInput->BindAction(
                    PlayerController->LookAction,
                    ETriggerEvent::Triggered,
                    this,
                    &ASpartaCharacter::Look
                );
            }
            
            if (PlayerController->SprintAction)
            {
                // IA_Sprint 액션 키를 "누르고 있는 동안" StartSprint() 호출
                EnhancedInput->BindAction(
                    PlayerController->SprintAction,
                    ETriggerEvent::Triggered, 
                    this, 
                    &ASpartaCharacter::StartSprint
                );
                // IA_Sprint 액션 키에서 "손을 뗀 순간" StopSprint() 호출
                EnhancedInput->BindAction(
                    PlayerController->SprintAction, 
                    ETriggerEvent::Completed, 
                    this, 
                    &ASpartaCharacter::StopSprint
                );
            }    
        }
    }
}

void ASpartaCharacter::Move(const FInputActionValue& value)
{
}

void ASpartaCharacter::StartJump(const FInputActionValue& value)
{
}

void ASpartaCharacter::StopJump(const FInputActionValue& value)
{
}

void ASpartaCharacter::Look(const FInputActionValue& value)
{
}

void ASpartaCharacter::StartSprint(const FInputActionValue& value)
{
}

void ASpartaCharacter::StopSprint(const FInputActionValue& value)
{
}

Character.cpp

  • FInputActionValue: Enhanced Input에서 액션 값 (축 이동값, 마우스 이동량 등)을 전달할 때 사용하는 구조체로, IA에서 설정한 Value Type이다.
  • UFUNCTION(): 입력 바인딩 함수는 언리얼 엔진 리플렉션 시스템과 연동되어야 한다. UFUNCTION()을 붙이지 않으면 바인딩에 실패할 수 있다.
    • 블루프린트 접근성을 설정하지 않았더라도, 기본적으로 메타데이터가 생성된다.
    • 언리얼 엔진의 입력 처리 시스템은 바인딩된 함수가 리플렉션 시스템을 통해 접근 가능한지 확한다.
  • BindAction 함수
    • 첫 번째 인자: 어떤 UInputAction과 연결할지. (예: MoveAction)
    • 두 번째 인자: 액션이 발생하는 트리거 이벤트 (Triggered, Ongoing, Completed 등).
    • 세 번째/네 번째 인자: 액션 발생 시 실행할 객체(this)와 함수 포인터.
  • 점프와 스프린트 함수 분리
    • “키를 누를 때”와 “뗄 때”가 다르게 처리될 수 있으므로 두 함수로 분리했다.

캐릭터의 이동 함수 구현하기


1️⃣ Move 함수 구현하기

void ASpartaCharacter::Move(const FInputActionValue& value)
{
		// 컨트롤러가 있어야 방향 계산이 가능
    if (!Controller) return;

		// Value는 Axis2D로 설정된 IA_Move의 입력값 (WASD)을 담고 있음
    // 예) (X=1, Y=0) → 전진 / (X=-1, Y=0) → 후진 / (X=0, Y=1) → 오른쪽 / (X=0, Y=-1) → 왼쪽
    const FVector2D MoveInput = value.Get<FVector2D>();

    if (!FMath::IsNearlyZero(MoveInput.X))
    {
		    // 캐릭터가 바라보는 방향(정면)으로 X축 이동
        AddMovementInput(GetActorForwardVector(), MoveInput.X);
    }

    if (!FMath::IsNearlyZero(MoveInput.Y))
    {
			   // 캐릭터의 오른쪽 방향으로 Y축 이동
        AddMovementInput(GetActorRightVector(), MoveInput.Y);
    }
}

Character.cpp

  • FInputActionValue::Get<FVector2D>()
    • IA_Move가 Axis2D로 설정되어 있으므로, 2차원 벡터 형태로 입력이 들어온다.
    • W(앞) / S(뒤) / D(오른쪽) / A(왼쪽)을 동시에 누를 수도 있으므로, (1,1) 같은 형태도 가능하다.
  • AddMovementInput(방향, 크기)
    • 첫 번째 파라미터: 월드 좌표 기준 이동 방향(Forward, Right 등)
    • 두 번째 파라미터: 이동 스케일(속도)
    • 내부적으로 CharacterMovementComponent가 이 요청을 받아 속도를 계산하고, 실제 이동을 구현한다.

캐릭터의 점프 함수 구현하기


1️⃣ StartJump, StopJump 함수 구현하기

void ASpartaCharacter::StartJump(const FInputActionValue& value)
{
    // Jump 함수는 Character가 기본 제공
    if (value.Get<bool>())
    {
        Jump();
    }
}

void ASpartaCharacter::StopJump(const FInputActionValue& value)
{
    // StopJumping 함수도 Character가 기본 제공
    if (!value.Get<bool>())
    {
        StopJumping();
    }
}

Character.cpp

  • value.Get<bool>()
    • Enhanced Input System에서 전달된 입력 값을 bool로 가져온다.
    • 이 값은 점프 키(예: 스페이스바)가 눌렸는지 여부를 나타낸다.
      • true: 키가 눌림.
      • false: 키가 눌리지 않음.
  • StopJumping() , Jump()
    • Character 클래스에서 기본 제공되는 함수로, 캐릭터가 점프를 하거나 멈추도록 만들어준다.

캐릭터의 기본 시점 회전 구현하기


void ASpartaCharacter::Look(const FInputActionValue& value)
{
    // 마우스의 X, Y 움직임을 2D 축으로 가져옴
    FVector2D LookInput = value.Get<FVector2D>();

    // X는 좌우 회전 (Yaw), Y는 상하 회전 (Pitch)
    // 좌우 회전
    AddControllerYawInput(LookInput.X);
    // 상하 회전
    AddControllerPitchInput(LookInput.Y);
}

Character.cpp

  • AddControllerYawInput(): 카메라의 Yaw 축 (수평 회전)을 변경
  • AddControllerPitchInput(): 카메라의 Pitch 축 (수직 회전)을 변경
  • 실제 어느 방향으로 얼마나 회전할지는, 프로젝트 세팅InputMouse SensitivityLookInput에 곱해줄 스케일 (Modifiers)을 통해 조정할 수 있다.

스프린트 동작 구현하기


1️⃣ 스프린트 관련 멤버 변수 및 함수 설정하기

  • 언리얼 엔진의 CharacterMovementComponent에는 MaxWalkSpeed라는 속성이 있다. MaxWalkSpeed 값을 변경하면, 캐릭터의 이동 속도가 즉시 바뀐다.
  • Shift 키를 누르는 순간 MaxWalkSpeed를 “빠른 속도”로, Shift 키를 뗄 때 (또는 놓았을 때) 다시 “기본 속도”로 돌려놓으면, 스프린트 효과를 낼 수 있다.
  • 우선, 캐릭터 클래스 SpartaCharacter.h 파일에서 스프린트 관련 멤버 변수를 추가해야 한다.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpartaCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
struct FInputActionValue;

UCLASS()
class QOOPROJECT_API ASpartaCharacter : public ACharacter
{
		GENERATED_BODY()

public:
		ASpartaCharacter();

protected:
		UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
		USpringArmComponent* SpringArmComp;
		UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
		UCameraComponent* CameraComp;
	
		// 이동 속도 관련 프로퍼티들
		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
		float NormalSpeed; // 기본 걷기 속도
		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
		float SprintSpeedMultiplier;  // "기본 속도" 대비 몇 배로 빠르게 달릴지 결정
		UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Movement")
		float SprintSpeed; 	// 실제 스프린트 속도
	
		virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	
		UFUNCTION()
		void Move(const FInputActionValue& Value);
		UFUNCTION()
		void StartJump(const FInputActionValue& Value);
		UFUNCTION()
		void StopJump(const FInputActionValue& Value);
		UFUNCTION()
		void Look(const FInputActionValue& Value);
		UFUNCTION()
		void StartSprint(const FInputActionValue& Value);
		UFUNCTION()
		void StopSprint(const FInputActionValue& Value);
};

Character.h

#include "SpartaCharacter.h"
#include "SpartaPlayerController.h"
#include "EnhancedInputComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"

ASpartaCharacter::ASpartaCharacter()
{
		PrimaryActorTick.bCanEverTick = false;

    SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    SpringArmComp->SetupAttachment(RootComponent);
    SpringArmComp->TargetArmLength = 300.0f;
    SpringArmComp->bUsePawnControlRotation = true;

    CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
    CameraComp->bUsePawnControlRotation = false;

    NormalSpeed = 600.0f;
    SprintSpeedMultiplier = 1.5f;
    SprintSpeed = NormalSpeed * SprintSpeedMultiplier;

    GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
}

Character.cpp

  • SprintSpeedMultiplier
    • 예: 1.5f → “기본 속도의 1.5배”
    • “Shift 키를 누른 상태”에서 이동 속도가 얼마나 빨라질지 결정
    • 필요에 따라 2.0f, 1.2f 등 자유롭게 조정 가능
  • SprintSpeed
    • “Shift 키를 누른 상태”에서 적용할 실제 속도값
    • 예) NormalSpeed 600 * 1.5f = 900
    • 숫자를 고정해두거나, 생성자에서 SprintSpeed = NormalSpeed * SprintSpeedMultiplier;로 연산할 수도 있다.

2️⃣ StartSprint, StopSprint 함수 구현하기

void ASpartaCharacter::StartSprint(const FInputActionValue& value)
{
    // Shift 키를 누른 순간 이 함수가 호출된다고 가정
    // 스프린트 속도를 적용
    if (GetCharacterMovement())
    {
        GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
    }
}

void ASpartaCharacter::StopSprint(const FInputActionValue& value)
{
    // Shift 키를 뗀 순간 이 함수가 호출
    // 평상시 속도로 복귀
    if (GetCharacterMovement())
    {
        GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
    }
}

Character.cpp

  • GetCharacterMovement() : ACharacter 클래스에 기본 내장된 함수로, UCharacterMovementComponent* 포인터를 반환한다.여기서 MaxWalkSpeed 등의 이동 관련 설정을 바꿀 수 있다.
  • AddMovementInput()는 키 입력 때마다 “어느 방향으로 이동”할지 전달하는 함수고,MaxWalkSpeed는 “최대 이동 속도가 얼마인지”를 결정한다. 둘이 함께 작동해 캐릭터가 더 빨리 달릴 수 있게 된다.

State Machine 설계를 통한 캐릭터 동작 애니메이션 적용하기

애니메이션 블루프린트 이해하기


1️⃣ 애니메이션 블루프린트 (Anim Blueprint)란?

  • 애니메이션 블루프린트는 언리얼 엔진에서 캐릭터의 골격(스켈레톤) 기반 애니메이션을 시각적으로 설계하는 데 특화된 전용 Blueprint이다.
  • 일반 블루프린트가 게임 로직(예: 캐릭터의 이동, 공격, 상호작용)을 시각적으로 구현하는 것과 유사하게, 애니메이션 블루프린트는 캐릭터의 골격 움직임과 모션 전이(Transition) 등을 그래프State Machine을 통해 손쉽게 구현하도록 돕는다.
  • 보통은 스켈레탈 메시, 애니메이션 시퀀스, State Machine 등을 연결하는 “중간 다리” 역할을 하며, 캐릭터가 달리는 동작, 점프하는 동작, 아이들(Idle) 동작 등을 매끄럽게 전환시키는 다양한 로직을 담게 된다

2️⃣ Anim Graph & Event Graph

  • 애니메이션 블루프린트에는 크게 Anim GraphEvent Graph 두 가지 그래프가 있습니다.
  • Anim Graph (애니메이션 그래프)
    • 입력 노드블렌딩 (Blend)출력 노드 (Output Pose) 순으로, 최종적으로 캐릭터가 어떤 포즈와 애니메이션을 취할지 결정한다.
      • 예: 캐릭터가 걷기 (Walk)에서 달리기 (Run)로 전환할 때, 얼마나 부드럽게 (Blend Time) 섞어줄지, 또 속도에 따라 Walk 애니메이션과 Run 애니메이션의 비율을 어떻게 조절할지를 정의한다.

이벤트 그래프에서 Movement Component 정보 받아오기


  • Event BlueprintInitializeAnimation 노드 사용

    • 애니메이션 블루프린트가 초기화될 때(즉, 한 번만 호출) 실행되는 이벤트다.
    • 여기서 Get Owning Actor 노드로 현재 애니메이션을 재생 중인 액터(우리 예시에서는 BP_SpartaCharacter)를 가져온다.
    • 캐스팅(Cast to BP_SpartaCharacter)을 통해 우리가 사용 중인 캐릭터가 맞는지 확인하고, 성공하면 해당 객체를 Character 변수에 저장한다.
    • Character에서 Get Character Movement 노드를 사용해 Movement Component를 얻어, 이를 CharacterMovement 변수에 저장한다.
  • 먼저 character가 Null값이 아닌지 즉 유효한지 is Valid노드로 체크한다.

  • 캐릭터가 제대로 셋업되지 않았다면, 이후 로직 실행 x

  • CharacterMovement의 Get Velocity노드로 캐릭터 현재 속도 벡터(X,Y,Z) 를 가져온다.

  • Vector LengthXY 노드를 사용해 X,Y,Z 성분의 길이만 구한다. 이를 변수로 저장한다. 여기서는 Ground Speed변수에 저장했다. 전체 속도 벡터는 Velocity 라는 벡터 변수에 저장해뒀다. 그 이유는 점프 중인 경우에 Z축 속도를 확인해야 하는 상황이 있을 수도 있기 때문.

  • Ground Speed가 어느 정도 이상이면 (3.0f 이상) “캐릭터가 움직이고 있다”고 판단할 수 있다.

  • CharacterMovementGet Current Acceleration 노드로 현재 가속도 값을 가져올 수도 있다.

  • 가속도 벡터가 (0, 0, 0)에 가깝다면, 입력 중이 아닌 상태일 수 있다.

  • 이동 여부와 사용자 입력(가속도 여부)을 AND 조건 등으로 조합해, "캐릭터가 실제로 이동 중인지”를 판정하는 변수를 만들어 둘 수 있다.

    • IsMoving = (Ground Speed > 3.0) AND (Acceleration != 0)

  • CharacterMovementIs Falling 함수를 사용해 캐릭터가 공중에 떠 있는지 확인할 수 있다.
  • 점프를 했거나 플랫폼에서 떨어졌을 때 등, 캐릭터가 지면에 붙어있지 않으면 True가 반환된다.
  • 이를 bool 변수에 저장하면, Anim Graph 내에서 캐릭터가 점프나 낙하 관련 애니메이션으로 전환하도록 제어할 수 있다.

애님 그래프에서 “Locomotion” State Machine 설계하기


1️⃣ State Machine (상태 기계)란?

  • State Machine (상태 기계)는 언리얼 엔진의 애니메이션 시스템에서, 캐릭터의 상태(Idle, Walk, Run, Jump 등)에 따라 어떤 애니메이션을 재생하고, 어떻게 전환할지를 결정하기 위한 논리적 구조다.
  • 가장 쉽게 이해하자면, “Idle 상태이면 Idle 애니메이션을 재생하고, 캐릭터가 움직이기 시작하면 Walking 애니메이션으로 전환한다.” 같은 로직을 직관적으로 구성하게 해준다.
  • State Machine의 핵심 개념
    1. State(상태)
      • 캐릭터가 현재 어떤 동작을 하고 있는지 나타낸다.
      • 예) Idle, Walking, Running, Jumping 등이 각각 하나의 상태가 될 수 있다.
    2. Transition(전환)
      • 한 상태에서 다른 상태로 언제 전환되는지 조건(Condition)을 정의한다.
      • 예) IdleWalking 전환 조건: “속도 > 0” (즉, 캐릭터가 이동을 시작)
    3. Animation Graph와의 연결
      • State Machine 내부의 상태 (State)는 실제로 재생할 애니메이션을 배치하는 장소이다.
      • State 간 전환 조건을 만족하면 다른 애니메이션이 재생되도록 한다.

2️⃣ “Locomotion” State Machine 생성

  • 이제 캐릭터의 기본 이동 (Idle, Walk, Run) 관련 애니메이션들을 전환하기 위해 Locomotion이라는 State Machine을 만들어 보자.

이것이 Locomotion State의 내부 모습이다.

3️⃣ Idle State 생성

이것은 캐릭터의 대기상태(Idle State)의 내부 모습이다. Idle 상태에 무한루프 애니메이션을 체크해주자. 체크해주지 않으면 한번만 애니메이션이 나오고 더이상 나오지않아 굳게된다.

4️⃣ Walk/Run State와 Blend Space 연결

  • 이 Blend Space는 캐릭터 속도(Ground Speed)를 입력으로 받아 걷기, 달리기 애니메이션을 부드럽게 혼합하여 재생한다.

5️⃣ Idle ↔ Walk/Run 전환 조건 설정

  • 화살표(Transition)은 전환 조건이다. Idle -> Walk/Run 전환조건은 아래와 같이 걸어주자
  • bShouldMove == true일 때 전환되도록 설정하면, 캐릭터가 이동 입력을 받고 있음을 의미하게 된다.
  • 이전 단계에서 만든 bShouldMove 변수가 있다면 그대로 사용하고, 없다면 Ground Speed > 3.0 등으로 조건을 걸어도 된다
  • 반대로 Walk/Run에서 Idle로 가는 Transition Rule은 *bShouldMove == false로 설정합니다.
  • 즉, 더 이상 움직이지 않을 때, Idle 상태로 돌아오게 됩니다.

애님 그래프에서 “Main States” State Machine 설계하기


1️⃣ “Main States” State Machine 생성과 Control Rig 설정

  • 새로 만든 Main States 노드를 Output Pose 직전에 연결해야 한다. 그런데 이번에는 Control Rig 노드를 한 번 더 거쳐야 한다. 즉, 메인 애니메이션 (상태머신) → Control Rig → 최종 포즈 순으로 데이터를 흐르게 만든다.
    1. Main States를 Control Rig 노드의 입력으로 연결
    2. Control Rig 노드를 Output Pose로 연결

  • Control Rig 설정
    • 애니메이션 로직 (상태머신)에서 나온 포즈를 한 번 더 가공하거나, Foot IK (지형이 울퉁불퉁해도 발을 지면에 맞추는 기능) 같은 것을 적용하기 위해 Control Rig 노드를 사용한다.
    • Control Rig를 클릭하고 Details 창에서 Control Rig Calss를 CR_Mannequin_BasicFootIK로 지정해준다. (프로젝트에 존재하는 IK용 Control Rig 에셋이랍니다.)
  • Set Initial Trnsforms From Mesh” 옵션에 체크를해준다. 이 옵션은 “현재 스켈레탈 메쉬 (Anim Graph에서 넘어온 포즈)의 뼈 변환을 그대로 초기값으로 사용하겠다”는 의미다.
    • 체크하면, 매 프레임 “State Machine에서 계산된 포즈”를 기반으로 Control Rig이 추가적인 IK나 변형을 진행하게 된다.
  • Input과 Output 카테고리 둘 다 ShouldDoIKTrace - Use Pin에 체크를 해두자.
    • 보통 CR_Mannequin_BasicFootIK에서는 ShouldDoIKTrace (또는 이와 유사한 이름의 Boolean 값)가 있어서, 실제로 발 위치를 추적 (Trace)할 것인지를 결정한다.
    • Use Pin”에 체크하게 되면, 이 값이 Anim Graph나 Blueprint에서 핀으로 연결 가능하게 된다.
      • 즉, “캐릭터가 공중에 있을 땐 발 IK 안 쓰고, 지면 위에서만 발 IK 사용” 등 조건에 따라 사용 가능하게 되는거다.

2️⃣ “Locomotion” State Machine을 캐시 포즈로 저장

  • 기존에 만든 Locomotion State Machine (Idle, Walk/Run 동작)을 캐시 포즈 (Cached Pose)로 만들어두면, 다른 곳에서도 재활용하기 쉽다다.
  • 이렇게 하면 Main States에서 Locomotion 상태를 하나의 포즈로 쉽게 가져와 사용할 수 있다.

3️⃣ “Main States” State Machine 설계

  1. Locomotiono(걷기/뛰기) State
  • Locomotion State를 더블 클릭하고 들어가서, 앞서 캐시 포즈로 저장한 Locomotion 포즈를 연결해주면 된다.
  1. Land (착륙) State
  • 새로운 State Land를 만들어, 착지 시 재생할 애니메이션(MM_Land)을 연결한다.
  • MM_Land 애니메이션은 Details 창에서 Loop Animation 조건에 체크해준다.
  • 여기서는 Locomotion 상태에서 넘어온 포즈를 적절히 착지 모션을 섞어준다. (Apply Additive)
  • 전환 조건 (Transition 설정)
    • Land에서 Locomotion으로 가는 전환 조건을 2개를 설정해야한다. (두번 드래그하면 두 개의 조건이 생성이 됩니다.)
    • LandLocomotion 첫 번째 조건은 bShouldMove == true
    • Land → Locomotion 두 번째 조건은 Details 창에서 애니메이션 시퀀스 재생 완료하면 자동으로 State가 변할 수 있는 조건인 “Automatic Rule Based on Sequence Player in State”에 체크를 한다.
  1. Fall Loop (추락) State
  • Loop Animation을 체크해두자.
  1. Jump (점프) State
  • Jump는 Loop Animation에 체크를 하지 말자.
  • 전환 조건
    • JumpFall Loop 조건은 Jump 시퀀스가 끝나면 자동으로 Fall Loop로 전환되게끔 Details 창에서 Automatic Rule Based on Sequence Player in State에 체크하자.
  1. State Alias 생성 - To Land (공중에 있는 = 지면으로 향하는) 그룹
  • State Alias는 대규모 State Machine에서 여러 상태를 한 번에 관리하기 유용하다.
  • Jump, Fall Loop 두 상태를 To Land State 그룹으로 묶어준다.
  • 전환 조건
    • To LandLand
      • bIsFalling == false가 되면 Land로 전환하도록 설정한다.
      • 이 부분은 공중에서 착지로 상태 전환을 해야하는 상황들을 가리킨다.
  1. State Alias 생성 - To Falling (지면에 있는 = 공중으로 향하는) 그룹
    Locomotion, Land 두 상태를 To Falling State 그룹으로 묶어줍니다.
  • 전환 조건
    • To FallingJump : Z축 속도가 일정 값(예: 100) 이상이고, bIsFalling == true라면 점프 상태로 인식한다.
    • To FallingFall Loop
      • 바닥에 있다가 공중으로 전환되는 상황을 말한다.

0개의 댓글