MOONSUN
[UE5] 입력 처리 : Axis, Action 바인딩 (Legacy 버전) 본문
| 구분 | Axis | Action |
| 입력 형태 | 연속 값 | 이벤트 |
| 함수 인자 | float | 없음 |
| 호출 방식 | 매 프레임 | 눌렀을 때 / 뗐을 때 |
| 대표 용도 | 이동, 회전 | 점프, 공격 |
1. SetupPlayerInputComponent()의 역할
UCLASS()
class UNREALSTUDY_API AIMCPawn : public APawn
{
...
public:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
...
}
SetupPlayerInputComponent()는
"어떤 입력이 들어왔을 때 어떤 함수를 실행할지 연결(바인딩)하는 함수” 이다.
즉, 이 함수는 직접 입력을 처리하는 곳 이라기보다,
- W 키를 누르면 어떤 함수가 실행될지
- 마우스를 움직이면 어떤 함수가 실행될지
- 스페이스바를 누르면 어떤 함수가 실행될지
이런 입력 규칙을 등록하는 곳.
- 예를 들어 아래 코드는
- "UpDown" 축 입력이 들어오면 UpDown() 함수를,
- "LeftRight" 축 입력이 들어오면 LeftRight() 함수를 실행하도록 바인딩하고 있다.
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AMyPawn::UpDown);
PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AMyPawn::LeftRight);
}
이 함수는 입력이 들어올 때마다 호출되는 함수가 아니라
보통 Pawn이 PlayerController에 의해 Possess될 때 한 번 호출되어, 입력 바인딩을 세팅한다.
2. Axis란?
Axis는 연속적인 값(continuous value)을 처리하는 입력 방식
쉽게 말해:
- 얼마나 앞으로 이동할지
- 얼마나 오른쪽으로 이동할지
- 마우스를 얼마나 움직였는지
- 조이스틱을 얼마나 기울였는지
처럼 크기와 방향이 계속 변할 수 있는 입력을 다룰 때 사용한다.
2-1. Axis의 특징
2-1-1. float 값을 받는다
Axis로 바인딩된 함수는 보통 float 값을 받는다.
void AMyPawn::UpDown(float Value)
{
// Value 사용
}
이 값은 보통 다음처럼 들어온다.
- 1.0 : 정방향 입력
- -1.0 : 반대 방향 입력
- 0.0 : 입력 없음
예를 들어 키보드에서:
- W = +1
- S = 1
로 설정하면, UpDown 값은 상황에 따라 -1 ~ 1 사이의 값이 된다.
2-1-2. Axis 바인딩 함수는 매 프레임 호출된다
- W를 누르고 있으면 계속 1.0
- 아무 입력이 없으면 계속 0.0
이런 식으로 현재 입력 상태를 지속적으로 전달 받는다.
(그래서 로그를 찍어보면 입력이 없을 때도 계속 함수가 불리는 것을 볼 수 있음)
2-1-3. 이동, 회전, 카메라 처리에 적합
- Axis는 계속 변화하는 입력을 처리하는 데 적합하다.
- 캐릭터 이동
- 카메라 회전
- 마우스 이동
- 게임패드 스틱 입력
2-2. Axis 예제 코드

void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AMyPawn::UpDown);
PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AMyPawn::LeftRight);
}
void AMyPawn::UpDown(float Value)
{
if (!FMath::IsNearlyZero(Value))
{
UE_LOG(LogTemp, Warning, TEXT("UpDown: %f"), Value);
}
// Pawn의 앞 방향으로 value 만큼 이동
AddMovementInput(GetActorForwardVector(), Value);
}
void AMyPawn::LeftRight(float Value)
{
if (!FMath::IsNearlyZero(Value))
{
UE_LOG(LogTemp, Warning, TEXT("LeftRight: %f"), Value);
}
AddMovementInput(GetActorRightVector(), Value);
}
3. Action이란?
Action은 이벤트성 입력(event input) 을 처리하는 방식
쉽게 말해:
- 버튼을 눌렀다
- 버튼을 뗐다
처럼 어떤 시점에 발생하는 입력을 다룰 때 사용한다.
3-1. Axis의 특징
3-1-1. Pressed / Released 같은 이벤트 기반이다
Action은 축 값처럼 연속적인 수치를 받는 것이 아니라, 특정 이벤트가 발생했을 때 함수가 호출된다.
- IE_Pressed : 눌렀을 때
- IE_Released : 뗐을 때
3-1-2. 매 프레임 호출되지 않는다
위와 같은 내용이지만, Axis와 달리 Action은 매 프레임 호출되지 않는다.
- 스페이스바를 눌렀을 때 한 번
- 스페이스바를 뗐을 때 한 번
이렇게 이벤트가 발생한 순간에만 호출된다.
3-1-3. 점프, 공격, 상호작용에 적합하다
Action은 순간적인 행동에 잘 어울린다.
- 점프
- 공격
- 상호작용
- 인벤토리 열기
- UI 확인 버튼
3-2. Action 예제 코드

void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction(TEXT("Jump"), IE_Pressed, this, &AMyPawn::JumpPressed);
PlayerInputComponent->BindAction(TEXT("Jump"), IE_Released, this, &AMyPawn::JumpReleased);
}
void AMyPawn::JumpPressed()
{
UE_LOG(LogTemp, Warning, TEXT("Jump Pressed"));
}
void AMyPawn::JumpReleased()
{
UE_LOG(LogTemp, Warning, TEXT("Jump Released"));
}
이 코드는 "Jump"라는 Action 입력이 발생했을 때
- 눌렀으면 JumpPressed()
- 뗐으면 JumpReleased()
를 실행
4. Axis와 Action의 차이
Axis
- 연속적인 값 처리
- float 값을 받음
- 매 프레임 호출됨
- 이동, 회전, 카메라 조작에 사용
Action
- 이벤트성 입력 처리
- Pressed / Released 같은 이벤트로 동작
- 이벤트가 발생할 때만 호출됨
- 점프, 공격, 상호작용에 사용
5. 최종 예시 코드
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
class UCapsuleComponent;
class USkeletalMeshComponent;
class UFloatingPawnMovement;
class UCameraComponent;
class USpringArmComponent;
UCLASS()
class UNREALSTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
AMyPawn();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
virtual void PostInitializeComponents() override;
virtual void PossessedBy(AController* NewController) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
public:
// 보통 Pawn/Character 은 CapsuleComponent를 Root Component로 설정하는 경우가 많음.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<UCapsuleComponent> Capsule;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<USkeletalMeshComponent> SkeletalMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<UFloatingPawnMovement> Movement;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<UCameraComponent> Camera;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<USpringArmComponent> SpringArm;
private:
// Axis
void UpDown(float value);
void LeftRight(float value);
// Action
void JumpPressed();
void JumpReleased();
bool bIsJumpPressed = false; // ing 상태는 따로 없음.
};
#include "MyPawn.h"
#include "Components/SceneComponent.h"
#include "Components/CapsuleComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/FloatingPawnMovement.h"
#include "GameFramework/SpringArmComponent.h"
AMyPawn::AMyPawn()
{
PrimaryActorTick.bCanEverTick = true; // Tick() 함수 안쓰면 false로 해주는게 좋음.
// 충돌체를 RootComponent
Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule"));
RootComponent = Capsule;
SkeletalMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalMesh"));
SkeletalMesh->SetupAttachment(Capsule);
SkeletalMesh->SetRelativeRotation(FRotator(0.0f, -90.f, 0.0f)); // 메시의 기본 방향이 X축이므로, Y축이 앞으로 오도록 회전
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(Capsule);
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
Movement = CreateDefaultSubobject<UFloatingPawnMovement>(TEXT("Movement"));
Movement->UpdatedComponent = Capsule; // Movement 컴포넌트가 이동을 업데이트할 때 기준이 되는 컴포넌트. 보통 RootComponent로 설정
static ConstructorHelpers::FObjectFinder<USkeletalMesh> SKM_Stone_Golem(TEXT("/Game/Stone_Golem/mesh/SKM_Stone_Golem.SKM_Stone_Golem"));
if(SKM_Stone_Golem.Succeeded())
{
SkeletalMesh->SetSkeletalMesh(SKM_Stone_Golem.Object);
}
}
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
}
void AMyPawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (bIsJumpPressed)
{
UE_LOG(LogTemp, Warning, TEXT("Jump Pressing"));
}
}
// [ 어떤 입력이 들어왔을 때 어떤 함수를 실행할지 연결(바인딩)하는 함수 ]
//
// - PlayerController에 의해 “Possess”될 때 한 번 호출
// - Axis 바인딩 => 매 프레임 호출
// - Action 바인딩 => 입력이 들어올 때 호출
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AMyPawn::UpDown);
PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AMyPawn::LeftRight);
PlayerInputComponent->BindAction(TEXT("Jump"), IE_Pressed, this, &AMyPawn::JumpPressed);
PlayerInputComponent->BindAction(TEXT("Jump"), IE_Released, this, &AMyPawn::JumpReleased);
}
void AMyPawn::UpDown(float value)
{
if (!FMath::IsNearlyZero(value)) // 입력값은 float라서 완전히 0이 되는 경우는 거의 없기 때문에, IsNearlyZero 함수로 0에 가까운지 체크
{
UE_LOG(LogTemp, Warning, TEXT("UpDown: %f"), value);
}
AddMovementInput(GetActorForwardVector(), value); // Pawn의 앞 방향으로 value 만큼 이동
}
void AMyPawn::LeftRight(float value)
{
if (!FMath::IsNearlyZero(value))
{
UE_LOG(LogTemp, Warning, TEXT("LeftRight: %f"), value);
}
AddMovementInput(GetActorRightVector(), value);
}
void AMyPawn::JumpPressed()
{
bIsJumpPressed = true;
UE_LOG(LogTemp, Warning, TEXT("Jump Pressed"));
}
void AMyPawn::JumpReleased()
{
bIsJumpPressed = false;
UE_LOG(LogTemp, Warning, TEXT("Jump Released"));
}
void AMyPawn::PostInitializeComponents()
{
Super::PostInitializeComponents();
}
void AMyPawn::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
}
사실 위 처럼 사용하는 방식은 Legacy 방식으로,
UE5 이후부터는 BindAxis, BindAction 방식보다 Enhanced Input을 더 많이 쓰는 편이라고 한다..!
하지만 이 개념들이 어느정도 잡혀있어야, 이후 Enhanced Input으로 넘어가는게 더 수월할 거라고 생각해 한번 정리해보았다..
'UE5' 카테고리의 다른 글
| [UE5] 로그 출력 (FString타입 , 커스텀 로그 카테고리, 로그 매크로) (0) | 2026.04.01 |
|---|---|
| [UE5] 입력 처리 : Enhanced Input (Input Mapping Context / Input Action) (0) | 2026.03.31 |
| [UE5] C++을 언리얼 오브젝트 클래스로 만드는 규칙 (0) | 2026.03.26 |
| [UE5] 언리얼 C++ 프로젝트의 컴파일 & 실행 과정 (0) | 2026.03.26 |
| [UE5] Unreal Engine 프로젝트 구조와 기본 개념 정리 (0) | 2026.03.09 |