본문 바로가기

Unity/Articles

Unity: 빌드 파이프라인 베이직

유니티 에디터로 프로그램(애플리케이션)을 개발하고 사용자에게 제공하기 위해서는 '빌드'라는 과정을 통해 실행 가능한 정돈된 패키지로 만들어야 합니다. '패키징' 등으로 부르기도 합니다. 실행에 필요한 파일들을 모아서 압축하고, 다양한 사용자의 환경에서 일관성있게 실행될 수 있도록 여러 의존성들을 해결하는 작업입니다.

 

빌드된 유니티 프로그램의 디렉터리를 확인하면 크게 3가지로 구분할 수 있습니다.

  1. 유니티 엔진 모듈
  2. 사용자 스크립트
  3. 아트 리소스

2021년 4월 현재는 2가지의 빌드 방식이 있습니다. 최종 사용자 스크립트를 C++ 문법으로 바꿔주는 IL2CPP와, 닷넷 VM 환경에서 바로 실행시키는 Mono 방식이 있습니다. 두 방식의 빌드 결과물을 조금 상이한 폴더 구조를 가집니다.

 

il2cpp 방식

mono 방식

 

유니티 엔진 모듈

유니티는 상용 게임엔진이며 게임을 만들기 위해 필요한 렌더링, 물리, 파일 관리 등 여러 요소들을 미리 만들어두고 개발자가 쉽게 사용할 수 있도록 SDK를 제공하고있습니다. 게임 개발자는 유니티가 제공하는 API 함수와 기능들을 이용해 자유롭게 게임 로직을 디자인 할 수 있습니다. 이 때, 사용자가 직접 정의한 스크립트가 동작하기 위해서는 앞서 제공된 API들이 필요합니다.

 

관련 파일 (Windows 기준)

  • Game.exe - 실행 파일
  • UnityCrashHandler64.exe - 예외가 발생해 크래쉬 났을 때 리포트를 도와줌
  • UnityPlayer.dll - 유니티 API가 정의되고 구현된 모듈
  • MonoBleedingEdge/...
  • Game_Data/app.info
  • Game_Data/boot.config
  • Game_Data/il2cpp_data/... - il2cpp로 동작할 때 필요한 부가 데이터들

사용자 스크립트

지원하는 언어로는 C#이 있습니다. 꽤 오래전 버전에서는 javascript와 비슷한 생김새의 unityscript가 존재했으나, 최신버전에서는 더 이상 지원되지 않고 있습니다. lua 같은 언어를 사용할 수 있게 구현해서 쓰기도 하지만 유니티에서 공식적으로 지원하는 언어는 아닙니다.

 

유니티는 mono를 기반으로 동작해왔습니다. .NET Framework가 개발하기 좋은 환경임을 분명하나 Windows 플랫폼만은 타겟으로 개발되었기에 크로스플랫폼을 지원하고 싶던 유니티는 이를 곧바로 활용할 수가 없었습니다. 그런데 mono는 linux/mac 환경에서도 .NET Framework를 지원하기 위해 시작된 프로젝트였기에 채택되었다고 합니다.

 

[프로젝트 설정 - 플레이어] 옵션에서 2가지 컴파일 옵션 중 선택할 수 있습니다. 기본은 mono로 선택되어 있으며 에디터에서의 컴파일과 동작은 항상 mono 기반으로 진행됩니다. IL2CPP 방식은 C#의 중간 언어 형태인 IL 코드를 CPP 코드로 바꾼 뒤 컴파일하는 방식입니다. mono로 빌드되는 경우 JIT 방식으로 컴파일되지만, CPP로 바꾼 뒤 컴파일하면 AOT 방식으로 컴파일됩니다. 성능상의 이점을 도모할 수도 있고, C# 코드로 원상복구가 쉬운 IL 방식의 배포보다 인간의 눈으로 식별하기 어려워진다는 부가적인 효과도 있습니다.

 

관련 파일 (Windows 기준)

  • GameAssembly.dll (il2cpp)
  • Game_Data/Managed/*.dll (mono)

 

아트 리소스

게임에는 코드 스크립트 뿐만 아니라 그래픽 리소스(캐릭터 모델, 텍스쳐, 사운드, 맵, 셰이더 등)도 포함됩니다. 에디터에 임포트 된 모든 에셋이 포함되는 것은 아니며, 빌드에 포함되는지 여부는 다음 간단한 2가지 규칙에 따라 결정됩니다.

 

1. 빌드에 포함된 씬의 게임오브젝트, 컴포넌트가 레퍼런스를 갖고 있는 에셋들

인스펙터에서 할당, 메쉬 렌더러에서 사용하는 머티리얼, 버튼에 넣어진 스프라이트 등의 방식으로 직접적 연결을 갖춘 링크만 포함됩니다. 게임 오브젝트에 달린 컴포넌트의 스크립트에서 동적으로 로드하는 경우는 포함되지 않습니다.

 

예시

3D 큐브 게임오브젝트가 씬에 있고 이 씬이 빌드에 포함된 상황

게임오브젝트가 가진 컴포넌트 : MeshFilter, MeshRenderer, BoxCollider, ShaderLoader(사용자 스크립트)

  • MeshFilter 에 할당된 CubeMesh 자동 포함
  • MeshRenderer에 할당된 CubeMaterial 자동 포함
  • BoxCollider에 할당된 PhysicsMaterial 자동 포함
  • ShaderLoader 스크립트에 할당된 targetMaterial 자동 포함
  • ShaderLoader 스크립트에서 로드하는 Shader 미포함
class ShaderLoader : MonoBehaviour {
    public Material targetMaterial = default;
    
    private void Load() {
        var shader = Shader.Find("MyCustomShader/Opaque");
        material.shader = shader;
    }
}

 

2. Resources 폴더에 포함된 에셋

1번 조건과 마찬가지로 Resources 폴더에 포함된 모든 에셋을 대상으로 직접적 레퍼런스를 갖고 있는 에셋들이 빌드에 포함됩니다. 특히 prefab을 Resources 폴더에 넣어둔 경우 prefab과 연계된 모든 에셋이 빌드에 포함됩니다.

 

 

관련 파일

  • Game_Data/Resources/unity default resources - 유니티 엔진 기본 사용 리소스들
  • Game_Data/Resources/unity_builtin_extra - 유니티 엔진 기본 사용 리소스들
  • Game_Data/globalgameamanagers - 전체 리소스 인덱싱 파일
  • level0, level1, levelN ... - 빌드에 포함된 레벨 N 번째
  • sharedassets0.assets - 공유하는 에셋 (에셋 간 의존성을 고려해 여러차례 사용되는 리소스들이 포함)
  • resources.assets, resources.assets.resS, resources.resource

 

빌드 결과물을 분석하려면 깃허브에서 AssetStudio, uTinyRipper 등 여러 오픈소스 도구들을 이용해 직접 해보시는 것 역시 큰 도움이 됩니다.