본문 바로가기

게임 개발 공부/언리얼엔진

UE5 | C++ 개발 - Controller 설정

저번에 언리얼 상에서 다뤘던 InputMappingContext와 PlayerController를 C++상에서 연결하는 방법을 배웠다.

 

1. Player Controller

- 플레이어 컨트롤러란 플레이어가 입력하는 컨트롤 신호를 분류해주는 역할을 한다.

- 키보드로 조종하는 가상의 조이스틱이랄까

- 조이스틱에 어떤한 버튼들을 배치할지는 개발자의 마음이다.

PlayerController.h
//평범한 헤더와 선언은 생략

class UInputMappingContext;  //전방선언
class UInputAction;          //전방선언

class 내부
{
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputMappingContext* InputMappingContext; // IMC와 IA를 넣을 공간들
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputAction* MoveAction;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputAction* LookAction;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputAction* JumpAction;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputAction* SprintAction;
}

==========================

PlayerController.cpp

#include "EnhancedInputSubsystem.h"

클래스::생성자 : InputMappingContext(nullptr), MoveAction(nullptr), LookAction(nullptr),
				JumpAction(nullptr),SPrintAction(nullptr) // 초기화리스트
{
}

비긴플레이
{
	if(ULocalPlayer* LocalPlayer = GetLocalPlayer()) // 로컬플레이어 정보 가져오기
    {
    	if(UEnhancedInputLocalPlayerSubsystem* Subsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
        {// 인헨스드 인풋 시스템을 관리하는 서브시스템 획득하기
        	if(InputMappingCotext)
            {
            	Subsystem->AddMappingContext(InputMappingContext, 0); // 서브 시스템에 IMC 추가 및 우선순위 최우선으로 두고 활성화
            }
        }
    }
}

 

위와 같이 하면 기본적인 컨트롤러는 완성이다.

 

 

2. PlayerCharacter

- 캐릭터가 완성된 컨트롤러를 사용할 준비가 되어야 최종적으로 캐릭터가 움직인다.

- 캐릭터에게 적절한 컨트롤러를 쥐어준다.

- 컨트롤러가 등록된 IMC로 신호를 분류해 "이 움직임에 해당하는 신호가 들어왔어"하고 플레이어에게 알려준다.

- 플레이어는 그 신호를 EnhancedInputSystem을 이용해 분류된 신호값을 알맞은 각 함수로 보내서 값을 데이터화 해 움직임에 적용한다.

사용자정의PlayerCharacter.h
// 주제에 맞는 내용만 표기

struct FInputActionValue; // 전방선언

class 내부
{

protected:
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    // 컨트롤러를 형변환하고 이벤트와 IA와 함수를 바인딩하고 신호값을 뿌려줄 함수
    
 	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:   // Sprint값에 사용할 변수들
	float NormalSpeed;
    float SprintSpeedMultiplier;
    float SprintSpeed;
    
=============================================

사용자정의playerCharacter.cpp

#include "사용자PlayerController.h"                			// 사용할 컨트롤러
#include "EnhancedInputSystem.h"             			// 인헨스드 인풋 시스템
#include "GameFrameWork/CharacterMovementComponent.h"	// 기본 무브먼트 컴포넌트(캐릭터 클래스)


클래스 : 생성자
{
    SprintSpeedMultiplier = 1.7f;   					// 변수 초기화
	NormalSpeed = 600;
	SprintSpeed = NormalSpeed * SprintSpeedMultiplier;

	GetCharacterMovement()->MaxWalkSpeed = NormalSpeed; // 속도 즉시 변경
}

 void 클래스::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
 {
 	Super::SetupPlayerInputComponent(PlayerInputComponent);
    
    if(UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent)) // 새로 선언한 EnhancedInputComponent에 기본 InputComponent를 형변환 하여 넣어준다.
    {
    	if(사용자정의PlayerController* PlayerController = Cast<사용자정의PlayerController>(GetController()))
 		{ // GetController를 하여 현재 신호를 임시로 받는 Controller를 가져와 형변환하여 새 사용자정의PlayerController에 넣어준다. 
			if(PlayerController->MoveAction) // null check
            {
            	EnhancedInput->BindAction(				// 실제로 이벤트와 함수를 연결하는 핵심 코드
                	PlayerController->MoveAction,		// 첫번째 인자. IA 연결
                    ETriggerEvent::Triggered,			// IA에 기반한 이벤트 발생 조건
                    this,								// 이 객체
                    &사용자정의Character::Move			// 실행할 함수 입력
                );
            }
            
            if (PlayerController->JumpAction) 			// bool 동작의 경우
			{
				EnhancedInput->BindAction(         
					PlayerController->JumpAction,  
					ETriggerEvent::Triggered,      
					this,                       
					&사용자정의Character::StartJump     
				);

				EnhancedInput->BindAction(         
					PlayerController->JumpAction,   
					ETriggerEvent::Completed,      
					this,                          
					&사용자정의Character::StopJump     
				);
			}
            
            .    //  필요한 동작들 모두 반복
            .
            .
            .
        }
    }
}

void ASpartaCharacter::Move(const FInputActionValue& value) // Move 함수 구현
{
	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)  // Jump 함수 구현
{
	// if (!Controller) return; <- 엔진 자체에 점핑관련 Controller체크가 이미 구현되어 있고, Controller가 필수적으로 필요하지 않은 무브먼트이기때문에 유효성 체크가 필요없다.

	if (value.Get<bool>())
	{
		Jump();
	}
}

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

void ASpartaCharacter::Look(const FInputActionValue& value)       // Look 함수 구현
{
	FVector2D LookInput = value.Get<FVector2D>();

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

void ASpartaCharacter::StartSprint(const FInputActionValue& value)   // Sprint 함수 구현
{
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
	}
}

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

 

 여기까지 완료하면 이제 Edit > Project Settings > Maps&Modes 에서

GameMode와 Default pawn Class, Player Controller Class를 알맞게 지정해주고

플레이어 컨트롤러 블루프린트에서 IMC와 IA를 지정해주면 모든 준비는 끝이다.

 

PlayerController BluePrint 내부에서 Input 세팅

 

기본 캐릭터 클래스와 캐릭터 무브먼트 기능 그리고 인헨스드 인풋 시스템 덕분에 꽤나 간단히 컨트롤러 설정을 할 수 있는 것 같다.

이 기능들 없이 구현하는 방법도 연구해봐야겠다.