SpartaCharacter)에서 “어떤 함수가 호출될지”를 바인딩해주어야 최종적으로 동작이 이루어진다.SpartaInputMappingContext(IMC)를 활성화한다.SpartaCharacter)가 SetupPlayerInputComponent() 함수를 통해 “각 액션이 발생했을 때 어떤 함수를 실행할지”를 등록해준다.#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 함수MoveAction)this)와 함수 포인터.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>()AddMovementInput(방향, 크기)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>()bool로 가져온다.true: 키가 눌림.false: 키가 눌리지 않음.StopJumping() , Jump()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
CharacterMovementComponent에는 MaxWalkSpeed라는 속성이 있다. MaxWalkSpeed 값을 변경하면, 캐릭터의 이동 속도가 즉시 바뀐다.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
SprintSpeedMultiplierSprintSpeedSprintSpeed = NormalSpeed * SprintSpeedMultiplier;로 연산할 수도 있다.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는 “최대 이동 속도가 얼마인지”를 결정한다. 둘이 함께 작동해 캐릭터가 더 빨리 달릴 수 있게 된다.
Event BlueprintInitializeAnimation 노드 사용
Get Owning Actor 노드로 현재 애니메이션을 재생 중인 액터(우리 예시에서는 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 이상) “캐릭터가 움직이고 있다”고 판단할 수 있다.
CharacterMovement의 Get Current Acceleration 노드로 현재 가속도 값을 가져올 수도 있다.
가속도 벡터가 (0, 0, 0)에 가깝다면, 입력 중이 아닌 상태일 수 있다.
이동 여부와 사용자 입력(가속도 여부)을 AND 조건 등으로 조합해, "캐릭터가 실제로 이동 중인지”를 판정하는 변수를 만들어 둘 수 있다.
IsMoving = (Ground Speed > 3.0) AND (Acceleration != 0)
CharacterMovement의 Is Falling 함수를 사용해 캐릭터가 공중에 떠 있는지 확인할 수 있다.Idle, Walking, Running, Jumping 등이 각각 하나의 상태가 될 수 있다.Idle → Walking 전환 조건: “속도 > 0” (즉, 캐릭터가 이동을 시작)

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

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




Main States 노드를 Output Pose 직전에 연결해야 한다. 그런데 이번에는 Control Rig 노드를 한 번 더 거쳐야 한다. 즉, 메인 애니메이션 (상태머신) → Control Rig → 최종 포즈 순으로 데이터를 흐르게 만든다.Main States를 Control Rig 노드의 입력으로 연결
CR_Mannequin_BasicFootIK로 지정해준다. (프로젝트에 존재하는 IK용 Control Rig 에셋이랍니다.)ShouldDoIKTrace - Use Pin에 체크를 해두자.CR_Mannequin_BasicFootIK에서는 ShouldDoIKTrace (또는 이와 유사한 이름의 Boolean 값)가 있어서, 실제로 발 위치를 추적 (Trace)할 것인지를 결정한다.



Land를 만들어, 착지 시 재생할 애니메이션(MM_Land)을 연결한다.MM_Land 애니메이션은 Details 창에서 Loop Animation 조건에 체크해준다.Land → Locomotion 첫 번째 조건은 bShouldMove == true



Jump → Fall Loop 조건은 Jump 시퀀스가 끝나면 자동으로 Fall Loop로 전환되게끔 Details 창에서 Automatic Rule Based on Sequence Player in State에 체크하자.Jump, Fall Loop 두 상태를 To Land State 그룹으로 묶어준다.
To Land → Land
Land로 전환하도록 설정한다.Locomotion, Land 두 상태를 To Falling State 그룹으로 묶어줍니다.
To Falling → Jump : Z축 속도가 일정 값(예: 100) 이상이고, bIsFalling == true라면 점프 상태로 인식한다.
To Falling → Fall Loop