3냥 집사이면서 게임 개발자입니다.
언리얼 빌드 시스템 본문
언리얼 엔진의 프로젝트 구성과 에디터 동작 방식의 이해, 언리얼 엔진의 모듈 시스템을 기반으로 소스코드를 구성하고 엔진 소스코드를 탐색하는 법, 언리얼 플러그인 시스템을 활용해 효과적으로 모듈을 구성하는 방법을 정리하고자 합니다.
언리얼 에디터 프로젝트 구성
언리얼 에디터 구성
- 게임 제작을 위해 에픽 게임즈가 제공하는 저작 도구입니다.
- 언리얼 엔진의 구성
- 에디터 : 게임 제작을 위해 제공되는 응용 프로그램 (일반적으로 인식하는 언리얼 엔진)
- 게임 빌드 : EXE 파일과 리소스로 이루어진 독립적으로 동작하는 게임 클라이언트
- 언리얼 에디터의 특징
- 게임 개발 작업을 위해 다양한 폴더와 파일 이름 규칙이 미리 설정되어 있습니다.
- 정해진 규칙을 잘 파악하고 프로젝트 폴더와 파일을 설정해야 합니다.
- 에디터에서 기획과 개발을 완료한 후, 게임 빌드를 통해 최종 게임 빌드를 제작하도록 설정합니다.
[언리얼 에디터에서 게임을 구성] => [게임 빌드를 수행하고 프로그램을 패키징]
언리얼 에디터의 동작
- 프로젝트 폴더의 uproject 확장자를 더블 클릭하면 에디터가 트리거됩니다.
- 에디터의 실행 방식
- uproject 확장자는 윈도우 레지스트리에 등록되어 있습니다.
- 등록이 안되어 있다면 런처를 실행해 등록합니다.
- UnrealVersionSelector 프로그램으로 프로젝트 정보가 넘겨집니다.
- UnrealVersionSelector는 런처가 저장한 에디터 정보로부터 버전에 맞는 에디터를 실행합니다.
- UnrealVersionSelector소스는 에픽 게임즈 GitHub에서 확인 가능합니다.
.uproject -> UnrealVersionSelector -> UnrealEditor
에디터 버전 정보의 파악
- 프로젝트 .uproject 텍스트 파일에 정해져있습니다.
- .uproject 확장자는 에디터를 띄우기 위한 명세서 역할을 합니다.
- 버전 내용은 JSON 형식으로 구성되어 있습니다.
- 파일에 기록된 버전 정보를 바탕으로 에픽 런처가 지정한 정보를 찾아 에디터를 실행합니다.
- ProgramData/Epic/UnrealLauncher 폴더에 관련 정보가 있습니다.
- 이 역시 JSON 형식으로 설치된 언리얼 버전 정보가 기록되어 있습니다.
더블 클릭을 하게되면 레지스트리에 기록된 정보에 의해서, UnrealVersionSelector 프로그램이 실행되고,
이 프로그램은 파일안에 명시된 5.1 버전의 정보를 에픽 게임즈 런처로부터 찾아 해당 에디터를 실행하게 됩니다.
Config : 프로젝트 설정에 필요한 정보를 보관하는 데 사용됩니다.
Content : 에셋 보관에 사용됩니다.
DerivedDataCache : 사용하는 에셋들의 주요 정보를 미리 캐싱하는데 사용됩니다.
(데이터 캐쉬가 있다면 로딩이 빠릅니다. 하지만 용량이 위험하면 지워도 문제되지 않습니다.)
Intermediate : 임시적으로 사용되는 중간 결과물을 보관하는데 사용합니다. (용량이 크다면 삭제해도 무방합니다.)
Saved : 임시로 무언가를 저장하는 용도로 활용됩니다. (의도적으로 저장한 데이터가 없다면 저장해도 문제 없습니다.)
여기서 어떻게 C++ 코드를 추가하는지 작업해보겠습니다.
블루프린트 프로젝트
- C++ 코드가 없는 언리얼 프로젝트를 의미합니다.
- 언리얼 엔진이 제공하는 기본 기능을 활용해 게임을 제작하는 프로젝트입니다.
- 언리얼 엔진은 게임 제작에 필요한 기능을 모듈이라는 단위로 제공하고 있습니다.
- 언리얼 엔진의 모듈을 상속받아 블루프린트를 활용해 모든 기능과 로직을 구현하는 방식입니다.
[ C++ 모듈을 상속받은 블루프린트로 제작한 기능 및 게임 로직]
[ 언리얼 엔진 C++ 모듈 ]
언리얼 C++ 프로젝트
- 언리얼 엔진 C++ 모듈에 개발자가 추가로 자신만의 C++ 모듈을 추가할 수 있습니다.
- 언리얼 엔진 모듈과 개발자 모듈을 함께 사용하는 프로젝트입니다.
[ C++ 모듈을 상속받은 블루프린트로 제작한 기능 및 게임 로직]
[ 개발자 C++ 모듈 ]<< C++ 작업 결과물
[ 언리얼 엔진 C++ 모듈 ]
언리얼 C++ 모듈
- 언리얼 엔진의 소스코드는 모두 모듈(Module) 단위로 구성되어 있습니다.
- 모듈을 컴파일함으로서 에디터 및 게임에 우리가 제작한 로직을 공급할 수 있습니다.
- 모듈 단위로 구성된 C++ 소스 코드를 컴파일한 결과물
- 에디터 용으로 DLL 동적 라이브러리
- 게임 용으로는 정적 라이브러리
- 에디터 용 모듈은 언제나 UnrealEditor-{모듈이름}.DLL 이름 규칙을 갖고 있습니다.
* 언리얼 엔진 설치 폴더에 가면 수많은 에디터 모듈이 설치된 것을 볼 수 있습니다.
언리얼 C++ 모듈의 추가
- 기본 언리얼 모듈에 우리가 제작한 C++ 모듈을 추가해 에디터를 띄우고 싶은 경우
- 직접 만든 에디터 모듈 (DLL 동적 라이브러리)을 빌드 폴더에 넣어줘야 합니다.
- Window의 경우 Binaries/Win64 폴더에 해당 DLL을 넣어야합니다.
- 빌드된 모듈 목록이 있는 UnrealEditor.modules 파일도 같은 폴더에 넣어주어야 인식됩니다.
- uproject 명세서에 모듈 이름을 지정하고 에디터를 실행합니다.
다른 프로젝트의 빌드 결과를 가져와서 포함하려면 즉, DLL 을 추가하려면 uproject를 다시 txt 파일처럼 수정해야합니다.
다시 실행하고 에디터에서 다른 모듈 폴더를 확인할 수 있으며, 나열된 언리얼 오브젝트들을 확인할 수 있습니다.
모듈 C++ 코드의 관리
- 언리얼 프로젝트가 소스 코드를 관리하는 규칙에 따라 소스 코드 구조를 구성해야 합니다.
- 소스 코드는 멀티 플랫폼 빌드 시스템을 지원하기 위해 특정 프로그램에 종속되어 있지 않습니다.
- 실제 빌드를 진행하는 주체 : Unreal Build Tool 이라는 C# 프로그램
- Source 폴더에 지정된 규칙대로 소스를 넣으면 플랫폼에 맞춰서 알아서 컴파일을 진행합니다.
소스 코드(Source 폴더) -> Unreal Build Tool (C# 프로그램) -> {윈도우즈 컴파일러 실행, 맥 컴파일러 실행, 리눅스 컴파일러 실행}
Source 폴더의 구조
- Source 폴더
- 타겟 설정 파일 (전체 프로젝트 관점에서 솔루션이 다룰 빌드 대상을 지정하는 파일)
- 모듈 폴더 (보통은 프로젝트 이름으로 모듈 이름을 지정합니다.)
- 모듈 설정 파일 (하나의 모듈을 빌드하기 위해 추가해야 될 정보를 설정하는 파일, 모듈을 빌드하기 위해 참조할 다른 모듈에 대한 정보를 지정)
- 소스 코드 파일(.h, 및 .cpp 파일들)
- 타겟 설정 파일 : 전체 솔루션이 다룰 빌드 대상을 지정합니다.
- {프로젝트이름}.Target.cs : 게임 빌드 설정
- {프로젝트이름}Editor.Target.cs : 에디터 빌드 설정
- 모듈 설정 파일 : 모듈을 빌드하기 위한 C++ 프로젝트 설정 정보
- {모듈이름}.Build.cs : 모듈을 빌드하기 위한 환경 설정
* C#이 가진 유연한 기능(compile on-the-fly)을 활용해 런타임에 cs파일을 읽어 빌드 환경을 구축하고 컴파일을 진행합니다.(이러한 특성을 사용하기 위해 언리얼 빌드 툴은 C#으로 만들어졌습니다.)
게임 프로젝트의 소스
- 직접 만든 소스가 게임 프로젝트의 C++ 모듈이 되기 위해 필요한 것
- 모듈(Module)을 구현한 선언한 헤더와 소스 파일이 있어야 합니다.
- 주로 {모듈이름}.h 와 {모듈이름].cpp로 지정합니다.
- 모듈의 뼈대를 제작합니다.
- 매크로를 통해 기본 뼈대 구조를 제작합니다.
- IMPLEMENT_MODULE : 일반 모듈
- IMPLEMENT_GAME_MODULE : 게임 모듈
- IMPLEMENT_PRIMARY_GAME_MODULE : 주 게임 모듈
- 일반적으로 게임 프로젝트는 주 게임 모듈을 하나 선언해야 합니다.
* 모든 준비가 완료되면 Generate Visual Studio project files. 메뉴를 선택
Intermediate 폴더에 프로젝트 관련 파일이 자동으로 생성됩니다.
Source 폴더를 규칙에 맞게 구성하면 Intermediate 폴더는 언제든지 재생성이 가능합니다.
UnrealBuildSystem 폴더 안에는 모듈.cs 파일이 들어가야하고 Source 폴더 안에는 타겟.cs파일이 필요합니다.
타겟.cs 파일을 먼저 만들어주겠습니다.
UnrealBuildSystem 이라고하는 모듈 이름, 프로젝트 이름이 아닙니다.
모듈 이름에 관련된 설정 파일을 폴더 안에 추가하겠습니다.
과정이 길어서 사진은 생략하고 글로 설명을 대체하겠습니다.
타겟.cs 파일과 모듈.cs 파일에 내용을 추가하고 uproject로 Generate Visual Studio project files 를 통해 새로운 솔루션 파일이 생성됩니다.
해당 솔루션을 열어서 헤더와 cpp 에 내용을 채워넣습니다.
그 후 프로젝트 빌드해 실행하면, 언리얼 에디터는 언리얼 빌드 시스템이라는 모듈을 탑재한 상태에서 실행이 됩니다.
모듈을 탑재했는데 Content에 관련 오브젝트가 나오지 않는 이유는 언리얼 오브젝트가 존재하지 않기 때문입니다.
이전에 진행했던 것과 같이 클래스를 생성하면 UnrealBuildSystem 이라고하는 모듈 안에 Student 라고 하는 오브젝트가 생성된것을 확인할 수 있습니다.
이로써 언리얼 엔진에서 게임을 담당하는 주 모듈을 직접 수동으로 추가하는 과정을 거쳐봤습니다.
모듈간의 종속관계
- 모듈 사이에 종속 관계를 설정해 다양한 기능을 구현할 수 있습니다.
- 직접 만드는 게임 모듈도 언리얼 엔진이 만든 모듈을 활용해야 합니다.
- 언리얼 엔진이 제공하는 모듈 사이에도 종속 관계가 있습니다.
*언리얼 엔진의 소스는 결국 수많은 모듈 집합이라고 해도 과언이 아닙니다.
가장 근본적인 모듈은 Core -> CoreUObject -> Engine = JsonUtilities
= Json
새로운 모듈의 추가
- 하나의 모듈에 너무 많은 코드가 들어가면 언리얼 엔진은 빌드 방식을 변경합니다.
- 그렇기에 프로젝트가 커질 수록 모듈을 나누어서 관리하는 것이 유리합니다.
* 언리얼 엔진은 하나의 프로젝트에 여러 개의 게임 모듈을 둘 수 있도록 설계되어있습니다.
이것을 주 게임 모듈, 서브 게임 모듈이라고 하겠습니다.
모듈의 공개와 참조
- 모듈 내 소스를 필요한 만큼만 공개해야 모듈 간 의존성을 줄이고 컴파일 타임을 최소화 할 수 있습니다.
- 공개할 파일은 모두 Public 폴더로 보관합니다.
- 예외) 예전 언리얼 엔진은 Classes 폴더가 있어 Public 폴더 역할을 하면서 언리얼 오브젝트를 관리했었습니다.
- 숨길 파일은 모두 Private 폴더로 보관합니다.
- 외부로 공개할 클래스 선언에는 {모듈이름}_DLL 매크로를 붙여야 합니다.
- 게임 모듈에서는 Build.cs 설정을 통해 참조할 모듈을 지정할 수 있습니다.
* 의존성을 최소화 시켜야 변경사항에 유연하게 대처할 수 있고, 빌드 시간을 단축시킬 수 있기 때문입니다.
플러그인 시스템
- 게임 프로젝트 소스에 모듈을 추가하는 방법은 분업이 어렵다는 단점이 있습니다.
- 모듈만 독립적으로 동작하는 플러그인 구조를 만들어 분업화하는 것이 바람직합니다.
예) 서브 게임 모듈을 플러그인으로의 분리(공용 기능들)
플러그인 구조
- 플러그인은 다수의 모듈과 게임 콘텐츠를 포함하는 포장 단위입니다.
- 에디터 설정을 통해 유연하게 플러그인을 추가하거나 삭제할 수 있습니다.
- 플러그인 구조
- 플러그인 명세서 (uplugin 파일)
- 플러그인 리소스 (Resource 폴더, 에디더 메뉴용 아이콘)
- 콘텐츠
- 모듈 폴더
- 이러한 플러그인은 마켓 플레이스 판매로도 이어질 수 있도록 여러 설정을 추가할 수 있습니다.
프로젝트 폴더 안에 Plugin 폴더를 생성해 플러그인을 만들어 새롭게 모듈을 추가해 보겠습니다.
플러그인 명세서인 uplugin 파일을 만들어줘야 합니다.
에디터 메뉴를 통해 추가할 수 있지만, 이러한 내부구조로 진행된다는 것을 알기 위해 수동으로 제작해봤습니다.
이 전에 작업했던 것과 같이 Source 폴더를 추가해 내용과 h, cpp 파일을 추가해줍니다.
이후 uproject 에서 다시 Generate Visual Studio project files 를 해준 뒤 솔루션을 열어보면 프로젝트에 추가되어 있는 것을 확인할 수 있습니다.
이후 이전과 동일하게 오브젝트를 추가할 때 어떤 모듈에 추가할 건지 선택지가 생깁니다.
이후에 플러그인 구조에서 public 과 private 로 구분해 보관하겠습니다.
모듈에 대한 헤더와 Person만 public 에 옮겨 정보를 제한하는 방식입니다.
정보를 제공하지 않는 private 에 속한 클래스 헤더는 모듈 이름_API 로 되어있는 매크로들을 지워주면서
안전하게 외부 모듈이 참조할 수 없도록 지정하는 것이 좋습니다.
이후 주 게임 모듈안에서 public, private 을 나눌 필요는 없습니다. 프로젝트의 빌드.cs 파일에서 CommonUtility 라는 모듈 이름을 추가함으로써 public 폴더의 위치를 자동으로 참조하고, 여기서 만들어진 라이브러리들을 자동으로 링크해 사용할 수 있습니다. 의존성을 설계하는 방법이 아주 편리한 것 같습니다.
이 모듈을 추가하면 주 게임 모듈 안에 있는 클래스에서 마치 해당 헤더를 갖고 있는 것처럼 Include 해 사용할 수 있게 됩니다.
게임 빌드
- 게임 타겟 설정을 추가하면 게임 빌드 옵션이 추가됩니다.
- 게임 타겟으로 빌드된 모듈은 정적 라이브러리로 실행 파일에 포함됩니다.
- 게임이 실행되기 위해서는 실행 파일과 콘텐츠 에셋이 함께 있어야 합니다.
- 빌드 : 실행 파일을 생성하기 위한 컴파일
- 쿠킹 : 지정한 플랫폼에 맞춰 콘텐츠 에셋을 변환하는 작업
- 패키징 : 이들을 모두 모아서 하나의 프로그램으로 만드는 작업
* 게임 패키지 : 실행 파일 + 콘텐츠 파일
언리얼 에디터에서 쿠킹과 빌드를 동시에 실행해줄 패키징을 진행합니다.
이후 Package 폴더를 생성해 경로를 지정하고 일정 시간이 지나면 실행 파일과 컨텐츠 파일이 생기는 것을 확인할 수 있습니다.
언리얼 빌드 시스템 정리
1. uproject 명세서를 사용한 언리얼 에디터 동작 원리를 이해할 수 있었습니다.
2. 언리얼 엔진의 모듈 시스템과 소스 코드 관리 방법을 이해할 수 있었습니다.
3. 프로젝트가 커지는 경우에 모듈 작업 분리를 위한 플러그인 시스템을 활용할 수 있습니다.
4. 언리얼 소스 코드의 구조를 이해할 수 있었습니다.
5. 게임 빌드의 설정과 게임 패키징 과정을 이해하고 활용할 수 있습니다.
'Unreal Engine 5 > 언리얼 C++' 카테고리의 다른 글
언리얼 엔진 게임 제작 기초 (0) | 2023.08.02 |
---|---|
언리얼 오브젝트 관리 2 - 패키지 (0) | 2023.07.28 |
언리얼 오브젝트 관리 1 - 직렬화 (Serialization) (0) | 2023.07.27 |
언리얼 엔진의 메모리 관리 (0) | 2023.07.27 |
언리얼 컨테이너 라이브러리 2 - 구조체와 Map (0) | 2023.07.27 |