본문 바로가기

Unreal

UE - 3인칭 플레이어 생성

1. Character를 상속받는 플레이어 생성

TPS 형태의 플레이어는 사용자의 제어를 받으면서 이동이 가능해야 합니다. Pawn을 부모로 삼아 추가적으로 몇 가지가 더해져 있습니다. 대표적으로는 Character Movement 컴포넌트가 이에 해당합니다.

 

 

2. 플레이어 블루프린트 제작

 

3. 게임 모드 클래스 정보 수정하기

클래스의 상속 관계는 Actor를 기본 오브젝트로 하여 사용자의 제어를 받을 수 있게 하는 Pawn 클래스, 폰을 기반으로 추가적인 기능을 포함하는 Character 클래스가 있습니다. 우리 TPSPlayer는 Character를 상속 받기 때문에 사용자의 제어를 받을 수 있는 기본 폰으로 등록이 가능합니다.
TPSPlayer는 Character를 상속받기 떄문에 사용자의 제어를 받을 수 있는 기본 폰으로 등록이 가능합니다. 이 역할을 담당하는 객체가 게임 모드입니다.

 

게임 모드란?

해당 클래스는 주인공이 누구인지, 플레이어 컨트롤러는 어떤 것인지, 게임 및 프레이어의 상태 담당은 어떤 클래스가 담당할지 등을 정할 수 있습니다. 따라서 게임 전반적인 규칙을 게임 모드에서 담당한다고 볼 수 있습니다.
레벨마다 게임 모드가 존재하며 가장 기본이 되는 게임 모드 클래스는 GameModeBase이며, 네트워크 관련 기능을 더 많이 담당하고 있는 GameMode 클래스가 있습니다.
프로젝트에서는 처음 생성 시 기본 C++ 기반의 게임 모드 클래스인 TPSProjectGameModeBase가 만들어져 있습니다. 이를 블루프린트로 승격해서 사용합니다.

 

Default Pawn Class 지정

BP_TPSProjectGameModeBase에서 Default Pawn Class를 지정합니다.

 

World Settings에 GameModeBase 지정

 

4. 플레이어 외관 붙이기

Add Feature or Content Pack

3인칭 슈팅 게임이기 때문에 플레이어 외관은 전체 모습이 다 화면에 보여야 합니다.
이를 위해 3인칭 템플릿에서 제공하는 그래픽 애셋을 가져와 사용하겠습니다.
콘텐트 브라우저의 [Add] 버튼을 클릭하여 Add Feature or Content Pack 항목을 선택해 줍니다.

 

 

Third Person 추가.

 

외관 데이터

SK_Mannequin 스켈레탈 메시(SkeletalMesh)

 

Mesh 설정.

TPS_Player의 Mesh를 SK_Mannequin으로 선택.

 

작업 순서

블루프린트에서 작업을 모든 설정을 작업한 뒤 세팅된 값들을 C++ 코드로 옮겨 처리합니다.

 

UClass, CDO

언리얼에서는 C++ 생성자를 전통적인 형태가 아닌 클래스 기본 객체(CDO, Class Default Object)를 만드는데 사용합니다.

 

UCLass 설명

C++ 소스 코드를 컴파일할 때 언리얼 헤더 툴에서 헤더 파일을 분석해서 클래스의 메타 정보를 UClass라는 특별한 크랠스에 저장합니다. 메타 정보는 대략 언리얼 오브젝트의 계층 구조 정보와 어떤 속성및 함수들이 있는지를 나타냅니다. 해당 정보는 런타임 시에 특정 클래스를 검색하고 그 클래스의 속성이나 멤버 함수를 호출하는 데 활용될 수 있습니다. 자바(Java)나 C#같은 언어에서 활용하는 리플렉션 기능을 C++에서 사용할 수 있도록 언리얼에서 제공하는 형태입니다.

 

CDO

컴파일 완료 후 언리얼 에디터를 실행시키면 UObject를 상속받는 언리얼 오브젝트의 생성자에서 인스턴스를 생성하는데 이를 클래스 기본 객체, 줄여서 ‘CDO’라고 지칭합니다. CDO는 언리얼 오브젝트의 기본 설정을 세팅하는 데 사용됩니다.

 

UCLass, CDO 정리

C++ 클래스를 컴파일하면 먼저 언리얼 오브젝트의 정보를 담은 UClass가 만들어지고, 언리얼 에디터를 실행하면 생성자 코드를 실행하여 클래스 기본 객체 CDO 인스턴스를 생성합니다. 즉 표준 C++와는 다르게 언리얼 엔진에서 생성자는 인스턴스를 초기화해 CDO를 만들기 위한 목적이 있습니다. 엔진에서는 인스턴스들을 생성할 때 매번 초기화 시키지 않고 이 CDO를 복제하여 생성합니다. 블루프린트의 설정 값을 생성자에 지정해 두면 언리얼 엔진에서 CDO 정보를 이용하여 블루프린트를 만들기 때문에 우리가 찾던 장소라고 할 수 있습니다

 

스켈레탈 메시 데이터 불러오기

// Sets default values
ATPSPlayer::ATPSPlayer()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// 1. 스켈레탈메시 데이터를 불러오고 싶다.
	ConstructorHelpers::FObjectFinder<USkeletalMesh> TempMesh(TEXT(""));
	if (TempMesh.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(TempMesh.Object);

		// 2. Mesh 컴포넌트의 위치와 회전 값을 설정하고 싶다.
		
	}

	

}

여기서 TEXT에 들어가는 부분은 언리얼 에디터에서 메시를 선택하고 Copy Reference를 통해 경로를 복사합니다.

 

 

그 다음 TempMesh의 TEXT 경로에 붙여줍니다.

ConstructorHelpers::FObjectFinder<USkeletalMesh> TempMesh(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequin_UE4/Meshes/SK_Mannequin.SK_Mannequin'"));

 

에셋 경로 적용하기

엔진 버전에 따라 붙여넣기 한 경로에 다음 코드와 같이 “/Script/Engine”이 붙을 수 있습니다. 이렇게 언리얼 엔진 5에서 추가된 전체 경로 값을 사용해도 되고 앞에서처럼 “/Script/Engine”을 삭제하고 사용해도 상관없습니다. 이 책에서는 이전 버전과의 호환성을 위해 “/Script/Engine.”같은 특정 경로는 삭제하고 사용합니다.

// Fill out your copyright notice in the Description page of Project Settings.

#include "TPSPlayer.h"

// Sets default values
ATPSPlayer::ATPSPlayer()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// 1. 스켈레탈메시 데이터를 불러오고 싶다.
	ConstructorHelpers::FObjectFinder<USkeletalMesh> TempMesh(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequin_UE4/Meshes/SK_Mannequin.SK_Mannequin'"));
	if (TempMesh.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(TempMesh.Object);
		
	}

	

}

 

Mesh컴포넌트 위치 회전 값 설정

// 2. Mesh 컴포넌트의 위치와 회전 값을 설정하고 싶다.
GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -90), FRotator(0, -90, 0));

 

5. 3인칭 카메라 붙이기

3인칭 시점의 카메라를 플레이어에 붙이기. 캐릭터 전용으로 촬영하는 카메라가 필요합니다.
public 접근자로 3인칭 카메라를 컨트롤할 USpringArmComponent 타입의 springArmComp 변수를 설정합니다.

 

SpringArmComponent

header (TPSPlayer.h)

public:
	UPROPERTY(VisibleAnywhere, Category=Camera)
	class USpringArmComponent* springArmComp;

 

생성자

#include "TPSPlayer.h"
#include <GameFramework/SpringArmComponent.h>

// Sets default values
ATPSPlayer::ATPSPlayer()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// 1. 스켈레탈메시 데이터를 불러오고 싶다.
	ConstructorHelpers::FObjectFinder<USkeletalMesh> TempMesh(TEXT("SkeletalMesh'/Game/Characters/Mannequin_UE4/Meshes/SK_Mannequin.SK_Mannequin'"));
	if (TempMesh.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(TempMesh.Object);

		// 2. Mesh 컴포넌트의 위치와 회전 값을 설정하고 싶다.
		GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -90), FRotator(0, -90, 0));
		
	}

	// 3. TPS 카메라를 붙이기
	// 3 - 1. SpringArm 컴포넌트를 붙이기
	springArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
	springArmComp->SetupAttachment(RootComponent);
	springArmComp->SetRelativeLocation(FVector(0, 70, 90));
	springArmComp->TargetArmLength = 400;
}

 

카메라 역할 컴포넌트 생성

Camera 컴포넌트를 생성하고 SpringArm 컴포넌트를 붙입니다.

Header

public:
	UPROPERTY(VisibleAnywhere, Category=Camera)
	class USpringArmComponent* springArmComp;
	UPROPERTY(VisibleAnywhere, Category = Camera)
	class UCameraComponent* tpsCamComp;

};

 

생성자

// Sets default values
ATPSPlayer::ATPSPlayer()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// 1. 스켈레탈메시 데이터를 불러오고 싶다.
	ConstructorHelpers::FObjectFinder<USkeletalMesh> TempMesh(TEXT("SkeletalMesh'/Game/Characters/Mannequin_UE4/Meshes/SK_Mannequin.SK_Mannequin'"));
	if (TempMesh.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(TempMesh.Object);

		// 2. Mesh 컴포넌트의 위치와 회전 값을 설정하고 싶다.
		GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -90), FRotator(0, -90, 0));
		
	}

	// 3. TPS 카메라를 붙이기
	// 3-1. SpringArm 컴포넌트를 붙이기
	springArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
	springArmComp->SetupAttachment(RootComponent);
	springArmComp->SetRelativeLocation(FVector(0, 70, 90));
	springArmComp->TargetArmLength = 400;
	// 3-2. Camera 컴포넌트 붙이기
	tpsCamComp = CreateDefaultSubobject<UCameraComponent>(TEXT("TpsCamComp"));
	tpsCamComp->SetupAttachment(springArmComp);
}

 

카메라 역할 컴포넌트 생성

Camera 컴포넌트를 생성하고 SpringArm 컴포넌트를 붙입니다.

 

Header

public:
	UPROPERTY(VisibleAnywhere, Category=Camera)
	class USpringArmComponent* springArmComp;
	UPROPERTY(VisibleAnywhere, Category = Camera)
	class UCameraComponent* tpsCamComp;

};

 

생성자

// Sets default values
ATPSPlayer::ATPSPlayer()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// 1. 스켈레탈메시 데이터를 불러오고 싶다.
	ConstructorHelpers::FObjectFinder<USkeletalMesh> TempMesh(TEXT("SkeletalMesh'/Game/Characters/Mannequin_UE4/Meshes/SK_Mannequin.SK_Mannequin'"));
	if (TempMesh.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(TempMesh.Object);

		// 2. Mesh 컴포넌트의 위치와 회전 값을 설정하고 싶다.
		GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -90), FRotator(0, -90, 0));
		
	}

	// 3. TPS 카메라를 붙이기
	// 3-1. SpringArm 컴포넌트를 붙이기
	springArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
	springArmComp->SetupAttachment(RootComponent);
	springArmComp->SetRelativeLocation(FVector(0, 70, 90));
	springArmComp->TargetArmLength = 400;
	// 3-2. Camera 컴포넌트 붙이기
	tpsCamComp = CreateDefaultSubobject<UCameraComponent>(TEXT("TpsCamComp"));
	tpsCamComp->SetupAttachment(springArmComp);
}