| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- ui
- Dots
- DotsTween
- sha
- 직장인공부
- 환급챌린지
- Framework
- 커스텀 패키지
- 패스트캠퍼스
- TextMeshPro
- RSA
- adfit
- Unity Editor
- 프레임워크
- Custom Package
- AES
- unity
- 2D Camera
- 패스트캠퍼스후기
- Job 시스템
- base64
- 최적화
- 오공완
- 샘플
- 직장인자기계발
- 가이드
- job
- C#
- Tween
- 암호화
- Today
- Total
EveryDay.DevUp
[TACampus] 8주차 과제 - Unity 물리, 오디오 최적화 본문
게임 성능 최적화를 논할 때 많은 개발자들이 GPU 최적화에만 집중하는 경향이 있습니다. 물론 GPU 최적화도 중요하지만, CPU 최적화 역시 전체적인 게임 성능에 결정적인 영향을 미칩니다. 실제로 CPU와 GPU는 독립적으로 작동하는 것이 아니라 긴밀하게 연결되어 있습니다.
CPU는 매 프레임마다 렌더링할 오브젝트들을 선별하고 GPU에게 드로우 콜을 전송하는 역할을 담당합니다. 만약 CPU에서 병목 현상이 발생하면 GPU에게 드로우 콜을 보내는 데 시간이 오래 걸리게 되고, 이는 결국 GPU가 할 일 없이 CPU의 작업 완료를 기다리는 상황으로 이어집니다. Unity 프로파일러에서 이런 현상을 확인할 때 Gfx.WaitForPresent 마커가 나타나는데, 이는 메인 스레드가 준비되었지만 GPU의 프레임 처리를 기다리고 있다는 의미입니다. 반대로 Gfx.WaitForCommands 마커가 나타난다면 렌더 스레드는 준비되었지만 메인 스레드의 병목을 기다리고 있다는 신호입니다.
특히 모바일 환경에서는 CPU와 GPU 모두 제한적인 자원을 가지고 있기 때문에, CPU 최적화가 더욱 중요해집니다. CPU 최적화 영역 중에서도 물리 시스템과 오디오 시스템은 가장 많은 CPU 자원을 소모하는 대표적인 영역입니다. 물리 시스템은 충돌 감지, 물리 시뮬레이션, Rigidbody 업데이트 등 복잡한 계산을 수행하며, 오디오 시스템은 사운드 디코딩, 3D 공간 계산, 믹싱 등의 작업을 처리합니다.
따라서 이번 포스팅에서는 CPU 최적화의 핵심 영역인 물리와 오디오 시스템 최적화에 집중하여, 체계적인 접근 방법과 실제 적용할 수 있는 구체적인 기법들을 상세히 다루어 보겠습니다.
물리 시스템 최적화의 핵심 원리
Fixed Timestep 최적화의 중요성
Fixed Timestep은 물리 엔진이 얼마나 자주 업데이트되는지를 결정하는 핵심 설정입니다. Unity의 기본값인 0.02는 초당 50회의 물리 업데이트를 의미합니다. 이는 매우 정밀한 시뮬레이션을 제공하지만, 30fps를 목표로 하는 모바일 게임에서는 과도한 설정일 수 있습니다.
여기서 중요한 점은 Fixed Timestep과 프레임 속도의 관계입니다. 만약 렌더링 부하로 인해 특정 프레임의 처리 시간이 길어지면, Unity는 물리 시뮬레이션의 시간을 실제 시간과 맞추기 위해 다음 프레임에서 밀린 FixedUpdate 호출을 한꺼번에 여러 번 실행합니다. 이는 프로파일러에서 Physics.Simulate 마커의 비용이 급증하는 스파이크 현상으로 나타나며, 이 스파이크가 다시 다음 프레임의 처리 시간을 늘려 더 많은 FixedUpdate 호출을 유발하는 악순환으로 이어질 수 있습니다.
따라서 30fps 목표 게임이라면 Fixed Timestep 값을 1/30, 즉 약 0.0333으로 설정하여 FixedUpdate 호출 횟수를 프레임 속도와 동기화함으로써 CPU 부하를 직접적으로 줄일 수 있습니다.
Maximum Allowed Timestep의 방어적 역할
Maximum Allowed Timestep은 한 프레임 내에서 물리 업데이트에 할애할 수 있는 최대 시간을 제한하는 안전장치입니다. 이 값을 기본값 0.3333333보다 낮은 0.1 정도로 설정하면, 프레임 저하가 심각해져 물리 업데이트가 이 시간을 초과할 것 같을 때 Unity가 물리 시뮬레이션을 잠시 느리게 만들어 게임 전체의 안정성을 확보합니다. 즉, 물리적 정확도를 일부 희생하여 프레임 속도의 완전한 붕괴를 막는 것입니다.
Layer Collision Matrix를 통한 불필요한 연산 제거
가장 비용 효율적이면서도 효과가 큰 물리 최적화 기법 중 하나는 Layer Collision Matrix를 설정하는 것입니다. Unity에서는 기본적으로 모든 레이어가 서로 충돌하도록 설정되어 있어, Player 레이어의 오브젝트가 EnemyBullet 레이어의 오브젝트와 충돌할 필요가 없음에도 불구하고 엔진이 매번 이 둘 사이의 충돌 가능성을 검사하고 있습니다.
Project Settings의 Physics 메뉴에서 Layer Collision Matrix의 불필요한 레이어 조합 체크박스를 해제하면, 이러한 불필요한 충돌 검사를 원천적으로 차단할 수 있습니다. 예를 들어 UI 레이어는 다른 어떤 물리 레이어와도 충돌할 필요가 없으며, Enemy 레이어끼리의 충돌도 게임 설계에 따라 비활성화할 수 있습니다.
콜라이더 선택과 성능의 상관관계
콜라이더는 물리 세계에서 오브젝트의 형태를 정의하며, 어떤 종류의 콜라이더를 선택하느냐에 따라 물리 성능이 크게 달라집니다.
Primitive Collider의 성능 우위
Box, Sphere, Capsule과 같은 Primitive Collider들은 수학적으로 단순한 형태로 정의되므로 충돌 계산이 매우 빠르고 메모리 사용량도 적습니다. 움직이는 대부분의 오브젝트, 예를 들어 캐릭터, 총알, 아이템 등에는 성능을 위해 가급적 Primitive Collider를 사용하는 것이 원칙입니다.
반면 Mesh Collider는 3D 모델의 폴리곤 메시를 그대로 사용하여 매우 정밀한 충돌 범위를 제공하지만, 버텍스 수가 많아질수록 CPU 연산 비용과 메모리 사용량이 기하급수적으로 증가합니다. 특히 여러 개의 Mesh Collider가 서로 충돌하는 상황은 성능에 큰 부담을 줍니다.
Compound Collider를 활용한 효율적인 충돌 모델링
복잡한 형태를 가진 움직이는 오브젝트에 대한 해결책은 Compound Collider를 사용하는 것입니다. 이는 하나의 Rigidbody 컴포넌트를 가진 부모 오브젝트 아래에 여러 개의 Primitive Collider를 가진 자식 오브젝트들을 조합하여 복잡한 형태를 근사하는 기법입니다. 예를 들어 자동차 모델은 몸체를 위한 Box Collider와 바퀴를 위한 4개의 Sphere Collider로 구성할 수 있으며, 이 방법은 Mesh Collider보다 훨씬 성능적으로 유리하면서도 충분히 정밀한 충돌 결과를 제공합니다.
런타임 로딩 속도 개선을 위한 Physics.BakeMesh
Mesh Collider는 씬이 로드되거나 런타임에 동적으로 생성될 때 물리 엔진이 사용할 수 있는 내부 데이터 구조를 만드는 쿠킹 과정을 거칩니다. 복잡한 메시는 이 과정에서 상당한 CPU 시간을 소모하여 로딩 지연이나 프레임 스파이크를 유발할 수 있습니다.
Physics.BakeMesh API는 이 쿠킹 과정을 에디터에서 미리 수행하고 그 결과를 메시 애셋에 저장할 수 있게 해줍니다. 이렇게 미리 베이킹된 메시를 사용하는 Mesh Collider는 런타임에 쿠킹 과정 없이 즉시 로드되어 성능을 크게 향상시킵니다.
Rigidbody 이동의 정석
Rigidbody 컴포넌트가 부착된 오브젝트를 움직일 때는 반드시 물리 엔진을 통해야 합니다. Transform 컴포넌트를 직접 조작하는 것은 심각한 성능 문제와 예측 불가능한 물리 현상을 야기할 수 있습니다.
Transform 직접 조작의 문제점
스크립트에서 transform.position을 직접 변경하면 물리 엔진은 해당 오브젝트가 순간이동했다고 판단합니다. 이 경우 물리 엔진은 해당 오브젝트의 속도나 가속도를 계산할 수 없으며, 주변의 다른 물리 오브젝트와의 상호작용을 올바르게 시뮬레이션하기 위해 물리 세계 전체를 재평가해야 하는 상황이 발생할 수 있습니다.
올바른 이동 메서드 사용
Rigidbody.MovePosition 함수는 FixedUpdate 내에서 사용되어야 하며, 물리 시뮬레이션과 완벽하게 동기화된 상태로 오브젝트를 목표 위치로 부드럽게 이동시킵니다. 물리 보간 설정을 존중하므로 캐릭터 이동과 같이 연속적이고 부드러운 움직임에 매우 적합합니다.
Rigidbody.AddForce는 물리 법칙에 기반하여 오브젝트에 힘을 가합니다. ForceMode 매개변수를 통해 순간적인 힘이나 지속적인 힘 등 다양한 방식으로 힘을 적용할 수 있으며, 질량, 마찰, 중력의 영향을 받는 현실적인 움직임을 구현하는 데 사용됩니다.
오디오 최적화
오디오는 종종 물리나 렌더링에 비해 성능에 미치는 영향이 적다고 간과되지만, 잘못 관리된 오디오 시스템은 메모리 누수와 CPU 스파이크의 주된 원인이 될 수 있습니다.
압축 포맷의 이해와 선택
오디오 클립을 프로젝트로 가져올 때 Inspector에서 설정하는 임포트 옵션은 런타임 성능에 지대한 영향을 미칩니다. PCM은 원본 데이터를 그대로 사용하는 무압축 포맷으로 최고의 음질을 보장하지만 파일 크기가 매우 큽니다. 디코딩 과정이 필요 없어 CPU 부하가 전혀 없으므로, 즉각적인 반응이 필수적인 매우 짧은 UI 효과음 등에 제한적으로 사용됩니다.
ADPCM은 PCM에 비해 약 3.5배의 압축률을 가지며 디코딩 속도가 매우 빠른 것이 장점입니다. 약간의 음질 손실이 있지만, 발소리나 총소리처럼 노이즈가 많고 반복적으로 재생되는 짧은 효과음에 사용하면 메모리와 CPU 부하 사이에서 좋은 균형을 맞출 수 있습니다.
Vorbis나 MP3는 가장 높은 압축률을 제공하여 파일 크기를 획기적으로 줄일 수 있지만, 런타임에 압축을 해제하는 과정에서 CPU 자원을 소모합니다. 따라서 길이가 긴 배경 음악, 앰비언스 사운드, 대사 등에 주로 사용됩니다.
Load Type과 메모리 관리 전략
Decompress on Load는 오디오 클립을 로드하는 시점에 압축을 완전히 해제하여 원본 PCM 데이터 형태로 메모리에 상주시키는 방식입니다. 재생 시 CPU 부하가 전혀 없다는 장점이 있지만 메모리 사용량이 가장 큽니다.
Compressed in Memory는 압축된 상태로 메모리에 올려두고 재생이 필요할 때마다 실시간으로 압축을 해제합니다. 메모리 사용량을 크게 절약할 수 있지만 재생 시점마다 약간의 CPU 부하가 발생합니다. 대부분의 효과음에 적용하기에 가장 균형 잡힌 옵션입니다.
Streaming은 오디오 데이터를 메모리에 올리지 않고 디스크에서 직접 읽어와 스트리밍으로 재생합니다. 메모리 사용량을 거의 제로에 가깝게 만들 수 있어 수 분 이상 길이의 배경 음악에 필수적인 옵션이지만, 지속적인 디스크 입출력과 디코딩으로 인해 CPU 부하가 가장 높습니다.
3D 공간 음향을 위한 Force to Mono 설정
3D 공간에서 특정 위치를 가지고 재생되는 사운드는 반드시 단일 채널, 즉 모노여야 합니다. 스테레오 오디오 클립을 3D 사운드로 사용하면 Unity는 런타임에 이를 모노로 변환하는 추가적인 CPU 연산을 수행하며, 두 채널 데이터를 모두 메모리에 올려두어 메모리를 낭비하게 됩니다. 임포트 설정에서 Force to Mono 옵션을 활성화하면 임포트 과정에서 미리 클립을 모노로 변환하여 이러한 런타임 비용을 원천적으로 제거할 수 있습니다.
효율적인 사운드 재생과 오브젝트 풀링
게임에서 총알 발사, 폭발, 타격 효과 등 짧은 시간 동안 수많은 사운드가 동시에 재생되는 경우가 많습니다. 이때마다 AudioSource 컴포넌트를 가진 게임 오브젝트를 Instantiate로 생성하고 Destroy로 파괴하는 방식은 심각한 가비지 컬렉션 부하를 유발하여 주기적인 프레임 끊김의 원인이 됩니다.
오브젝트 풀링 패턴을 적용하면 이 문제를 해결할 수 있습니다. 게임 시작이나 레벨 로딩 시점에 일정 개수의 AudioSource 오브젝트를 미리 생성하여 비활성화 상태로 풀에 보관해 둡니다. 사운드 재생이 필요할 때마다 풀에서 비활성화된 오브젝트를 하나 가져와 활성화하고 필요한 설정을 적용하여 재생한 후, 재생이 끝나면 오브젝트를 파괴하는 대신 다시 비활성화하여 풀에 반납합니다.
Max Real Voices와 Priority를 통한 사운드 관리
동시에 재생할 수 있는 사운드의 개수에는 물리적인 한계가 있으며, Project Settings의 Audio에서 Max Real Voices 설정으로 이 한계치를 정의합니다. 현재 재생 중인 사운드 수가 이 값을 초과하면 Unity는 우선순위가 가장 낮은 사운드부터 강제로 중단시켜 새로운 사운드를 재생합니다.
AudioSource의 Priority 속성은 어떤 사운드가 더 중요한지를 엔진에게 알려주며, 0에서 256까지 설정할 수 있고 숫자가 낮을수록 우선순위가 높습니다. 플레이어의 공격음이나 피격음, 중요한 알림음 등 게임 플레이에 필수적인 사운드는 높은 우선순위를, 배경음이나 덜 중요한 환경음은 낮은 우선순위를 부여해야 합니다.
프로파일러를 활용한 병목 현상 분석
물리와 오디오 성능 문제를 해결하기 위해서는 먼저 Unity 프로파일러를 통해 병목 현상을 정확히 식별해야 합니다. 프로파일러의 CPU Usage 모듈에서 Physics.Simulate 마커나 Audio 관련 마커의 스파이크를 확인하고, Physics Debugger와 Audio Profiler를 통해 문제의 원인을 시각적으로 분석할 수 있습니다.
특히 Physics Debugger는 씬 뷰에 모든 콜라이더의 와이어프레임을 렌더링하여 눈에 보이지 않는 콜라이더들의 실제 형태, 크기, 위치를 정확히 파악할 수 있게 해주며, Audio Profiler는 현재 재생 중인 사운드의 상태와 메모리 사용량을 실시간으로 모니터링할 수 있습니다.
참고 자료
Unity 공식 문서
- 레이어 기반 충돌 감지 - Unity 매뉴얼
- Audio Files - Unity - Manual
- Scripting API: Rigidbody.MovePosition
- Scripting API: Rigidbody.AddForce
- Fixed updates - Unity - Manual
- Colliders - Unity - Manual
- Introduction to Mesh colliders - Unity - Manual
- 물리 디버그 시각화 - Unity 매뉴얼
- Scripting API: Physics.BakeMesh - Unity - Manual
- Physics Manager - Unity - Manual
- Scripting API: Rigidbody.solverIterations - Unity - Manual
- Physics - Unity - Manual
- Audio - Unity - Manual
- Audio Profiler module - Unity - Manual
- Physics Profiler module - Unity - Manual
- Profiler markers reference - Unity - Manual
- Common Profiler markers - Unity - Manual
- Optimizing Physics Performance - Unity - Manual
Unity Learn 학습 자료
- Physics Best Practices - Unity Learn
- Use object pooling to boost performance of C# scripts in Unity
- Using the Physics Debugger - 2019.3 - Unity Learn
Unity 공식 블로그 및 가이드
- 매끄러운 게임플레이를 위한 향상된 물리 성능 | Unity
- Analyze Memory Usage with Memory Profiling Tools - Unity
- How to use Unity's memory profiling tools
- How to optimize your game with the Profile Analyzer - Unity
- Optimize your mobile game performance: Expert tips on graphics and assets - Unity
- Optimize your mobile game performance: Tips on profiling, memory, and code architecture from Unity's top engineers
- Optimize your mobile game performance: Get expert tips on physics, UI, and audio settings
- Best practices for profiling game performance - Unity
- Enhanced physics performance for smooth gameplay - Unity
- Profiling in Unity 2021 LTS: What, when, and how
기술 블로그 및 커뮤니티 자료
- [유니티(Unity)] 프로젝트 기본 세팅 - (2) 사운드 (AudioSource, AudioListener), 음악 파일 기본 세팅(배경음악, 효과음) - 개발 일기
- 유니티 오디오 클립 임포트 설정 가이드 | Overworks' lab in GitHub
- Optimizing Unity Physics: 300% Performance Boost Strategies - Coders.dev
- Advanced Unity Game Development Techniques - Number Analytics
- Best Practices For Rigid Bodies in Unity - Digital Opus
- Unity Performance Optimization Ⅶ: Physics | by UWA - Medium
- 10 Strategies to Optimize Physics in Mobile Games - Daily.dev
- 10 Unity Audio Optimisation Tips - Game Dev Beginner
- Profiling Mobile Games(Unity) - Medium
- Unity Profiler: Identifying and Fixing Performance Issues - Wayline
외부 기술 자료
- A Guide for Choosing the Right Codec | Audiokinetic Blog
- Mesh Colliders vs Primitive Colliders - Sloyd.ai
- Object pooling - Unity Multiplayer
동영상 자료
- Simplify Collision Geometry in Unity, with ProBuilder - YouTube
- How to use Collision Matrix in Unity - My Collider Strategy - YouTube
커뮤니티 토론
'TA > TACampus-Com2us' 카테고리의 다른 글
| [TACampus] 9주차 과제 - Unity 최적화 플로우 차트이해하기 (3) | 2025.07.28 |
|---|---|
| [TACampus] 7주차 과제 - 알파 소팅, 알파테스트, 알파블렌딩 (0) | 2025.07.13 |
| [TACampus] 6주차 과제 - Unity Animation Optimize (1) | 2025.07.08 |
| [TACampus] 5주차 과제 - 빛의 사분면 (0) | 2025.06.22 |
| [TACampus] 4주차 과제 - GPU 대역폭과 최적화 방법 (4) | 2025.06.15 |