2. 움직이는 플레이어 캐릭터 만들기

개발 일지|2024. 8. 26. 22:41

이제 레벨에서 캐릭터를 움직여보고 총을 쏘는 부분을 구현해보려고 한다.

 

먼저 Tools 에서 C++ 클래스를 하나 만드는데, 캐릭터로 선택했다.

 

Actor는 레벨에 배치하는 가장 기본적인 클래스. 

Pawn은 마우스나 키보드의 입력을 받아서 처리하는 클래스.

Character는 마우스나 키보드의 입력을 받아 플레이어가 움직이는 클래스.

 

변수 앞에 붙는 접두사에는

A(Actor 클래스), U(Unreal 클래스), F(구조체 이름), b(boolean 타입) 등이 있다.

 

라고 이해하고 우선 개발을 시작해보겠다.

 

 

캐릭터 클래스를 생성하면 처음에 뜨는 cpp 코드이다.

맨위는 생성자

2번째는 BeginPlay() 함수로 레벨이 시작될 때 처음 호출되는 함수이다.

3번째는 매 프레임 마다 호출되는 Tick() 함수

마지막 SetupPlayerComponent함수로 키보드나 마우스 입력을 바인딩하는 함수이다.

 

이렇게 생성해놓고, 우선 카메라를 세팅한다.

protected:

	UPROPERTY(EditAnywhere)
	class UCameraComponent* Camera;

 

FirstPersonCharacter.h 파일의 맨 밑에 protected로 Camera Unreal C++ 클래스를 생성한다.

U가 붙으면 일반 C++ 클래스가 아닌 언리얼에서 컴파일 단계에서 관리하는 언리얼 C++ 클래스라고 배운것 같다.

컴파일 단계에서 관리한다는 말이 맞는지 정확하지 않아 다시 알아봐야겠다.

 

그리고 FirstPersonCharacter.cpp 파일의 생성자에 다음과 같이 추가한다.

Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Player_Camera"));
Camera->SetupAttachment(RootComponent);
Camera->bUsePawnControlRotation = true;

 

Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Player_Camera"));

-> "UCameraComponent" 라는 카메라 컴포넌트 생성하는 역할.

-> "CreateDefaultSubobject"컴포넌트를 생성하고 초기화 할때 사용

 

Camera->SetupAttachment(RootComponent);

-> 카메라 컴포넌트를 액터의 기본 컴포넌트인 "RootComponent"에 할당. RootComponent가 이동할 때 카메라도 함께 따라가도록 설정

-> 카메라를 캐릭터 움직임에 맞춰 위치시킨다고 이해

 

Camera->bUsePawnControlRotation = true;

-> 카메라가 Pawn의 controller 회전을 따라가도록 설정. 이 설정으로 플레이어가 캐릭터를 움직일 때 카메라 방향이 함께 변경된다.

-> 카메라가 캐릭터의 회전을 따라가도록 설정한다고 이해

 

여기까지 한 후, 언리얼5 엔진을 킨다.

아까 만들어뒀던 캐릭터의 블루프린트를 열면 카메라가 생겨있는데, 위치를 조정하고 간단한 캐릭터 메쉬도 배치해준다.

 

그리고 Lobby Map의 World setting을 열어서 Gamemode Override를 내가 만들었던 Default Game Mode로 설정한다.

 

이러면 게임을 실행시켰을 때 캐릭터가 현재 위치에서 스폰되는걸 알 수 있다.

아직 캐릭터에 키보드나 마우스 입력을 바인딩하지 않아 움직이지 않는데, 이것부터 해결해서 캐릭터로 맵을 뛰어다녀보자.

 

기본값
내가 쓸 키보드&마우스에 맞춰서 이름도 바꾸고 값을 세팅해줬다

 

엔진의 input창을 보면 기본적으로 wasd, 마우스 시점 컨트롤, 스페이스바 점프까지 바인딩 되어있는걸 확인할 수 있다.

여기 적혀있는 이름대로 cpp 코드에서 구현하여 바인딩해주자.

 

근데 노란색 경고문이 있어 읽어보니 더이상 쓰이지 않는 기능? 인것 같다. 더이상 쓰이지 않으면 왜 넣어놓은거지? 일단 cpp로 구현해보자.

 

	// character movement function
	void MoveForward(float val);
	void MoveRight(float val);

	void Turn(float val);
	void LookUp(float val);

	void StartJump();
	void StopJump();

 

우선 키보드와 마우스를 받을 함수들을 Character 헤더파일에 선언해준다.

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

	// Keyboard Binding
	PlayerInputComponent->BindAxis("MoveForward", this, &AFirstPersonCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &AFirstPersonCharacter::MoveRight);

	PlayerInputComponent->BindAxis("Turn", this, &AFirstPersonCharacter::Turn);
	PlayerInputComponent->BindAxis("LookUp", this, &AFirstPersonCharacter::LookUp);

	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFirstPersonCharacter::StartJump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFirstPersonCharacter::StopJump);

}

// Character, Camera Movement Function
void AFirstPersonCharacter::MoveForward(float val)
{
	if (Controller && val != 0.0f) {
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);
		
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		AddMovementInput(Direction, val);
	}
}

void AFirstPersonCharacter::MoveRight(float val)
{
	if (Controller && val != 0.0f) {
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
		AddMovementInput(Direction, val);
	}
}


void AFirstPersonCharacter::Turn(float val)
{
	AddControllerYawInput(val);
}

void AFirstPersonCharacter::LookUp(float val)
{
	AddControllerPitchInput(val);
}

void AFirstPersonCharacter::StartJump()
{
	bPressedJump = true;
}

void AFirstPersonCharacter::StopJump()
{
	bPressedJump = false;
}

 

MoveForward 함수에서는 w와 s의 입력에 따른 캐릭터의 움직임을 계산한다.

If 조건문에서 캐릭터가 플레이어에 의해 제어되고 있는지 확인 && 키보드 val 값이 실제로 입력이 들어왔는지 확인.

Rotation 변수에서 카메라나 플레이어의 컨트롤러가 현재 바라보는 방향값을 Controller->GetControlRotation() 구문으로 받는다.

YawRotation(0, Rotation.Yaw, 0)로 Yaw값을 사용하는 회전 값을 만든다.

FRotator는 회전을 나타내는 구조체(F 접두사가 구조체를 가리킨다)인데, Pitch, Yaw, Roll 축을 나타낸다.

더보기

[Pitch, Yaw, Roll에 관하여]

 

Pich는 상하, Yaw는 좌우, Roll은 좌우 기울기.

Pitch와 Roll을 0으로 설정하고, Yaw값만 사용하면 캐릭터가 위아래를 바라보더라도 이동할땐 Yaw값만 사용해 좌우로만 이동한다.

Yaw는 Z축 회전각 -> 도리도리

Pitch는 Y축 회전각 -> 끄덕끄덕

Roll은 X축 회전각 -> 갸우뚱갸우뚱

 

참고한 블로그분이 정말 자세하게 남겨주셔서 잘 이해되었다.

 

참고: https://wise-eun.tistory.com/entry/Unreal-Yaw-Pitch-Roll-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

FRotationMatrix 함수를 사용해 회전을 방향 벡터로 변환한다.

YawRotation 값을 회전 행렬로 변환해서, GetUnitAxis(EAxis::X) 를 사용해 회전행렬값에서 X축 방향을 얻어 전진한다.

캐릭터가 앞을 향해 이동할 방향 벡터를 계산한다고 이해하자.

 

AddMovementInput 함수를 사용해 캐릭터를 특정 방향으로 이동시킨다. Direction 변수는 앞에서 계산한 X축 방향, val은 이동할 때 입력 강도(아까 엔진에서 설정했던 값에 따라 1.0이면 앞으로, -1.0이면 뒤로 이동).

 

MoveRight 함수는 위와 비슷하고 X축 대신 Y축 벡터를 사용하는것만 다르다.

 

Turn과 LookUp 함수에서는 AddControllerYawInput 함수를 사용해 Value값만큼 카메라를 회전시킨다.

 

StartJump와 StopJump 함수에선 bPressedJump를 T나 F값으로 설정해 점프를 시작하거나 멈춘다.

 

여기까지 해주고, 컴파일 한 후 LobbyMap에서 실행시키면 wasd 이동, 마우스에 따른 카메라의 회전, 스페이스바 입력시 점프가 잘 동작한다.

 

다음에는 메인메뉴에서 게임 시작시 캐릭터 스폰 위치 조정, 카메라 시점에 맞게 팔 달아주기, 총 하나 쥐여줘서 쏴보기 구현해보자.

 

[참고 많이 한 블로그]

https://tech-interview.tistory.com/295

'개발 일지' 카테고리의 다른 글

4. 서버 구축을 위한 삽질  (0) 2024.09.02
3. 플레이어 랜덤 스폰 구현  (0) 2024.08.31
1. 메인메뉴 구성  (0) 2024.08.26
시작  (0) 2024.08.21

댓글()