본문 바로가기
Engine/Unreal

언리얼 엔진의 기초

by 뇌 속의 통 2024. 11. 27.

언리얼이라는 게임을 만들 때 구현한 엔진.

언리얼이라는 게임은 1인 FPS 슈팅 장르다.

 

즉, Map은 동일한데 게임 모드를 다르게 해서 진행했었다.(데스매치, 깃발뺏기 등등)

 

그렇기 때문에 언리얼 엔진에서는 Game Mode를 설정하고 해당 Mode별로 Pawn, Controller등을 설정해 줄 수 있으며, 가장 우선적으로 해야되는 작업이다.

 

GameMode에서 Player Start를 override 할 수도 있고, Login, Logout 등도 override 할 수 있다.

이처럼 게임의 전반적인 기틀뿐만 아니라 게임 서버 역할도 GameMode에서 하는 것이다.

 

다만, Camera는 GameMode에서 만드는게 아니라 Player Controller에서 생성하고 관리한다.

없으면 Actor 위치에 기본으로 생성을 해준다.

 

언리얼의 Framework

int main()
{
	init();
	while(!g_exit_requested)
	{
		poll_input();
		update();
		render();
	}
	
	shutdown();
}

 

 

앞서 설명한 대로 Unreal Engine은 기초부터 작업하지 않고 GameMode의 하위 클래스를 정의하고 상속받은 함수들을 재정의하여 시작한다.

 

그리고 각 Actor, Object들의 Beginplay, Tick 함수들을 재정의하여 게임 환경을 만들어 간다.

Unreal Engine에 다른 모든 것을 처리해주기 때문이다.

 

 

Unreal Engine이 실행되는 Main 함수를 찾아보자.

 

#include "LanchEngineLoop.h"

FEngineLoop GEngineLoop;
bool GIsRequestingExit = false;

int32 GuardedMain(const TCHAR* CmdLine)
{
	int32 ErrorLevel = GEngineLoop.PreInit(CmdLine);
	//대부분의 모듈이 로드되는 곳
	
	if(ErrorLevel != 0 || GIsRequestingExit)
	{
		return ErrorLevel;
	}
	
	ErrorLevel = GEngineLoop.Init();
	//엔진 생성 및 초기화 -> Start 되는 단계

 

int32 ErrorLevel = GEngineLoop.PreInit(CmdLine);

 

 

PreInit 구간의 초기화 되는 모듈

 

1. 하위 수준 엔진 모듈을 로드하여 필수 시스템이 초기화되고 필수 유형이 정의된다.

2. 그 다음 플러그인, 소스 모듈이 있는 경우 해당 모듈이 로드된다.

3. 더 높은 수준의 엔진 모듈 대부분이 로드된다.

4. 프로젝트와 플러그인 모듈이 로드되는 기본 지점에 도달한다.

 

이때 게임 상태가 생성되기 전이다.

모듈이 로드되면 해당 모듈에 정의된 UObject class를 등록한다.

엔진의 Reflection System이 해당 class를 인식하고 각 class에 대한 CDO를 구성한다.

모든 class의 CDO가 생성되며 이 CDO는 Class의 모든 기본 속성을 설정. 모든 Instance가 이 기본 값을 참조한다.

만약 생성자에 다른 코드를 기재하였다면, 이 과정에서 생성자 코드가 실행되어 오류가 발생할 수 있다.

그러므로 생성자에서는 게임플레이에 관한 코드를 작성하지 않는 편이 좋다.

 

	ErrorLevel = GEngineLoop.Init();

 

Engine의 구성 파일을 확인하여 어떤 GameEngine class를 사용해야하는 지 파악하고 Instance 생성 생성한 Engine을 초기화 map을 로드 하기전에 GameInstance, GameViewportClient, LocalPlayer 등 몇가지 중요한 개체 생성 개체 초기화 늦게 로드되도록 구성된 모든 프로젝트 또는 플러그인 모듈을 로드한다.

 

엔진 시작 LoadMap에 대한 초기 호출이 발생하고 완료되면 맵에 저장된 UWorld -> ULevel 등 Game Object가 생성 및 초기화 된다.

 

위는 Map이 로드되기 전, 아래는 Map이 로드 된 후 생성된 개체들이다.

그래서 위는 Engine Object, 아래는 Game Object라고 한다.

Game Object는 해당 Map에서만 존재한다. Game Object를 유지하며 Map만 변경할 수 있으나 다른 Server에 연결하거나 다른 Map으로 그냥 이동해버리면 모든 Game Object는 파괴된다.

 

//LoadMap 호출 단계

Map이 변경될 것임으로 알리고 로드된 Map이 있다면 해당 Map 제거

WorldContext라는 로드되는 Map을 추적하는 영구적인 개체를 이용하여 Map의 데이터를 관리.

(GameInstance에 의해 생성)

 

WorldPackage를 Load하며 UWorld를 생성한다.

(UWorld : 우리가 Map에 배치한 Actor들은 저장하고 있는 ULevel과 함께 메모리에 로드되어 있다. 데이터들은 직렬화하여 .umap file에 기록된다.)

 

* UWorld 초기화

물리학, 네비게이션, AI, 오디오와 같은 시스템을 설정.

SetGameMode 함수가 호출되어 GameMode가 생성된다.

 

 

GameMode가 생성되며 Engine은 Map을 완전히 로드한다.

 

이제 UWorld에서 작업이 진행된다.

모든 Actor 구성요소를 World에 등록 이떄 Actor의 ActorComponent는 Actor에게 중요한 3가지 데이터를 제공한다.

 

  1. 로드된 world에 대한 참조 제공
  2. OnRegister()를 제공하여 조기 초기화 기회를 제공
  3. PrimitiveComponent인 경우 Uworld의 FScene에 Scene 추가.

 

구성 요소가 등록 되면 GameMode의 InitGame 함수 호출

GameMode가 GameSession 생성 → 각 Level별로 자기에게 저장된 Actor들 Initialize 진행.

초기화는 조기, 일반 두가지로 진행.

 

그리고 이때 GameMode는 GameState 생성 및 UWorld와 연결. GameNetworkManager 생성.

GameMode의 InitGameState 함수 호출

Actor들의 조기 초기화가 완료되며 이제 일반 초기화가 진행된다.

 

일반 초기화에서는 bAutoActivate 변수를 확인하여 true면 구성요소를 활성화시켜준다.

bWantsInitializeComponent 변수를 확인하여 해당 Component를 초기화해준다.

그리고 이제 PostInitializeComponent 함수도 실행되며 여기서 대부분의 초기화가 이뤄진다.