Unity를 오래 사용하다 보면 단순히 GameObject, MonoBehaviour, Transform을 사용하는 것만으로는 부족하다.
실제 프로젝트에서는 회전이 왜 이상하게 되는지, 물리 오브젝트를 왜 Transform으로 직접 움직이면 안 되는지, Resources를 왜 남발하면 안 되는지, Lightmap과 Light Probe가 각각 무엇을 해결하는지, Texture의 Mipmap과 Anisotropic Filtering이 왜 필요한지 같은 세부적인 문제가 계속 발생한다.
이 글은 과거 Unity에서 자주 사용되던 개념을 기준으로 정리된 내용을 Unity 6 기준으로 다시 정리한 Unity 핵심 지식 모음이다.
Unity의 기본 구조
Unity의 기본 구조는 GameObject -> Component -> Scene으로 이해하면 된다.
GameObject는 씬에 존재하는 객체이고, 실제 기능은 Component를 조합해서 만든다.
예를 들어 플레이어는 다음과 같이 구성할 수 있다.
Player GameObject -> Transform / Animator / Rigidbody / Collider / PlayerController
Transform은 위치, 회전, 크기를 담당하고 나머지 Component가 실제 행동과 표현을 담당한다.
Scene은 GameObject들의 집합이다.
Scene -> Player / Enemy / Camera / Light / UI
Unity 6에서도 이 구조 자체는 변하지 않는다.
Transform과 좌표계
Unity는 왼손 좌표계를 사용한다.
X -> Right
Y -> Up
Z -> Forward
Transform의 위치에는 두 가지 중요한 개념이 있다.
World Position -> Scene 전체 기준 위치
Local Position -> Parent 기준 위치
Parent가 존재한다면 다음 관계가 성립한다.
World Transform = Parent Transform + Local Transform
정확히는 위치, 회전, 스케일 모두 부모 Transform의 영향을 받아 계층적으로 계산된다.
따라서 캐릭터의 무기나 손에 붙어 있는 오브젝트는 월드 좌표보다 로컬 좌표를 사용하는 것이 자연스럽다.
Character -> Hand -> Weapon
Weapon의 localPosition은 Hand를 기준으로 결정된다.
Quaternion과 Euler Angle
Unity에서 회전을 다룰 때 가장 많이 발생하는 문제가 Euler Angle과 Quaternion의 차이다.
Euler Angle은 사람이 이해하기 쉽다.
Rotation -> X / Y / Z
하지만 Unity는 실제 Transform의 회전을 Quaternion으로 저장한다. Unity의 공식 문서에서도 Euler Angle은 표현 방식이고 내부 회전 표현은 Quaternion이라고 설명한다.
Euler Angle -> Quaternion -> Transform Rotation
따라서 다음과 같은 코드는 주의해야 한다.
transform.eulerAngles.x += 10f;
eulerAngles는 Quaternion에서 변환되어 나온 값이기 때문에 특정 축만 직접 누적하는 방식은 의도하지 않은 회전이나 드리프트를 발생시킬 수 있다. Unity도 Euler Angle을 점진적으로 수정할 때 이런 방식을 피하도록 권장한다.
회전값을 직접 관리해야 한다면 별도의 변수에 Euler Angle을 저장한 뒤 최종적으로 Transform에 적용하는 방식이 안전하다.
private Vector3 rotation;
void Update()
{
rotation.y += 90f * Time.deltaTime;
transform.rotation = Quaternion.Euler(rotation);
}
보간이 필요한 경우에는 Quaternion 기반 API를 사용하는 것이 일반적이다.
Quaternion -> Slerp / Lerp -> Smooth Rotation
Transform Constraint
Transform에는 다른 Transform의 움직임을 제약하는 Constraint 계열 기능이 존재한다.
대표적으로 다음과 같은 용도로 사용할 수 있다.
Position Constraint -> 위치 추적
Rotation Constraint -> 회전 추적
Scale Constraint -> 크기 추적
Parent Constraint -> 부모처럼 추적
Aim Constraint -> 특정 대상을 바라봄
예를 들어 카메라가 특정 오브젝트를 바라보거나 무기가 특정 Bone을 따라가야 하는 경우 직접 코드를 작성하지 않고 Constraint를 활용할 수 있다.
다만 복잡한 게임 로직까지 Constraint에 몰아넣는 것은 좋지 않다.
Constraint는 Transform 관계를 표현하는 도구이고 게임 규칙을 구현하는 도구는 아니다.
Gizmo
Gizmo는 Scene View에서 개발 정보를 시각적으로 표시하기 위한 기능이다.
대표적인 예가 다음과 같다.
Collider 영역
Raycast 방향
Detection Range
Spawn 위치
Path
Trigger 영역
예를 들어 AI의 탐지 범위를 표시할 때 OnDrawGizmos를 활용할 수 있다.
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, detectionRange);
}
게임 화면에 렌더링되는 그래픽이 아니라 개발 및 디버깅을 위한 시각화라고 이해하면 된다.
Prefab
Prefab은 GameObject와 Component 구성을 Asset으로 저장한 것이다.
Prefab -> 여러 GameObject에서 재사용
예를 들어 Enemy Prefab 하나를 만들어 놓으면 여러 위치에 동일한 구조를 배치할 수 있다.
Enemy Prefab -> Enemy A / Enemy B / Enemy C
Prefab의 핵심은 재사용성과 변경 전파다.
원본 Prefab을 수정하면 해당 Prefab을 기반으로 생성된 인스턴스에도 변경 사항을 적용할 수 있다.
게임에서 반복적으로 사용되는 다음 객체들이 대표적이다.
Player
Enemy
Weapon
Projectile
Item
UI
Effect
Preset
Preset은 Unity Editor의 Component나 Asset 설정을 저장해 재사용하는 기능이다.
예를 들어 여러 오브젝트에 동일한 설정을 적용해야 한다면 Preset을 이용할 수 있다.
Component Settings -> Preset -> 여러 대상에 재사용
Prefab이 GameObject의 구조와 데이터를 재사용하는 개념이라면 Preset은 Inspector 설정값을 재사용하는 개념에 가깝다.
ScriptableObject
ScriptableObject는 Unity에서 데이터를 Asset 형태로 저장할 수 있는 객체다.
특히 여러 객체가 동일한 데이터를 공유해야 할 때 유용하다.
ScriptableObject
-> Item Data
-> Weapon Data
-> Character Data
-> Skill Data
-> Game Config
예를 들어 100개의 아이템 Prefab 각각에 동일한 아이템 데이터를 복사해 넣는 것보다 하나의 ScriptableObject를 참조하도록 만들 수 있다.
Item Prefab A -> ItemData
Item Prefab B -> ItemData
Item Prefab C -> ItemData
이 방식은 중복 데이터 저장을 줄이는 데 유리하다. Unity 공식 문서에서도 ScriptableObject를 여러 객체가 공유하는 데이터 컨테이너로 설명한다.
단, ScriptableObject가 게임 세이브 시스템 자체는 아니다.
Editor에서 저장된 Asset 데이터와 런타임 중 변경된 게임 상태는 별개의 문제다.
ScriptableObject와 JSON
둘은 경쟁 관계가 아니다.
ScriptableObject -> Unity 프로젝트 내부의 정적 데이터
JSON -> 외부 파일 / 네트워크 / 저장 데이터
예를 들어 개발자가 관리하는 아이템 정의는 ScriptableObject로 만들고 플레이어의 저장 데이터는 JSON이나 별도의 Save 시스템으로 관리할 수 있다.
Game Design Data -> ScriptableObject
Player Save Data -> JSON / Binary / Database
무조건 JSON으로 만들거나 무조건 ScriptableObject로 만드는 것이 아니라 데이터의 생명주기와 소유자를 기준으로 선택해야 한다.
Profiler
Profiler는 Unity에서 CPU, GPU, 메모리 등의 성능을 분석하기 위한 도구다.
성능 문제가 발생했을 때 가장 중요한 것은 감으로 최적화하지 않는 것이다.
문제 발생 -> Profiler 측정 -> 병목 확인 -> 수정 -> 다시 측정
예를 들어 CPU 사용량이 높다고 해서 무조건 특정 Update가 문제라고 판단하면 안 된다.
CPU
-> Script
-> Physics
-> Animation
-> Rendering
-> Other
어느 영역에서 시간이 소비되는지 먼저 확인해야 한다.
Resources
Resources 폴더는 Resources.Load()를 통해 런타임에 Asset을 로드할 수 있게 한다.
var prefab = Resources.Load<GameObject>("Prefabs/Enemy");
하지만 Unity 6에서는 Resources를 대규모 콘텐츠 관리 시스템으로 사용하는 것은 권장되지 않는다.
Unity 공식 문서도 Resources 시스템은 확장성이 좋지 않으며 일반적인 대규모 콘텐츠 관리에는 AssetBundle이나 Addressables를 사용하는 것을 권장한다.
Resources -> 소규모 / 초기화 / 프로토타이핑
Addressables -> 일반적인 런타임 Asset 관리
AssetBundle -> 저수준 콘텐츠 패키징 / 배포
Resources에 Asset이 많아질수록 빌드와 실행 관리에도 부담이 생길 수 있다.
Resources.UnloadUnusedAssets
사용하지 않는 Asset을 메모리에서 해제하기 위해 Resources.UnloadUnusedAssets()를 사용할 수 있다.
yield return Resources.UnloadUnusedAssets();
하지만 이것을 일반적인 메모리 관리 함수처럼 매번 호출하면 안 된다.
Unity는 GameObject 계층과 Script Component, static 변수 등을 기준으로 사용되지 않는 Asset을 판단한다. 다만 현재 실행 중인 Script의 execution stack은 참조 분석 대상이 아니므로 주의해야 한다.
따라서 대규모 Scene 전환 같은 명확한 메모리 정리 시점에서 사용하는 것이 일반적이다.
Scene Unload
-> 불필요한 Object 정리
-> UnloadUnusedAssets
-> 다음 Scene 로드
AssetBundle
AssetBundle은 여러 Asset을 묶어 런타임에 로드할 수 있는 패키지 형식이다.
Assets
-> Build AssetBundle
-> Bundle File
-> Download / Load
-> Asset 사용
DLC, 패치, 원격 콘텐츠 같은 용도로 사용할 수 있다. Unity 6에서도 AssetBundle은 유효하며 의존성, 캐싱, 메모리 관리 등의 저수준 제어가 필요할 때 사용할 수 있다.
다만 직접 AssetBundle 시스템을 구축하는 것은 관리 비용이 크다.
일반적인 게임 프로젝트에서는 Addressables가 상위 수준의 Asset 관리 시스템으로 더 적합한 경우가 많다.
Addressables -> AssetBundle 기반의 상위 Asset 관리
AssetBundle.Unload
AssetBundle은 Unload()를 호출할 때 옵션에 따라 동작이 달라진다.
AssetBundle.Unload(false)
-> Bundle 자체의 메모리 해제
-> 이미 로드된 Object는 유지
AssetBundle.Unload(true)
-> Bundle 및 해당 Bundle에서 로드한 Object까지 해제
따라서 단순히 Unload()를 호출하는 것이 항상 정답은 아니다.
Asset의 참조 관계와 실제 Object의 생명주기를 함께 관리해야 한다.
Unity Accelerator
과거 Unity의 Cache Server라는 표현을 현재 프로젝트에서 그대로 사용하는 것은 적절하지 않다.
현재 Unity 생태계에서는 Unity Accelerator를 사용해 팀 환경에서 Asset Import 결과 등을 공유하고 Editor 작업을 가속할 수 있다.
Developer A -> Accelerator
Developer B -> Accelerator
Developer C -> Accelerator
핵심 목적은 팀 구성원이 동일한 Asset Import 작업을 반복하지 않도록 캐시를 공유하는 것이다.
UI와 Animation
IMGUI
IMGUI는 OnGUI() 기반의 Immediate Mode GUI 시스템이다.
void OnGUI()
{
GUI.Label(new Rect(10, 10, 200, 30), "Debug");
}
게임의 일반적인 런타임 UI 시스템으로 사용하기보다는 Editor 확장이나 디버그 UI 등 특정 용도로 사용하는 것이 적합하다.
현재 Unity에서는 UI Toolkit과 uGUI가 주요 UI 기술이며 IMGUI는 주로 Editor 및 특수한 UI 용도로 이해하는 것이 좋다.
UI Toolkit
Unity의 현재 UI 기술을 이해할 때는 UI Toolkit도 알아둘 필요가 있다.
UI Toolkit
-> VisualElement
-> UXML
-> USS
-> C#
HTML/CSS와 완전히 동일하지는 않지만 구조적으로 UI와 스타일을 분리할 수 있다는 점에서 웹 기술과 비슷한 사고방식을 사용할 수 있다.
Editor 확장에서는 특히 강력하다.
TreeView
TreeView는 계층적인 데이터를 표현하기 위한 UI 구조다.
Root -> Folder / Item / Item
Folder -> Item / Item
현재 UI Toolkit에도 TreeView 계열 기능이 존재한다.
게임 데이터 에디터, 파일 브라우저, Hierarchy 형태의 Editor Tool 등을 만들 때 유용하다.
Avatar Mask
Avatar Mask는 Animation에서 특정 Body Part의 애니메이션만 선택적으로 적용하기 위한 기능이다.
Animation Layer
-> Upper Body
-> Lower Body
예를 들어 캐릭터가 달리면서 상체로 총을 쏘는 경우를 생각할 수 있다.
Lower Body -> Run Animation
Upper Body -> Shooting Animation
이를 Animation Layer와 Avatar Mask로 분리하면 하나의 애니메이션을 모든 신체에 적용하지 않고 필요한 부분만 사용할 수 있다.
Animation Curve
Animation Curve는 시간에 따른 값을 곡선으로 정의하는 시스템이다.
Time -> Value
Animation뿐만 아니라 게임 로직에서도 사용할 수 있다.
예를 들어 데미지 감소율, 이동 속도, 카메라 줌, 이펙트 강도 등을 Curve로 표현할 수 있다.
Distance -> Damage
Time -> Scale
Progress -> Speed
코드에 복잡한 수식을 직접 박아 넣는 대신 디자이너가 Curve를 조정하도록 만들 수 있다는 것이 장점이다.
Animator.MatchTarget
Animator.MatchTarget()은 애니메이션 중 특정 신체 부위를 목표 위치에 맞추는 데 사용할 수 있다.
대표적으로 다음과 같은 상황에서 사용된다.
Jump Animation -> 착지 위치 보정
Vault Animation -> 손 위치를 난간에 맞춤
Climb Animation -> 손 / 발 위치 보정
애니메이션을 단순히 재생하는 것이 아니라 월드 공간의 목표 위치와 애니메이션을 결합할 수 있다는 점이 핵심이다.
Lighting
Unity의 조명 시스템은 사용하는 Render Pipeline에 따라 기능과 설정이 달라진다.
현재 Unity의 주요 Render Pipeline은 다음과 같다.
Built-in Render Pipeline / URP / HDRP / Custom SRP
Unity 6에서는 특히 URP와 HDRP를 기준으로 조명 시스템을 이해할 필요가 있다.
Realtime Light
Realtime Light는 런타임에 조명을 계산한다.
Light Position / Direction
-> Runtime Lighting
-> Object
빛의 위치나 방향이 실시간으로 변해야 하는 경우 적합하다.
대표적으로 다음과 같은 상황이다.
Flashlight
Moving Light
Sun Movement
Dynamic Light
대신 런타임 GPU 비용이 발생한다.
Baked Light
Baked Lighting은 조명 결과를 미리 계산하여 저장한다.
Scene
-> Light
-> Bake
-> Lightmap
-> Runtime
런타임에 매번 복잡한 조명 계산을 하지 않아도 되므로 정적인 환경의 조명을 저렴하게 처리할 수 있다.
Mixed Light
Mixed Light는 Realtime과 Baked를 조합한다.
대표적인 Shadowmask 방식에서는 직접 조명은 실시간으로 처리하면서 간접 조명과 그림자 관련 정보를 베이크된 데이터와 결합한다.
Mixed Light
-> Realtime Direct Lighting
-> Baked Indirect Lighting
-> Shadowmask
정적 환경과 동적 캐릭터가 동시에 존재하는 게임에서 중요한 개념이다.
Lightmap
Lightmap은 미리 계산한 조명 정보를 Texture 형태로 저장하는 방식이다.
Scene Lighting
-> Bake
-> Lightmap Texture
-> Surface Lighting
정적인 환경의 조명을 미리 계산하기 때문에 런타임 비용을 줄일 수 있다.
대신 다음과 같은 비용이 발생한다.
Bake Time
+
Lightmap Memory
+
Lightmap Texture Size
따라서 Lightmap은 무조건 사용하는 것이 아니라 정적 환경의 조명 비용을 메모리와 빌드 데이터로 교환하는 기술이라고 이해하면 된다.
Global Illumination
Global Illumination은 직접광뿐만 아니라 반사와 간접광에 의해 발생하는 조명 효과를 다루는 개념이다.
Light
-> Surface A
-> Reflected Light
-> Surface B
현재 Unity 6에서는 과거의 Enlighten 기반 Realtime GI를 그대로 이해해서는 안 된다. Unity 문서상 Enlighten Realtime GI는 Unity 2024 LTS 이후 지원 종료 대상으로 분류되어 있다.
따라서 Unity 6에서는 프로젝트의 Render Pipeline과 요구사항에 맞춰 Baked GI, Mixed Lighting, Light Probe, Adaptive Probe Volumes 등의 시스템을 구분해서 사용하는 것이 중요하다.
Light Probe
Lightmap은 주로 정적인 환경에 적합하다.
그렇다면 움직이는 캐릭터는 어떻게 환경의 조명을 받을까?
이때 사용하는 대표적인 방법이 Light Probe다.
Light Probe
-> 주변 조명 데이터 샘플
-> Dynamic Object
-> 주변 환경에 맞는 Lighting 적용
즉, Lightmap이 정적 표면을 위한 조명 데이터라면 Light Probe는 움직이는 Object가 환경 조명을 받을 수 있도록 샘플링된 조명 정보를 제공하는 기술이라고 이해하면 된다.
Adaptive Probe Volume
Unity 6의 URP/HDRP에서는 Adaptive Probe Volumes, APV도 중요하다.
기존의 개별 Light Probe를 배치하는 방식보다 넓은 공간의 조명을 볼륨 기반으로 관리할 수 있다.
Scene
-> Probe Volume
-> Baked Lighting Data
-> Dynamic Objects
Unity 6의 Lighting 창에서도 APV가 URP와 HDRP의 주요 조명 시스템으로 제공된다.
따라서 과거의 LPPV를 현재 Unity 6의 대표적인 동적 조명 시스템처럼 설명하는 것은 적절하지 않다.
Legacy -> Light Probe Proxy Volume
Modern SRP -> Adaptive Probe Volumes
Reflection Probe
Reflection Probe는 주변 환경의 반사 정보를 캡처한다.
Environment
-> Reflection Probe
-> Cubemap
-> Material Reflection
금속이나 반사 재질을 사용할 때 주변 환경을 표현하는 데 중요하다.
Light Probe가 주로 조명 정보를 제공한다면 Reflection Probe는 환경 반사 정보를 제공한다고 구분하면 된다.
Material과 Texture
Material
Material은 Shader와 Property 값을 조합하여 오브젝트의 표면을 정의한다.
Material
-> Shader
-> Texture
-> Color
-> Metallic
-> Smoothness
-> 기타 Property
사용하는 Render Pipeline에 따라 Shader와 Material의 구조가 달라질 수 있다.
따라서 Built-in Pipeline의 Material 설정을 URP/HDRP 프로젝트에 그대로 적용한다고 생각하면 안 된다.
Opaque와 Transparent
대표적인 Surface Type은 다음과 같이 이해할 수 있다.
Opaque -> 불투명
Transparent -> 투명
투명 재질은 일반적인 불투명 재질보다 렌더링 비용과 정렬 문제가 복잡해질 수 있다.
따라서 유리, UI, 이펙트처럼 실제로 투명도가 필요한 경우에 사용하는 것이 좋다.
Albedo / Base Color
Albedo는 표면의 기본 색상 정보다.
현재 PBR Material에서는 일반적으로 Base Color라는 개념으로 이해하는 것이 자연스럽다.
Base Color
-> Surface Base Color
Metallic
Metallic은 표면이 금속인지 비금속인지 나타내는 값이다.
Metallic 0 -> Non-Metal
Metallic 1 -> Metal
금속과 비금속은 빛을 반사하는 방식이 다르다.
Smoothness
Smoothness는 표면이 얼마나 매끄럽게 빛을 반사하는지를 표현한다.
Low Smoothness -> Rough Surface
High Smoothness -> Smooth Surface
따라서 Metallic과 Smoothness는 서로 다른 의미다.
Metallic -> 재질의 금속성
Smoothness -> 표면의 매끄러움
Normal Map
Normal Map은 실제 Mesh의 Vertex를 추가하지 않고 표면의 법선 방향을 조작하여 디테일을 표현한다.
Low Poly Mesh
+
Normal Map
-> Detailed Lighting
실제 Geometry를 증가시키지 않으므로 고해상도 표면 디테일을 비교적 저렴하게 표현할 수 있다.
단, Normal Map은 실제 실루엣을 변경하지 않는다.
Normal Map -> Shading Detail
Mesh -> Silhouette
따라서 벽의 표면이 울퉁불퉁해 보일 수는 있어도 실제 실루엣이 바뀌지는 않는다.
Height Map
Height Map은 표면의 높이 정보를 저장한다.
Shader의 구현 방식에 따라 Parallax, Parallax Occlusion Mapping 등의 효과에 사용할 수 있다.
Height Map
-> Surface Depth Illusion
Normal Map보다 더 깊이 있는 표면 표현이 가능하지만 실제 Geometry가 증가하는 것은 아니다.
Occlusion Map
Occlusion Map은 주변광이 차단되는 정도를 표현하는 데 사용된다.
Crevice
-> More Occlusion
-> Darker
좁은 틈이나 접합부 같은 영역의 깊이감을 표현하는 데 활용할 수 있다.
Emission
Emission은 표면 자체에서 빛이 방출되는 것처럼 보이게 하는 Material 정보다.
Emission
-> Self Illuminated Appearance
단순히 밝게 보이는 것과 실제로 주변을 밝히는 것은 다르다.
Emission 값을 높였다고 해서 반드시 주변 Geometry에 실제 조명이 발생하는 것은 아니다.
실제 조명 효과를 만들려면 Render Pipeline과 GI 설정에 따라 별도의 조명 처리나 베이킹이 필요하다.
Detail Map
Detail Map은 기본 Texture 위에 추가적인 고주파 디테일을 표현할 때 사용할 수 있다.
Base Texture
+
Detail Texture
-> Final Surface Detail
멀리서는 기본 Texture를 사용하고 가까이에서는 세부적인 표면 정보를 추가하는 방식으로 활용할 수 있다.
Fresnel Effect
Fresnel Effect는 표면을 정면으로 볼 때와 비스듬히 볼 때 반사 특성이 달라지는 현상이다.
View Direction
-> Surface Angle
-> Reflection Intensity
게임에서는 다음과 같은 효과에 많이 사용된다.
Shield
Force Field
Rim Light
Stylized Character
Water
Energy Effect
Texture Filtering
Mipmap
Mipmap은 하나의 고해상도 Texture만 사용하는 것이 아니라 여러 해상도의 Texture를 미리 생성해 두는 방식이다.
2048
-> 1024
-> 512
-> 256
-> 128
-> ...
카메라에서 멀리 있는 물체는 작은 Mipmap을 사용하여 Texture Sampling 비용과 메모리 대역폭을 줄일 수 있다.
또한 Texture가 멀리서 깜빡이는 현상인 aliasing을 줄이는 데도 도움이 된다.
Anisotropic Filtering
Anisotropic Filtering은 비스듬한 각도에서 바라보는 Texture의 품질을 개선한다.
대표적으로 바닥, 도로, 벽처럼 시야각에 따라 길게 늘어져 보이는 Texture에 효과가 크다. Unity 6에서는 Texture별 anisoLevel을 0~16 범위에서 설정할 수 있다.
Normal Filtering
-> Oblique Surface -> Blur
Anisotropic Filtering
-> Oblique Surface -> Sharper
다만 품질을 높일수록 GPU 비용이 증가할 수 있으므로 프로젝트의 목표 플랫폼에 맞춰 설정해야 한다.
Post Processing
과거 Unity에서는 Post Processing Stack을 별도의 패키지로 사용하는 방식이 많이 사용됐다.
현재 Unity 6에서 URP/HDRP를 사용한다면 Post Processing은 Render Pipeline의 Volume 시스템과 통합된 방식으로 이해하는 것이 적절하다.
대표적인 효과는 다음과 같다.
Bloom
Color Adjustments
Depth of Field
Motion Blur
Vignette
Film Grain
Tonemapping
처리 구조는 대략 다음과 같다.
Scene Rendering -> Camera Image -> Post Processing -> Final Image
Bloom
Bloom은 밝은 영역 주변에 빛이 퍼지는 것처럼 보이게 만드는 효과다.
Bright Pixel
-> Threshold
-> Blur
-> Additive
-> Bloom
네온, 광원, 마법 이펙트 등에 사용된다.
Vignette
Vignette는 화면 가장자리를 어둡게 만들어 시선을 중앙으로 유도하는 효과다.
Screen Center -> Normal
Screen Edge -> Darker
과도하게 사용하면 화면이 답답해질 수 있으므로 연출 목적에 맞게 사용해야 한다.
Film Grain
Film Grain은 화면에 필름 노이즈를 추가하는 효과다.
게임 플레이 정보 전달보다는 영상적인 연출에 가깝다.
Final Image
-> Noise
-> Film-like Appearance
성능보다는 시각적 스타일을 위해 사용하는 기능이다.
Physics
Unity의 Physics는 물리 시뮬레이션을 담당한다.
핵심 구성은 다음과 같다.
Rigidbody + Collider -> Physics Simulation
Collider만 있다고 해서 물리적으로 움직이는 Object가 되는 것은 아니다.
Rigidbody가 추가되면 해당 Object의 움직임을 물리 엔진이 제어한다.
Rigidbody
Rigidbody는 Object를 물리 시뮬레이션에 참여시키는 Component다.
Rigidbody
-> Mass
-> Velocity
-> Gravity
-> Collision
-> Force
물리 기반 이동에서는 Transform을 직접 조작하기보다 Rigidbody에 적절한 물리 API를 사용하는 것이 중요하다.
예를 들어 힘을 적용할 때는 다음과 같이 한다.
rb.AddForce(force, ForceMode.Impulse);
AddForce의 효과는 물리 시뮬레이션 시점에 적용되며 Force, Acceleration, Impulse, Velocity Change 등의 Mode를 선택할 수 있다.
FixedUpdate
Physics Simulation은 일반적인 Frame Update와 별도의 시간 간격으로 동작한다.
Update -> Frame 기반
FixedUpdate -> Physics Step 기반
따라서 Rigidbody 관련 물리 연산은 일반적으로 FixedUpdate()와 함께 설계한다.
Input -> Update
Physics Movement -> FixedUpdate
다만 입력을 반드시 FixedUpdate에서 직접 읽어야 한다는 의미는 아니다.
입력을 Update에서 수집하고 물리 적용을 FixedUpdate에서 수행하는 구조가 일반적이다.
Transform과 Rigidbody
Rigidbody가 물리 시뮬레이션을 담당하고 있는데 Transform을 직접 변경하면 물리 시스템과 Transform의 상태가 충돌할 수 있다.
Physics System -> Rigidbody
Transform 직접 수정 -> 별도 위치 변경
이 둘을 무분별하게 섞으면 충돌 검출이나 움직임이 예상과 다르게 나타날 수 있다.
물리 Object라면 물리 시스템을 기준으로 움직이는 것이 기본이다.
Is Kinematic
isKinematic이 활성화된 Rigidbody는 일반적인 Force나 Collision에 의해 직접 움직이지 않는다.
isKinematic = false
-> Physics Controlled
isKinematic = true
-> Script / Animation Controlled
하지만 Kinematic Rigidbody가 물리 시스템에서 완전히 사라지는 것은 아니다. 다른 Rigidbody의 움직임에 영향을 줄 수 있으며 Joint에도 사용할 수 있다.
대표적인 사용 사례가 Ragdoll이다.
Animation
-> isKinematic = true
Ragdoll
-> isKinematic = false
Collider
Collider는 물리 충돌 영역을 정의한다.
대표적으로 다음이 있다.
Box Collider
Sphere Collider
Capsule Collider
Mesh Collider
Character Controller
Collider는 시각적인 Mesh와 동일할 필요가 없다.
오히려 성능과 충돌 안정성을 위해 단순한 Collider를 사용하는 경우가 많다.
Visual Mesh -> High Detail
Physics Collider -> Low Detail
Trigger
Collider의 Is Trigger가 활성화되면 물리적인 밀어내기 충돌 대신 이벤트 영역으로 사용할 수 있다.
Player
-> Trigger Enter
-> Event
대표적으로 다음에 사용한다.
Item Pickup
Area Detection
Quest Trigger
Enemy Detection
Damage Zone
Teleport Zone
Character Controller
Character Controller는 Rigidbody 물리를 기반으로 하지 않는 캐릭터 이동 시스템이다.
주로 1인칭이나 3인칭 플레이어 컨트롤에 사용된다.
Character Controller
-> Move
-> Collision Detection
-> Step Offset
-> Slope Limit
따라서 Rigidbody 기반 캐릭터와 동일한 방식으로 생각하면 안 된다.
Rigidbody Character -> Physics Driven
Character Controller -> Controller Driven
Joint
Joint는 두 Rigidbody 사이의 관계를 정의한다.
Rigidbody A <-> Joint <-> Rigidbody B
대표적인 Joint는 다음과 같다.
Fixed Joint
Hinge Joint
Spring Joint
Character Joint
Configurable Joint
예를 들어 Hinge Joint는 문처럼 특정 축을 중심으로 회전하는 구조에 적합하다.
Character Joint는 Ragdoll에서 자주 사용되며 여러 축의 회전을 제한할 수 있다.
Ragdoll
Ragdoll은 캐릭터의 각 신체 부위를 Rigidbody와 Joint로 구성해 물리적으로 움직이게 만드는 시스템이다.
Character
-> Bones
-> Collider
-> Rigidbody
-> Character Joint
-> Ragdoll Physics
평상시에는 Animation으로 움직이다가 사망이나 특정 이벤트가 발생하면 Physics 기반으로 전환하는 구조가 일반적이다.
Animation
-> Ragdoll Trigger
-> Rigidbody Physics
이때 isKinematic과 Collider 상태를 전환하는 방식이 많이 사용된다.
Unity에서 중요한 데이터 흐름
Unity 개발을 하면서 개별 Component만 이해하면 시스템 전체를 놓치기 쉽다.
결국 대부분의 기능은 다음 구조로 연결된다.
Input -> Gameplay Logic -> Component State -> Physics / Animation -> Rendering -> Post Processing -> Display
Asset 관리 역시 별도의 흐름을 가진다.
Asset -> Import -> Runtime Loading -> Instantiation -> Usage -> Release
조명은 다음과 같다.
Scene -> Lighting Setup -> Bake / Realtime -> Lightmap / Probe -> Runtime Rendering
물리는 다음과 같다.
Input / Force -> Rigidbody -> Physics Simulation -> Collision -> Gameplay Event
Animation은 다음과 같다.
Input / Gameplay State -> Animator -> Animation State -> Bone Transform -> Skinned Mesh Rendering
Unity 6에서 특히 달라진 관점
과거 Unity 지식 중 지금도 그대로 사용할 수 있는 것도 많지만, 몇 가지는 반드시 현대적인 관점으로 바꿔야 한다.
Resources 중심
-> Addressables / AssetBundle 중심
Realtime GI / Enlighten 중심
-> Baked GI / Mixed Lighting / Probe 기반
LPPV
-> Adaptive Probe Volumes
Legacy Post Processing Stack
-> URP / HDRP Volume 기반 Post Processing
IMGUI 중심 UI
-> UI Toolkit / uGUI
Cache Server
-> Unity Accelerator
Unity 6의 Render Pipeline은 Built-in, URP, HDRP에 따라 지원하는 조명과 렌더링 기능이 다르기 때문에 “Unity의 기능”이라는 표현만으로 설명하면 부족하다. 어떤 Render Pipeline을 사용하는지가 실제 구현 방법을 결정한다.
핵심 정리
Unity를 제대로 이해한다는 것은 API를 많이 외우는 것이 아니다.
각 시스템의 책임을 구분하는 것이 중요하다.
GameObject -> 존재
Component -> 기능
Transform -> 위치 / 회전 / 크기
Prefab -> 객체 재사용
ScriptableObject -> 데이터 공유
Scene -> 게임 공간
Addressables / AssetBundle -> Asset 관리
Rigidbody -> 물리 상태
Collider -> 충돌 영역
Animator -> 애니메이션 상태
Lightmap / Probe -> 조명 데이터
Material / Shader -> 표면 표현
Texture -> 표면 데이터
Post Processing -> 최종 화면 효과
Profiler -> 성능 측정
그리고 실제 게임에서는 이들이 독립적으로 존재하지 않는다.
GameObject -> Component -> Gameplay
Gameplay -> Animation / Physics
Animation / Physics -> Transform
Transform + Mesh + Material -> Rendering
Rendering -> Post Processing
Post Processing -> Final Frame
결국 Unity 개발의 핵심은 각 시스템의 경계를 알고, 필요한 시스템을 올바른 책임에 배치하는 것이다.
특히 Unity 6에서는 Render Pipeline, Asset Management, Lighting, UI, Rendering의 현대적인 구조를 이해해야 과거 Unity 지식을 그대로 답습하면서 생기는 설계 문제를 피할 수 있다.