이번 과제는 언리얼 엔진에서 C++ 스크립팅을 통해 간단한 좌표이동 프로그램을 제작하는 것이다.
우선 언리얼 프로젝트를 실행할 때 매번 블루프린트로 체크하고 실행했었는데 C++을 배우고 처음 C++로 실행시켜주었다. 프로젝트를 실행시키면 디폴트로 설정되어 있는 컴파일러 프로그램이 켜진다. 아니면
이런식으로 수동으로 켜줄 수 있다.
이제 간단한 좌표이동과, 그에 따른 로그를 출력하는 것을 하기 위해 C++ 클래스 기반 액터를 생성해주었다.
콘텐츠 브라우저에서 마우스 우클릭을 통해 이런식으로 접근하거나 맨 위 상단 카테고리 탭에서
이런 식으로 생성해주면 된다.
클릭해보면 아래 사진처럼 부모클래스를 선택할 수 있게된다.
언리얼에서 제공하는 일반적인 클래스(Common Classes)만 해도 17가지나 된다. All Classes에는 훨씬 많은 클래스가 있지만 이번 프로젝트에서는 월드에 스폰을 할 수 있고 간단하게 좌표이동과 로그출력만 할 것이기 때문에 Actor
을 부모클래스로 선택하여 생성해 주었다.
MyActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class NBCJUNG_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
FVector2D Start;
int EvCount;
TArray<FVector2D> Arr;
float totDis;
public:
// Called every frame
// virtual void Tick(float DeltaTime) override;
float Distance(FVector2D current, FVector2D later);
void Move(FVector2D current);
int Step();
void CreateEvent();
};
우선 앞서서 OOP Summary 실습 때도 했었지만 언리얼에서 마찬가지로 선언부는 헤더파일.h, 구현부는 .cpp에 작성한다. 맨 위부터 중요한 것들을 살펴보자
#pragma once
: 헤더 파일의 중복 포함을 방지하기 위해 사용하는 전처리기 지시문이다. 쉽게 말해서 '한번만 포함해!' 라는 뜻
#include "CoreMinimal.h"
: 언리얼 엔진에서 사용하는 헤더 파일로, C++에서 게임 개발에 필요한 기본적인 기능과 클래스를 제공하는 파일. 언리얼의 핵심 요소들을 빠르게 불러오는 필수세트라고 생각하면 된다.
FString
, FVector
, FRotator
, TArray
, TMap
등UE_LOG
, TSharedPtr
, TWeakObjectPtr
등UCLASS
, UFUNCTION
, UPROPERTY
, GENERATED_BODY()
등#include "GameFramework/Actor.h"
: 언리얼 엔진에서 AActor
클래스를 사용하기 위해 포함하는 헤더파일 (부모클래스를 Actor로 설정) 언리얼 엔진에서 모든 게임 오브젝트의 기본이 되는 클래스가 바로 AActor
이기 때문에, 이 파일을 포함하면 게임 오브젝트를 만들거나 조작할 때 필요한 기능을 사용할 수 있다.
#include "MyActor.generated.h"
: 언리얼 엔진에서 리플렉션과 관련된 코드를 자동으로 생성하고 포함하기 위해 사용하는 헤더 파일. 이 파일은 Unreal Header Tool(UHT)에 의해 생성되며, 언리얼 엔진이 제공하는 특별한 기능들을 사용할 수 있도록 지원
NBCJUNG_API
: API 키워드는 해당 클래스나 함수가 다른 묘듈 또는 외부프로그램에 사용가능하도록 공개해야함을 나타내는 것이다. 주로 dll 파일로 컴파일된 모듈간의 연결과 관련이 있다. 즉, 클래스 이름 앞에 적음으로써 모듈간의 클래스 함수 공유를 가능케 한다.
MyActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyActor.h"
// 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;
Start = { 0, 0 };
Arr.SetNum(11);
EvCount = 0;
totDis = 0;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
for (int i = 0; i < 10; i++) {
if (i == 0) { Arr[0] = Start; }
Arr[i + 1] = { Arr[i].X + Step(), Arr[i].Y + Step() };
Move(Arr[i]);
Move(Arr[i + 1]);
Distance(Arr[i], Arr[i+1]);
if (i == 0) { continue; }
CreateEvent();
}
UE_LOG(LogTemp, Warning, TEXT("Total Distance : %f"), totDis);
UE_LOG(LogTemp, Warning, TEXT("Total Event : %d"), EvCount);
}
// Called every frame (틱 함수 제외)
/*void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
*/
float AMyActor::Distance(FVector2D current, FVector2D later)
{
FVector2D result = later - current;
UE_LOG(LogTemp, Warning, TEXT("Distance : %f"), result.Size());
totDis += result.Size();
return result.Size();
}
void AMyActor::Move(FVector2D current)
{
UE_LOG(LogTemp, Log, TEXT("Location : [%f, %f]"), current.X, current.Y);
}
int AMyActor::Step()
{
int randnum = FMath::RandRange(-1, 1);
return randnum;
}
void AMyActor::CreateEvent()
{
int Cointoss = FMath::RandRange(0, 1);
if (Cointoss == 1) {
++EvCount;
UE_LOG(LogTemp, Error, TEXT("You have encountered the %d enemy "), EvCount);
}
}
FVector
언리얼에서의 벡터는 이름만 벡터가 아니라 실제로 3D 공간에서 좌표나 방향을 다룰 때 사용하는 "진짜" 수학적 벡터이다. 위치, 방향, 크기 등을 표현하기 위해 만들어진 구조체. 아래처럼 X, Y, Z 로 바로 원소에 접근할 수 있다.FVector Location(100.0f, 200.0f, 300.0f);
float Xcoord = Location.X;
UE_LOG
언리얼 엔진에서 로그 메세지를 출력하는데 사용하는 매크로이다. 게임 개발 중 디버깅이나 상태 확인을 위해 콘솔 창이나 출력 로그에 메세지를 기록할 수 있도록 도와준다.
FVector::Size()
함수는 벡터의 크기(길이)를 계산하는데 사용된다. C++에서는 인자의 개수를 구하는데 사용됐었는데 언리얼에서는 인자의 개수를 구하는 함수는 TArray::Num()
함수를 이용하면 된다.
FMath
는 언리얼 엔진에서 수학 관련 기능을 제공하는 유틸리티 클래스이다.
FMath::Abs()
: 절대값 FMath::Abs(-5) = 5
FMath::Sqrt()
: 제곱근 FMath::Sqrt(16) = 4
FMath::Pow()
: 거듭제곱 FMath::Pow(2, 3) = 8
FMath::Fmod()
: 나머지 FMath::Fmod(10, 3) = 1
FMath::RandRage()
: 지정된 범위 내에서 랜덤 값 반환FMath::Rand()
: 0과 RAND_MAX
사이의 랜덤 정수 반환결과는 매우 준수하게 나왔다. 랜덤으로 X,Y 좌표가 2미만의 정수 값으로 움직이고 있고 이동했을 때의 거리계산도 잘 나온다. 또한 이벤트 발생도 50% 확률로 나오고 있는 모습이다. 마지막 총 거리계산, 이벤트 발생횟수도 문제 없이 잘 나왔다.
이번과제는 언리얼이랑 처음 연동하여 코딩을 해서 그런지 너무 재미있었다. 처음이라 모르는게 매우매우매우 많았지만 내가 작성한 코드들이 그래픽 비주얼로써 나오고 있는 것은 아니지만 언리얼 엔진의 화면에 로그라도 나오고 있다는 사실이 너무 감격스러웠다. 약간 C언어 배울 때 처음 "Hello World"를 찍어본 느낌이랄까?..ㅋㅋ 난이도는 이번게 훨씬 힘들었지만ㅎㅎ
오늘 나는 작지만 거대한 발걸음을 내딛었다고 생각한다. 습득하고 배워서 얼른 게임캐릭터가 도끼를 던지는걸 구현해보고 싶다. 마치 내가 선망하는 갓오브워의 크레토스처럼