신입 개발자 공부 과정

이득우의 언리얼 C++ 게임 개발의 정석 Ch1~3 공부하기 본문

UnReal/공부

이득우의 언리얼 C++ 게임 개발의 정석 Ch1~3 공부하기

Lewisjkim 2023. 5. 8. 01:39

언리얼 버전: 책에서 안내 된 버전인 4.19 버전으로 진행하고있다.

월드 = 언리얼 엔진의 뷰포트 윈도우에서 보이는 가상의 작업 공간을 칭한다.

공간 = 가상 세계를 구성하는 3차원의 영역. 게임 을 구성하는 물체는 월드, 곧, 가상의 공간 안에 존재해야 하는데  기본적으로 제공되는 Transform이라는 구조체를 사용한다. 공간의 기본 단위는 cm이다.

시간 = 현실 세계와 동일하게 흘러가지만, 멈추거나 느리게 혹은 빠르게 시간의 스케일을 조절할 수 있다.

물리 = 월드 공간에 배치된 물체에 작용하는 물리적인 환경. ex) 중력. 공간에 배치된 물체가 월드로부터 물리적인 영향을 받으려면  출돌체Collision정보가 있어야 된다.

렌더링 = 엔진이 제공하는 시각적인 기능으로 빛과 이에 반응하는  Material로 구성되며 현실 세계와 유사하게 동작하도록 물리 기반 렌더링 시스템을 제공한다.

Setting -> World Setting에서 더 다양한 기능이 제공된다.

액터 = 콘텐츠를 구성하는 최소 단위의 물체. 게임 월드의 특정 공간에서 자신에게 주어진 역할을 수행하는 물체. 월드에 존재하는 모든 액터들은 월드 아웃라이너 윈도우 목록에서 확인가능하다.

뷰 포트 상에서 선택한 액터의 세부 정보는 디테일 윈도우를 통해 볼 수 있다.

게임로직 = 액터에 특정 상황이 발생할 때 이에 대응할 구체적인 행동을 명령하기위한 프로그래밍 코드. 블루프린터와 C++ 두 가지 언어를 지원한다. (둘 다 할 수 있어야 좋다)

컴포넌트 =액터를 설계할 때 시각적 기능, 물리적 기능 움직임 등을 조합할 수 있는데 이런 기능들을 컴포넌트라고한다.

스테틱 컴포넌트 = 시각 + 물리 기능을 제공 배경 물체에 사용

스켈레탈메시 컴포넌트 = 애니메이션 정보가 + 시각적 + 물리적 기능을 제공하고 주로 캐릭터에 사용

콜리전 컴포넌트 = 지정한 영역에 물리적인 기능을 설정하는 모듈. 시각적인 기능은 없다. 주로  충돌 감지에 사용

카메라 컴포넌트 = 가상 세계에서 보여지는 현재 상황을 플레이어의 모니터 화면에 출력해주는 기능

오디오 컴포넌트 = 소리를 발생시키는데 사용하는 기능

파티클 시스템 컴포넌트 = 파티클 시스템으로 설계된 이펙트를 화면에 보여주기 위한 기능

라이트 컴포넌트 = 전구, 헤드라이트 등과 같이 물체에 광원 효과를 부여하는 기능

무브먼트 컴포넌트 = 물체에 특정한 움직임을 부여하는 기능


UPROPERTY 매크로 = 자동으로 메모리를 관리할 수있게 해주는 기능. 단 언리얼 오브젝트에만 가능하다.

C++ 클래스가 언리얼 오브젝트 클래스가 되려면 클래스 선언에 언리얼 엔진이 정의한 특별한 메크로와 규칙을 부여해야 한다.

언리얼 클래스 선언 매크로 = UCLASS라는 매크로를 선언하고 클래스 내부에는 GENERATED_BODY 매크로를 선언해야된다.

언리얼 클래스 이름 접두사 = 언리얼 오브젝트에는 항상 규칙에 맞게 접두사가 붙어야 한다.
A - 액터 클래스에 사용한다 ex) AObject - actor
U - 액터가 아닌 클래스에 사용한다 ex)UStaticMeshComponent - Component

generated.h - 소스 코드 컴파일 전에 언리얼 헤더 툴이라는 도구를 사용해 선언을 분석하고 언리얼 실행 환경에 필요한 부가 정보를 별도의 파일에 생성. 고로 반드시 선언해야한다.

외부 모듈에의 공개 여부 = 위도우 DLL 시스템은 DLL 내 클래스 정보를 외부에 공개할지 결정하는 _declspec(dllexport)라는 키워가 있는데 언리얼 엔진에서 이를 사용하려면 ' 모듈명_API'라는 키워드를 클래스 선언 앞에 추가해야한다. 이게 없으면 다른 모듈에서 해당 객체에 접근 할 수 없다.

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

#pragma once

#include "EngineMinimal.h"
#include "GameFramework/Actor.h"
#include "Fountain.generated.h"

UCLASS()
class ARENABATTLE_API AFountain : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AFountain();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UPROPERTY()//자동으로 메모리를 관리할 수 있는다. 언리얼 오브젝트라는 특별한 객체에만 사용가능하다.
	UStaticMeshComponent *Body;

	UPROPERTY()
	UStaticMeshComponent *Water;
	
};

컴포넌트를 생성하는 용도로 언리얼 엔진은 new가 아닌 CreateDefaultSubobject API라는 특별한 함수를 제공한다.
문자열 값은 컴포넌트를 구별하기 위한 Hash값 생성에 사용된다.

대표할 루트 컴포넌트 지정 = 
RootComponent = 대표 컴포넌트
자식 컴포넌트->SetupAttachment(대표 컴포넌트)

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

#include "Fountain.h"


// Sets default values
AFountain::AFountain()
{
 	// 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;

	Body = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Body"));
	Water = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Water"));

	RootComponent = Body;
	Water->SetupAttachment(Body);
}

// Called when the game starts or when spawned
void AFountain::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AFountain::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

hot reload = 언리얼 엔진 실행 중에 에디터가 컴파일을 하면 기존의 모듈을 내리고 신규 모듈로 바꾸는 작업을 수행. 기존 모듈을 덮어 쓰지 않고 숫자를 붙인 새로운 파일로 생성된다. 에디터를 완전히 종료하고 컴파일을 수행하면 생성된 임시 모듈들은 자동으로 제거된다. (실시간 리로드)

지금 바로 컴포넌트를 클릭해도 수정 할 수 없게 되어있다. 이를 편집하려면 UPROPERTY(VisibleAnywhere)라고 내부키워드를 추가하고 컴파일 해야된다.


마켓플레이스에서 다운 받은 에셋 중 원하는 스테틱 메시를 적용해준다

바닥에 완전히 붙히려고하면 뷰 포트에서 액터를 선택후 End버튼을 누르면 아래처럼 자동으로 바닥과 밀착하게 된다.


액터를 생성할 때마다 컴포넌트의 위치를 수동으로 설정하지 않도록 액터의 기본값을 설정하는 방법 =

SetRelativeLocation을 사용해서 기본 위치 값을 변경할 수 있다. 언리얼 엔진이 제공하는 구조체 FVector를 사용한다.

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

#include "Fountain.h"


// Sets default values
AFountain::AFountain()
{
 	// 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;

	Body = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Body"));
	Water = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Water"));

	RootComponent = Body;
	Water->SetupAttachment(Body);

	Water->SetRelativeLocation(FVector(0.f, 0.f, 13.5f));
}

// Called when the game starts or when spawned
void AFountain::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AFountain::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

조명과 물이 찰랑이는 이펙트를 추가해 보기 위해 UParticleSystemComponent 와 UPointLightComponent를 사용한다


C++코드에 에셋이 자동으로 로딩되도록 기능을 추가 =
모든 에셋은 레벨 에셋을 제외하고 모두 uasset이라는 확장자로 되어있다.
ConstructorHelpers라는 클래스의 FObjectFinder를 사용해 포인터 변수를 선언하고 이 변수에 경로 값을 전달한다.
SM_BODY - 포인터 변수
SetStaticMesh함수에 전달하면 C++ 코드로 애셋을 로딩하는 기능이 완성
생성자 코드가 여러 번 호출될 때마다 지역 변수를 생성하고 초기화하는 것은 불필요하기에 처음 한 번만 초기화하는 static을 사용한다.


로깅 수준 = 로그의 중요도를 나타내는 로깅 수준은 메시지, 경고, 에러로 나누어진다.

형식 문자열 = TEXT 매크로를 사용하는게 좋다. FString 클래스를 제공하는데. FString으로 선언된 변수에서자열 정보를 얻어오려면 반드시 *연산자를 앞에 지정해줘야 한다.

로그 분류하기 위해 로그 카테고리를 직접 선언할 것이다 = 로깅을 위한 공용 매크로 설정 =

ArenaBattle.h에 카테고리 선언
ArenaBattle.cpp에 정의

컴파일 후 플레이 버튼을 누르면 출력 로그에 분수대 액터의 수만큼 로그가 출력된다.

ABLOG_S = 코드가 들어있는 파일 이름과 함수, 그리고 라인 정보를 추가해 ArenaBattle 카테고리로 로그를 남긴다. 로그를 사용한 함수의 실행 시점을 파악할 때 유용하다.

ABLOG = ABLOG_S정보에 형식 문자열로 추가 정보를 지정해 로그를 남긴다.

ArennaBattle.h에 아래 코드 추가

#define ABLOG_CALLINFO (FString(__FUNCTION__) + TEXT("(") + FString::FromInt(__LINE__) + TEXT(")"))
#define ABLOG_S(Verbosity) UE_LOG(ArenaBattle, Verbosity, TEXT("%s"), *ABLOG_CALLINFO)
#define ABLOG(Verbosity, Format, ...) UE_LOG(ArenaBattle, Verbosity, TEXT("%s %s"), *ABLOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))

위 매크로를 사용해서 작성

결과 :


 어설션 = debugging symbol

epik game 엔진의 옵션에서 디버깅을 위한 편집기 기호를 체크해서 다운로드 해준다

ensure를 사용해 출력 로그에 오류를 띄우면 에디터를 다시 종료하고 띄우지 않아도 된다.


액터의 주요 이벤트 함수 = 자동으로 호출되는 중요한 함수


움직이는 액터 만들기

 

FRotator를 사용하여 분수가 회전하도록 할것이다.

Pitch = Y축 회전 (좌우 기준) = 앞뒤로 회전

Yaw = Z축 회전(상하 기준) =  좌우로 회전

Roll = X축 회전(정면 기준) = 좌우로 기울어지는 듯한 회전

 


무브먼트 컴포넌트의 활용

FloatingPawnMovement = 중력의 영향을 받지 않는 떠있는 액터의 움직임을 제공

RotatingMovement = 지정한 속도로 액터를 회전시킨다

InterpMovement = 지정한 위치로 액터를 이동시킨다

ProjectileMovemnet =  액터의 중력의 영향을 받아 포물선을 그리는 발사체의 움직임을 제공한다. 주로 총알, 미사일 등에 사용한다.

위 코드를 Fountain.h에 추가하고 cpp에 아래와 같이 수정 및 추가하면 AddActorLocalRotation함수로 회전 시킨것과 동일한 결과물이 나온다.

스태틱메시 컴포넌트처럼 트랜스폼 정보가 필수적인 컴포넌트를 씬 컴포넌트라하고

무브먼트 컴포넌트와 같이 기능만 제공하는 컴포넌트를 액터 컴포넌트라고한다


프로젝트의 재구성

액터 제거는 엔진상에서 제공하지 않음으로 수동으로 제거해야된다. 폴더에서 vs, binaries, intermediate, sln파일을 지운다

uproject->우클릭->generateVisualSudio project files 클릭 = 자동으로 비주얼 스튜디오 솔루션을 생성해준다

생성된 파일을 열고 컴파일하면 지웠던 파일들이 새롭게 생성되고 binaries안에는 engine이 사용할 DLL동적 라이브러리 파일이 생성된다.

액터를 제거하려면 source폴더에서 관련 액터파일을 지우고 generate Visual Studio project files를 통해 다시 비주얼 스튜디오 프로젝트를 재생성해야된다. 그 후 sln파일을 열어보면 솔루션 탐색기 상에서 actor의 header파일과 cpp파일이 사라진것을 확인 할 수 있다.