언리얼 엔진4에 요소들의 개념을 알아보자. (여기서 부터 Unity 와 달라진다.)
언리얼 엔진에서 움직이는 캐릭터 하나를 만든다고 가정해보자. (C++ class로 진행)
[C++ 클래스 추가] 를 통해 생성을 진행
그럼 부모 클래스 선택 을 진행해야 하는데 Cheracter, Pawn, Actor 등 다양한 부모 클래스를 선택할 수 있다.
처음 접하는 사람이라면 아마 무턱대로 Cheracter 를 선택할 수 있다. (물론 나도 그랬다.)
하지만 Actor -> Pawn -> Cheractor 순으로 진행하는 것이 Unreal Engine에서는 추천하고 있다.
이유는 간단하다. 바로 Cheracter 를 생성하는 것 보다 Acter, Pawn등을 거치고 Cheracter를 생성하는 것이 기능적으로 더 많은 것을 구현할 수 있기 때문이다.
그럼 Unity와는 어떤 차이가 있는 것인가?
앞서 Unreal Engine 에서는 캐릭터를 하나 만드는데 있어 Actor -> Pawn -> Cheracter 순으로 거쳐서 생성이 된다.
즉, 이 Object가 어떤 역할을 할 것인지 생성 전 부터 설정을 한다는 개념이다.
하지만 Unity의 경우, 생성 후 어떤 역할을 할 것인지 설정한다.
이 부분에 있어 Unreal Engine과 Unity의 차이가 발생한다.
이 차이가 중요한 차이인가?
중요한 차이라고 생각이 될 수 있다고 보는 이유는 이러하다.
작업에 있어 편리함과 불편함의 차이가 각각 있다고 본다.
Unity의 경우, 간단한 Object 를 생성 후, 이를 필요에 따라 설정하고 관리할 수 있다.
반면에 Unreal Engine의 경우, Object 를 생성하는데 있어 Actor -> Pawn -> Cheracter 를 설정하고 넘어와야 하기에 과정이 길어진다.
Unity의 경우, Object에 기능을 하나하나 부여해야하는 단점이 있다.
반면에 Unreal Engine의 경우, Object 를 생성하는데 있어 Actor -> Pawn -> Cheracter 를 거치고 왔기에 해당 Object의 기능이 기초적으로 설정이 된 상태이기에 편리하다.
Unity의 경우, 파일의 이름을 변경하거나 파일을 삭제한다면 연관된 부분의 수정 혹은 해당 파일이 import된 부분을 지우면 된다.
놀랍게도 ... Unreal Engine은 이게 적용되지 않는다.
자 그럼 Actor에 대해 알아보자
인게임에 배치를 할 수 있는 Object를 가리키는 용어이다.
[C++ 클래스 추가] 를 통해 생성을 진행하자.
프로젝트에 코드 추가가 완료되면 ?! 띠리릭 소리와 함께
콘텐츠 브라우저에 MyActor 라는 이름으로 방금 전에 생성한 C++ 클래스 기반의 MyActor Object가 생성된 모습을 확인할 수 있다.
이제 [월드 아웃라이더]를 살펴보자.
이는 Unreal Engine 의 게임 화면에 구성된 모든 Object를 표시한다.
그럼 MyActor를 어떻게 여기에 추가할까 ?
방법은 의외로 간단하다. MyActor를 클릭 후, 화면에 드래그하면 된다.
성공적으로 MyActor를 게임에 추가된 모습을 확인할 수 있다.
하지만 ?! 화면에서 MyActor의 모습을 확인할 수 없다?!
이유는 우리가 MyActor의 기능, 매쉬(형태) 를 정해주지 않았기에 게임에 추가하여도 형태를 알아볼 수 없는 것이다.
그럼 형태를 어떻게 넣어야 하는거지?
이는 형태를 추가하는 C++ Class Code를 작성하는 것이다.
나는 Xcode로 이를 진행하기에 Xcode로 확인해보자.
위의 사진은 MyActor를 생성하면서 생긴 헤더파일과 CPP파일이다.
생성된 MyActor.h 파일을 살펴보자
UCLASS()
class TESTUNREALENGINE_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// 게임을 시작하거나 처음 이 Actor가 spawn 될 때, 호출이 되는 기능
virtual void BeginPlay() override;
public:
// 매 프레임 마다 호출이 되는 기능
virtual void Tick(float DeltaTime) override;
};
다음과 같은 코드들 중에서 살펴볼 것을 간단하게 보고 넘어가자.
바로 BeginPlay() 와 Tick(float DeltaTime) 이다.
protected:
virtual void BeginPlay() override;
BeginPlay() => 게임이 시작하거나 처음 Actor가 Map에 Spawn 될 때, 호출되는 기능을 의미한다.
public:
virtual void Tick(float DeltaTime) override;
Tick(float DeltaTime) => 매 프레임 마다 호출이 되는 기능을 의미한다.
그럼 우리는 Actor에 Mesh를 어떻게 적용하는거지 ?
이제 MyActor class 에 기능을 추가해보는 Code를 작성해보자.
private:
UStaticMeshComponent* Mesh;
UStaticMeshComponent* 기능을 포인터, Mesh; 라고 선언했다.
여기서 끝나지 않고 추가적으로 작성하는 부분이 있다. (Unreal Engine 한정)
기능을 선언한 부분에 UPROPERTY() 를 선언하는 작업이다.
앞서 MyActor.h 라는 파일의 전체코드를 살펴보면
UCLASS(), GENERATED_BODY(), UPROPERTY() 등 이전에는 보지 못했던 코드가 존재한다.
이들의 역할이 무엇이길래 선언해야 하는건가?
C++ 에는 reflection 이라는 기능이 없다. (C#에는 존재한다.)
Reflection = 객체 지향 프로그램언어에서 런타임에 객체의 형을 결정할 수 있는 능력을 의미
즉, 컴파일러가 이해하는 주석에 가까운 기능이다.
하지만 앞서 본 UCLASS(), GENERATED_BODY(), UPROPERTY() 의 코드들은 Unreal Engine 에서 만든 reflection 개념의 기능이다.
정리하자면!!
UCLASS() = Unreal Engine Class 이런 형태를 나타내며 컴파일러가 런타임때
컴파일러 : MyActor.h 파일은 Unreal Engine Class 구나? 그럼 부가적인 메타 데이터고 같이 기입해줄께!!
이렇게 인식하게 해주는 고급기능인 것이라고 생각하면 된다.
다시 Mesh로 돌아와서 .... (앞서 배울게 많다!!!)
UPROPERTY()
private:
UStaticMeshComponent* Mesh;
MyActor.h 파일에 Mesh를 선언했으니 MyActor.cpp파일로 넘어가자.
// Sets default values
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
MyActor.cpp 파일에서 Mesh를 선언해보자.
기존의 C++ 대로 했었다면 AMyActor::AMyActor() 아래에 다음과 같이 작성할 것이다.
Mesh = new UStaticMeshComponent();
하지만 UPROPERTY() 를 헤더파일에서 선언했기에 이는 unreal engine에서 관리하는 메모리이기에 조금 다르게 cpp파일에 선언해야 한다.
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEST("MESH"));
기존 C++ 작성법과는 다르게 작성된 모습을 볼 수 있다.
CreateDefaultSubobject(); 는 unreal engine에서 자체적으로 메모리를 관리하기 위해 선언하는 방식이라고 보면 된다.
TEXT("MESH") 에 해당하는 부분은 해시값으로 사용된다. (일종의 이름 개념)