Unity 게임 엔진 완전 가이드
TL;DR
- Unity의 본질은 GameObject-Component 조합, Scene/Prefab 자산 구조, MonoBehaviour 라이프사이클이다.
- Mono, IL2CPP, Burst, SRP, Addressables, asmdef 같은 런타임·빌드 도구를 이해해야 팀 규모가 커져도 유지된다.
- XR에서는 Unity OpenXR Plugin, XRI, Meta OpenXR 조합이 현재 기본 경로다.
1. 개념
Unity는 C# 스크립팅과 GameObject-Component 모델, 에디터 중심 제작 방식, 멀티플랫폼 빌드를 묶은 실시간 3D 엔진이다. 게임뿐 아니라 모바일 앱, 시뮬레이터, XR 콘텐츠 제작에 널리 쓰인다.
2. 배경
2000년대 중반 게임 엔진 시장은 비싸고 대형 스튜디오 중심이었다. Unity는 저렴한 가격, 쉬운 에디터, 빠른 멀티플랫폼 전개, Asset Store 생태계를 무기로 인디와 모바일 시장에서 빠르게 확산됐다.
3. 이유
Unity 프로젝트는 코드만이 아니라 Scene, Prefab, Package, Import 설정, 빌드 파이프라인이 함께 동작한다. 이 구조를 모르면 Missing Reference, 빌드 실패, 성능 문제, XR 설정 충돌을 반복하게 된다.
4. 특징
- GameObject–Component, Scene, Prefab 기반 제작 모델
- Mono/IL2CPP/Burst와 asmdef를 조합한 런타임·컴파일 구조
- URP/HDRP, Addressables, DOTS/ECS 등 확장 가능한 엔진 스택
- XR Plugin Framework, OpenXR, XRI, PolySpatial 같은 플랫폼 연동
- 에디터·패키지·CI/CD를 포함한 제작 파이프라인 중심 개발 경험
5. 상세 내용
Unity 게임 엔진 완전 가이드
작성일: 2026-04-29 카테고리: Game Dev / XR / Unity / C# 포함 내용: Unity Technologies, GameObject, Component, Prefab, Variant Prefab, Nested Prefab, Scene, Hierarchy, Project 구조, Library/Packages/ProjectSettings, AssetDatabase, .meta, GUID, FileID, Mono/IL2CPP/Burst/Mono.Cecil, Editor/Player, Build Profile, Inspector/Hierarchy/Project Window, Awake/Start/Update/FixedUpdate/LateUpdate, OnEnable/OnDisable/OnDestroy, Coroutine, async/await, UniTask, Awaitable, ScriptableObject, MonoBehaviour, Transform/Rigidbody/Collider, Canvas/RectTransform, URP/HDRP/Built-in/SRP, ShaderGraph, VFX Graph, Addressables/Asset Bundles, DOTS/ECS/Burst/Job System, UI Toolkit/UGUI/IMGUI, Input System, Cinemachine/Timeline, Visual Scripting (Bolt), TextMeshPro, NGO/Mirror/Photon/FishNet, Unity Hub/Cloud/Build Server, Plastic SCM/Unity Version Control, Polyspatial visionOS, Unity 6 GPU Resident Drawer, Single Pass Instanced, Foveated Rendering, Application SpaceWarp, XR Plugin Framework, Unity OpenXR Plugin, XR Interaction Toolkit, AR Foundation, Meta XR SDK, Genshin Impact / Among Us / Pokemon GO / Hearthstone / Cuphead / Hollow Knight / Beat Saber / VRChat / MRTK / Polyspatial 사례, ScriptableObject Event Channel, Zenject/VContainer DI, .asmdef, GameCI
1. Unity란?
┌─────────────────────────────────────────────────────────────────┐
│ Unity 한 줄 정의 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "C# 스크립팅 + GameObject-Component 모델 + 멀티플랫폼 빌드를 │
│ 하나의 에디터에서 통합 제공하는 게임/XR 개발 엔진" │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 특징: │ │
│ │ ├── 1) Composition over Inheritance │ │
│ │ ├── 2) MonoBehaviour 라이프사이클 (Awake/Start/...) │ │
│ │ ├── 3) 단일 코드베이스 → iOS/Android/PC/콘솔/웹/XR │ │
│ │ ├── 4) Mono(JIT) / IL2CPP(AOT) / Burst(LLVM SIMD) │ │
│ │ ├── 5) URP/HDRP — Scriptable Render Pipeline │ │
│ │ ├── 6) Asset Store + UPM (Package Manager) │ │
│ │ └── 7) DOTS/ECS — 데이터 지향 고성능 옵션 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2. 용어 사전 (Terminology Dictionary)
2.1 회사 / 엔진
| 용어 | 풀 이름 | 의미와 어원 |
|---|---|---|
| Unity | — | “통일/하나됨”. (1) 기술적 통합: 하나의 에디터에서 멀티플랫폼 빌드, (2) 커뮤니티 통합: 대형 스튜디오와 인디 개발자 통합 |
| Unity Technologies | — | 2004년 8월 코펜하겐 “Over the Edge I/S”로 창립, 2007년 사명 변경. 미션: “Democratize game development” |
2.2 핵심 객체 모델
| 용어 | 의미와 어원 |
|---|---|
| GameObject | 씬의 모든 존재의 기본 단위. 자체 기능 없음, Transform만 보유. 캐릭터/카메라/조명/빈 컨테이너 모두 GameObject |
| Component | 라틴어 componere (조합). GameObject에 부착되는 기능 모듈. Composition over Inheritance 패턴. GoF Component Pattern에서 차용 |
| Prefab | prefabricated (사전 제작된)의 줄임말. 건축업 “조립식 건물”에서 차용. .prefab 에셋으로 저장되어 인스턴스화 가능 |
| Prefab Variant (2018.3+) | Base Prefab을 상속받아 일부만 오버라이드한 파생. 객체지향 상속을 Prefab 시스템에 도입 |
| Nested Prefab (2018.3+) | Prefab 내부에 다른 Prefab 포함. 2018.3 이전에는 불가능 |
| Scene | 연극/영화의 “장면”. .unity 파일. GameObject가 배치된 3D 공간 |
| Hierarchy | 그리스어 hierarchia (성스러운 지배). Transform의 부모-자식 관계와 1:1 대응하는 트리 |
| Inspector | 라틴어 inspicere (들여다보다). 선택된 객체의 모든 속성 편집 창 |
2.3 에셋 / 직렬화
| 용어 | 풀 이름 | 의미 |
|---|---|---|
| AssetDatabase | — | Unity Editor 전용 API. 에셋 임포트/수정/삭제/검색 프로그래밍 |
| .meta | — | 모든 에셋에 자동 생성되는 사이드카 파일. VCS 필수 포함 |
| GUID | Globally Unique Identifier | 128-bit 고유 ID. 파일 경로/이름 변경에도 참조 유지 |
| FileID (LocalIdentifierInFile) | — | 단일 에셋 내 객체 구별. Prefab 내 GameObject/Component 각각 다른 FileID |
| Force Text | — | Project Settings → Editor → Asset Serialization. .asset/.prefab/.unity를 YAML 텍스트로 → git diff 가능 |
2.4 런타임 / 컴파일
| 용어 | 풀 이름 | 의미 |
|---|---|---|
| Mono | — | 그리스어 monos (단독). MS .NET의 오픈소스 재구현. JIT. 에디터 + 일부 Standalone |
| IL2CPP | Intermediate Language to C++ | C# IL → C++ 변환 → 네이티브 컴파일. AOT. iOS/WebGL/콘솔 필수 |
| Burst | — | “burst”(폭발적 처리). LLVM 기반 AOT/JIT. SIMD 벡터화. Job System / DOTS 전용 |
| Mono.Cecil | — | .NET IL 바이트코드를 Reflection 없이 읽고 수정. Weaver 도구, Mirror/NGO RPC 자동생성 |
2.5 MonoBehaviour 라이프사이클
| 콜백 | 설명 |
|---|---|
| Awake | 스크립트 인스턴스 로드 시. 비활성 오브젝트도 호출. 자기 자신 초기화 |
| OnEnable | 활성화될 때마다. Start 이전 |
| Reset | Editor만. Add Component / Reset 메뉴 시 |
| Start | 첫 Update 직전 1회. 모든 Awake 완료 후. 다른 오브젝트 참조 안전 |
| FixedUpdate | 물리 스텝 (기본 0.02초 = 50Hz). Rigidbody.AddForce 등 |
| OnTrigger/CollisionEnter/Stay/Exit | 물리 콜백. FixedUpdate 직후 |
| Update | 매 프레임. 메인 게임 로직 |
| LateUpdate | 모든 Update 후. 카메라 추적, 최종 상태 |
| OnPreCull / OnBecameVisible/Invisible / OnRenderImage | 렌더링 콜백 |
| OnDisable / OnDestroy | 비활성화 / 파괴 |
| OnApplicationQuit / Pause / Focus | 앱 종료 / 일시정지 / 포커스 (모바일) |
| OnValidate | Editor만. Inspector 값 변경 시 |
2.6 Coroutine vs async/await
| 용어 | 의미 |
|---|---|
| Coroutine | 1958년 Melvin Conway 제안. C# IEnumerator + yield return. 메인 스레드 전용, Unity API 완전 사용 가능 |
| async/await | C# 5.0 (2012). Unity 2017.1 지원. Task |
| UniTask | Cysharp 오픈소스. async/await 대체. 거의 zero allocation, Unity 메인 스레드 통합 |
| Awaitable | Unity 2023+ 내장. UniTask 유사. await NextFrameAsync() / WaitForSecondsAsync() |
2.7 ScriptableObject
| 용어 | 의미 |
|---|---|
| ScriptableObject | “스크립트 가능한 객체”. GameObject가 아닌 에셋 파일(.asset). 씬 독립, 메모리 공유 |
| CreateAssetMenu | [CreateAssetMenu] 어트리뷰트로 Editor에서 우클릭 생성 |
| 이벤트 채널 | Unity 공식 Open Project 1 패턴. UnityAction + ScriptableObject 조합 |
2.8 물리 / UI
| 용어 | 어원 |
|---|---|
| Transform | 위치/회전/크기 변환 행렬. 모든 GameObject에 필수 |
| Rigidbody | 물리학 “강체(Rigid Body)”. PhysX가 이동/회전 제어 |
| Collider | “충돌하는 것”. Rigidbody와 분리 → Trigger 구현 가능 |
| Canvas | “화폭”. UI 요소가 그려지는 2D 공간. 4.6에 UGUI와 함께 도입 |
| RectTransform | Transform 파생. Width/Height/Anchors/Pivot 추가 |
2.9 렌더링
| 용어 | 풀 이름 | 의미 |
|---|---|---|
| SRP | Scriptable Render Pipeline | 2018.1 도입. C# API로 렌더링 파이프라인 전체 커스터마이징 |
| Built-in | — | 2018 이전 고정 파이프라인. “레거시”. 신규 비권장 |
| URP | Universal Render Pipeline | 2019.3에 LWRP에서 개명. 모바일~PC 범용 |
| HDRP | High Definition Render Pipeline | 고사양 PC/콘솔. PBR, 레이트레이싱, 볼류메트릭 |
| ShaderGraph | — | 2018 도입. 노드 기반 비주얼 셰이더 → ShaderLab 자동 생성 |
| VFX Graph | — | 2018.3 도입. GPU 컴퓨트 셰이더, 수백만 파티클 |
2.10 데이터 지향 (DOTS)
| 용어 | 풀 이름 | 의미 |
|---|---|---|
| DOTS | Data-Oriented Technology Stack | ECS + Job System + Burst |
| ECS | Entity Component System | Entity(정수 ID), Component(데이터 struct), System(로직) |
| Job System | — | C# 멀티스레드 작업 안전 스케줄링. NativeArray로 race condition 방지 |
| Burst | — | LLVM AOT. SIMD. NativeContainer만 사용 |
2.11 UI / 입력
| 용어 | 의미 |
|---|---|
| IMGUI | Immediate Mode GUI. OnGUI(). 매 프레임 즉시 그림. Editor 확장에만 사용 |
| UGUI | Unity GUI / uGUI. 4.6 도입. GameObject 기반. Canvas/Image/Text |
| UI Toolkit (UIElements) | 2019.1 Editor용 → 2021.2 런타임. UXML/USS/C#. Flexbox 레이아웃. Retained Mode |
| Legacy Input Manager | Input.GetKey(). Edit > Project Settings > Input |
| New Input System | 2019.1+. Action Map → Action → Binding. 동적 리매핑, 멀티플레이어 |
2.12 미디어 / 도구
| 용어 | 의미 |
|---|---|
| Cinemachine | “영화적 카메라”. 가상 카메라, 돌리 트랙. 2017 통합 |
| Timeline | 시퀀서. 트랙/클립 기반 컷씬 편집 |
| Visual Scripting (Bolt) | Ludiq의 Bolt를 2020년 4월 Unity가 인수. 2021 LTS 내장 |
| TextMeshPro | Stephan Bouchard 개발 → Unity 2017 인수. SDF 폰트. 2023.2부터 uGUI 패키지에 병합 |
2.13 네트워킹
| 솔루션 | 개발사 | 특성 |
|---|---|---|
| NGO (Netcode for GameObjects) | Unity 공식 | GameObject 기반, 소규모 협동 (2-10인) |
| Mirror | OSS | UNET 후속, 성숙, LAN |
| Photon (PUN/Fusion) | Exit Games | 클라우드 호스팅, 매치메이킹 |
| FishNet | OSS | 고성능, 대역폭 효율 |
2.14 인프라 / 플랫폼
| 용어 | 의미 |
|---|---|
| Unity Hub | 2018 도입. 다중 에디터 버전, 프로젝트, 라이선스 관리 |
| Unity Cloud | UGS (Unity Gaming Services). 빌드/분석/멀티플레이어 |
| Unity Build Server | 사내 서버 빌드 라이선스. CI/CD |
| Plastic SCM → Unity Version Control | 2020.08 인수. 대용량 바이너리 강함 |
| Polyspatial | Poly(다양) + Spatial(공간). visionOS 패키지. 렌더링은 Apple RealityKit 위임 |
3. Unity 등장 배경
3.1 2004년의 게임 엔진 시장
┌─────────────────────────────────────────────────────────────────┐
│ Pre-Unity (2004) 풍경 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Unreal Engine 2/3 $$$ 수십만 달러 라이선스 → 대형 스튜디오 전용│
│ CryEngine 1 사내 전용 (Far Cry용) │
│ Torque $99 (혁신적이었지만 UX 열악, 문서 부족) │
│ GameMaker 무료/저가, 비프로그래머용 2D │
│ Adventure Game Studio 무료, 어드벤처 전용 │
│ │
│ 결과: "3D 멀티플랫폼을 합리적 비용으로" 가능한 도구 부재 │
└─────────────────────────────────────────────────────────────────┘
3.2 Unity 승리의 5가지 요인
- 가격 파괴: 초기 $1,500 (Pro), $200 (Hobby) — 경쟁사의 1/100
- 접근성: 드래그앤드롭, 비주얼 에디터 중심
- 2009 무료 버전: 사용자 수 하룻밤 사이 2배 → 대학 강의실
- 2008 iPhone App Store: 대형사가 iOS를 무시하는 사이 Unity 사용자가 시장 선점
- Asset Store (2010): 커뮤니티 에셋/플러그인 마켓 → Network Effect
4. 역사 연대표
| 연도 | 사건 |
|---|---|
| 2002 | 세 창립자 OpenGL 포럼에서 만남 |
| 2004.08 | 코펜하겐 “Over the Edge I/S” 설립 (Helgason, Ante, Francis) |
| 2005.06 | WWDC에서 Unity 1.0 발표 (Mac OS X 전용). 첫 게임 GooBall 상업 실패 → 엔진 회사로 피벗 |
| 2007 | “Unity Technologies”로 사명 변경. Unity 2.0: DirectX, 동적 그림자 |
| 2008 | iPhone App Store 출시 → Unity iOS 지원 |
| 2009.03 | Unity 2.5: Windows 에디터 지원 |
| 2009.10 | 무료 버전 출시 → 사용자 폭증 |
| 2010 | Unity 3.0: Android, Beast 라이트맵. Asset Store 오픈 |
| 2012 | Unity 4.0: DirectX 11, Mecanim 애니메이션, Linux 프리뷰 |
| 2015 | Unity 5.0: PBR, WebGL, 무료 Personal Edition |
| 2016.12 | 버전 체계 변경: 순번 → 연도 (2017.x …) |
| 2017 | Timeline, Cinemachine, Autodesk 통합 |
| 2018 | SRP (URP/HDRP), ShaderGraph, VFX Graph, Burst, ECS 프리뷰. Unity Hub |
| 2019 | URP/HDRP 프로덕션, 새 Input System, UI Toolkit Editor |
| 2020.08 | Plastic SCM 인수 (Unity Version Control 전신) |
| 2020.09 | NYSE 상장 (티커: U). $52 → $200 돌파 |
| 2021 LTS | Visual Scripting(Bolt) 내장, Apple Silicon, UI Toolkit 런타임 |
| 2022.07 | ironSource $4.4B 인수 발표 (전액 주식) |
| 2022.11 | ironSource 합병 완료 |
| 2023.09 | Runtime Fee 발표 → 대규모 반발. Godot/Unreal 이탈 |
| 2023.10 | John Riccitiello CEO 사임. James Whitehurst 임시 |
| 2024 | Matthew Bromberg 신임 CEO. Runtime Fee 완전 철회 |
| 2024.10 | Unity 6 출시 (연도 체계 탈피). GPU Resident Drawer, AI(Muse/Sentis), OpenXR 주력, ECS 코어 통합 |
| 2025.04 | Unity 6.1: Deferred+, 폴더블 |
| 2025+ | Unity 6.2/6.4 베타. DirectStorage |
Runtime Fee 사태 (2023)
2023.09.12 설치당 요금 발표 — 수익 $200K + 설치 200K 초과 시 $0.01~0.20/설치
├── 설치 = 매출 아님 (F2P 위협)
├── 소급 적용 (계약 신뢰 훼손)
└── 설치 수 조작 공격 가능
반응 대형 스튜디오 사용 중단 선언
voodoo, Century Games — Unity Ads 수익화 일시 중단
Godot 후원 폭증
2023.09.22 수정안 (수익 2.5%) — 여전히 반발
2023.10.09 Riccitiello CEO 사임
2024 Bromberg CEO, Runtime Fee 완전 철회 → 구독 인상 방식
유산 "한번 잃은 신뢰는 회복하기 어렵다"
5. 프로젝트 구조
5.1 폴더 구조
MyProject/
├── Assets/ ← VCS 포함 필수
│ ├── _Project/ (컨벤션: 프로젝트 전용)
│ │ ├── Core/
│ │ │ └── Core.asmdef
│ │ ├── Features/
│ │ │ ├── Player/
│ │ │ │ ├── Scripts/
│ │ │ │ ├── Prefabs/
│ │ │ │ └── Player.asmdef
│ │ │ └── Enemy/
│ │ └── Shared/
│ ├── Editor/ (어디 있든 Editor 빌드만)
│ ├── Plugins/ (Phase 1 컴파일, 서드파티 DLL)
│ ├── Resources/ (런타임 Resources.Load — 레거시)
│ ├── StreamingAssets/ (압축 없이 그대로 패키징)
│ └── ThirdParty/
├── Library/ ← VCS 제외
│ ├── ArtifactDB (애셋 임포트 DB)
│ ├── ScriptAssemblies/ (컴파일 .dll)
│ ├── ShaderCache/
│ └── il2cpp_cache/
├── Packages/ ← VCS 포함 필수
│ ├── manifest.json
│ └── packages-lock.json
├── ProjectSettings/ ← VCS 포함 필수
│ ├── ProjectVersion.txt
│ ├── TagManager.asset
│ ├── InputManager.asset
│ └── GraphicsSettings.asset
├── UserSettings/ ← VCS 제외 (개인 설정)
├── Logs/ ← VCS 제외
├── Temp/ ← VCS 제외 (자동 생성/삭제)
└── Build/ ← VCS 제외
5.2 .meta / GUID / FileID
# Sword.prefab.meta
fileFormatVersion: 2
guid: e3ad2bf01b7a6b7409eb683402aa8668 ← 불변 식별자
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 100100000
userData:
# Scene 파일 내 컴포넌트 참조
m_Script: {fileID: 11500000, guid: e3ad2bf01b7a6b7409eb683402aa8668, type: 3}
| 필드 | 의미 |
|---|---|
guid |
어느 파일인지 (.meta GUID) |
fileID |
파일 내 어느 객체인지 |
type |
0=embedded, 2=external, 3=meta-referenced |
Missing Reference의 가장 흔한 원인: 탐색기/Finder에서 파일 직접 이동/이름 변경 → .meta 미동행 → GUID 연결 끊김.
5.3 .gitignore + .gitattributes
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/UserSettings/
/Logs/
/*.csproj
/*.sln
.vs/
*.png filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.unity filter=lfs diff=lfs merge=lfs -text
*.prefab filter=lfs diff=lfs merge=lfs -text
Project Settings → Editor → Asset Serialization → Force Text 필수 (YAML diff/merge).
5.4 Assembly Definition (.asmdef)
{
"name": "MyGame.Runtime",
"rootNamespace": "MyGame",
"references": ["Unity.TextMeshPro", "Unity.Addressables"],
"includePlatforms": [],
"allowUnsafeCode": false,
"autoReferenced": false,
"noEngineReferences": false
}
효과: Player 코드 변경 시 Player.asmdef + 참조 어셈블리만 재컴파일 → 수십 초 → 수 초.
컴파일 4단계:
| Phase | Assembly | 위치 |
|——-|———|—–|
| 1 | Assembly-CSharp-firstpass | Plugins/ |
| 2 | Assembly-CSharp-Editor-firstpass | Plugins/Editor/ |
| 3 | Assembly-CSharp | 그 외 런타임 |
| 4 | Assembly-CSharp-Editor | 그 외 Editor |
Phase N은 Phase N+1을 참조 못 함.
5.5 특수 폴더
| 폴더 | 동작 |
|---|---|
Editor/ |
어디 있든 Editor 빌드에만 포함 |
Plugins/ |
Phase 1 컴파일 |
Resources/ |
Resources.Load("path") 가능. 모두 빌드에 포함 (레거시, Addressables 권장) |
StreamingAssets/ |
압축 없이 그대로 복사. Android는 jar URL |
Editor Default Resources/ |
EditorGUIUtility.Load |
6. MonoBehaviour 라이프사이클 (Unity 핵심)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
UNITY MONOBEHAVIOUR LIFECYCLE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[ 씬 로드 / 오브젝트 생성 ]
│
▼
┌─────────┐
│ Awake │ ← 비활성 오브젝트도 호출. 자기 자신 초기화
└─────────┘ 모든 Awake 완료 후 Start
│
▼ (활성화된 경우)
┌──────────┐
│ OnEnable │ ← 활성화될 때마다 (Start 이전)
└──────────┘
│
▼ [첫 프레임 직전, 1회]
┌─────────┐
│ Start │ ← 다른 오브젝트 참조 안전 (모든 Awake 완료 보장)
└─────────┘
│
━━━━━━━━━━━━━━━━━━━━━━━━ [프레임 루프 반복] ━━━━━━━━━━━━━━━
│
▼ [물리 — 고정 timestep 0.02s]
┌─────────────┐
│ FixedUpdate │ ← 한 프레임에 0회 또는 N회
└─────────────┘
│
▼
┌────────────────────────────┐
│ Physics callbacks │
│ OnTriggerEnter/Stay/Exit │
│ OnCollisionEnter/Stay/Exit│
└────────────────────────────┘
│
▼ [입력 → 메인 로직]
┌────────┐
│ Update │ ← 매 프레임. 프레임률 의존
└────────┘
│
▼ [코루틴 재개]
┌──────────────────────────────┐
│ Coroutines (yield null) │
│ Coroutines (WaitForSeconds) │
└──────────────────────────────┘
│
▼ [모든 Update 완료 후]
┌────────────┐
│ LateUpdate │ ← 카메라 추적, 애니메이션 후처리
└────────────┘
│
▼ [애니메이션]
┌────────────────┐
│ OnAnimatorMove │
│ OnAnimatorIK │
└────────────────┘
│
▼ [렌더링 콜백]
┌──────────────────────────────┐
│ OnPreCull / OnBecameVisible │
│ OnPreRender / OnRenderObject │
│ OnPostRender │
│ OnRenderImage (Built-in RP) │
│ OnGUI │
│ OnDrawGizmos (Editor) │
└──────────────────────────────┘
│
▼ [프레임 끝]
┌────────────────────────────────┐
│ Coroutines (WaitForEndOfFrame) │
└────────────────────────────────┘
│
└─────────── [다음 프레임] ─────────┐
│
━━━━━━━━━━━━━━━━━━━━━━━ [비활성화/제거] ━━━━━━━━━━━━━━━━━━
│
▼
┌───────────┐ ┌───────────┐
│ OnDisable │ → │ OnDestroy │
└───────────┘ └───────────┘
│
▼ [앱 종료]
┌────────────────────────┐
│ OnApplicationPause │ (모바일)
│ OnApplicationFocus │
│ OnApplicationQuit │
└────────────────────────┘
6.1 Awake vs Start (면접 단골)
| 항목 | Awake | Start |
|---|---|---|
| 호출 시점 | 오브젝트 생성 즉시 | 첫 Update 직전 |
| GameObject 활성 여부 | 무관 | 활성화 시에만 |
| 다른 오브젝트 참조 | 위험 (순서 미보장) | 안전 (전체 Awake 완료) |
| 주 용도 | 자체 초기화, 컴포넌트 캐싱 | 외부 참조, 이벤트 구독 |
public class PlayerHealth : MonoBehaviour
{
[SerializeField] private int maxHealth = 100;
private int currentHealth;
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>(); // 자기 컴포넌트 OK
currentHealth = maxHealth; // 자체 변수 OK
// GameManager.Instance 참조는 위험
}
void Start()
{
// 모든 오브젝트의 Awake 완료 보장
GameManager.Instance.RegisterPlayer(this); // 안전
UIManager.Instance.SetMaxHealth(maxHealth);
}
}
6.2 FixedUpdate — 물리 전용
시간: 0 0.016 0.032 0.048 0.064 (60fps)
프레임: │F1 │ │F2 │ │F3
물리: │P │P │P │P │P (50Hz, 0.02s 간격)
30fps일 때: 프레임당 P 1~2회 실행
// 잘못된 패턴: Input을 FixedUpdate에서 읽기
void FixedUpdate()
{
if (Input.GetButtonDown("Jump")) // ← 입력 놓칠 수 있음
rb.AddForce(Vector3.up * 500);
}
// 올바른 패턴: Input은 Update, 물리는 FixedUpdate
private bool jumpQueued = false;
void Update()
{
if (Input.GetButtonDown("Jump")) jumpQueued = true;
}
void FixedUpdate()
{
if (jumpQueued)
{
rb.AddForce(Vector3.up * 500, ForceMode.Impulse);
jumpQueued = false;
}
}
6.3 Update vs LateUpdate
// Player.cs
void Update()
{
transform.position += movement * speed * Time.deltaTime;
}
// CameraFollow.cs
void LateUpdate() // ★ Update가 아니라 LateUpdate
{
// Update에서 플레이어 이동 완료 후 카메라가 추적 → 떨림 없음
transform.position = Vector3.Lerp(transform.position,
player.position + offset,
smoothSpeed);
}
6.4 Coroutine 내부 동작
IEnumerator MyCoroutine()
{
Debug.Log("시작");
yield return new WaitForSeconds(2f);
Debug.Log("2초 후");
yield return null;
Debug.Log("다음 프레임");
}
C# 컴파일러가 IEnumerator 스테이트 머신으로 변환. Unity 스케줄러는:
- 활성 코루틴 목록 순회
- yield 조건 확인
- 충족 시
MoveNext()호출 → 다음 yield까지 실행 MoveNext()가 false 반환 → 목록에서 제거
주의: GameObject 비활성화 시 코루틴 일시정지(취소 X). 재활성화 시 자동 재개 안 됨 → 수동 재시작 필요.
// 안티: 매 호출 21바이트 GC 할당
yield return new WaitForSeconds(1f);
// 정답: 캐시
private readonly WaitForSeconds wait1s = new WaitForSeconds(1f);
yield return wait1s;
7. 컴포넌트 기반 아키텍처
7.1 Composition over Inheritance
[전통 OOP — 폭발적 계층]
Entity
├── Character
│ ├── Player
│ │ └── Mage
│ └── Enemy
│ └── FlyingEnemy ← 비행 + AI 다중상속?
└── Prop
└── InteractiveProp
└── FlyingInteractiveProp ← 폭발
[Unity Component — 조합]
GameObject (FlyingEnemy)
├── Transform
├── Rigidbody
├── HealthComponent ← 재사용
├── AIMovementComponent
├── FlyingBehavior
└── EnemyAnimator
7.2 GetComponent 성능
// 안티
void Update()
{
GetComponent<Rigidbody>().AddForce(Vector3.up); // O(n) 매 프레임
}
// 정답 1: SerializeField로 Inspector 연결
[SerializeField] private Rigidbody rb;
// 정답 2: Awake 캐싱
void Awake() { rb = GetComponent<Rigidbody>(); }
// 정답 3: TryGetComponent (2019.2+, ~20% 빠름, 할당 없음)
if (TryGetComponent<Collider>(out var col)) col.isTrigger = true;
// 필수 의존성 명시
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class PhysicsObject : MonoBehaviour { }
8. Prefab 시스템
8.1 Prefab 종류
Original Prefab → 씬 인스턴스, Override 가능
Prefab Variant → Base 상속 + 일부 Override (2018.3+)
Nested Prefab → Prefab 안 Prefab (2018.3+)
8.2 Override 우선순위
Variant Override > Original Prefab Value
인스턴스 Override > Variant Override > Original Prefab Value
8.3 함정
| 함정 | 해결 |
|---|---|
| 탐색기에서 Prefab 이동/이름변경 | Editor 내에서만 변경 (.meta도 동행) |
| 필드명 변경 후 Override 손실 | [FormerlySerializedAs("oldName")] |
| Nested Prefab 내부 수정이 인스턴스 Override로 기록 | 실제 Nested Prefab 파일을 직접 열어 수정 |
[FormerlySerializedAs("speed")]
public float moveSpeed = 5f;
9. ScriptableObject
9.1 vs MonoBehaviour
| 항목 | MonoBehaviour | ScriptableObject |
|---|---|---|
| 부착 | GameObject 필수 | 독립 .asset |
| 씬 | 종속 | 독립 |
| 게임 루프 | Awake/Start/Update | 없음 |
| 메모리 | 씬마다 복사 | 1개 인스턴스 공유 |
| 용도 | 런타임 로직 | 데이터 컨테이너 |
9.2 데이터 컨테이너 패턴
[CreateAssetMenu(fileName = "WeaponData", menuName = "Game/Weapon")]
public class WeaponData : ScriptableObject
{
public int damage = 10;
public float attackSpeed = 1.5f;
public Sprite icon;
public AudioClip swingSound;
}
public class Enemy : MonoBehaviour
{
[SerializeField] private EnemyData data; // 참조만 → 100마리가 1개 데이터 공유
}
9.3 Event Channel 패턴 (Ryan Hipple, Unite Austin 2017)
Unity 공식 Open Project 1 패턴.
[CreateAssetMenu(menuName = "Events/Int Event Channel")]
public class IntEventChannelSO : ScriptableObject
{
public UnityAction<int> OnEventRaised;
public void RaiseEvent(int value) => OnEventRaised?.Invoke(value);
}
// 발신자 — 수신자를 모름
public class EnemyDeath : MonoBehaviour
{
public IntEventChannelSO OnScoreUpdated;
void Die() => OnScoreUpdated.RaiseEvent(100);
}
// 수신자 — 발신자를 모름
public class ScoreUI : MonoBehaviour
{
public IntEventChannelSO OnScoreUpdated;
void OnEnable() => OnScoreUpdated.OnEventRaised += UpdateUI;
void OnDisable() => OnScoreUpdated.OnEventRaised -= UpdateUI;
void UpdateUI(int score) { /* ... */ }
}
[EnemyDeath] ──RaiseEvent()──▶ [IntEventChannelSO Asset]
│
├──▶ [ScoreUI]
├──▶ [AchievementManager]
└──▶ [AudioManager]
씬 경계 초월 가능, Singleton 없음, 테스트 가능.
9.4 제한사항
1. 런타임 변경은 Editor에서 반영되지만 빌드된 플레이어에서 세션 간 유지 안 됨
→ 저장은 PlayerPrefs / JSON / DB 필요
2. ScriptableObject는 씬 모름 → 씬 오브젝트 직접 참조 저장 시 손실
3. Play Mode → Edit Mode 전환 시 일부 수정 사항 자동 롤백
10. 런타임 모델
10.1 세 가지 런타임
C# 소스
│
▼ [Roslyn]
.NET IL (bytecode)
│
├──▶ [Mono] JIT, 빠른 빌드, 느린 런타임. Editor + 일부 Standalone
│
├──▶ [IL2CPP] IL → C++ → 네이티브. AOT. iOS/WebGL/콘솔 필수
│ 긴 빌드, 빠른 런타임, Reflection.Emit/dynamic 불가
│
└──▶ [Burst] LLVM AOT, SIMD. NativeContainer만. Job/DOTS
10.2 IL2CPP 파이프라인
[C# 소스] → [Roslyn] → [.NET IL DLL] → [Stripping]
│
▼
[IL2CPP] → [C++ 코드]
│
▼
[플랫폼 C++ 컴파일러]
Xcode (iOS/macOS)
Android NDK (Android)
MSVC (Windows)
│
▼
[.ipa / .apk / .exe]
10.3 IL2CPP 제한 / link.xml
// 안티
var method = new DynamicMethod(...); // Reflection.Emit 런타임 오류
dynamic obj = ...; // 컴파일 경고, 런타임 오류
Code Stripping 보호:
<!-- Assets/link.xml -->
<linker>
<assembly fullname="MyGame.Runtime" preserve="all"/>
<assembly fullname="mscorlib">
<type fullname="System.Collections.Generic.Dictionary`2" preserve="all"/>
</assembly>
</linker>
[Preserve]
public class SaveData
{
[Preserve] public string playerName;
[Preserve] public int score;
}
// AOT 제네릭 힌트
void UsedOnlyForAOTCodeGeneration()
{
new Dictionary<string, MyCustomData>();
new List<MyCustomData>();
}
10.4 Burst Compiler
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
[BurstCompile]
public struct PhysicsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float3> positions;
[ReadOnly] public NativeArray<float3> velocities;
public NativeArray<float3> results;
public float deltaTime;
public void Execute(int index)
{
// Burst → SIMD 네이티브 코드. 수십~수백배 빠름
results[index] = positions[index] + velocities[index] * deltaTime;
}
}
제약: 참조 타입(class), GC 객체, 대부분의 Unity API 불가. struct + NativeArray만.
11. 빌드 파이프라인
11.1 Build Profile (Unity 6 신규)
Unity 6 이전 Build Settings에서 단일 설정만 저장 가능 → Profile로 다중 환경(iOS Release, Android Debug 등) 저장.
11.2 플랫폼별 핵심
iOS
├── Scripting Backend: IL2CPP (강제)
├── Graphics API: Metal
├── Architecture: ARM64
└── Code Stripping: 활성화 (앱스토어 크기 제한)
Android
├── Scripting Backend: Mono (개발) / IL2CPP (릴리즈)
├── Graphics API: Vulkan (권장) / OpenGL ES 3.x
├── Target ABI: ARM64 (필수, Play 정책)
├── Minification: ProGuard / R8
└── Build System: Gradle
WebGL
├── Scripting Backend: IL2CPP
├── Graphics API: WebGL 2.0
├── Compression: Brotli / Gzip
└── 메모리 힙 제한 주의
Standalone
├── Mono (기본) / IL2CPP (권장)
└── Windows DX11/12, macOS Metal, Linux Vulkan/GL
11.3 에셋 로딩 전략
Resources/ (레거시)
+ 간단, Resources.Load("path")
- 폴더 전체 빌드 포함, 메모리 해제 불완전
AssetBundles (직접 관리)
+ 세밀한 제어
- 의존성/메모리 수동, 복잡
Addressables (권장)
+ AssetBundles 자동화, 원격 CDN, 명시적 해제
- 학습 곡선
// Addressables — 반드시 명시적 해제
private AsyncOperationHandle<GameObject> handle;
async UniTask SpawnEnemy()
{
handle = enemyRef.LoadAssetAsync<GameObject>();
var prefab = await handle.ToUniTask();
Instantiate(prefab);
}
void OnDestroy()
{
if (handle.IsValid()) Addressables.Release(handle); // 안 하면 누수
}
12. Time / Threading
12.1 Time API
Time.deltaTime // 직전 프레임 경과(초). timeScale 영향
Time.unscaledDeltaTime // timeScale 무시 (UI, 일시정지 메뉴)
Time.fixedDeltaTime // FixedUpdate 간격 (기본 0.02)
Time.time // 누적 시간 (timeScale 영향)
Time.unscaledTime // 누적 (timeScale 무시)
Time.realtimeSinceStartup // 일시정지/timeScale 무관
Time.frameCount // 누적 프레임 번호
Time.timeScale // 0=일시정지, 1=정상, 0.3=슬로우
// 슬로우 모션
Time.timeScale = 0.3f;
Time.fixedDeltaTime = 0.02f * Time.timeScale; // 물리도 슬로우
12.2 스레딩 모델
[ 메인 스레드 ] ← Unity 게임 루프 전체
│
├── GameObject API (생성/파괴/이동)
├── Component API
├── Transform
├── Physics, Rendering, Audio (대부분)
└── Scene Management
[ 백그라운드 스레드 ] ← 안전
│
├── 순수 C# 계산
├── System.IO, System.Net
├── Burst Job (NativeContainer)
└── UnityWebRequest 내부
// 안티
async Task BadAsync()
{
await Task.Run(() =>
{
transform.position = Vector3.zero; // 런타임 오류!
});
}
// 정답
async Task GoodAsync()
{
var result = await Task.Run(() => HeavyComputation()); // 백그라운드
transform.position = result; // 메인 스레드 (async/await SyncContext)
}
12.3 Coroutine vs UniTask vs Task
| 항목 | Coroutine | UniTask | Task |
|---|---|---|---|
| 메인 스레드 | 항상 | 항상 | 주로 (재개 시 컨텍스트 의존) |
| GC 할당 | 있음 | 거의 없음 | 있음 |
| 취소 | StopCoroutine | CancellationToken | Cancel |
| 예외 처리 | 어려움 | try/catch | try/catch |
| Unity 6 | 완전 지원 | 서드파티 (UPM) | Awaitable로 부분 통합 |
// UniTask 패턴 (권장)
async UniTask LoadSceneAsync(string sceneName, CancellationToken ct)
{
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
op.allowSceneActivation = false;
while (op.progress < 0.9f)
{
loadingBar.value = op.progress;
await UniTask.Yield(ct);
}
await UniTask.Delay(500, cancellationToken: ct);
op.allowSceneActivation = true;
}
// Unity 6 Awaitable (내장)
async Awaitable LoadAsync()
{
await Awaitable.NextFrameAsync();
await Awaitable.WaitForSecondsAsync(0.5f);
await Awaitable.MainThreadAsync();
}
13. XR & OpenXR 통합 (Unity의 핵심 사용 영역)
13.1 XR Plugin Framework
[Unity App C#]
│
▼
[High-Level XR Packages]
XR Interaction Toolkit (com.unity.xr.interaction.toolkit)
AR Foundation (com.unity.xr.arfoundation)
XR Hands (com.unity.xr.hands)
│
▼
[XR Subsystems Layer — Engine 내장]
XRDisplaySubsystem → 스테레오 렌더링
XRInputSubsystem → 추적, 컨트롤러
XRMeshSubsystem → 환경 메시
XRRaycastSubsystem → AR 레이캐스트
ARCameraSubsystem → AR 카메라
ARPlaneSubsystem → 평면 감지
XRHandSubsystem → 손 추적
│
▼
[XR Provider Plugin — 하나만 활성]
com.unity.xr.openxr ← 권장
com.unity.xr.meta-openxr ← Meta 확장
com.unity.xr.arcore ← Android 폰 AR
com.unity.xr.arkit ← iOS 폰 AR
com.unity.polyspatial.visionos ← visionOS (별도 경로)
com.unity.xr.oculus ← [DEPRECATED]
│
▼
[Native Runtime / OS]
OpenXR Loader / ARCore / ARKit / RealityKit
13.2 Unity OpenXR Plugin
com.unity.xr.openxr
v0.1.x preview (2020) → v1.0.x (2021) → v1.5+ (2022 권장)
v1.14 (2023) Oculus XR Plugin 기능 동등성 달성
v1.16 (2024 현재) OpenXR-SDK 1.1.53 기반, Unity 2022.3 LTS+
Feature Groups (Project Settings → XR Plug-in Management → OpenXR):
- Interaction Profiles: Oculus Touch, Vive, Index, WMR, Eye Gaze, Khronos Simple
- Features: Meta Quest Support, Hand Tracking Subsystem, Eye Gaze Interaction, Mock Runtime, Performance Settings
13.3 XR Interaction Toolkit (XRI v3)
[XR Origin (XR Rig)]
└─ Camera Offset
├─ Main Camera (HMD)
├─ Left Controller
│ ├─ XR Controller (Action-based)
│ ├─ XR Ray Interactor
│ ├─ XR Direct Interactor
│ └─ XR Socket Interactor
└─ Right Controller (동일)
[Interactive Objects]
└─ XR Grab Interactable / Simple Interactable
[Interaction Manager]
└─ XRInteractionManager
[Locomotion System]
├─ Continuous Move / Snap Turn / Teleportation
└─ Climb (v3 신규)
Action-based Input (v3에서 Device-based deprecated):
[SerializeField] InputActionReference triggerAction;
void OnEnable()
{
triggerAction.action.Enable();
triggerAction.action.performed += ctx => { /* 트리거 */ };
}
13.4 Meta 통합 — 3가지 경로
| 경로 | 패키지 | 권장 |
|---|---|---|
| A | com.unity.xr.openxr + com.unity.xr.meta-openxr |
신규 프로젝트 |
| B | Meta XR All-in-One SDK (Asset Store) | 풍부한 Meta 생태계 (Avatars, Voice, Presence Platform) |
| C | com.unity.xr.oculus |
[DEPRECATED, 사용 금지] |
13.5 PolySpatial (visionOS) — OpenXR 아님
[Unity Simulation: Logic / Physics / MonoBehaviours]
↓ (PolySpatial 중간 레이어)
[Apple RealityKit Rendering] ← OpenXR 미사용
↓
[visionOS Display]
| 모드 | 설명 |
|---|---|
| Window | 2D/3D 플랫 |
| Bounded Volume | 3D 박스 (다른 앱과 공존) |
| Unbounded / Full Immersive | 단독 몰입 |
제한: 전체화면 포스트 프로세싱, Decal, 일부 파티클 미지원. HDRP 완전 지원 부재.
13.6 VR/MR 성능 최적화
| 항목 | 권장 |
|---|---|
| 렌더링 (Quest) | URP + Vulkan |
| 스테레오 | Single Pass Instanced (드로우 콜 절반) |
| FFR | Foveated Rendering API: SRP Foveation (OpenXR 1.11+) |
| ASW | Application SpaceWarp (Vulkan + URP, 36→72 FPS 합성) |
| 텍스처 | ASTC (모바일), BC (PC) |
| Quest 주사율 | 72/90/120Hz. 90Hz = 11.1ms 예산. CPU 3-4ms, GPU 8ms 목표 |
// SRP Foveation
List<XRDisplaySubsystem> displays = new();
SubsystemManager.GetSubsystems(displays);
displays[0].foveatedRenderingLevel = 0.75f;
displays[0].foveatedRenderingFlags =
XRDisplaySubsystem.FoveatedRenderingFlags.GazeAllowed; // 아이트래킹
// Application SpaceWarp (Meta)
OVRManager.SetSpaceWarp(true);
13.7 Single Pass Instanced 셰이더 호환
Project Settings → Player → XR Settings → Stereo Rendering Mode
= Single Pass Instanced
커스텀 셰이더:
struct appdata { UNITY_VERTEX_INPUT_INSTANCE_ID };
v2f vert(appdata v) {
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
...
}
14. 아키텍처 패턴
14.1 Dependency Injection: Zenject vs VContainer
| 항목 | Zenject (Extenject) | VContainer |
|---|---|---|
| 성능 | 기준 | 5~10x 빠름 |
| GC 할당 | 있음 | Resolve 시 0 |
| AOT/IL2CPP | 제한적 | 완전 지원 |
| 유지보수 | 비활성 (legacy) | 활성 |
| 신규 권장 | X | O |
// VContainer
public class GameLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
builder.Register<AudioService>(Lifetime.Singleton);
builder.Register<IPlayerRepository, PlayerRepository>(Lifetime.Scoped);
builder.RegisterComponentInHierarchy<PlayerController>();
}
}
public class PlayerController : MonoBehaviour
{
private AudioService _audio;
[Inject]
public void Construct(AudioService audio) => _audio = audio;
}
14.2 Service Locator vs Singleton vs DI
Singleton ─── 전역, 숨겨진 의존성, 테스트 어려움
│
Service Locator ─ 인터페이스 교체 가능, 여전히 숨겨진
│
DI ─── 명시적 의존성, 테스트 용이, 복잡도 증가
현실적 선택:
- 소규모: Singleton (적은 수, 규율)
- 중간: Service Locator + SO Event Channel
- 대규모/장기: VContainer + UniRx/R3 또는 MessagePipe
14.3 DOTS / ECS
OOP (MonoBehaviour):
[GameObject][Transform][Renderer][Health][AI]
메모리 분산, cache miss 높음
ECS:
Entity 0001 → [Position][Velocity][Health]
Entity 0002 → [Position][Velocity][Health]
Archetype Chunk: [Pos|Pos|...][Vel|Vel|...][HP|HP|...]
연속 메모리, SIMD, Burst
public struct MoveSpeed : IComponentData { public float Value; }
public partial struct MovementSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
float dt = SystemAPI.Time.DeltaTime;
foreach (var (transform, speed) in
SystemAPI.Query<RefRW<LocalTransform>, RefRO<MoveSpeed>>())
{
transform.ValueRW.Position.y += speed.ValueRO.Value * dt;
}
}
}
Unity 6 현황: ECS 1.x Production-ready (V Rising 등). “ECS for All” 완전 통합은 Unity 7+ 예정. Unity 6에서는 Hybrid (MonoBehaviour + ECS 서브시스템) 가 현실적.
14.4 ScriptableObject 기반 FSM
[CreateAssetMenu(menuName = "AI/State")]
public abstract class StateSO : ScriptableObject
{
public abstract void Enter(AIController ai);
public abstract void Execute(AIController ai);
public abstract void Exit(AIController ai);
}
[CreateAssetMenu(menuName = "AI/Transition")]
public class TransitionSO : ScriptableObject
{
public DecisionSO Decision;
public StateSO TrueState;
public StateSO FalseState;
}
복잡한 AI는 UnityHFSM OSS 권장.
14.5 MVVM + UI Toolkit (Unity 6)
public class PlayerViewModel : INotifyPropertyChanged
{
private int _health;
public int Health
{
get => _health;
set { _health = value; PropertyChanged?.Invoke(this, new(nameof(Health))); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
void OnEnable()
{
var label = root.Q<Label>("health-label");
label.SetBinding("text", new DataBinding {
dataSource = viewModel,
dataSourcePath = new PropertyPath(nameof(PlayerViewModel.Health))
});
}
15. 성능 최적화
15.1 Update Manager 패턴
Unity 공식: 수백~수천 MonoBehaviour의 네이티브→매니지드 전환 오버헤드 감소.
public interface IUpdatable { void CustomUpdate(float dt); }
public class UpdateManager : MonoBehaviour
{
public static UpdateManager Instance { get; private set; }
private readonly List<IUpdatable> _updatables = new();
void Awake() { Instance = this; DontDestroyOnLoad(gameObject); }
public void Register(IUpdatable u) => _updatables.Add(u);
public void Unregister(IUpdatable u) => _updatables.Remove(u);
void Update()
{
float dt = Time.deltaTime;
for (int i = 0; i < _updatables.Count; i++)
_updatables[i].CustomUpdate(dt);
}
}
public class Enemy : MonoBehaviour, IUpdatable
{
void OnEnable() => UpdateManager.Instance.Register(this);
void OnDisable() => UpdateManager.Instance.Unregister(this);
public void CustomUpdate(float dt) { /* ... */ }
}
10,000 Update 콜 시나리오에서 유의미한 CPU 절약 (Unity 공식 벤치마크).
15.2 렌더링 최적화
드로우콜 배칭 우선순위:
SRP Batcher (최우선, 같은 Shader Variant)
└─ Static Batching (정적, 같은 Material)
└─ GPU Instancing (동일 Mesh+Material, SRP Batcher 비호환)
└─ Dynamic Batching (소규모 메시)
GPU Resident Drawer (Unity 6 URP):
- BatchRendererGroup API 자동 GPU 인스턴싱
- 복잡 씬 CPU 부하 최대 50% 감소
- URP Asset → GPU Resident Drawer + Forward+ 패스
15.3 메모리 / GC 최적화
// 안티 — Update 내 할당
void Update()
{
string log = "Pos: " + transform.position; // 문자열 → GC
var enemies = FindObjectsOfType<Enemy>(); // O(n), GC
var list = new List<Vector3>(); // 매 프레임 할당
float val = (float)(int)someBoxedValue; // boxing
}
// 정답
private Enemy[] _enemies;
private List<Vector3> _reusable = new(64);
void Awake() { _enemies = FindObjectsOfType<Enemy>(); }
void Update() { _reusable.Clear(); /* 재사용 */ }
15.4 모바일 특화
| 항목 | 권장 |
|---|---|
| 텍스처 | ASTC 6x6 (품질/크기 균형) |
| 메시 | LOD groups, Mesh Compression |
| 오디오 | Vorbis (스트리밍), ADPCM (짧은 효과음) |
| 조명 | Light Probes 우선, realtime 최소 |
| Adaptive Performance | 패키지로 동적 품질 조정 |
16. 안티패턴 / 함정
16.1 Coroutine
// 안티: 매 호출 21바이트 GC
yield return new WaitForSeconds(1f);
// 정답: 캐시
private readonly WaitForSeconds wait1s = new(1f);
yield return wait1s;
// StopCoroutine은 참조로
private Coroutine c;
c = StartCoroutine(Routine());
StopCoroutine(c); // 이름 문자열 X
16.2 async/await
// 안티
async void LoadData() { ... } // 예외 삼켜짐, 절대 금지
async Task LoadAsset()
{
await Task.Delay(1000); // ThreadPool 스레드 재개
transform.position = Vector3.zero; // 크래시!
}
// 정답: UniTask
async UniTaskVoid LoadAsset(CancellationToken ct)
{
await UniTask.Delay(1000, cancellationToken: ct);
transform.position = Vector3.zero; // 메인 스레드 안전
}
// GameObject 파괴 시 자동 취소
LoadAsset(this.GetCancellationTokenOnDestroy()).Forget();
16.3 Physics
// 안티 — transform 직접 조작
void Update()
{
transform.position += Vector3.right * speed * Time.deltaTime;
}
// 정답 — Rigidbody, FixedUpdate
private Rigidbody rb;
private Vector3 inputDir;
void Update() { inputDir = ... ; } // 입력은 Update
void FixedUpdate()
{
rb.MovePosition(rb.position + inputDir * speed * Time.fixedDeltaTime);
}
16.4 DontDestroyOnLoad 이중 생성
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject); // 중복 파괴 필수
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
더 나은 대안: Bootstrap 씬 — 앱 시작 시 1회만 로드.
16.5 메모리 누수 패턴
| 패턴 | 해결 |
|---|---|
| Singleton에 Activity context | applicationContext |
| 비정적 내부 클래스 Handler/Runnable | WeakReference 또는 정적 |
| 미등록 BroadcastReceiver | 대칭 등록/해제 |
| 미종료 Cursor | using 블록 |
16.6 Deprecated
| 안티 | 대체 |
|---|---|
| AsyncTask | Coroutine + viewModelScope (Android) — Unity는 UniTask |
| Loaders / CursorLoader | Room + Flow |
| LocalBroadcastManager | SharedFlow / StateFlow |
| Apache HTTP (API 28 제거) | OkHttp / HttpURLConnection |
16.7 Magic String 회피
// 안티
SceneManager.LoadScene("GameScene");
// 정답: SceneReference 패턴
[Serializable]
public class SceneReference
{
#if UNITY_EDITOR
[SerializeField] private UnityEditor.SceneAsset _sceneAsset;
#endif
[SerializeField] private string _scenePath;
public AsyncOperation LoadAdditive() =>
SceneManager.LoadSceneAsync(_scenePath, LoadSceneMode.Additive);
}
17. 테스트 전략
Assets/_Project/Tests/
├── EditMode/
│ ├── EditMode.Tests.asmdef ← includePlatforms: ["Editor"]
│ └── PlayerHealthTests.cs ← 순수 로직, Unity 런타임 X
└── PlayMode/
├── PlayMode.Tests.asmdef ← includePlatforms: []
└── IntegrationTests.cs ← MonoBehaviour, 물리, 코루틴
// Edit Mode
[TestFixture]
public class PlayerHealthTests
{
[Test]
public void TakeDamage_ReducesHealth()
{
var hp = new PlayerHealth(100);
hp.TakeDamage(30);
Assert.AreEqual(70, hp.Current);
}
}
// Play Mode
public class SpawnTests
{
[UnityTest]
public IEnumerator Enemy_SpawnsAndDies()
{
var enemy = Object.Instantiate(Resources.Load<GameObject>("Enemy"));
yield return new WaitForSeconds(0.1f);
Assert.IsNotNull(enemy);
}
}
18. CI/CD — GameCI + GitHub Actions
name: Unity Build & Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { lfs: true }
- uses: actions/cache@v3
with:
path: Library
key: Library-Test-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
- uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with: { testMode: all, artifactsPath: test-results }
build:
needs: test
strategy:
matrix:
targetPlatform: [StandaloneWindows64, WebGL, Android]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { lfs: true }
- uses: actions/cache@v3
with:
path: Library
key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('Assets/**', 'Packages/**') }}
- uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with: { targetPlatform: ${{ matrix.targetPlatform }} }
캐시 활용 시 빌드 시간 50%+ 단축.
19. 빅테크 / 실전 사례
19.1 Genshin Impact (miHoYo/HoYoverse)
| 항목 | 내용 |
|---|---|
| 출시 | 2020 |
| 플랫폼 | iOS/Android/PC/PS4/PS5/Switch |
| Unity 선택 | 모바일 우선 + 크로스플랫폼 |
| 렌더링 | Built-in Pipeline 기반 대규모 커스텀 (URP/HDRP 미사용) |
| 핵심 | 손그림 셀 셰이딩(SDF 외곽선, 페이셜 라이팅 맵), 그래픽 라이브러리 자체 구현 |
| PS5 업그레이드 | CPU-GPU 비동기, 고해상도 텍스처 스트리밍 |
2018년 miHoYo 기술 디렉터 Jack He의 GDC 발표 “From mobile to high-end PC: Achieving high quality anime style rendering on Unity”.
19.2 Among Us (Innersloth)
3인 개발팀. 2018 출시, 2020 폭발.
- 엔진: Unity + Adobe Animate(2D 애니메이션)
- 서버: Google App Engine + Cloud Datastore
- 위기: 5M+ 동시 접속 → Unity Multiplay 도입으로 안정화 (Unity 공식 케이스 스터디)
19.3 Pokemon GO (Niantic)
2016 출시. AR + 위치 기반.
- AR: 초기 자체 → ARKit/ARCore 통합
- 서버: Java GAE + Cloud Datastore + GCP
- 출시 직후 서버 다운, 미국 외 지역 출시 지연 → GCP 탄력 스케일링으로 점진 해결
19.4 Hearthstone (Blizzard)
2014 출시. 디지털 카드 게임. Blizzard가 자체 엔진 보유했음에도 Unity 채택 이유는 모바일 멀티플랫폼. 한 번 작성, 여러 플랫폼 빌드. PC/iPad UI 통합.
19.5 Cuphead (StudioMDHR)
- 2인 형제. XNA → Unity 전환.
- 모든 프레임 손 그림 (~50,000장, 한 프레임 25분)
- 게임 60fps, 애니메이션 24fps (1930년대 느낌)
- Unity Sprite Renderer + Asset Store 2D Toolkit
19.6 Hollow Knight (Team Cherry)
- 3인 호주 인디. Stencyl → Unity 2015.
- 2D in 3D (스프라이트를 3D 공간 레이어로)
- Asset Store: 2D Toolkit, PlayMaker (비주얼 FSM)
- 조명: 소프트 투명 도형 직접 구현
19.7 Beat Saber (Beat Games / Meta)
2018 출시. Unity 2019 기반.
- 초기: SteamVR + OculusVR SDK + PSVR
- 2023.04 OpenXR 이식 “매우 간단한 작업이었다”
- 커뮤니티 모드 SDK (Unity 2019 기반)
19.8 VRChat
소셜 VR 플랫폼. VRChat 자체가 Unity 기반.
- VRChat SDK (VRSDK), VPM (VRChat Package Manager)
- Avatars 3.0: PhysBones (Dynamic Bones 대체), Contact Receiver/Sender, Eye Tracking
- 아바타/월드는 AssetBundle로 빌드 → 런타임 로드
19.9 MRTK (HoloLens)
Microsoft Mixed Reality Toolkit.
- HoloLens 1/2 핸드/아이 트래킹, 공간 매핑 추상화
- MRTK3: Unity XR Management + XRI 기반. OpenXR 기반. Unity 2021.3 LTS+
19.10 Apple Vision Pro Polyspatial
2024 출시. Unity PolySpatial 패키지.
- Unity 게임 로직 + 물리 + 애니메이션 → RealityKit 위임
- Shared Space (멀티태스킹) vs Full Space (몰입)
- Play to Device (에디터 → Vision Pro 실시간 스트리밍)
20. 경험 많은 엔지니어의 실제 선택
| 영역 | 초보자 | 경험자 |
|---|---|---|
| 이벤트 통신 | UnityEvent Inspector | SO Event Channel |
| 서비스 접근 | Singleton 남발 | VContainer DI |
| 비동기 | async Task / Coroutine 혼용 | UniTask 일관 |
| 물리 이동 | transform.position in Update | Rigidbody.MovePosition in FixedUpdate |
| 씬 참조 | “GameScene” 매직 스트링 | SceneReference + Bootstrap |
| 에셋 로드 | Resources.Load | Addressables |
| 컴파일 속도 | asmdef 없음 | Feature별 asmdef |
| 많은 오브젝트 | MB Update 개별 | UpdateManager 또는 DOTS |
| CI | 없음 | GameCI + GitHub Actions |
| XR Provider | Oculus XR Plugin | Unity OpenXR + Meta OpenXR |
| XRI Input | Device-based | Action-based (InputActionReference) |
| Quest 렌더링 | Built-in | URP + Vulkan |
21. 참고 자료
공식 문서
- Unity Manual: Order of Execution — MonoBehaviour 라이프사이클
- Unity Manual: ScriptableObject
- Unity Manual: IL2CPP Introduction
- Unity Blog: IL2CPP Internals
- Unity Manual: Asset Metadata
- Unity Manual: Coroutines
- Unity Manual: Assembly Definitions
- Unity Manual: Scripting Restrictions (AOT/IL2CPP)
- Unity Manual: Custom Update Manager
- Unity Blog: 10000 Update Calls
- Unity ECS
- DOTS development status (Sep 2024)
- Unity 6 Preview Release Features
XR 공식
- Unity OpenXR Plugin 1.16 Manual
- Unity XR Plugin Architecture
- XRI 3.1 Manual
- XRI Examples
- XR Hands
- Meta — Unity & OpenXR Compatibility
- Application SpaceWarp
- OpenXR Foveated Rendering
- Single Pass Instancing
- PolySpatial visionOS Overview
- MR Example Meta OpenXR (GitHub)
아키텍처 패턴
- Use ScriptableObjects as Event Channels
- Open Project 1 Wiki — Event System
- VContainer Official
- VContainer vs Zenject
- VContainer GitHub
- Game Programming Patterns — Service Locator
- UnityHFSM GitHub
- UnityMvvmToolkit GitHub
- UI Toolkit Data Binding
- GPU Resident Drawer (Unity 6)
- SRP Batcher
비동기 / 도구
CI/CD / 테스트
- Edit Mode vs Play Mode Tests
- Unity Automated Tests How-To
- GameCI Builder
- GameCI Activation
- Git + Unity 완전 가이드
- UnityYAMLMerge 설정
역사 / 사례
- Unity Wikipedia
- Unity Technologies Wikipedia
- TechCrunch: How Unity Built the World’s Most Popular Game Engine
- Variety: Unity CEO Resigns (Runtime Fee)
- Unity Case Study: Among Us
- Unity Case Study: Hearthstone
- Team Cherry Blog: Unity and PlayMaker (Hollow Knight)
- Khronos Blog: Beat Saber OpenXR Port
- VRChat Creator Documentation
- Microsoft Learn: MRTK3
- Unity Blog: visionOS Support
- Unite 2016: Unity Architecture in Pokemon GO (YouTube)
도서
- Androids: The Team That Built the Android OS (Chet Haase, No Starch 2021) — Android지만 게임 엔진 역사 관점에서 함께 추천
- Game Programming Patterns (Robert Nystrom)
- Unity in Action (Joseph Hocking)