언리얼엔진

[C++] Character의 이동 구현 / SpringArm, Camera 컴포넌트 생성

찬이2 2023. 9. 12. 03:28

BP_Character 이동과 시야 변경

저번 시간에 BP_Character 블루프린트에서 Mesh에 SK_Mannequin을 지정해주고, Capsule Component에 맞춰 위치와

회전도 설정해주었다.

그리고 ABP_Character 블루프린트 애니메이션을 BP_Character에 연결시켜주었다.

 

 

이번에는 캐릭터의 시점과 이동을 구현할 것이다. 저번에 추가했던 축매핑에 LookLR과 LookUD 또한 추가해준다.

마우스의 움직임에 따라 시점을 변환시킬 것이다.

프로젝트 세팅 - 엔진 / 입력 - 축 매핑 추가

캐릭터 이동

먼저 GPCCharacter 클래스에 입력받아 이동시킬 함수를 만들어줄 것이다.

class GPC_CPP_12_API AGPCCharacter : public ACharacter
{

.
.
.

private:
	void MoveFB(float const Value);
	void MoveLR(float const Value);
	void LookLR(float const Value);
	void LookUD(float const Value);
};

MoveFB/LR로 캐릭터의 이동을, LookLR/UD로 시점을 조작하게 될 것이다.

 

먼저 이동을 구현하기 위해 MoveFB()와 MoveLR()함수 먼저 정의해주었다.

void AGPCCharacter::MoveFB(float const Value)
{
	//먼저 카메라의 위치를 받아와줄것임(카메라는 Controller에 있음)
	FRotator const Rotator = GetControlRotation();

	//Rotator : 4원수를 활용한 자료형
	FVector const Direction = Rotator.Quaternion().GetForwardVector();

	//방향, Scale
	AddMovementInput(Direction, Value);
}

 

void AGPCCharacter::MoveLR(float const Value)
{
	FRotator const Rotator = GetControlRotation();
	FVector const Direction = Rotator.Quaternion().GetRightVector();

	AddMovementInput(Direction, Value);
}

먼저 캐릭터는 카메라가 보고있는 방향에 따라서 움직이게 된다.

따라서 카메라가 있는 Controller의 Rotation을 받아와 Rotator 변수에 담아둔다.

* Rotator : 사원수를 활용한 자료형

 

이어서 Rotator에서 ForwardVector값을 받아와 Direction 변수에 담았다.

그리고 Direction 변수의 방향값을 토대로 AddMovementInput()에 값을 넣어준다.(방향과 크기값을 넣음)


이렇게 MoveFB/LR함수 정의 후에, SetupPlayerInputComponent()함수에서 키 입력을 감지하여 받아 MoveFB/LR함수를

호출하게 될 것이다.

void AGPCCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	//이름,오브젝트,함수
	PlayerInputComponent->BindAxis("MoveFB", this, &ThisClass::MoveFB);
	PlayerInputComponent->BindAxis("MoveLR", this, &ThisClass::MoveLR);
	//멤버함수의 주소를 넘기는 방법은 &와 소속을 붙여줘야함
	//해당 멤버함수가 속한 클래스 또는 ThisClass를 사용한다.
}

PlayerInputComponent->BindAxis()에 함수의 이름, 움직일 오브젝트, 멤버함수를 차례로 넣어주게 된다.

그런데 여기서 멤버 함수를 넘길 때 주소를 넘겨주어야 한다.

& 레퍼런스를 붙여주면서 함수의 소속을 적어주면 된다.

* (&AGPCCharacter::MoveFB or &ThisClass::MoveFB)

 

 

이제 빌드하고 언리얼 에디터로 넘어와서, BP_Character 블루프린트에서 SpringArm과 Camera Component를 추가한다.

SpringArm 디테일에서 Use Pawn Control Rotation을 켜주었다.

SpringArm Component 디테일

 

이어서 클래스 디폴트에서 Use Controller Rotation Yaw를 잠깐 꺼주고, Posses Player 0으로 설정한다.플레이하면 움직이는 것을 볼 수 있다.(움직임을 보기 위해 임시로 수정해주었음)

BP_Character 클래스 디폴트


SpringArm과 Camera를 C++ 클래스에서 추가하여 사용해보기
class GPC_CPP_12_API AGPCCharacter : public ACharacter
{

.
.
.

public:
	UPROPERTY(VisibleAnywhere)
		class USpringArmComponent* SpringArm;

	UPROPERTY(VisibleAnywhere)
		class UCameraComponent* Cam;
};

GPCCharacter에서 먼저 SpringArm과  Camera Component를 사용하기 위해 선언해주었다.

그런데 클래스에 SpringArm.h과 Camera.h를 포함하지 않고 선언하는 것이 어떻게 가능할까?

 

- class * 참조형에 대해서는 어떤 내용 / 크기를 가진 클래스인지 확인하지 않는다. 그래서 헤더파일을 포함하지 않아도

  선언이 가능해진 것이다.

 

- 본래 헤더를 포함하려면 #include "GameFramework/SpringArmComponent.h" / "Camera/CameraComponent.h" 헤더를

  포함해야 하지만, 이런 식으로 사용하여 무분별한 헤더 포함을 막아줄 수 있기 때문에 이러한 방식을 사용한다.


그리고SpringArm과 Camera의 인스턴스를 생성해주기 위해  GPCCharacter.cpp에서 각각의 헤더파일을 포함해주었다.

#include "GPCCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"

 

이제 GPCCharacter 클래스의 생성자에서 인스턴스 생성을 구현해줄 것이다.

AGPCCharacter::AGPCCharacter()
{
    SpringArm = CreateDefaultSubobject<USpringArmComponent>("SpringArm");
}

CreateDefaultSubobject()를 통해 SpringArmComponent의 인스턴스를 생성해줄 수 있다.

그런데 이렇게 문자열을 이용하여 인스턴스를 만들어야 할까?


이는 매크로 연산자(# Characterize : 매크로 인자를 문자열화)을 이용해 수정해볼 수 있다.

#define CreateDefaultSubobjectAuto(Type, Pointer)\
Pointer = CreateDefaultSubobjectAuto<Type>(#Pointer)

AGPCCharacter::AGPCCharacter()
{
	//1. 매크로 연산자를 활용한 줄여서 인스턴스 생성(타입/이름)
    CreateDefaultSubobjectAuto(USpringArmComponent, SpringArm)
    
    //2. 기본적인 인스턴스 생성
    //SpringArm = CreateDefaultSubobject<USpringArmComponent>("SpringArm");
}

여기서 decltype(Declared Type : 선언된 형식)을 통해 조금 더 일원화해볼 수 있다.

앞에서 SpringArm 형식을 SpringArmComponent로 이미 선언해 두었기 때문에 할 수 있다.

#include <type_traits>

#define CreateDefaultSubobjectAuto(Pointer)\
Pointer = CreateDefaultSubobjectAuto<std::remove_reference<decltype(*pointer)>::type>(#Pointer)

AGPCCharacter::AGPCCharacter()
{
    CreateDefaultSubobjectAuto(SpringArm);
    CreateDefaultSubobjectAuto(Cam);
}

여기서 decltype(*pointer)로 SpringArm의 형식을 뽑아낼 수 있다. 그런데 decltype으로 나오는 것이 레퍼런스로 나온다.

그래서 remove_reference를 적용한 type을 반환하게 된다.

 

그래서 이제 이런식으로 SpringArm과 Cam의 인스턴스를 간단하게 만들 수 있게 되었다.

 

좀 많이 헷갈리네...