[TIL] 2026-01-12 | UE C++ 캐릭터 컨트롤

2026. 1. 12. 20:58·내배캠Unreal_TIL/UE
C++와 Unreal Engine으로 3D 게임 개발 2-1~3

1. WHAT

- GameMode 기능

  • 플레이어 Pawn/Character 스폰
    • Pawn - PlayerController 연동
  • PlayerController 지정
  • 게임 규칙 관리
    • 게임 전반의 규칙 정의 및 유지
    • 승리/패배 결정 조건 관리 > 승/패 후속 처리
  • GameState / PlayerState 사용

 

GameMode 클래스 적용

1. 프로젝트 전역 설정: Edit → Project Settings → Maps & Modes → Default GameMode → 클래스 지정

2. 레벨 별 설정: World Settings →  GameMode Override → 클래스 지정

 

Character 클래스 생성 및 스켈레탈 메시 설정

언리얼에서 일반적으로 캐릭터의 전방 방향은 X축

 

SpringArm 및 CameraComponent 추가

// SpartaCharacter.h
#pragma once

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

class USpringArmComponent; // 스프링 암 관련 클래스 헤더
class UCameraComponent; // 카메라 관련 클래스 전방 선언

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; // 이 함수는 이후에 다루게 되니, 우선 삭제하기 않고 둡니다.
};
// SpartaCharacter.cpp
#include "SpartaCharacter.h"
// 카메라, 스프링 암 실제 구현이 필요한 경우라서 include
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

ASpartaCharacter::ASpartaCharacter()
{
		// Tick 함수는 우선 꺼둡니다.
		PrimaryActorTick.bCanEverTick = false;
		
		// (1) 스프링 암 생성
    SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    // 스프링 암을 루트 컴포넌트 (CapsuleComponent)에 부착
    SpringArmComp->SetupAttachment(RootComponent);
    // 캐릭터와 카메라 사이의 거리 기본값 300으로 설정
    SpringArmComp->TargetArmLength = 300.0f;  
    // 컨트롤러 회전에 따라 스프링 암도 회전하도록 설정
    SpringArmComp->bUsePawnControlRotation = true;  

    // (2) 카메라 컴포넌트 생성
    CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    // 스프링 암의 소켓 위치에 카메라를 부착
    CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
    // 카메라는 스프링 암의 회전을 따르므로 PawnControlRotation은 꺼둠
    CameraComp->bUsePawnControlRotation = false;
}

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

 

GameMode에서 DefaultPawnClass 설정

#include "SpartaGameMode.h"
#include "SpartaCharacter.h"

ASpartaGameMode::ASpartaGameMode()
{
    DefaultPawnClass = ASpartaCharacter::StaticClass();
}

 

- 입력 매핑 구현

IA 설정 > IMC 설정 > PlayerController에서 IMC 활성화하기

// SpartaPlayerController.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"

class UInputMappingContext; // IMC 관련 전방 선언
class UInputAction; // IA 관련 전방 선언

UCLASS()
class SPARTAPROJECT_API ASpartaPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	ASpartaPlayerController();

  // 에디터에서 세팅할 IMC
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputMappingContext* InputMappingContext;
	// IA_Move를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputAction* MoveAction;
  // IA_Jump를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputAction* JumpAction;
  // IA_Look를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input")
  UInputAction* LookAction;
  // IA_Sprint를 지정할 변수
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
  UInputAction* SprintAction;
  
  virtual void BeginPlay() override;
};
// SpartaPlayerController.cpp
#include "SpartaPlayerController.h"
#include "EnhancedInputSubsystems.h" // Enhanced Input System의 Local Player Subsystem을 사용하기 위해 포함

ASpartaPlayerController::ASpartaPlayerController()
    : InputMappingContext(nullptr),
      MoveAction(nullptr),
      JumpAction(nullptr),
      LookAction(nullptr),
      SprintAction(nullptr)
{
}

void ASpartaPlayerController::BeginPlay()
{
    Super::BeginPlay();

		// 현재 PlayerController에 연결된 Local Player 객체를 가져옴    
    if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
    {
		    // Local Player에서 EnhancedInputLocalPlayerSubsystem을 획득
        if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
        {
	          if (InputMappingContext)
            {
            		// Subsystem을 통해 우리가 할당한 IMC를 활성화
		            // 우선순위(Priority)는 0이 가장 높은 우선순위
                Subsystem->AddMappingContext(InputMappingContext, 0);
            }
        }
    }
}

 

- Character 클래스에 액션 바인딩 추가 및 이동 함수 구현

// SpartaCharacter.h
#pragma once

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

class USpringArmComponent;
class UCameraComponent;
struct FInputActionValue;

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

public:
	// Sets default values for this character's properties
	ASpartaCharacter();

	UPROPERTY(visibleAnywhere, BlueprintReadOnly, Category = "Camera")
	USpringArmComponent* SpringArmComp;
	UPROPERTY(visibleAnywhere, BlueprintReadOnly, Category = "Camera")
	UCameraComponent* CameraComp;

protected:

	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);

private:
	float NormalSpeed;
	float SprintSpeedMultiplier;
	float SprintSpeed;
};
// SpartaCharacter.cpp
#include "SpartaCharacter.h"
#include "EnhancedInputComponent.h"
#include "Camera/CameraComponent.h"
#include "SpartaPlayerController.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"

// Sets default values
ASpartaCharacter::ASpartaCharacter()
{
	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	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 = 2.0;
	SprintSpeed=NormalSpeed*SprintSpeedMultiplier;

	GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
}

// Called to bind functionality to input
void ASpartaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	if(UEnhancedInputComponent* EnhancedInput=Cast<UEnhancedInputComponent>(PlayerInputComponent))
	{
        if (ASpartaPlayerController* PlayerController = Cast<ASpartaPlayerController>(GetController()))
		{
			if (PlayerController->MoveAction)
			{
				EnhancedInput->BindAction(
					PlayerController->MoveAction,
					ETriggerEvent::Triggered,
					this,
					&ASpartaCharacter::Move
				);
			}
			if (PlayerController->JumpAction)
			{
				EnhancedInput->BindAction(
					PlayerController->JumpAction,
					ETriggerEvent::Triggered,
					this,
					&ASpartaCharacter::StartJump
				);
				EnhancedInput->BindAction(
					PlayerController->JumpAction,
					ETriggerEvent::Completed,
					this,
					&ASpartaCharacter::StopJump
				);
			}
			if (PlayerController->LookAction)
			{
				EnhancedInput->BindAction(
					PlayerController->LookAction,
					ETriggerEvent::Triggered,
					this,
					&ASpartaCharacter::Look
				);
			}
			if (PlayerController->SprintAction)
			{
				EnhancedInput->BindAction(
					PlayerController->SprintAction,
					ETriggerEvent::Triggered,
					this,
					&ASpartaCharacter::StartSprint
				);
				EnhancedInput->BindAction(
					PlayerController->SprintAction,
					ETriggerEvent::Completed,
					this,
					&ASpartaCharacter::StopSprint
				);
			}
		}
    }
}

void ASpartaCharacter::Move(const FInputActionValue& value)
{
	if (!Controller) return;

	const FVector2D MoveInput = value.Get<FVector2D>();

	if (!FMath::IsNearlyZero(MoveInput.X))
	{
		AddMovementInput(GetActorForwardVector(),MoveInput.X);
	}
	if (!FMath::IsNearlyZero(MoveInput.Y))
	{
		AddMovementInput(GetActorRightVector(), MoveInput.Y);
	}
}

void ASpartaCharacter::StartJump(const FInputActionValue& value)
{
	if (value.Get<bool>())
	{
		Jump();
	}
}

void ASpartaCharacter::StopJump(const FInputActionValue& value)
{
	if (!value.Get<bool>())
	{
		StopJumping();
	}
}

void ASpartaCharacter::Look(const FInputActionValue& value)
{
	FVector2D LookInput = value.Get<FVector2D>();

	AddControllerYawInput(LookInput.X);
	AddControllerPitchInput(LookInput.Y);
}

void ASpartaCharacter::StartSprint(const FInputActionValue& value)
{
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
	}
}

void ASpartaCharacter::StopSprint(const FInputActionValue& value)
{
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
	}
}

 

2. RESULT

점프 횟수를 세는 간단한 변수를 C++에 추가 (JumpCount++),

점프할 때마다 콘솔에 "Jump Count: x" 형태로 출력하도록 해보세요.

void ASpartaCharacter::StartJump(const FInputActionValue& value)
{
	if (value.Get<bool>() and !JumpCheck)
	{
		Jump();
		JumpCount += 1;
		JumpCheck = true;
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, FString::Printf(TEXT("Jump Count: %d"),JumpCount));
	}
}

void ASpartaCharacter::StopJump(const FInputActionValue& value)
{
	if (!value.Get<bool>())
	{
		StopJumping();
		JumpCheck = false;
	}
}

 

'내배캠Unreal_TIL > UE' 카테고리의 다른 글

[TIL] 2026-01-14 | UE Pawn 클래스로 3D 캐릭터 만들기  (2) 2026.01.14
[TIL] 2026-01-13 | State Machine 설계를 통한 캐릭터 애니메이션 적용  (0) 2026.01.13
[TIL] 2026-01-09 | C++ 문자 종류 판단, Actor 라이프 사이클, Transform, 리플렉션 시스템  (0) 2026.01.09
[TIL] 2026-01-08 | 언리얼 엔진 기본 구조, 빌드 문제 해결, Actor 클래스에 컴포넌트 추가  (0) 2026.01.08
[TIL] 2025-12-24 | 과제 5 구현, 레벨 테스트 후기  (1) 2025.12.24
'내배캠Unreal_TIL/UE' 카테고리의 다른 글
  • [TIL] 2026-01-14 | UE Pawn 클래스로 3D 캐릭터 만들기
  • [TIL] 2026-01-13 | State Machine 설계를 통한 캐릭터 애니메이션 적용
  • [TIL] 2026-01-09 | C++ 문자 종류 판단, Actor 라이프 사이클, Transform, 리플렉션 시스템
  • [TIL] 2026-01-08 | 언리얼 엔진 기본 구조, 빌드 문제 해결, Actor 클래스에 컴포넌트 추가
윤윤씨
윤윤씨
🎮 내일배움캠프 Unreal 7기
  • 윤윤씨
    컴퓨터온열맛사지
    윤윤씨
  • 전체
    오늘
    어제
    • 분류 전체보기 (62)
      • 내배캠Unreal_TIL (62)
        • C++ (23)
        • UE (31)
        • 팀프로젝트 (7)
      • etc (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
    • Solved.ac
    • YouTube
  • 태그

    머티리얼
    챌린지
    스테이트머신
    오토마타
    STL
    프로그래머스
    코드카타
    gas
    언리얼과제
    디자인패턴
    ta
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.6
윤윤씨
[TIL] 2026-01-12 | UE C++ 캐릭터 컨트롤
상단으로

티스토리툴바