월세 500 호기심 천국
8주차: 야물(YAML)딱진 그 녀석의 은밀한 속마음 - Unity Scene 파일 (.unity) 본문
8주차: 야물(YAML)딱진 그 녀석의 은밀한 속마음 - Unity Scene 파일 (.unity)
원펀치 쓰리 갓냥이 2024. 2. 14. 05:060주차: 인트로 - 새롭고 신기한 것을 좋아하거나, 모르는 것을 알고 싶어 하는 마음
1주차: Unity 엔진은 왜 개발 언어로 C#을 선택했을까?
2주차: 의외로 세상 잡다한 Transform에 대한 모든 것
3주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (1/2)
4주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (2/2)
5주차: 헐크 아저씨도 깜짝 놀란 Unity Render Pipeline이 작동하는 과정
6주차: 매 프레임 나보다 갓생 사는 Unity의 Life Cycle
7주차: 남들보다 더 갓생 사는 Coroutine의 충격적인 정체
8주차: 야물(YAML)딱진 그 녀석의 은밀한 속마음 - Unity Scene 파일 (.unity)
9주차: 내 과자 포장지 누가 버렸어? - meta data 파일 (.meta) 파헤치기
10주차: 철가방 채로 배달온 Unity 빌드 파일. 난 단무지 시킨 적 없는데, 왜 있지?
11주차: 마무리 - 후기
지금까지는 주로 유니티 내의 코드들이 작동되는 원리를 뜯어보았었다.
오일러각과 쿼터니온, 유니티 생명 주기, 코루틴 등등...
이제는 유니티의 씬 자체가 저장되는 방식,
.unity 파일에 대해서 알아볼 차례다.
사실 지금까지는 유니티를 조금 공부해봤다 싶으면 조금씩은 도전을 해봤을 만한 부분의 주제들이었다면,
이제부터는 정말 말 그대로 심층분석, 말을 좀 다르게 해보자면 아무도 관심이 없었을 만한 주제이다.
연재 시리즈가 후반으로 향해가면서 점점 취지에 맞는 것들이 등장하는 것이다.
.unity란?

유니티의 씬(Scene)이 통째로 저장돼있는 파일을 나타내는 확장자명이다.

그렇다면 씬 내에 게임의 정보가 전부 다 들어있는 만큼, 용량 역시도 무지막지할까?
게임 자체가 여러 개의 씬이 모여 하나의 빌드로 구성되는 방식인데,
빌드가 500MB고 씬이 5개면 개당 평균 100MB는 되지 않겠냐는 것이다.
몰론 유니티, 혹은 동영상 편집 툴을 조금이라도 써본 사람이라면 알겠지만, 답은 NO다.
빌드가 GB 단위의 큼직한 규모의 게임이더라도,
정말로 필요한 정보만 주소와 ID 형식으로 최소화하여 저장하기 때문에,
씬 파일은 보통 몇십MB 언저리를 넘지 않는다.
어떻게 이런 용량 절약이 가능한걸까?
그 답은 .unity 파일이 쓰여지는 방식에 있다.
Scene 파일 내 데이터의 직렬화, YAML
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 2100000, guid: 9a819b815e161b64e8c78164c1902994, type: 2}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_UseRadianceAmbientProbe: 0
.unity 파일을 파일 탐색기를 통해 열람할 경우 자동으로 Unity Editor로 이동되지만,
GitHub를 통해 열람할 경우 위 형식의 YAML 코드로 이루어져있음을 확인할 수 있다.
YAML은 특정 데이터를 기록하고 전달하는 데에 목적을 둔 언어이며,
json처럼 사람이 읽을 수 있을 정도로 직관적인 형태로 씬 전체를 직렬화한다.
그럼 위의 코드를 한줄한줄 뜯어보자.
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
모든 .unity YAML 파일에 있어야 하는 코드이다.
코드의 맨 앞줄에 이 두 줄을 기재함으로써 우리가 사용할 YAML이 1.1 버전이고,
보통의 YAML과 다소 차이가 있는 Unity 맞춤형 YAML을 사용할 것임을 명시한다.
--- !u!1 &1947408731
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1947408733}
- component: {fileID: 1947408732}
m_Layer: 0
m_Name: CubeController
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- 은 파일 내의 오브젝트를 하나 소개하겠다는 뜻이며,
이후는 공백 한 칸 이후 !u! + 숫자A + & + 숫자B 구조이다.
숫자A는 해당 오브젝트의 클래스를 나타내며,
다음의 사전에 정해진 구분을 따른다.
| ID | 클래스 |
| 0 | 오브젝트 |
| 1 | GameObject |
| 2 | Component |
| 3 | LevelGameManager |
| 4 | Transform |
| 5 | TimeManager |
| 6 | GlobalGameManager |
| 8 | Behaviour |
| 9 | GameManager |
| ..... | ..... |
| 2058629511 | VisualEffectResource |
| 2059678085 | VisualEffectObject |
| 2083052967 | VisualEffect |
| 2083778819 | LocalizationAsset |
| 2089858483 | ScriptedImporter |
이어서 & 뒤에 오는 숫자B의 경우 각 파일 내의 고유한 ID인 fileID가 할당된다.
--- !u!1 &1947408731
즉 위의 경우 소개하려는 오브젝트는 GameObject이며,
해당 파일의 fileID는 1947408731인 것이다.
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1947408733}
- component: {fileID: 1947408732}
m_Layer: 0
m_Name: CubeController
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
그 이후로는 해당 GameObject의 프로퍼티들이 나열되어있다.
m_ObjectHideFlags처럼 UnityC#의 GameObject 스크립트 내에 실제로 존재하는 프로퍼티들도 있었고,
스크립트 상에서는 존재하지 않는 것도 있었다.


m_Component:
- component: {fileID: 1947408733}
- component: {fileID: 1947408732}
또한 해당 GameObject는 1947408733과 1947408732 두 개를 Component로 가진다는 코드가 같이 적혀있었는데,
해당 fileID를 토대로 이들을 추적해보면...
--- !u!114 &1947408732
MonoBehaviour:
m_ObjectHideFlags: 0
.....
--- !u!4 &1947408733
Transform:
m_ObjectHideFlags: 0
.....
GameObject가 가지고 있는 MonoBehaviour 클래스와 Transform 클래스를 말한 것이었음을 알 수 있었고,
두 클래스 모두 각자의 데이터 역시도 포함하고 있었다.
--- !u!4 &1947408733
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1947408731}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 7
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
특히 1947408732의 fileID를 가진 Transform의 경우 위의 표에서 정리되어있던 대로 4라는 클래스 ID를 가졌으며,
localRotation, localPosition, localScale 등 우리에게 이미 익숙한 변수들도 보였다.
물론 xyzw의 실제 벡터 정보들도 모두 포함되어있었다.
worldRotatiln, worldPosition 등이 없는 이유는
부모의 Transform 정보와 자신의 local한 정보를 통해서 역으로 충분히 계산해서 나타낼 수 있기도 하고,
애초에 에디터의 인스펙터 창만 봐도 local한 좌표를 기준으로 관리되고 있어서 그런 것도 있는 것 같다.
추가로 m_GameObject의 경우 해당 Transform을 담고 있는 GameObject의 fileID인 1947408731가 입력되어 있었다.
--- !u!4 &12438314 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 4808409092926342, guid: 70704ddec6bf2c543b7008ab52a9006d,
type: 3}
m_PrefabInstance: {fileID: 12438313}
m_PrefabAsset: {fileID: 0}
YAML 파일을 이곳저곳 훑다보면 guid라는 녀석도 가끔 보였다.
guid는 fileID와는 달리 에셋이 Unity Editor에 처음 잡히자마자 할당되는 고유의 번호라고 한다.
우리가 유니티 에셋 폴더에 리소스를 드래그 앤 드롭하면 약간의 로딩과 함께 .meta 파일이 생성되는데,
그 과정에서 자동으로 할당되는 것 같다.
어쩌면... 다음 주에 다루게 될 .meta 파일과 연관이 있을 수도 있겠다.
이 녀석은 조금 더 두고봐야겠는걸...
실제로 열어본 .unity 후기
막상 열어보니 구성 자체는 간단했다.
씬 내의 모든 오브젝트가 --- !u!*classID* &*fileID* 식으로 소개되고
그 안의 프로퍼티값들이 세세히 나열되는 방식이었다.
문제는, 이 '모든 오브젝트'의 양이 방대했다.
말 그대로 씬 내의 모든 것들이 기재되어있어야 했기에,
잠깐 1~2주 짤막하게 개발 중인 토이 프로젝트였음에도
메인 씬의 YAML 코드는 11,000자를 육박했다.
본격적인 프로젝트의 .unity 파일은 얼마나 내용이 길련지...
아... 따로 압축은 안하는 건가?
하긴 어차피 용량도 별로 안 큰 텍스트 파일이니 압축해봤자일 것 같기도 하고.
오히려 YAML의 사용 목적에 맞게 가독성을 더 살리고자 압축을 하지 않았을 수도 있다.
씬 내에 100MB짜리 비디오 파일이 포함된다고 해서,
.unity 파일에도 100MB짜리 영상을 넣어 저장하는 것보다는 이미 훨씬 효율적이지 않은가.

씬의 저장은... 이런 식으로 이루어지는구나.
참으로 기묘하도다.
그럼 이번 주도 끝!
0주차: 인트로 - 새롭고 신기한 것을 좋아하거나, 모르는 것을 알고 싶어 하는 마음
1주차: Unity 엔진은 왜 개발 언어로 C#을 선택했을까?
2주차: 의외로 세상 잡다한 Transform에 대한 모든 것
3주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (1/2)
4주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (2/2)
5주차: 헐크 아저씨도 깜짝 놀란 Unity Render Pipeline이 작동하는 과정
6주차: 매 프레임 나보다 갓생 사는 Unity의 Life Cycle
7주차: 남들보다 더 갓생 사는 Coroutine의 충격적인 정체
8주차: 야물(YAML)딱진 그 녀석의 은밀한 속마음 - Unity Scene 파일 (.unity)
9주차: 내 과자 포장지 누가 버렸어? - meta data 파일 (.meta) 파헤치기
10주차: 철가방 채로 배달온 Unity 빌드 파일. 난 단무지 시킨 적 없는데, 왜 있지?
11주차: 마무리 - 후기
'내 맘대로 쓰는 Unity 심층분석 析深層分' 카테고리의 다른 글
| 10주차: 철가방 채로 배달온 Unity 빌드 파일. 난 단무지 시킨 적 없는데, 왜 있지? (0) | 2024.03.01 |
|---|---|
| 9주차: 내 과자 포장지 누가 버렸어? - meta data 파일 (.meta) 파헤치기 (0) | 2024.02.24 |
| 7주차: 남들보다 더 갓생 사는 Coroutine의 충격적인 정체 (0) | 2024.02.08 |
| 6주차: 매 프레임 나보다 갓생 사는 Unity의 Life Cycle (1) | 2024.02.04 |
| 5주차: 헐크 아저씨도 깜짝 놀란 Unity Render Pipeline이 작동하는 과정 (3) | 2024.01.27 |