유니티 최적화 "항상 프로파일링! 눈에 보이는 게 다가 아니다"

이두현 기자 | 댓글: 1개 |
재미있는 게임을 위해서는 아이디어가 중요하다. 하지만 아이디어만 좋고 최적화가 덜 된 게임이라면, 유저가 온전히 그 재미를 느끼기 힘들다. 이처럼 최적화는 개발자가 자신의 게임을 온전히 유저에게 전달할 수 있도록 만드는 필수 과정이다. 또한, 최적화를 통해 게임의 최소 사양과 권장 사양을 낮춘다면, 유저 폭 역시 넓어지기 때문에 상업적인 측면에서도 유리하다.

구글이 게임 개발의 퀄리티 향상을 위한 '유니티를 사용한 안드로이드 게임 개발 응용' 강좌를 유니티와 협력하여 26일, 자사의 서울 캠퍼스에서 진행했다. 구글은 유니티 에반젤리스트 등 전문가를 초청해 유니티 엔진의 실제 활용 방법을 강의했다.

오늘 '유니티를 사용한 안드로이드 게임 개발 응용 강좌'에 오지현 유니티 에반젤리스트가 강단에 섰다. 오지현 에반젤리스트는 "최적화에 있어서 무엇보다 중요한 것은 '프로파일링(profiling)'이다"라고 강조하며 강좌를 시작했다.


■ 강좌 주제: Unity Tech: 성능 최적화, 가즈아~!



▲ 오지현 유니티 에반젤리스트

◎ 줄인다고 해결되나? 중요한 건 프로파일링!

개발자들이 최적화를 시작하면서 자주 저지르는 실수는 '그냥' 최적화 작업을 진행한다는 것이다. 텍스처 크기가 커 보여서 일단 줄이고, 물리 연산도 복잡해 보이니 일단 요소들을 줄이고, 스크립트도 무거워 보이니 나름대로 최적화 작업을 진행한다. 그러나, 이 작업을 진행해도 성능 향상이 안 되는 경우가 태반이다. 이유는 프로파일링 과정 없이 지레짐작으로 최적화를 진행해서다. 병목 구간을 먼저 파악해 효과적으로 수정해야 좋은 최적화 작업이 이루어진다.

이날 오지현 에반젤리스트는 '트랜스폼'을 중심으로 프로파일링 작업을 살펴봤다. 게임 내 모든 오브젝트는 '트랜스폼'이 공기처럼 존재한다. 보이지는 않지만 굉장히 중요한 공기처럼, '트랜스폼' 역시 개발 과정에서 바로 알아차리기 힘들지만 성능에 큰 영향을 끼친다. 개발 과정에서 '트랜스폼'이 일어나면 변경된 '부모'를 따라 '자식'도 함께 수정된다. 만약 '부모'에 딸린 '자식'이 5개일 경우, 부모 하나를 수정하는 과정에서 자식 5개까지 '트랜스폼' 된다. 여기에 변경 전과 후까지 포함하면, 개발자는 단 한 번의 수정을 진행했지만 유니티 엔진 내에서는 12번의 메시지가 오간다.

유니티 엔진 내에는 많은 컴포넌트(Component)가 존재하다. Physics, Renderers, UnityUI 등 많은 컴포넌트가 '트랜스폼'을 포함하기에 개발자들은 사용에 신경을 써야 한다. 대표적으로 position, rotate, scale은 C# 단계에서 변경된 '트랜스폼' 메시지가 오고 간다. Animator와 Physics로 인한 이동도 마찬가지다. 겉으로 보기에는 간단한 수정을 거쳤지만, 유니티 엔진 내 많은 컴포넌트에서는 '부모'와 '자식'을 포함해 수많은 변경이 일어난다.

간혹 "우리 게임은 트랜스폼 많이 안 쓰는데요? 그래도 신경 써야 하나요?"라고 물을 수 있다. 이 질문에 오지현 에반젤리스트는 '유니티짱'을 보여줬다.



▲ 수많은 계층으로 구성된 유니티짱

보이는 건 캐릭터 하나지만, 캐릭터의 계층 구조를 살펴 보면 팔뚝과 손가락, 관절, 머리 하나마다 모두 게임 오브젝트다. 화면 좌측에도 유니티짱의 수많은 오브젝트를 확인할 수 있다. 만약 캐릭터가 100개의 계층으로 구성되어 있다면 하나의 수정에 100개의 '트랜스폼' 메시지가 발생한다. 화면에 캐릭터 100개가 있다면 트랜스폼 메시지 1만 번이 일어나게 된다. 수정한 건 캐릭터 하나지만, 트랜스폼 메시지는 1만 개가 생성되는 것이다.

오지현 에반젤리스트가 트랜스폼 최적화를 위해 "모든 변경 사항들을 모아서 한 군데서 처리"하라고 전한다. 만약 position과 rotate를 같이 수정해야 한다면, Unity5.6에 추가된 API ' SetPositionAndRotation'을 사용하는 식이다. 앞서 두 번의 함수로 수정작업이 이루어지던 일이 뒤의 함수로는 한 번에 이루어진다.

또는 Inspector 메뉴에서 Optimize Game Object를 체크하는 것이다. 이 체크로 애니메이션 데이터를 multi threading이 가능하도록 재배치할 수 있다. 이 체크로 모델 트랜스폼 계층의 'extra transform'을 제거하고, 또 필요한 경우에는 추가해 성능을 향상 시킬 수 있다. 그리고 '메시 스키닝'을 '멀티 스레드'로 연산해 최적화를 돕는다. 퍼포먼스 테스트를 통해 최적화를 확인한 결과 iPad Air2에서 33.35의 수치가 26.96으로 향상됐음을 확인할 수 있었다.


◎ 안 쓰는 물리 연산은 미리 빼두자! Physics 최적화

물리 연산은 크게 시뮬레이션과 쿼리의 영향을 받는다. 연결된 오브젝트는 중력의 영향을 받게 하는 Rigidbody와 Raycast, SphereCast 등이 이에 해당한다. 모든 물리 연산 비용은 씬의 복잡도, 밀도와 밀접한 연관을 가지는데 물리 연산을 해야 하는 오브젝트가 많아질수록, 그만큼 많은 연산이 필요하기 때문이다. 특히 Mesh Collider는 box나 sphere와 비교해 비싼 값을 치러야 한다. box와 sphere의 연산은 생략할 수 있지만, polygon 자체를 collider하는 경우에는 하나하나가 충돌되기 때문이다.

Physics 쿼리는 1. 잠재적인 충돌을 world space로부터 수집하고 2. layer를 기반으로 잠재적인 collision들을 분류해 3. 실제 collison을 찾아내기 위한 테스트를 거친다. 물리 연산은 마지막 단계에서 큰 비용을 치루는데, 최적화를 위해서는 세 번째로 전달되는 메시지를 최대한 줄이는 게 중요하다. 필요 없는 건 최대한 걸러내야 연산 실수가 적게 일어난다.



▲ 대부분 처음 설정 그대로 쓰는 Physics layer, 필요 없는 건 끄자


또한 개발자는 정확도와 퍼포먼스 사이에서 무엇을 더 중요하게 여길지 고민해야 한다. 30 FPS로 구현되는 게임에서 60 FPS의 연산 과정을 거칠 필요는 없다. 그러나 앵그리 버드와 같이 충돌 계산이 중요한 게임은 물리 연산에 더 신경을 써야 한다. 게임마다 설정이 다르므로 대부분 기본값 그대로 사용하는 physics time-step을 내 작품에 맞게 수정해야 한다. 오지현 에반젤리스트는 "대부분의 게임은 timestep 0.04~0.08이면 충분!"하다고 귀띔했다.

다음으로 개발자들이 많이 저지르는 실수는 Rigidbody를 쓰고서 트랜스폼하는 경우다. 이러면 이중 업데이트가 이루어지기 때문에 성능에 영향을 미친다. 따라서 Rigidbody를 사용했다면, Rigidbody로만 이동해야 한다.

이러한 최적화 작업 과정에서 연산 비중이나 메모리 할당 등을 눈으로 확인할 수 있으면 큰 도움이 된다. 이 과정을 위해 오지현 에반젤리스트가 추천한 도구는 '메모리 프로파일러(Memory Profiler)'이다. 아직 유니티에 내장되어 있지 않아 따로 구해 사용해야 하는 메모리 프로파일러는 Bitbucket에서 다운로드 받을 수 있다.

메모리 프로파일러는 스크립트 백엔드를 IL2CPP로 설정해 이용하면 된다. 메모리 프로파일러는 기본적으로 유니티 프로파일러 API를 사용하기 때문에 디벨롭먼트 빌드로 실행해야 하며 유니티 프로파일러 윈도우를 실행하고 디바이스를 연결한 후 사용할 수 있다. 메모리 프로파일러로 UnityEngine.Object와 C# Object의 메모리를 영역으로 비교할 수 있고 각 부분을 클릭하면 상세한 사용 내역을 확인할 수 있다.



▲ 메모리 프로파일러 사용 화면

이렇듯 메모리 할당을 눈으로 확인할 수 있으면, 프로그래머와 아티스트 간 커뮤니케이션에도 도움이 된다. 일반적으로 아티스트는 비주얼을 중요시해 고해상도 텍스처(1024x1024)를 선호한다. 그러나 프로그래머 입장에선 최적화를 위해 해상도를 낮추고 싶어 한다. 이런 상황에서 무턱대고 "최적화를 위해 해상도를 낮춰주세요"라고 아티스트에게 요청하기보다, 도표를 보여줘 "현재 캐릭터의 머리카락에서 이만큼의 높은 연산이 이루어지니 해상도를 낮추는 게 어떨까요?"라고 물어보는 게 좋다.

댓글

새로고침
새로고침

기사 목록

1 2 3 4 5