게임엔진관련/유니티 엔진

[Unity] 유니티 최적화, 로딩, 관리, 리소스 관련

AlrepondTech 2019. 3. 24. 19:42
반응형

 

 

=======================

=======================

=======================

 

 

 

 

 

 

 

출처: http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_Lecture&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=hit&desc=desc&no=275

 

 

[유니티 엔진 팁 - 로딩시간 최적화에 대해서]
 
 
로딩시간 최적화
- 직감도 좋지만 프로파일러를 적극 활용하는것이 정답입니다.
에디터의 profiler탭을 보면 함수들의 수행시간이 표시되는데
시간을 많이 잡아먹는 부분을 집중적으로 수정하면 됩니다.
 
프로파일러를 확인하지 않았을때 모르고 지나갔던 부분들...
불필요한 물리 오브젝트 제거!
스프라이트 출력용도로만 사용하던 오브젝트에 BoxCollider가 붙어있었는데,
프로파일러를 확인해 보니 이 collider를 재설정 하는데 많은 시간이 소요되더군요.
이부분을 제거하고 나니 로딩속도가 눈에띠게 빨라졌습니다.
 
- Find류의 함수는 주의할 것.
특히 Update등 매프레임마다 호출되는 곳에서는 GameObject.Find같은 검색함수는
되도록 사용하지 않는것이 좋습니다. 초기화할때 미리 레퍼런스를 갖고 있는것을 권장하고요,
성능좋은 PC에서는 차이가 없어보이지만 모바일에서는 확연한 차이를 발생시킵니다.
 
- 릴리즈 전 Debug.Log 지우기.
릴리즈 할때는 Debug.Log등의 로그출력부분은 모두 제거하는것이 좋습니다.
로그 남기는것 자체가 프레임에 영향을 주기 때문이죠.
예)
//Debug.Log("[ERROR] " + msg);   // 이런 문자열 합성코드가 들어있으면 가비지 컬렉터의
영향을 받습니다.
그리고 Debug.Log는 콘솔이나 파일로 기록을 남기기 때문에 속도가 굉장히 느리죠.
 
- 유니티 로딩 시스템 이해
아이폰이나 안드로이드용 유니티 어플을 실행해보면 다음과 같은 순서로 진행됩니다.
 
1) 어플실행 -> 2) 유니티 로고 출력 -> 3) 게임화면
 
"2)번 유니티 로고 출력"시 씬에 올라와 있는 오브젝트들을 모두 로딩하게 됩니다.
이때에는 로고 이미지 한장만 랜더링 할 수 있는 상태이기 때문에
로딩 진행상황등을 표시 하기가 어렵습니다(스크립트로 제어가 불가능함).
따라서 이부분을 빨리 지나가게 한 뒤 "3)번 게임화면"에 들어왔을때
별도의 로딩화면을 만들어 동적으로 로딩하도록 하는 시스템을 갖추는것이 좋아 보이더군요.

 

 

 

 

 

=======================

=======================

=======================

 

 

 

 

 

출처: https://loadofprogrammer.tistory.com/148

 

 

[최적화의 시작은 병목 파악부터]
 
# CPU
 - 너무 많은 DP CALL
 - 복잡한 스크립트나 물리 연산
 
# Vertex Processing
 - 너무 많은 버텍스들
 - 버텍스당 너무 많은 연산 (Vertex Shader)
 
# Fragment Processing
 - 너무 많은 픽셀, 오버 드로우 (Over Draw)
 - 프래그먼트당 너무 많은 연산 (Fragment Shader / Pixel Shader)
 
# Band Width
 - 크고, 압축되지 않은 텍스쳐
 - 고해상도 프레임 버퍼
 
[스크립트 최적화]
 
# 유니티의 핵심 기능은 모두 C++로 제작되어 있다.
 
# 예) Transform.position 에서 Transform은 C# 속성, Position은 C++ 영역
 
# 유니티 객체들을 멤버 변수에 저장해서 캐싱하여 사용하는 것이 좋다.
 
# FindObject 계열 함수들은 매우 느리다. (미리 찾아서 캐싱)
 - 예를 들어 Find 라는 검색이 붙은 것들은 웬만하면 지속적으로 사용하지 않는 것이 좋습니다. Find는 프로젝트 안의 모든 오브젝트를 순환하며 그 안의 클래스의 스트링을 비교하기 때문입니다.
 
# Instanitate 와 Destory 함수를 이용한 프리팹의 생성/해제는 비용이 크다
 - 활성화/비활성화를 활용한 오브젝트 풀을 사용하는 것이 좋습니다.
 
# Update 함수보다는 Coroutine을 활용한다.
 
# 박싱과 언박싱은 부하가 큰 작업이다.
 - C# 박싱과 언박싱에 관한 자세한 내용은 http://vallista.tistory.com/entry/C-%EB%B0%95%EC%8B%B1%EA%B3%BC-%EC%96%B8%EB%B0%95%EC%8B%B1 아주 간단한 설명은 제 블로그 내용인 http://loadofprogrammer.tistory.com/79 을 참조하시면 됩니다.
 
# 나눗셈보다는 곱셈이 몇십 배 빠르다.
 - 나눗셈은 곱셈보다 연산 속도가 월등히 느립니다. 100 / 10 이런 식이 아닌 100 * 0.1을 사용
 
# magnitude 보다는 sqrMagnitude를 사용해서 비교한다. (제곱근 계산 x)
 - Unity 공식 문서에서 쓰여있습니다.
 
# 삼각함수의 값은 상수로 저장하고 사용하는 것이 좋다.
 
# 문자열은 readonly 혹은 const 키워드를 사용해 가비지 컬렉션으로부터 벗어나도록 한다.
 
[만흉의 원인 : 가비지 컬렉터]
 
가비지 컬렉터(GC)는 언제 일어날지 모릅니다.
 
# Mono의 동적 메모리 관리 때문에 메모리 해제를 위해 GC가 자동 호출된다.
 
# GC는 언제 일어날지 모른다.
 
# GC가 일어나면 게임이 멈추는 현상이 발생하게 된다.
 
# 동적 메모리 해제가 가능한 일어나지 않도록 하는 것이 GC 관리의 핵심
 
- 우리가 쓰고 있는 MonoBehavior는 메모리 관리에 GC가 자동 호출되도록 설계되어 있습니다. 이 GC가 프로그래머의 입장에서 좋을 수도 있고 그렇지 않을 때도 있습니다. GC가 실행되는 동안 많은 양을 처리하게 되면 렉 현상을 겪게되며 그렇지 않기 위해서는 게임을 만들 때 GC를 고려하여 만들어야합니다. 가비지 컬렉터의 할 일을 줄여주기 위한 방법에 대해 알아보겠습니다.
 
[1] 무엇이든 동적 생성 및 해제는 굉장히 비용이 큰 작업입니다.
 위에 언급된 바가 있는 오브젝트 풀링 기법을 사용해 메모리를 관리하는 것이 좋습니다.
 
[2] 오브젝트가 해제되면 다음 과정으로는 GC가 동작되어 렉이 걸릴 수 밖에 없습니다.
 즉 오브젝트를 만들어둔 후 활성화 또는 비활성화를 이용해 사용하도록 하는 것이 좋습니다.
 
[3] 문자열 병합은 StringBuilder의 Append를 사용하면 좋습니다.
 왜냐하면 string + string은 임시 문자열을 뱉기 때문에 가비지 컬렉션이 일어나는 환경을 제공하기 때문입니다.
 
[4] foreach 대신에 for를 이용하도록 합니다.
 foreach는 한번 돌리면 24byte의 가비지 메모리를 생성시키게 되며 수많이 돌면 더 많은 메모리를 생성시키게 되므로 for 문을 이용하도록 하는 편이 좋습니다.
 
[5] 태그 비교에서는 CompareTag()를 사용하도록 합니다.
 객체의 tag 프로퍼티를 호출하는 것은 추가 메모리를 할당하며 복사를 하게됩니다.
 
[6] 모든 비교문에서 .equals()를 사용하도록 합니다.
 "==" 구문으로 사용하게되면 임시적인 메모리가 남게 되며 가비지 컬렉션이 할 일이 늘게 됩니다.
 
[7] 데이터 타입은 Class 대신 Struct를 사용하여 만들어 주면 메모리 관리가 된다.
 구조체는 메모리 관리를 Stack에서 하므로 GC에 들어가지 않게 됩니다.
 
[8] 즉시 해제시에는 Dispose를 수동으로 호출하게 되면 즉시 클린업됩니다.
 
[9] 임시 객체를 만들어내는 API를 조심해야합니다.
 GetComponents<T>, Mesh, Vertices, Camera.allCameras 등등..
 
[10] 객체의 변경 사항에 대해 캐싱합니다.
 객체의 이동과 변형에 대한 처리를 캐싱해서 매 프레임당 한번만 처리합니다.
 
[11] 컴포넌트 참조를 캐싱한다.
 GetComponent()는 한번만 호출하며 객체를 캐싱해서 사용한다.
 
[12] 콜백 함수중 쓰지 않는 함수는 제거합니다.
 Start(), Update(), OnDestroy() 등.. 비어있어도 성능에 영향을 끼치므로 지워주도록 합니다.
 
[리소스 최적화]
 
★ 권장 압축 텍스쳐 사용하기

# 아이폰(PowerVR) : PVRCT

 

 

 

# 안드로이드(Tegra) : DXT

 

 
# 안드로이드(Adreno) : ATC
 
# 안드로이드(공통) : ETC1
 

 

★ 텍스쳐
 
# 텍스쳐 사이즈는 무조건 2의 제곱이어야합니다.
 - POT(Power of Two)
 - POT가 아닌 경우 POT 텍스쳐로 변환되어 로딩된다.
 - 900 x 900 -> 실제로는 1024 X 1024로 변환
 - 화면 해상도에 맞추어 1280으로 만드는 경우엔 실제 메모리에 2048로 생성됩니다.
 
# 텍스쳐 아틀라스를 활용하라.
 - 텍스쳐 아틀라스로 최대한 묶음
 - UI만이 아니라 같은 재질의 오브젝트들을 묶음
 - 한 화면에 나오는 텍스쳐끼리(UI)
 - 알파가 있는 텍스쳐끼리, 알파가 없는 텍스쳐끼리 묶어야합니다.

 

 - 압축된 텍스쳐와 밉맵을 사용하자. (대역폭 최적화)
 - 32 bit가 아닌 16 bit 텍스쳐 사용도 상황에 맞게 고려합니다.
 
# 텍스쳐 메모리 사용량 프로파일링

 

 - 메모리 사용량 로그를 서버에 남기면 프로파일링에 도움이 됩니다.

 

 
★ Mesh
 

 

# Import시에 언제나 "Optimize Mesh" 옵션 사용
 - 변환 전, 후 버텍스 캐쉬를 최적화 해줍니다.

 

 
# 언제나 Optimize Mesh Data 옵션을 사용한다.
 - Player Setting -> Other Settings
 - 사용하지 않는 버텍스 정보들을 줄여준다.
 

 

★ 오디오
 
# 모바일에서 스트레오는 의미 없다.
 - 모두 92kb, 모노로 인코딩

 

 
# 사운드 파일을 임포트하면 디폴트로 3D 사운드로 설정
 - 2D 사운드로 변경
 
# 압축 사운드 (mp3, ogg), 비압축 사운드 (wav) 구별
 - 비압축 사운드 : 순간적인 효과음, 이펙트 등..
 - 압축 사운드 : 배경 음악
 

 

★ 폰트 리소스 최적화
 
# Packed Font를 사용
 - R, G, B, A 채널에 저장하는 기법으로 메모리 용량을 1/4로 절약하도록 합니다.
 - Packed Font는 단점이 너무 많습니다. 일반적으로 글씨에 그림자도 못 넣고 알파도 적용이 안됩니다. NGUI Atlas 적용도 되지 않습니다.

 

 
★ 리소스 기타
 
# ResourceLoadAsync() 함수는 엄청 느리다.
 - 게임 레벨 로드시에 사용했을 경우, 일반 함수에 비해 수십 배나 더 느립니다.

 

 
[그래픽스 최적화]
 

 

# Draw Call (DP Call)
 - "적절한 DP Call은 얼마 정도 인가요?" 일반적으로 100 이하를 추천합니다. 보통 70 ~ 100 정도가 일반적입니다.
 - 하지만 실제로는 케이스 바이 케이스
 
# Culling
 - 가장 빠르게 그리는 방법은 아무것도 그리지 않는 것입니다.
 
# 프러스텀(Frustum) 컬링
 - 각 Layer 별로 컬링 거리를 설정하는 것이 가능합니다.
 - 멀리 보이는 중요한 오브젝트(ex. 성, 산맥..)는 거리를 멀게 설정하고 중요도가 낮은 풀이나 나무 등은 컬링 거리를 짧게 설정합니다.
 
# 오클루젼(Occlusion) 컬링
 - Window -> Occlusion Culling 메뉴에서 설정 가능합니다.
 - 카메라에 보이는 각도의 오브젝트들만 렌더링하는 기법.
 
# 오브젝트 통합 (Combine)
 - 드로우 콜은 오브젝트에 설정된 재질의 셰이더 패스당 하나씩 일어납니다.
 - 렌더러에 사용된 재질의 수만큼 드로우 콜이 발생합니다.
 - Combine (통합)
   [1] 성질이 동일한 오브젝트들은 하나의 메쉬와 재질을 사용하도록 통합
   [2] Script 패키지 - CombineChildren 컴포넌트 제공 (하위 오브젝트를 모두 하나로 통합)
   [3] 통합하는 경우 텍스쳐는 하나로 합쳐서, Texture Atlas를 사용해야된다.
 
# Batch
 - Static Batch
   [1] Edit -> Project Setting -> Player에서 설정합니다.
   [2] 움직이지 않는 오브젝트들은 static으로 설정해서 배칭이 되게 합니다.
   [3] Static으로 설정된 게임 오브젝트에서 동일한 재질을 사용할 경우 자동으로 통합합니다.
   [4] 통합되는 오브젝트를 모두 하나의 커다란 메쉬로 만들어서 따로 저장합니다.(메모리 사용량 증가)
 
 - Dynamic Batch
   [1] 움직이는 물체를 대상으로 동일한 재질을 사용하는 경우 자동으로 통합합니다.
   [2] 동적 배칭은 계산량이 많으므로 정점이 900개 미만인 오브젝트만 대상이 됩니다.
 
# Lighting (라이팅)
 - 라이트 맵을 사용하자
   [1] 고정된 라이트와 오브젝트의 경우(배경) 라이트 맵을 최대한 활용한다.
   [2] 아주 빠르게 실행됩니다. (Per-Pixel Light 보다 2~3배)
   [3] 더 좋은 결과를 얻을 수 있는 GI와 Light Mapper를 사용할 수 있습니다.
 
 - 라이트 렌더 모드
   [1] 라이팅 별로 Render Mode : Important / Not Important 설저어 가능
   [2] 게임에서 중요한 동적 라이팅만 Important로 설정 (Per-Pixel Light)
   [3] 그렇지 않은 라이트들은 Not Important로 설정
 
# Overdraw
 - 화면의 한 픽셀에 두 번 이상 그리게 되는 경우 (Fill rate)
   [1] DP Call의 문제만큼이나 Overdraw로 인한 프레임 저하도 중요한 문제
   [2] 특히 2D 게임에서는 DP Call보다 더욱 큰 문제가 됩니다.
 
 - 기본적으로 앞에서 뒤로 그린다
   [1] Depth testing으로 인해 오버드로우를 방지합니다.
   [2] 하지만 알파 블렌딩이 있는 오브젝트의 경우에는 알파 소팅 문제가 발생합니다.
 
 - 반투명 오브젝트의 개수의 제한을 건다
   [1] 반투명 오브젝트는 뒤에서부터 앞으로 그려야합니다. -> Overdraw 증가
   [2] 반투명 오브젝트의 지나친 사용에는 주의해야합니다.
 
 - 유니티 Render Mode를 통해 overdraw 확인이 가능합니다.
 
# 유니티 셰이더
 - 기본 셰이더는 모바일용 셰이더 사용
   [1] 기본 셰이더를 사용할 경우에 모바일용 셰이더를 사용합니다. Mobile -> VertexLit은 가장 빠른 셰이더
 
 - 복잡한 수학 연산
   [1] pow, exp, log, cos, sin, tan 같은 수학 함수들은 고비용입니다.
   [2] 픽셀별 그런 연산을 하나 이상 사용하지 않는 것이 좋습니다.
   [3] 텍스쳐 룩업 테이블을 만들어 사용하는 방법도 좋습니다.
   [4] 알파 테스트 연산(discard)은 느립니다.
   [5] 기본적인 연산보다는 최적화시키고 간략화시킨 공식들을 찾아서 사용할 수 있습니다.
 
 - 실수 연산
   [1] float : 32bit - 버텍스 변환에 사용. 아주 느린 성능 (픽셀 셰이더에서 사용은 피함)
   [2] Half : 16bit - 텍스쳐 uv에 적합하며 대략 2배 빠릅니다.
   [3] fixed : 10bit - 컬러, 라이트 계산과 같은 고성능 연산에 적합. 대략 4배 빠릅니다.
 
 - 라이트 맵을 사용하자
   [1] 위의 라이팅에서 설명한 라이트 맵을 사용합니다.

 

 
[물리엔진 최적화]

 

 
# Fixed Update 주기 조절
 - FixedUpdate()는 Update와 별도로 주기적으로 불리며, 주로 물리 엔진 처리
 
 - 디폴트는 0.02초, 즉 1초에 50번 호출합니다.
 
 - TimeManager에서 수정이 가능합니다.
 
 - 게임에 따라 0.2초 정도(혹은 이상)로 수정해도 문제 없습니다.
 
# 물리 엔진 설정
 - Static Object
   [1] 움직이지 않는 배경 물체는 Static으로 설정합니다.
 
 - 충돌체의 이동
   [1] 리지드 바디가 없는 고정 충돌체를 움직이면 CPU 부하가 발생합니다 - 물리 월드 재구성
   [2] 이럴 경우에 리지드 바디를 추가하고 IsKinematic 옵션을 사용.
 
 - Maximum Allowed timestep 조정
   [1] 시스템에 부하가 걸려 지정된 시간보다 오래 걸릴 경우, 물리 계산을 건너 뛰는 설정
 
 - Solver Iteration Count 조정
   [1] 물리 관련 계산을 얼마나 정교하게 할지를 지정합니다. (높을수록 정교함)
   [2] Edit -> Project Setting -> Physics
 
 - Sleep 조절
   [1] 리지드바디의 속력이 설정된 값보다 작을 경우 휴면 상태에 들어갑니다.
   [2] Physics.Sleep() 함수를 이용하면 강제 휴면 상태를 만들 수 있습니다.
 
# 2D 물리 vs 3D 물리
 - 2D 게임에는 2D 물리를 (RigidBody2D), 3D 게임에는 3D 물리를(RigidBody)
 
# 물리 엔진 스크립트
 - 래그돌 사용을 최소화합니다.
   [1] 랙돌은 물리 시뮬레이션 루프의 영역이 아니기 때문에 꼭 필요할 때만 활성화합니다.
 
 - 태그 대신 레이어 사용
   [1] 물리 처리에서 레이어가 훨씬 유리합니다. 성능과 메모리에서 장점을 가집니다.
 
 - 메쉬 콜라이더는 절대 사용하지 않는다.
 
 - 레이캐스트와 Sphere Check 같은 충돌 감지 요소를 최소화합니다.
 
# Tilemap Collision Mesh
 - 2D 게임에서 타일 맵의 Collision Mesh를 최적화하라
   [1] Tilemap을 디폴트로 사용해서 각 타일별로 충돌 메쉬가 있는 경우 물리 부하가 커집니다.
   [2] 연결된 Tilemap을 하나의 Collision Mesh로 물리 연산을 최적화 합니다.

 

 

참조한 사이트 URL :

[1] : https://trello.com/c/ZnFgG0n9/36--

[2] : https://www.slideshare.net/agebreak/unite2015-47100325?next_slideshow=1



출처: https://loadofprogrammer.tistory.com/148 [큰 꿈을 그리는 프로그래머의 공간.]

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: https://docs.unity3d.com/kr/530/Manual/OptimizingGraphicsPerformance.html

 

 

그래픽 성능 최적화

 

많은 게임에 있어 좋은 퍼포먼스는 결정적인 것입니다. 게임의 그래픽 렌더링 속도를 극대화하기 위한 간단한 지침을 아래에서 정리합니다.

어디에서 그래픽 비용이 드는가

게임의 그래픽 부분은 주로 컴퓨터의 GPU와 CPU 두 시스템에 비용이 많이 듭니다. 어떤 최적화를 하던지 먼저 할 일은 퍼포먼스 문제가 어디에 있는지 를 찾는 것입니다. GPU와 CPU에서 최적화의 내용은 상당히 다릅니다. (경우에 따라서는 상반되는 내용입니다 - CPU 최적화를 위해 GPU의 처리를 늘리거나, 그 반대 등)

병목 현상 및 그것을 검사하는 방법으로 :

  • GPU는 종종 필레이트(fillrate) 또는 메모리 대역폭 제한되어 있습니다
  • 낮은 해상도로 게임 실행함으로써 빨라졌다면, GPU의 필레이트에 의해 제한되어 있을 가능성이 높습니다.
  • CPU는 종종 렌더링되야 하는 배치의 수에 제한됩니다.
  • Rendering Statistics 창에서 “batches”를 확인하세요.

Less-common bottlenecks:

  • GPU가 처리하는 정점이 너무 많은 경우. 얼만큼의 정점이어야 “문제 없다”인가는 GPU와 정점 쉐이더의 복잡성에 따라 달라집니다. 전형적인 지표는 모바일에서 “십만 이하”, PC에서 “수백만 이하”입니다.
  • The CPU has too many vertices to process. This could be in skinned meshes, cloth simulation, particles, or other game objects and meshes. As above, it is generally good practice to keep this number as low as possible without compromising game quality. See the section on CPU optimization below for guidance on how to do this.
  • GPU, CPU 렌더링이 문제가 아닌 경우. 예를 들어, 스크립트 및 물리 연산이 문제일지도 모릅니다. Profiler를 통해 확인하십시오.

CPU optimization

화면의 오브젝트를 렌더링하는 데 CPU가 해야 할 일이 몇 가지 있습니다 - 어떤 라이트가 오브젝트에 영향을 주는지를 확인하는 쉐이더 및 쉐이더 파라미터 설정, 드로잉 명령을 그래픽 드라이버에 보내고 그래픽 카드로 보내는 명령을 준비하는 등입니다. 이들은 “오브젝트 단위”의 CPU 비용이 저렴하지 않기 때문에 나타나는 오브젝트가 많이 있는 경우, 계속 쌓여버리는 경우가 있습니다.

All this “per object” CPU usage is resource-intensive, so if you have lots of visible objects, it can add up. For example, if you have a thousand triangles, it is much easier on the CPU if they are all in one mesh, rather than in one mesh per triangle (adding up to 1000 meshes). The cost of both scenarios on the GPU is very similar, but the work done by the CPU to render a thousand objects (instead of one) is significantly higher.

Reduce the visible object count. To reduce the amount of work the CPU needs to do:

  • 오브젝트를 수동 또는 Unity에서 드로우콜 배칭을 사용하여 정리합니다.
  • 오브젝트의 메테리얼 수를 줄이기 위해 다른 텍스처를 더 큰 텍스처 Atlas에 정리 합니다.
  • 오브젝트가 여러 번 렌더링되는 것을 줄입니다(리플렉션, 그림자, 픽셀 라이트, 기타, 아래에서 설명)

오브젝트를 정리하고, 각 메쉬가 적어도 수백개의 삼각형을 가지게 하고, 메쉬 전체에서 Material이 한 개가 되도록 합니다. 메테리얼이 공통적이지 않은 두 오브젝트를 정리해도 퍼포먼스는 전혀 향상되지 않는다는 것을 이해하는 것이 중요합니다. 여러 메테리얼을 사용하는 일반적인 이유는 두 ​​메쉬가 같은 텍스처를 사용하지 않기 때문에, CPU 성능을 최적화하려면 오브젝트가 같은 텍스쳐로 결합되어 공유하는 것을 보장해야 합니다.

그러나, Forward Rendering 경로에서 픽셀 라이트를 많이 사용하는 경우, 뒷부분에 나오는 바와 같이 오브젝트를 정리하는 것이 무의미한 경우가 있습니다.

GPU : 모델 지오메트리 최적화

There are two basic rules for optimizing the geometry of a model:

  • 필요 이상으로 삼각형을 사용하지 말 것
  • UV 매핑의 솔기(seams) 및 하드 엣지(이중화한 정점)를 가능한 한 적은 수로 억제

실제로 그래픽 하드웨어가 처리해야 할 것은 3D 응용 프로그램에서 나타나는 수와 일반적으로 일치하지 않는 점에 유의하십시오. 모델링 응용 프로그램은 일반적 지오메트리 정점 수, 즉 모델을 구성하는 개별적인 코너의 수를 표시합니다. 그래픽 카드는 어떤 지오메트리 정점은 두 개 이상의 논리적인 정점으로 나눌 필요가 있습니다. 정점은 여러 법선, UV 좌표 또는 정점 컬러가 있는 경우에 분할해야 합니다. 결과적으로, Unity의 정점 수는 항상 3D 응용 프로그램에서 표시되는 것보다 커집니다.

모델 지오메트리의 수가 GPU에 가장 관련이 있으며, Unity의 일부 기능, 예를 들면 메쉬 스키닝 모델은 은 CPU에서 처리 합니다.

Lighting performance

계산이 필요없는 라이팅이 항상 가장 빠릅니다! 매 프레임 계산하는 것 대신, Lightmapping를 사용하여 라이팅 “베이크”를 한 번 해주시기 바랍니다. 라이트맵 환경을 생성하는 과정은 씬 안에서 단순히 라이트 배치를 하는 것보다 조금 더 시간이 걸립니다. 하지만:

  • 아주 빠르게 실행됩니다(2개의 픽셀 라이트에서는 2–3 배)
  • 전역 조명 및 라이트 매퍼의 베이크에 의해 결과를 매끄럽게 할 수 있기 때문에 외형 또한 개선됩니다.

많은 경우에서, 라이트를 여기저기 배치하는 대신에 쉐이더와 컨텐츠에 간단한 트릭을 사용하는게 가능해 집니다. 예를 들어, “Rim Lighting”을 얻기 위해 카메라에 직접 빛을 추가하는 대신 전용 “Rim Lighting”계산을 쉐이더에 직접 추가하는 것이 좋습니다.

forward rendering에서의 라이트

forward rendering에서의 라이트

동적 픽셀 라이팅에 의해 영향을 받는 픽셀에 오버 헤드가 증가하고 오브젝트가 여러 경로에서 렌더링되는 것으로 이어질 수 있습니다. 모바일 및 저가형 PC의 GPU 등 그다지 강력하지 않은 장치는 여러개의 Pixel Light가 하나의 오브젝트를 비추는 것은 피하고, 매 프레임마다 계산시키지 말고 라이트 맵을 사용하여 정적 오브젝트를 조명 계산을 사용하십시오. 동적 정점 라이팅도 정점 변환에 의해 비용을 상당히 증가시킵니다. 여러 라이트가 하나의 오브젝트를 비추는 것을 피하십시오.

만약 픽셀 라이팅을 사용하는 경우, 픽셀 라이트에 의해 각 메쉬가 비춰질만큼 렌더링이 이루어집니다. 만약 떨어진 두 메쉬를 결합하면 결합한 오브젝트의 유효 크기를 늘립니다. 이 결합한 오브젝트의 일부라도 비추는 모든 픽셀 라이트는 렌더링 대상으로 고려되므로, 렌더링 패스의 횟수가 늘어날지도 모릅니다. 일반적으로 결합한 오브젝트의 렌더링은 각각의 다른 오브젝트의 패스 수의 합계이기에, 결합함으로써 얻을 수 있는 없습니다. 이러한 이유로 다른 세트의 픽셀 라이트에 의해 영향을 받을 정도로 멀리 떨어진 메쉬는 결합해서는 안됩니다.

렌더링시 Unity는 메쉬를 둘러싼 모든 라이트를 찾아, 어떤 라이트가 그것에 가장 영향을 주는지를 계산합니다. Quality Settings에 의해 몇 개의 라이트가 픽셀 라이트와 정점 라이트로 남는가를 조정할 수 있습니다. 각 라이트는 메쉬에서의 거리 및 비추는 강도에 기초하여 각자의 중요도를 계산합니다. 게다가 몇 가지 라이트는 게임의 흐름을 기준으로 중요도가 다릅니다. 이러한 이유로, 모든 라이트는 Render Mode 설정이 있습니다. Important 또는 Not Important에서 설정할 수 있습니다. Not Important에 설정된 빛은 일반적으로 더 낮은 렌더링 오버 헤드가 될 것입니다.

예를 들어 자동차 경주 게임에서 플레이어의 차량이 어둠 속에서 헤드 라이트를 켜고 달리고 있다고 가정합니다. 헤드 라이트는 가장 중요한 광원이 되기 때문에 Render Mode는 아마 Important로 설정 될 것입니다. 반대로 게임의 중요성이 다소 떨어지는 것(다른 차량의 백라이트 등) 및 픽셀 라이트에 의해 시각적 효과가 개선되지 않는 경우가 있습니다. 그 라이트의 Render Mode는 Not Important로 설정함으로써, 효과가 약한 곳에서의 렌더링 소비를 피합니다.

픽셀 라이팅 최적화는 CPU와 GPU를 모두 절약할 수 있습니다 : CPU가 처리하는 드로우콜이 줄어들고, GPU가 처리하는 정점 수 및 추가 오브젝트 렌더링으로 래스터라이즈(Rasterize)하는 픽셀 수가 줄어듭니다.

GPU : 텍스처 압축과 밉맵

압축 텍스처를 사용하여 텍스처의 크기를 감소(결과적으로 로드 시간의 속도 및 메모리 사용 최소화)하고, 렌더링 성능을 비약적으로 향상시킬 수 있습니다. 압축 텍스처는 압축되지 않은 32비트 RGBA 텍스처와 비교하면 약간의 메모리 대역폭 밖에 사용하지 않습니다.

텍스처 밉맵을 사용하세요

원칙적으로 3D 씬에서 사용되는 텍스처에서 Generate Mip Maps를 활성화 하십시오. 압축 텍스처에 의해 GPU가 렌더링시에 전송되는 텍스처 데이터의 양을 제한하는 것과 마찬가지로, Mip Map 된 텍스처에 의해 GPU가 더 작은 삼각형에는 낮은 해상도를 사용합니다.

이 규칙의 유일한 예외가 되는 것은 텍셀(텍스처 픽셀)이 렌더링된 화면 픽셀에 1:1로 매핑하는, 즉 UI 요소나 2D 게임의 경우입니다.

LOD 및 레이어 당 컬링 거리(Cull Distance)

Culling objects involves making objects invisible. This is an effective way to reduce both the CPU and GPU load.

일부 게임에서는 작은 물체를 큰 오브젝트와 비교하여 무시하고 CPU 및 GPU 로드를 감소하는 것이 적절합니다. 예를 들어, 작은 바위와 모래는 먼 거리에서는 안보이게 하고 큰 건물은 보이는 상태로 할 수 있습니다.

There are a number of ways you can achieve this:

  • Use the Level Of Detail system
  • Manually set per-layer culling distances on the camera

이것은 Level Of Detail 시스템이나 수동으로 레벨 당 컬링 거리를 카메라에 설정 하여 사용 할수 있습니다. 작은 오브젝트는 separate layer에서 레벨 당 컬링 거리를 Camera.layerCullDistances 스크립트 함수를 사용하여 설정할 수 있습니다.

실시간 그림자

실시간 그림자가 좋긴하지만 CPU의 과도한 드로우콜과 GPU에서 추가 작업 면에서 보았을 때 퍼포먼스 비용을 상당히 소모합니다. 자세한 내용은 Light Performance page를 참조하십시오.

GPU: 고성능 쉐이더 작성 팁

Different platforms have vastly different performance capabilities; a high-end PC GPU can handle much more in terms of graphics and shaders than a low-end mobile GPU. The same is true even on a single platform; a fast GPU is dozens of times faster than a slow integrated GPU.

모바일 플랫폼의 GPU 퍼포먼스 및 저가형 PC는 당신의 개발 기계보다 훨씬 느릴 것입니다. 일반적으로 쉐이더를 수동으로 최적화하여 계산량 및 텍스처 로딩을 절감하지 않으면 좋은 퍼포먼스를 얻을 수 없습니다. 예를 들어, Unity의 일부 내장 쉐이더는 “모바일”에 해당하는 것이 있으며, 더 빠릅니다 (몇 가지 제한 사항이나 근사치는 있지만, 그것이 바로 그들을 빠르게 하는 것입니다).

모바일 및 저가형 PC 그래픽 카드에서 가장 중요한 것을 아래에 가이드 라인으로 정리했습니다.

복잡한 수학 연산

Transcendental mathematical functions (such as powexplogcossintan) are quite resource-intensive, so avoid using them where possible. Consider using lookup textures as an alternative to complex math calculations if applicable.

Avoid writing your own operations (such as normalizedotinversesqrt). Unity’s built-in options ensure that the driver can generate much better code. Remember that the Alpha Test (discard) operation often makes your fragment shader slower.

부동 소수점 연산

While the precision (float vs half vs fixed) of floating point variables is largely ignored on desktop GPUs, it is quite important to get a good performance on mobile GPUs. See the Shader Data Types and Precision page for details.

쉐이더 퍼포먼스에 대한 자세한 내용은 쉐이더 퍼포먼스를 참조하십시오.

게임을 더 빠르게 하기 위한 간단한 체크리스트

  • PC 용으로는 타겟 GPU의 스펙에 맞게 정점 수를 20만에서 300만보다 적게합니다.
  • 내장 쉐이더를 사용하는 경우, Mobile 또는 Unlit의 카테고리에서 선택합니다. 모바일이 아닌 플랫폼에서도 작동하지만 더 복잡한 쉐이더가 단순화되고 근사화(approximated)된 버전입니다.
  • 씬에서 다른 메테리얼의 수가 낮도록 억제합니다 - 서로 다른 오브젝트간에 메테리얼을 최대한 공유합니다.
  • 움직이지 않는 오브젝트에 대해 Static을 설정하고 Static Batching과 같은 내부 최적화를 허용합니다.
  • Only have a single (preferably directional) pixel light affecting your geometry, rather than multiples.
  • Bake lighting rather than using dynamic lighting.
  • 가능한한 압축 텍스처 포맷을 사용하고, 그 외의 경우는 32비트보다 16비트를 선택합니다.
  • Avoid using fog where possible.
  • Occlusion Culling의 장점을 배운 뒤, 복잡하고 static한 씬에 의해 표시되는 지오메트리의 양 및 드로우콜을 줄일 수 있습니다. 오클루전 컬링의 효과가 나오는 레벨의 설계를 합니다.
  • Skybox을 사용하여 멀리 있는 지오메트리를 “진짜처럼 보이게 합니다.”
  • 픽셀 쉐이더를 사용하거나 Texture Combiner를 사용하여 다중 패스가 아닌 복수의 텍스처를 믹스합니다.
  • Use half precision variables where possible.
  • 복잡한 숫자 계산에서는 사용을 최소화하고, 예를 들면 powsincos 등을 픽셀 쉐이더에서의 사용을 최소화합니다.
  • 프라그먼트(Fragment) 당 텍스처를 보다 적어지도록 합니다.

 

 

 

=======================

=======================

=======================

 

 

 

 

 

출처: https://unityindepth.tistory.com/30

 

 

"14일만에 0에서 60프레임 만들기" 
Unity3D를 사용하여 우리의 게임을 최적화시키면서 배웠던 것들

부드러운 게임 플레이는 매끄러운 프레임 레이트를 기반으로하는데, 아이폰과 아이패드를 대상으로 60프레임에 이르게하는 것은 우리의 다가오는 액션 게임, Shadow Blade의 중요한 목표였다. (http://shadowblade.deadmage.com)

다음은 우리가 고려해야 했던 것과 성능을 개선시키기위한 게임 내의 변화, 그리고 집중적인 최적화 기간 동안 목표 프레임에 도달하게하는 것들에 대한 요약이다.

기본적인 게임 기능이 잘 작동되고 난 후, 목표로 설정한 게임 성능을 만족시켜야 했다. 우리의 주요 성능 측정 툴은 내장 유니티 프로파일러와 Xcode 프로파일링 툴이였다. 유니티 프로파일러를 사용하여 장치에 실행중인 코드를 프로파일하는 것은 매우 유용한 특징이라는 것을 증명했다.

여기 우리의 요약과, 집중적인 측정, 수정, 재측정에 대해서 배운 것들이 있고, 그 결과 우리의 목표 장비에 고정된 60프레임을 만들 수 있었다.

1 – 가비지 컬렉터라고 불리는 흉포한 괴물과 직접 맞서라.

C/C++ 게임 프로그래밍 배경에 비롯하여, 우리는 가비지 컬렉터의 특정한 행동을 사용하지 않았다. 사용하지 않는 메모리를 당신 대신에 자동으로 깨끗이 하는 것은 일단 매우 멋진 일이지만, 곧 실제로 효과가 서서히 나타나기 시작하고, 쓰지 않는 메모리를 모으는 가비지 컬렉터에 의해 당신은 CPU Load를 보여주는 프로파일러에서 정기적인 급등을 목격할 것이다. 이는 특별히 모바일 장비에서 거대한 이슈로 드러났다. 메모리 할당과 이를 제거하는 것은 최우선순위가 되었고, 여기에 우리가 취했던 몇가지 주요 행동들이 있다:
 
  1. 코드에서 문자열 병합을 제거하라. 이는 가비지 컬렉터가 수거할 수 있는 많은 쓰레기를 남긴다.
  2. foreach 반목문을 간단한 "for" 반복문으로 교체해라. "foreach" 반복문에서 한번 돌 때마다 24Byte의 쓰레기 메모리를 생성한다. 10번을 반복하는 간단한 반복문은 납득할 수 없는 GC가 수거 가능한 240byte의 메모리를 남긴다.
  3. 게임 오브젝트의 태그를 비교하는 방법을 교체해라. if(go.tag == "Enemy")를 사용하는 대신에, 우리는 if(go.CompareTag("Enemy")를 사용했다.
    객체에 tag 프로퍼티를 호출하는 것은 추가적인 메모리를 할당하고 복사하며, 이 같은 비교문이 내부 반복문에 있다면 정말 나쁘게 작용한다.
  4. 오브젝트 풀은 훌륭하며, 우리는 게임이 실행되는 동안에, 레벨을 진행하고 있을 때, 어떤 것도 동적으로 할당하지 않기 위해서 동적 게임 오브젝트를 위한 풀을 만들고 사용 했다.
  5. LINQ 명령어를 사용하지 않았다. 그들은 중간 버퍼를 할당하는 경향이 있는데, 이는 가비지 컬렉터의 음식이다.
2 – High-Level 스크립트와 네이티브 엔진 C++코드간의 커뮤니케이션 오버헤드에 신경써라.

유니티3D를 사용하여 작성된 모든 게임플레이 코드는, 우리의 경우 Mono runtime을 사용하여 처리되는 C#으로된 스크립트 코드였다. 엔진 데이터와 커뮤니케이션하기 위한 모든 요구사항은 High-Level 스크립트 언어가 네이티브 엔진 코드를 호출하는 것이다.  물론 이 자체로도 오버헤드를 가지고 있고, 게임코드에서 이 같은 호출을 줄이는 것은 두번째 우선순위였다.
 
  1. 씬을 돌아다니는 움직이는 객체는 스크립트 코드가 엔진 코드를 호출하도록 요구하는데, 우리는 게임플레이 코드에서 한 프레임 동안 객체의 변형 요구사항을 캐싱했고, 호출 오버헤드를 줄이기 위해 이 요구사항을 엔진에 딱 한번 보냈다. 우리는 객체의 움직임과 회전이 필요한 곳 외에 다른 유사한 곳에서도 이 패턴을 사용하였다.
  2. 지역적으로, 컴포넌트의 참조를 캐싱하는 것은, 매번 게임 오브젝트의 "GetComponent"를 사용하여 컴포넌트 참조를 가져오는 것을 제거할 수 있다. 이는 위에서 봤던 네이티브 엔진 코드 사용을 줄일 수 있는 또 다른 예이다.
3 – 물리학, 물리학, 물리학 그 이상.
 
  1. 물리 시뮬레이션 시간간격을 가능한 한 최소한으로 설정해라. 우리의 경우 16밀리세컨트 이하로 설정하지 않았다.
  2. 캐릭터 컨트롤러의 Move 명령어를 호출하는 것을 줄여라. 캐릭터 컨트롤러를 움직이는 것은 동시에 발생하며, 매 호출마다 상당한 성능 비용을 불러올 수 있다. 우리가 한 것은 매 프레임마다 움직임 요구 사항을 캐시하였고 이를 그들에게 딱 한번 적용했다.
  3. "ControllerColliderHit" 콜백에 의존하지 않게 코드를 수정하라. 이들의 콜백은 매우 빠르게 처리되지 않는다는 것으로 드러났다.
  4. 좋지 않는 장비들을 위해 천 물리를 스킨드 메쉬로 교체하라. 천 파라미터는 성능에 있어서 중요한 역할을 하고, 미학적인 부분과 성능사이의 적절한 균형을 찾기 위해 많은 시간을 지불한다.
  5. 레그돌은 물리 시뮬레이션 루프의 영역이 아니기 때문에 활성화 하지 않았으며, 꼭 필요할 때만 활성화 시켰다.
  6. 트리거의 "OnInside"콜백은 신중하게 평가되어야한다. 그리고 우리의 경우, 가능한 이것을 사용하지 않고 로직을 짜기위해 노력했다.
  7. 태그 대신에 레이어! 레이어와 태그는 객체에 손쉽게 할당될 수 있고, 특정 객체를 질의하는데 사용될 수 있다. 그러나 충돌 로직에 관해서, 레이어는 적어도 성능에 있어서 확실한 장점을 가진다. 더 빠른 물리 계산과 덜 요구되는 새로운 할당 메모리가 근본적인 이유이다.
  8. 메쉬 콜라이더는 절대 해서는 안된다.
  9. 레이캐스트와 구 확인(sphere check)같은 충돌 감지 요구 사항을 최소화 하라. 그리고 각각의 확인(Check)로부터 많은 정보를 얻으려고 해라.
4 – AI 코드를 더 빠르게 만들자!
 
우리는 메인 닌자 영웅을 저지하고, 그와 싸우는 적에 대한 인공지능을 사용한다. 다음의 주제는 AI 성능 이슈에 관해서 다뤄져야 한다.
 
  1. A lot of physical queries are generated from AI logic like visibility checks. The AI update loop could be set to something much lower than the graphics update loop to reduce CPU load.
    시야 확인같이, AI 로직으로 부터 많은 물리적인 질의들이 생성된다. CPU Load를 줄이기위해서 AI 업데이트 루프는 그래픽 업데이트 루프보다 훨씬 더 낮게 설정할 수 있다.(?)
5 – 최고의 성능은 전혀 코드로부터 달성되지 않는다!

아무것도 발생하지 않을 때, 성능은 좋다. 이것은 우리가 지금 필요하지 않는 것에 벗어나게하는 기본적인 철학이었다. 우리의 게임은 횡스크롤 액션게임이고, 그래서 많은 동적 객체들은 씬에서 보이지 않을 때 꺼질 수 있다.
 
  1. 세부 계획된 커스텀 레벨을 사용하여, 멀어질 때 적 AI는 꺼진다.
  2. 움직이는 플래폼과 위험요소 그리고 그들의 물리적인 충돌체는 멀어졌을 때 꺼진다.
  3. 유니티에 내장된 "animation culling" 시스템은 랜더되지 않는 객체의 애니메이션을 끄는데 사용된다.
  4. 동일한 비활성화 메커니즘은 모든 단계의 파티클 시스템에 사용된다.
6 – 콜백! 빈 콜백은 어때?

유니티 콜백은 되도록 많이 감소되어야 했다.  심지어 텅 빈 콜백조차 성능에 영향을 줬다. 빈 콜백을 가져야 할 이유는 없다. 하지만 단지 많은 코드 재작성과 리팩토링을 하면서 코드에 빈 콜백들을 남겨두는 경우가 있다.

 
7 – 도움에 강력한 아티스트.
 
아티스트들은 언제나 좀 더 프레임 레이트를 올리기 위해 머리를 쥐어짜는 프로그래머를 매혹적으로 도와줄 수 있다.
 
  1. 유니티에서 게임 오브젝트에 대해 머티리얼을 공유하는 것과 그들을 정적(static)으로 만드는 것은 그들이 함께 배치되도록 하고, 그 결과 감소된 드로우 콜은 모바일 성능에 매우 중요하다.
  2. 텍스쳐 아틀라스는 특별히 UI 요소에 대해 많은 도움을 줬다.
  3. 적절한 압축을 한 정사각형(2의 제곱승)은 필수였다.
  4. 횡 스크롤은 우리 아티스트가 모든 먼 배경 메쉬를 제거할 수 있게 했고, 대신에 그들을 간단한 2D 평면으로 변환했다.
  5. 라이트 맵은 매우 소중하다.
  6. 우리의 아티스트는 여러 경로사이의 추가적인 정점을 제거했다.
  7. 적절한 텍스처 밉 레벨은 다른 해상도를 가진 장비에서 좋은 프레임 레이트를 가지는데 좋은 선택이었다.
  8. 매쉬를 합치는 것은 아티스트가 할 수 있는 또 다른 성능 개선 방법이다.
  9. 우리의 애니메이터들은 가능한 한 각 캐릭터 사이에 애니메이션을 공유할 수 있도록 노력했다.
  10. 파티클에 대한 많은 반복은 미적인 부분과 성능적인 부분의 균형을 찾는 것이 필요했다. emitter의 수를 줄이는 것과 투명도 요구사항을 줄이는 것은 주된 도전중 하나였다. 
8 – 이제, 메모리 사용은 감소 되어야 한다!

많은 메모리를 사용하는 것은 성능에 부정적인 영향을 준다. 우리의 경우에, 메모리 초과로 인해 아이팟에서 많은 충돌을 경험했는데, 이는 매우 중요한 문제 였다. 우리의 게임에서 가장 큰 메모리 고객은 바로 텍스쳐였다.
 
  1. 다른 텍스처 사이즈는 각 장비에 사용되어졌는데, 특별히 UI에 사용된 텍스처와 큰 배경이 바로 그것이었다. Shadow Blade는 유니버셜 빌드를 사용하지만, 시작하는 순간에 장비 사이즈와 해상도가 감지될 때, 각 애셋들은 로드 된다.
  2. 우리는 사용하지 않는 애셋은 메모리에 올라가지 않도록 해야 했다. 우리는 프로젝트에서, 오직 프리팹 인스턴스에 의해 참조되는 애셋과, 절대 인스턴스되지 않는 애셋들이 완전하게 메모리에 로드되었는지를 확인해야만 했다.
  3. 메쉬로부터 여분의 폴리곤을 제거하는 것은 도움이 되었다.
  4. 우리는 몇몇 애셋의 수명관리를 다시 해야했다. 예를 들어, 메인 메뉴 애셋, 마지막 레벨 에셋, 게임 음악에대한 로드/언로드 시간을 수정해야 했다.
  5. 각 레벨마다 그들의 동적 객체 요구사항에 잘들어맞고, 가장 적은 메모리 사용에 최적화된, 특별한 오브젝트 풀을 가져야 했다. 오브젝트 풀은 유연하고, 개발기간 동안 많은 객체들을 담을 수 있다. 그러나 게임 오브젝트 요구사항이 알려졌을 때, 그들은 구체적이어야 한다.
  6. 메모리에 압축된 사운드 파일을 유지하는 것이 필요했다.
게임 성능 향상은 길고 도전적인 여행이며, 우리는 이 여행의 작은 부분을 경험하는 즐거운 시간을 가졌다. 게임 개발 커뮤니티에 의해 공우된 방대한 지식과 유니티에서 제공되는 매우 훌륭한 프로파일링 툴은 shaodw Blade의 목표 성능에 도달을 하게 해주었다.

여기에 우리의 게임, Shadow Blade의 트레일러가 있다.
 

http://youtu.be/tgSXLVAwZJs

Game website: shadowblade.deadmage.com



출처: https://unityindepth.tistory.com/30 [UNITY IN DEPTH]

 

 

 

=======================

=======================

=======================

 

 

 

 

 

 

반응형

 

 

728x90

 

 

 

 

 

출처: https://vallista.tistory.com/entry/Unity-%EA%B2%8C%EC%9E%84-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B8%B0%EB%B2%95

 

필자의 컴퓨터는 사양이 굉장히 좋다고 자부할 수 있는 컴퓨터였다. 유니티 에디터가 잘못인지 소스코드 상에서 문제가 있는 것인지 보기 위해 탐색을 시작하게 되었고. 프로파일러를 통해 개선할 부분을 다수 개선하였다.

 

이 밑부터는 내가 경험한 것을 바탕으로 최적화를 한 방법에 대해 서술한다.

 - 프로젝트 진행시 참고했던 박민근 님의 유니티 최적화 테크닉을 참고하였다.

 - http://www.slideshare.net/agebreak/141206-42456391 [링크]

 - 아마 위 파워포인트를 정리한거라고 보면 될 듯 싶다. (그 외에 추가한 것들도 존재한다. 이것들은 오리지날..)

 

1. 소스코드 병목의 파악.

 

그 누구나 먼저 볼 곳이 아마도 자신의 소스코드에 대한 문제가 아닐까 싶다.

리소스가 문제라고 해서 리소스를 바꿧는데, 그럼에도 불구하고 문제가 있으면 그 것은 좀 아니다 싶지 않은가?

 

- DP Call

- 복잡한 연산

- 3D의 수많은 버텍스, 연산

- 픽셀, 오버 드로우

- 셰이더의 많은 연산

- 압축되지 않고 큰 텍스쳐들

- 고 해상도 프레임 버퍼

 

2. 스크립트 최적화

 

- 유니티 객체들은 멤버변수에 저장하여, 캐싱을 이용하는게 좋다.

 -> 예를 들자면, Find 라는 검색이 붙은 것들은 왠만하면 지속적으로 사용하지 않는게 좋다. 왜냐하면 Find 들은 프로젝트안의 모든 오브젝트를 순환하며 그 안의 클래스의 스트링을 비교하기 때문이다.

 

- Instantiate, Destroy 이 두 놈의 프리팹 생성/해제는 비용이 상당히 크다.

 -> 프리펩은 미리 생성해둔후 오브젝트를 풀에서 쓰도록 하자.

 

- Update 함수 보다는 Coroutine을 사용하는게 좋다.

 -> Update 함수를 쓰지말고, Coroutine을 무한 루틴시켜서 사용하도록 하자. 필자같은 경우에는 게임 관련은 전부 Coroutine에서 사용을 했으며 UI 같은 경우 메인 업데이트에서 돌렸다.

 

- 나눗셈 말고 곱셈의 사용.

 -> 나눗셈은 곱셈보다 연산속도가 월등히 느리다 100 / 10 이런식으로 사용하지말고 100 * 0.1 을 사용하라는 소리.

 

- 박싱과 언박싱은 부하가 큰 작업 이므로 많이 사용하지 말자.

 -> 박싱과 언 박싱이란. 

 --> http://vallista.tistory.com/entry/C-%EB%B0%95%EC%8B%B1%EA%B3%BC-%EC%96%B8%EB%B0%95%EC%8B%B1 [링크]

 

- Magnitude 보다 sqrMagnitude를 사용하여 비교해서 쓴다. (제곱근 계산 X)

 -> Unity 공식 문서에도 써져있다. 

 --> If you only need to compare magnitudes of some vectors, you can compare squared magnitudes of them using sqrMagnitude (computing squared magnitudes is faster).

 

- 삼각함수의 값은 상수로 저장하고 사용하는게 좋다.

 

- 문자열은 readonly 혹은 const 키워드를 사용하여, 가비지 컬렉션으로부터 벗어나도록 한다.

 

3. 가비지 컬렉터

 

- 우리가 쓰고있는 MonoBehavior는 메모리 관리에 GC가 자동호출되도록 설계되어 있는데, 이 GC가 프로그래머들한테는 좋을 수도 있고, 안좋을 수도 있다. GC가 실행되는 동안 유저들은 게임에서 갑자기 렉이 걸리며, 그렇지 않기위해 우리는 게임을 GC를 피해 만들어야 한다. (필자한테는 상당히 거리낌이 있다.) 이제 이 GC와 친하게 지내기위해 우리도 몇가지 방법을 써서 길들여야하는데, 그 방법에 대해 서술한다. 

 

- 무엇이든 동적 생성 및 해제는 부하가 굉장히 큰 작업이다. 그래서 위에 언급했다시피 오브젝트 풀 기법을 사용하여 메모리를 관리하도록 하자.

 

- 오브젝트가 해제되면 다음과정으로는 GC가 동작되서 렉이 걸릴수 밖에 없다. 즉 오브젝트를 만들어 둔 후 활성화 또는 비 활성화를 이용하여 사용하도록 하자.

 

- 문자열 병합은 StringBuilder의 Append를 사용하면 된다. 왜냐하면 string + string은 임시 문자열을 뱉기 때문에 가비지 컬렉션이 일어나는 환경을 제공한다.

 

- foreach 대신에 for를 이용하도록 한다. Foreach는 한번 돌리면 24byte의 쓰레기 메모리를 생성시키며 수많이 돌면 더 많은 메모리를 생성시키므로 for을 이용하도록 한다.

 

- 태그 비교에서는 CompareTag() 를 사용하도록 한다.

객체의 tag 프로퍼티를 호출하는 것은 추가 메모리를 할당하며 복사를 하게된다.

 

- 모든 비교문에서 .equals()를 사용하도록 하자. == 구문으로 사용하면 임시적인 메모리가 나오게 되며 가비지 컬렉션의 먹이를 준다.

 

- 데이터 타입은 Class 대신 Struct 를 사용하여 만들어 주면 메모리 관리가 된다. 구조체는 메모리 관리를 Stack에서 하므로 GC에 들어가지 않는다.

 

- 즉시 해제시에는 Dispose를 수동으로 호출하게 되면 즉시 클린업된다.

 

- 임시 객체를 만들어내는 API를 조심해야한다.

GetComponents<T>, Mesh, Vertices, Camera.allCameras, 이런것들..

 

- 객체의 변경 사항에 대해 캐싱한다. 객체의 이동과 변형에 대한 처리를 캐싱해서 매프레임당 한번만 처리

 

- 컴포넌트 참조를 캐싱한다.

GetComponent()는 한번만 호출되며, 객체를 캐싱해서 사용한다.

 

- 콜백함수 안쓰는 것들을 제거해야한다. Start(), Update(), OnDestroy() 같은 것들은 비어있어도 성능에 영향을 끼치므로 지워주도록 하자.

 

4. 텍스쳐

 

- 텍스쳐를 압축 할때는 권작 압축 텍스쳐를 사용하도록 하자.

 -> 아이폰 : PVRCT

 -> 안드로이드 (Tegra) : DXT

 -> 안드로이드 (Adreno) : ATC

 -> 안드로이드 (공통) : ETC1

 

- 텍스쳐 사이즈는 2의 제곱이어야 한다.

 -> POT(Power of Two)

 -> POT가 아닌경우 POT 텍스쳐로 자동 변환 로딩이 된다.

 -> 900x900은 실제로는 1024 x 1024로 된다.

 

- 텍스쳐 아틀라스를 활용

 -> 텍스쳐 아틀라스로 최대한 묶어 사용 (NGUI Atlas 같은 사용법)

 -> UI 만 아니라, 같은 재질의 오브젝트를 묶어 사용하게 된다.

 

- 압축된 텍스쳐와 밉맵을 사용한다. (대역폭 최적화)

 

- 32bit가 아닌 16bit 텍스쳐 사용도 상황에 맞게 고려한다.

 

 

 

[사진 1] 보통 이렇게 최적화 하면 된다. (안드로이드)

 

5. Mesh

 

- Import 시에 언제나 "Optimize Mesh" 옵션을 사용한다.

 -> 변환 전/ 변환 후 버텍스 캐쉬를 최적화 해준다.

 

- 언제나 optimize Mesh Data 옵션을 사용한다.

 -> Player Setting > Other Settings

 -> 사용하지 않는 버텍스 정보들을 줄여 준다. (tangents, Normal, Color, ETC...)

 

6. 오디오

 

- 모바일에서 스테레오는 의미 없음

 -> 모두 92kb, 모노로 인코딩

 

- 사운드 파일을 임포트하면 디폴트로 3D 사운드 설정

 -> 2D 사운드로 변경

 

- 압축 사운드 (mp3, ogg), 비압축 사운드 (wav) 구별

 -> 비압축 사운드 : 순간적인 효과음, 이펙트

 -> 압축 사운드 : 배경 음악

 

7. 폰트 리소스 최적화

 

Packed Font를 사용

 -> R,G,B,A 채널에 저장하는 기법으로 메모리 용량을 1/4로 절약하도록 하자.

 -> 단 Packed Font는 단점이 너무 많다. 일반적으로 글씨에 그림자도 못 넣을 뿐더러 알파도 적용이 안된다. 그리고 NGUI Atlas 적용도 안됨.

 

8. 리소스

 

- ResourceLoadAsync() 함수는 엄청 느리다.

 -> 게임 레벨 로드시에 사용했을 경우, 일반 함수에 비해 수십배나 느리다.

 

9. 컬링

 

- Frustum Culling (프러스텀 컬링)

 -> Layer 별로 컬링 거리 설정이 가능 (NGUI 의 경우 Panel 에서 Smooth Culling 도 먹일 수 있다.)

 -> 멀리 보이는 중요한 오브젝트는 거리를 멀게 설정하고 중요도가 낮은 풀이나 나무등은 컬링 거리를 짧게 설정하여 컬링한다.

 

- Occlusion Culling (오클루젼 컬링)

 -> Window->Occlusion Culling 메뉴에서 설정 가능

 -> Occlusion Culling 이란 카메라에서 보이는 각도의 오브젝트 들만 렌더링 하는 기법을 뜻한다.

 

- Combine (오브젝트 통합)

 -> 드로우콜은 오브젝트에 설정된 재질의 셰이더 패스당 하나씩 일어남.

 -> 렌더러에 사용된 재질의 수 만큼 드로우 콜이 발생함.

 ->> Combine (통합)

  ->>> 성질이 동일한 오브젝트들은 하나의 메쉬와 재질을 사용하도록 통합

  ->>> Script 패키지 - CombineChildren 컴포넌트 제공

   ->>>> 하위 오브젝트를 모두 하나로 통합

  ->>> 통합하는 경우 텍스쳐는 하나로 합쳐서, Texture Atlas를 사용해야함.

 

- Batch

 

- Static Batch

 -> Edit > Project Setting > Player 에서 설정한다.

 -> 움직이지 않는 오브젝트들은 static으로 설정해서, 배칭이 되게 함.

 -> Static으로 설정된 게임 오브젝트에서 동일한 재질을 사용 할 경우, 자동으로 통합된다.

 -> 통합되는 오브젝트를 모두 하나의 커다란 메쉬로 만들어서 따로 저장한다. (메모리 사용량 증가)

 

- Dynamic Batch 

 -> 움직이는 물체를 대상으로 동일한 재질을 사용하는 경우, 자동으로 통합

 -> 동적 배칭은 계산량이 많으므로, 정점이 900개 미만인 오브젝트만 대상이 된다.

 

10. 라이팅

 

- 라이트 맵 사용

  -> 고정된 라이트와 오브젝트의 경우(배경) 라이트 맵을 최대한 활용

  -> 아주 빠르게 실행됨 (Per-Pixel Light 보다 2~3배)

  -> 더 좋은 결과를 얻을 수 있는 GI와 Light Mapper를 사용할 수 있다.

 

- 라이트 렌더 모드

 -> 라이팅 별로 Render Mode : Important / Not Important 설정이 가능하다.

 -> 게임에서 중요한 동적 라이팅만 Important 로 설정 (Per-Pixel Light)

 -> 그렇지 않은 라이트들은 Not Important로 설정한다.

 

11. Overdraw

 

- 화면의 한 픽셀에 두 번 이상 그리게 되는 경우 (Fill rate)

 -> DP Call의 문제만큼 Overdraw 로 인한 프레임 저하도 중요한 문제

 -> 특히 2D 게임에서는 DP Call 보다 더욱 큰 문제가 된다.

 

- 기본적으로 앞에서 뒤로 그린다.

 -> Depth testing 으로 인해서 오버드로우를 방지한다.

 -> 하지만 알파 블렌딩이 있는 오브젝트의 경우에는 알파 소팅 문제가 발생한다.

 

- 반투명 오브젝트의 개수의 제한을 건다.

 -> 반투명 오브젝트는 뒤에서부터 앞으로 그려야 한다. -> Overdraw 증가

 -> 반투명 오브젝트의 지나친 사용에는 주의해야 한다.

 

- 유니티의 Render Mode를 통해서 overdraw 확인이 가능하다.

 

12. 유니티 셰이더

 

- 기본 셰이더는 모바일용 셰이더 사용

 -> 기본 셰이더를 사용할 경우, 모바일용 셰이더를 사용한다.

  ->> Mobile > VertexLit는 가장 빠른 셰이더

 

- 복잡한 수학 연산

 -> pow, exp, log, cos, sin, tan 같은 수학 함수들은 고비용

 -> 픽셀별 그런 연산을 하나 이상 사용하지 않는 것이 좋다.

 -> 텍스쳐 룩업 테이블을 만들어서 사용하는 방법도 좋다.

 -> 알파 테스트 연산 (discard)는 느리다.

 -> 기본적인 연산 보다는 최적화 시키고 간략화시킨 공식들을 찾아서 사용할 수 있다.

 

- 실수 연산

 -> float : 32bit - 버텍스 변환에 사용. 아주 느린 성능 (픽셸 셰이더에서 사용은 피함)

 -> Half : 16bit - 텍스쳐 uv에 적합. 대략 2배 빠름

 -> fixed : 10bit - 컬러, 라이트 계산과 같은 고성능 연산에 적합. 대략 4배 빠르다.

 

- 라이트 맵을 사용하자.

 -> 고정된 라이트와 오브젝트의 경우 (배경) 라이트 맵을 최대한 활용.

 -> 아주 빠르게 실행됨 (Per-Pixel Light 보다 2~3배)

 -> 더 좋은 결과를 얻을 수 있는 GI와 Light Mapper 사용가능

 

13. Fixed Update 주기 조절

 

- FixedUpdate()는 Update와 별도로 주기적으로 불리는 물리엔진 처리 업데이트

 

- 디폴트는 0.02초 1초에 50번

 

- TimeManager에서 수정

 

- 게임에따라 0.2초 정도로 수정해도 문제 없음. 

 

14. 물리 엔진 설정

 

- Static Object

 -> 움직이지 않는 배경 물체는, static으로 설정

 

- 충돌체의 이동

 -> 리지트 바디가 없는 고정 충돌체를 움직이면, CPU 부하 발생

 -> 이럴 경우 리지드 바디를 추가하고, IsKinematic 옵션 사용

 

- Maximum Allowed Timestep 조정

 -> 시스템에 부하가 걸려, 지정된 시간보다 오래 걸릴 경우, 물리 계산을 건너뛰는 설정

 

- Solver Iteration Count 조정

 -> 물리 관련 계산을 얼마나 정교하게 할지를 지정. (높을수록 정교)

 -> Edit > Project Setting > Physics

 

- Sleep 조절

 -> 리지드 바디의 속력이 설정된 값보다 작을 경우, 휴면 상태에 들어감

 -> Physics.Sleep() 함수를 이용하면, 강제 휴면 상태를 만들기 가능

 

15. 물리 엔진 스크립트

 

- 래그돌 사용 최소화

 -> 래그돌은 물리 시뮬레이션 루프의 영역이 아니기 때문에, 꼭 필요할 때만 활성화 함.

 

- 태그 대신 레이어

 -> 물리 처리에서 레이어가 훨씬 유리하다. 성능과 메모리에서 장점을 가진다.

 

- 메쉬 콜라이더는 절대 사용하지 않는다.

 

- 레이캐스트와 Sphere Check 같은 충돌 감지 요소를 최소화 한다.

 

16. Tilemap Collision Mesh

 

- 2D 게임에서 타일맵의 Collision Mesh를 최적화 하라.

 -> Tilemap을 디폴트로 사용해서, 각 타일별로 충돌 메쉬가 있는 경우, 물리 부하가 커짐.

 -> 연결된 Tilemap을 하나의 Collision Mesh로 물리 연산을 최적화 하라

 



출처: https://vallista.tistory.com/entry/Unity-게임-최적화-기법 [VallistA]

 

 

 

=======================

=======================

=======================

 

 

 

 

 

출처: https://ijemin.com/blog/%EC%9C%A0%EB%8B%88%ED%8B%B0-2d-%EA%B2%8C%EC%9E%84-%EB%B9%8C%EB%93%9C-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%8C%81/

유니티 (Unity) 2D 모바일 게임 최적화 팁 & 체크리스트

업데이트

  • 2018/02/10 가독성 개선
  • 2017/12/13 가독성 개선
  • 2017/6/26 레퍼런스 추가
    • 로깅/ GC, 필수 작업/ 퀄리티 세팅 추가
  • 2017/5/26 유니티 5.6에 맞추어 갱신

레퍼런스

유니티2D로 제작한 모바일 게임에서 60프레임을 안정적으로 지키고, 빌드 용량을 줄이기 위한 팁들입니다.

  • 빌드전 마지막으로 체크해야 될 옵션들 위주로 나열했습니다.
  • 최적화 방법 중 디자인 패턴과 관련된 부분은경우의 수가 너무 많아 제외했습니다.
  • 유니티 5.6 이상의 2D 게임, 안드로이드를 기준으로 했습니다.
    • 다른 버전과 플랫폼에도 일반적으로 적용할 수 있는 내용들도 포함했습니다.

최적화 하기전에!

코드 최적화는 추상적이고 간결하며 범용적인 코드를 만드는 것과 반대되는 경우가 많습니다.

최적화는 알아보기 쉬운 코드를 프로젝트에 집역적으로 만듭니다. 성능은 좋으나 알아보기 힘들고 재활용 할수 없는 형태로 만듭니다.

코드 뿐만 아니라 콘텐츠 자체에도 비슷한 논리가 적용됩니다.

그렇기에 최적화는 게임 개발의 후반부에 신경써야 합니다. 릴리즈 후에는 정말 미친듯이 최적화와 싸워야 합니다.

하지만 릴리즈 전에는 코드 구조와 콘텐츠가 허구한날 새롭게 바뀝니다.

게임 출시전에는 소요되는 시간 대부분을 이미 만든것에 대한 최적화 보다는 새로운 것에 집중하는게 좋을 수도 있습니다. 

😛

간단하게 할수 있는 것들

  1. Application.targetFrameRate 를 60으로 설정합니다.
    1. 모바일에서는 30이 기본으로 설정되어 있습니다.
  2. 이상이 없다면 vSyncCount 를 Quality 설정에서 켭니다. 몇몇 안드로이드 기기에서는 60fps 이상 랜더하여 기기가 과열되는 것을 막아줍니다.
  3. 비어있는 유니티 메세지 함수들은 지우세요. Update 등의 함수 내부가 비어있어도, 호출은 계속 됩니다.
  4. GameObject.Find() 같이 씬 전체를 검색하는 함수를 되도록 쓰지 마세요.
    1. Update/FixedUpdate 에서는 절대 안됩니다.
  5. 사용하지 않는 게임 오브젝트는 미리 비활성화 하세요.
  6. 오브젝트를 실시간으로 파괴하는 것보다, 비활성화 하는게 비용이 싸다는 것을 알아두세요.

글로벌 일루미네이션 제거

  • 만약 라이팅을 가지고 노는 일이 전혀 없는 게임이라면, Precomputed Realtime GI, Based GI, Fog 등을 전부 끄세요.

Quality Setting 트윅

  1. default 보다 높은 프로필은 모두 해제하세요. (모바일이라면 Simple)
  2. 사용하지 않는다면, Pixel Light Count 를 0으로 하세요.
  3. Texture Quality 를 가능한 낮게 잡으세요. Half Res 일때도 모바일에서는 눈치채기 힘든 경우가 많은데 75%나 메모리를 절약해줍니다.
  4. 텍스쳐에 확대 축소를 사용하지 않는다면, minmaps 를 해제하세요. 33% 메모리를 절약해줍니다.
  5. 비스듬한 각도에서 텍스쳐를 봐야하는 일이 없다면, Anisotropic Textures 를 해제합니다.
  6. Anti Aliasing 을 끄거나 2 passes 로 설정하세요. 그정도만 해도 지글거림을 해결해줍니다. LineRenderer 를 사용한다면 눈에 거슬릴순 있습니다.
  7. Soft Particle을 끕니다. 메쉬들을 적절히 섞어주는 역할을 하지만, 3D 상에서의 문제입니다.
  8. Shadow 를 끕니다.

여기서 가장 눈에 띄는 효과를 가진 것은, 텍스쳐 설정과 안티 얼라이언싱 입니다.

텍스쳐 퀄리티는 메모리에 직접적인 타격을 가하고 GPU 에 이미지를 보내는데 얼마나 시간을 먹을지 결정합니다.

안티얼라이언싱은 전체 화면에 적용되므로 해상도가 커짐에 따라 비용이 가파르게 증가합니다.

 


알아차리기 힘든 메모리 할당들

게임 엔진을 쓰면 C++ 로 직접 로우레벨을 통제할때 보다 메모리 관리에 대한 통제권이 적을 수 밖에 없습니다.

그래도 메모리가 언제 점유되는지 알고 쓰는게 최적화에 도움이 됩니다.

오브젝트 풀링 반드시 쓰기

게임 도중에 GC 가 발동된다면 게임이 실행 도중 뚝뚝 끊길수 있습니다.

GC 는 어플리케이션이 메모리를 추가로 요구할때 발동됩니다.

필요한 것들을 미리 게임 초반에 인스턴스화 하여 메모리를 점유하고, 게임 플레이 도중에는 더이상 요구하지 않는 다면 GC 문제를 겪지 않습니다.

실시간으로 많이 찍어내고 파괴하는 오브젝트는 오브젝트 풀링으로 관리하세요.

리듬게임의 노트 처럼 싱크가 중요한 경우라면 더더욱 써야 합니다.

가장 간단한 구현 방법:

  • 오브젝트를 파괴할때: 파괴대신 오브젝트를 끕니다.
  • 오브젝트를 생성할때: 꺼져있는 오브젝트를 가져와서 리셋하고 씁니다.

string 은 사용하는 만큼 생성됩니다.

 

string message = “Hello” + “World”; 같이 연결하는 부분을 주의하세요.

이 경우 실제로는 3개의 string 이 메모리에 할당합니다.

  • StringBuilder 나 String.Format() 을 사용하세요.

클래스와 구조체의 차이점을 고려하세요.

클래스는 Pass by Reference,  구조체는 Pass by Value 로 동작합니다.

값을 전달할때 class 는 레퍼런스가 할당되고, struct 는 복제본을 전달합니다.

struct

단순 데이터 컨테이터용으로 50개의 struct를 사용해봅시다.

struct는 메모리를 많이 사용합니다.

A->B->C 로 값을 전달하면, 총 150 개의 struct 가 생성되게 됩니다.

레퍼런스와 관련된 처리가 없으므로 더 빠를 수 있습니다.

구조체는 스택에 할당되고, 자신을 호출한 함수가 종료되면 즉시 사라집니다.
GC를 유발하지 않고 빠르게 동작합니다.

오히려 더 느려질 수도 있습니다.

단일 struct 오브젝트의 크기가 너무 커지거나, 너무 긴 리스트에 넣고 쓰면, 복사하는 시간이 길어집니다. 이때 class 형보다 처리 시간이 더 길어질 수 있습니다.

  • 변수가 2~3개 뿐인 단순한 데이터 컨테이너라면 struct 를 사용하는게 빠릅니다.
  • 일반적인 경우에는 class 를 쓰세요.

필자는 그리드형 게임에서 2차원 좌표용으로 struct를 사용합니다.

struct Coord { int x; int y;}

프로파일러를 사용합시다

  1. 유니티 프로파일러로 병목 지점을 확인하세요
  2. CPU Usage 란에서, GC Alloc 기준으로 정렬하면, 누가 할당을 언제 시도하는지 볼 수 있습니다. 할당을 최대한 줄이고 몰아서 하면, 게임이 부드러워집니다. physics.engine 할당 같은 것은 제어할 수 없지만, 그 정도는 괜찮죠.
  3. Memory Area 에서는, 현재 메모리 상태의 스냅샷을 찍을 수 있습니다. 어디에서 메모리 누수가 발생하는지 파악가능합니다.

System.GC.Collect() 로 GC를 원하는 시점에 발동시킬 수도 있습니다.

컷신이 끝난 직후나, 다음 플레이어의 조작이 활성화 되기 전이라던지, 그런 시점에서 쓸수 있습니다.

먼저 GC를 해둠으로서, 예상치 못한 시점에 발동될 확률을 낮추는 거죠.

 


로그와 메모리 낭비

의외로 성능을 먹는 Debug.Log

Debug.Log() 등의 호출을 가능한 지우는 것이 좋습니다.

  • 로그를 호출하는 함수는, 콘솔창이 없는 빌드된 게임에서도 동작합니다.
  • 의외로 가시적인 성능을 요구합니다.

프로파일링 할때,  Development Build 를 선택하고 프로파일링 해보면 체감이 됩니다. 로그 메세지가 스택 트레이스에 잡힐때 게임이 느려집니다.

  • 입력에 string 을 합쳐서 전달하면 메모리를 더욱더 많이 요구합니다.
  •  ToString 을 사용하면 더 심합니다.
  • 예 – Debug.Log( “Player ” + player + ” is doing something in game ” + game );

 

해결책

세가지 해결책이 있습니다.

Debug.Log 를 랩핑합니다.

노가다로 Debug.Log 를 전처리기로 랩핑하고 함수를 만듭니다.

public class DebugX {     public static void Log(string msg)     {         #if UNITY_EDITOR         Debug.Log(msg);         #endif     } }

 

하지만 이 방법은 string 입력을 받는 순간에 일어나는 메모리 할당을 막을 수는 없습니다.

Find and Replace

빌드할때만 잠시 모든 디버그 라인을 죄다 찾아가 지웁니다. 가장 무식하지만 효과적입니다.

하지만 다시 매번 디버그 코드를 되돌리는 수고를 해야 합니다.

C# 조건 속성을 사용합니다.

참고하기: https://msdn.microsoft.com/en-us/library/4xssyw96(v=vs.90).aspx

깔끔하고 우아한 방법입니다. 이 방법을 추천합니다.

using System; using System.Diagnostics;  public class DebugX {     [Conditional("UnityEditor")]     public static void Log(string msg)     {         Debug.Log(msg);     } }  // PlayerSettings > Scripting Define Symbols 에 ACTIVE_DEBUG 가 없다면 아래 코드 호출은 아예 처음부터 없었던 것으로 처리됩니다. DebugX.Log( "Hello world" );

클래스 내부에 Conditional 속성이 지정된 static void 함수를 추가합니다.

  1. 만약 Conditional 에 따라오는 심볼이 정의됬다면 static 함수는 존재합니다.
  2. 중요한것은 static 함수 뿐만이 아니라, 그것을 호출하는 부분 까지 모조리 빌드에서 빠져나가므로 매우 간편합니다.

조건 속성으로 묶인 디버그 함수를 사용한 코드들은 빌드시에 처음부터 존재하지도 않은 것이 됩니다.

다만 유니티 콘솔창에서 로그를 더블 클릭하면, 오류가 난 지점이 아닌 디버그를 랩핑한 코드로 이동하는 사소한 불편은 있습니다.

 


오디오 (사운드)

오디오 기본 설정

Force To Mono 사용

모바일에서 스트레오는 큰 의미를 가지지 않습니다. Force To Mono 를 사용합시다.

Preload Audio Data

Preload Audio Data는 기본 옵션입니다. 미리 오디오 데이터를 로드하므로, 씬 로드에 조금 부담을 줍니다.

이것을 사용안하면, 오디오를 재생하는 순간에 오디오를 로드 합니다. 그러면 게임 도중에 약간의 오버헤드가 생길 수 있습니다.

Load in Background

엄격하게 재생 타이밍을 맞춰야 하는 경우가 아니라면 체크해도 됩니다.

엄격하게 로드 타이밍을 지키지 않고, 느긋하게 백그라운드에서 로드 합니다.

씬이 시작되자 마자 재생을 시도할때, 조금의 지연시간이 지난 다음에야 재생되게 됩니다. 그래도 로딩 속도를 적게나마 향상시킬 수 있습니다.

오디오 매니저 (프로젝트 세팅에 있음)

스트레오 사운드가 필요없으면 오디오 매니저에서 스피커 모드를 Mono나 Raw로 합시다.

로드 타입

메모리 많이 먹고 빠릿함 vs 메모리 적게 먹고 오버헤드

  • Decompress On Load

미리 압축을 해제해서 메모리에 적재하기에, 플레이 하는 순간에 오버헤드가 생기지 않습니다.

적은 용량의 효과음에 사용합시다. 압축을 미리 메모리에 풀기 때문에 메모리를 많이 먹기 때문입니다.

  • Compress On Memory

메모리에 로드할때는 압축되어 있다가 재생시 압축을 해제합니다.

플레이하는 순간에 압축을 푸는 과정이 생기기에, 오버헤드가 생길 수 있습니다. 메모리에 그대로 적재하기는 힘든 큰 용량의 배경 음악에 사용합시다.

오디오 압축 포맷 (Compression Format)

오디오 압축은 기본적으로 아래의 사항에서 적절한 중간 지점을 선택하는 과정입니다.

높은 품질 + 많은 메모리 사용 + 적은 오버헤드 vs
낮은 품질 + 적은 메모리 사용 + 많은 오버헤드

얻는게 있으면 잃는게 있는법이죠.

압축률이 낮을수록 품질도 높고 오버헤드도 적으나, 빌드 용량과 메모리를 많이 먹습니다.

압축률이 높으면 빌드 용량과 메모리를 적게 먹으나, 압축을 푸는 과정에서 재생이 지연될 수 있습니다.

중요: 유니티에 임포트하기전에 미리 손실 압축을 거는 것은 의미 없습니다!

PCM

품질이 제일 높습니다. 무압축 입니다.

  • 압축을 푸는 과정이 없어서 오버헤드가 없고, 재생이 지연 되지도 않습니다.
  • 빌드 용량과 메모리를 제일 많이 차지합니다.
  • 즉시 재생해야 하는 매우 짧은 효과음에 씁시다.

ADPCM

PCM 과 Vorbis/MP3 중간의 퀄리티와 압축률을 가집니다.

  • PCM 과 비교해서: 높은 압축률, 낮은 품질, 높은 오버헤드
  • Vorbis/Mp3 와 비교해서: 낮은 압축률, 높은 품질, 낮은 오버헤드
  • 어디까지나 사실상 무압축인 PCM 보다 용량이 낮고,  품질이 나쁘다는 얘기입니다.
    일반적인 손실 압축에 비해 품질 유지가 뛰어나고 CPU 오버헤드도 크지 않죠.
  • PCM 보다는 지연과 오버헤드가 존재하지만, Vorbis/MP3 와 비교할시 지연과 오버헤드 없이 즉시 재생됩니다.
  • 총격 소리와 같이 무압축에 가까운 품질 까지는 필요없지만, 지연시간 없이 자주 반복 재생 되야 하는 경우 적절합니다.

Vorbis/MP3

가장 높은 압축률을 가집니다.

  • 당연히 품질이 제일 안좋습니다. 하지만 용량을 제일 덜 먹어요.
  • 로드 혹은 재생시 압축을 풀기 때문에 지연 재생될 수 있습니다.
  • 무압축으로 할시 용량을 감당할수 없고, 지연 재생 되어도 무방한 일반적인 배경음에 씁시다.

2D 게임 텍스쳐 관리하기

중요! TinyPNG 같은거 쓰지마세요.
유니티가 알아서 손실/무손실 압축합니다.

임포트 하기전에 손실 압축 하는 것은 의미가 없어요.

이미지(텍스쳐) 설정

Generate Mil Maps 해제

카메라와의 거리를 실시간으로 조정하는 게임이 아닌 이상 필요 없습니다.

필터링

도트형 레트로 스타일의 게임에 필터를 사용하면 안됩니다!

도트형 게임은 필터를 적용하지 않는 Point 로 적용 합시다. 외곽선을 부드럽게 꺽기 때문에 비주얼상 더 난잡해 보일 수 있어요. 이외에는 무난하게 Linear 면 적당.

아틀라스 aka 스프라이트 팩킹

반드시 사용해야 하는 이유

  • 이미지는 2의 배수의 정사각형으로 메모리에 적재됩니다.
    하나의 이미지에 대해서 많은 여백이 남게되죠.
    이 여백을 낭비하지 말고 다른 이미지로 채워서 메모리를 아낍니다.
  • 자주 사용되는 이미지 끼리 하나의 파일로 뭉쳐 한번에 호출하면, 호출을 개별로 여러번 할 필요가 없어서 성능이 절약됩니다.

결론: 메모리와 성능이 절약됩니다.

사용방법

  1. Packing Tag 를 팩킹할 이미지 끼리 같은 이름으로 지정해주면 유니티가 알아서 팩킹해줍니다.
  2. 그외 옵션은 이곳을 참고할수 있습니다. http://lhh3520.tistory.com/350
  3. Resources 폴더의 텍스쳐는 엔진 구조상의 문제로 유니티 기능만으로는 팩킹 불가능합니다.

이미지 압축 (Compression) – 안드로이드 기준

 

상식

압축 포맷이 빌드 타켓 플랫폼과 호환되는지 확인하세요!

호환되지 않는 포맷은 해당 플랫폼에서 무압축으로 변환되어 사용됩니다. 그리고 압축된 포맷을 무압축으로 해제하는 과정에 오버헤드가 발생합니다.

=> 처음부터 무압축으로 올렸을 때 보다 오버헤드가 더 생깁니다.

알파 값이 없는 이미지라면, 알파값을 지원하지 않는 압축 포맷을 사용합니다.

알파값을 쓰지도 않는데, 알파값에 데이터를 할당하므로 RGB 컬러 퀄리티는 상대적으로 손상될수 밖에 없습니다.

특정 플랫폼에 무관하게 압축을 설정할시

Override 를 사용하지 않고 일괄 적용하는 경우입니다.

  • 압축 옵션의 포맷은 알파값이 있다면 Truecolor 아니면 Compressed 옵션을 추천 합니다.
  • 알파값이 있는 이미지는, Compressed 옵션으로 아낀 용량에 비해, 품질 손상이 좀 많이 심합니다.
  • 반대로 알파값이 없는 경우, 큰 용량 절감 효과를 보여주고 상대적으로 알파값이 있는 경우보다 품질 손상도 적습니다.
    • 알파값이 없는데 알파값이 있는 이미지로서 인식됬다면 Advanced에서 RGBA가 아닌 RGB 압축 포맷으로 지정해줍시다.

ETC2 vs ETC1

안드로이드의 메이저한 압축 포맷은 ETC2 와 ETC1 입니다.

  • ETC2 는 더 나은 성능과 품질을 제공하지만 OpenGL ES3.0 에서 지원됩니다.
    • 즉 안드로이드 4.4 버전 이하에서는 제대로 동작하지 않는 경우가 많습니다.
  • ETC1 은 OpenGL ES2.0 에서 지원되나, 알파 (투명도) 를 지원하지 않습니다.
    • ETC1 을 설정할 경우 Spilt Alpha Channel 옵션을 통해 알파를 분리한 또다른 파일을 생성하여 알파를 지원하는 꼼수가 있습니다.
      • 다만 이 옵션은 버그인지, UGUI 에서 사용할시 비정상적으로 표현됩니다. (유니티 5.6.1 기준)
  • ECT1 지원폰도 ETC2 호환은 됩니다. 다만 무압축으로 변환되서  메모리가 터집니다.

무엇을 선택할 것인가 에 대한 딱 떨어지는 답이 없어요.

  • ETC2 가 대세긴 하지만 아직도 ETC1 만 지원하는 석기시대 안드로이드 폰의 점유율이 작지 않기 때문에, 타겟을 잘 고려해야 합니다.


ES2.0 의 징그러운 생존율.

 

안드로이드 고유의 파편화 때문에 조금 걸러 볼 필요는 있습니다. 예를 들어, 대시보드에서 ES2.0 이라고 나와도 ES3.0 을 제한적으로 지원하는 경우도 많으니까요.

이외의 압축 포맷

RGBA32

  • 무압축 입니다. 가장 높은 품질을 가집니다. 메모리를 많이 먹지만, 품질을 높게 유지해야 하는 경우 추천 합니다.
  • 특히 유저가 매우 자주 보게 될 UI 에서는 이 옵션을 사용합시다.

RGBA16

  • 알파 품질이 조악합니다.
  • 제한된 16비트에 RGB 는 물론 A 채널까지 포함하니 품질이 나쁠 수 밖에 없죠.

RGB16

  • 쓸만한 압축률과 좋은 컬러 품질을 보여 줍니다.
  • RGBA 와 달리 16비트를 온전히 RGB에 다 쓰기 때문이죠. 물론 알파 값은 지원 못합니다.

RGB24

  • RGBA32 와 동등한 품질을 보여줍니다. 단 알파값을 지원 못합니다.

기타 참고사항

압축시 2의 제곱수를 변길이로 가진 정사각형으로 이미지를 사용합니다.

여백을 활용하기 위해 정사각형에 가깝게 이미지를 제작하는 걸 고려할 수 있겠네요.

  • 예를 들면 900×1600의 해상도를 가진 물체라면 1024×1024와 2048×2048 중에 maxSize를 고민하게 됩니다. 전자는 화질이 손상되고 후자는 낭비니까요.
  • Sprite Packing 기능으로 묶어서 여백을 활용해도 되고요.

Compress Quality 를 통해 손실 압축 포맷들은 압축의 품질을 결정할 수 있습니다.

당연히 Best 에 가까울수록 동일 품질에 대한 압축률이 좋아지겠지만, 유니티 에디터 상에서 최초 압축 및 팩킹시 매우 많은 시간을 소비합니다.

  • 즉시 빌드를 해야할때, 긴 압축시간으로 피볼수도 있습니다.

UGUI 오버헤드 관리

하나의 캔버스에 모든 UI 요소를 넣는 것이 꼭 좋은것은 아닙니다.

캔버스에서 어떤 UI 요소의 스프라이트가 동적으로 변할때, 캔버스에 있는 전체 요소들도 동시에 갱신됩니다.

즉 하나가 변하면, 같은 캔버스에 다른 친구들도 같이 갱신됩니다.

한 UI 오브젝트가 변해도, 갱신 비용은 같은 캔버스 밑에 모든 UI 오브젝트 만큼 드는 거죠.

  • 따라서 서로 같은 타이밍에 내용이 (텍스트나 이미지가) 바뀌는 오브젝트 끼리 같은 캔버스로 묶으면 성능에 좋습니다.

그러나 개발자가 직관적으로 편집하기 좋게 하이라키에 배치하는 것도 고려해야 하니, 어디까지나 케이스 바이 케이스.

폰트

  1. 완성형 한글의 모든 글자를 메모리에 집어넣는 메모리 빌런이 되고 싶지 않다면 Dynamic을 사용합시다. 폰트를 임포트할때 기본값으로 Dynamic 이 지정됩니다.
  2. 만약 해당 폰트를 꼭 써야하는게 아니라면 Incl. Font Data를 해제해도 됩니다. 해당 폰트가 시스템에 존재하면 사용하고, 없으면 시스템 폰트를 가져다 쓸것입니다. 빌드 사이즈가 줄어듭니다.
    다만 의도한 디자인을 유지하기 힘들어서, 이 방법을 추천하진 않네요.

물리 설정

  1. 물리, 레이캐스팅을 사용하지 않는 형태의 게임이라면 충돌 레이어 설정을 해제해도 됩니다. 프로젝트 세팅 중 Physics 에서 Collision 레이어 항목 체크를 해제하면 되죠.
  2. FixedUpdate는 랜더와 상관없이 물리를 위해 독립적으로 일정 간격 호출되는 함수입니다.
    리기드 바디 등을 사용하지 않는 게임이라면, 물리 체크를 자주 할 필요가 없겠죠. 필요에 따라 호출 간격을 길게 해서 성능을 아낄수 있습니다.

기타 & 빌드시 세팅

  1. 절대 Keystore 파일 잃어 먹지 마세요. (미아앱 되기 싫으면 클라우드 저장소에 백업 좀 하셈)
  2. 유니티가 빌드시 알아서 사용되지 않는 애셋은 제외합니다.
    • 하지만 스크립트 파일은 사용되든 사용되지 않든, Editor 폴더에 있지 않는한, 모두 포함되서 빌드됩니다.
  3. Resource 폴더는 안쓸수 있다면 최대한 쓰지 맙시다.
    • 여기 들어가는 요소들은 실제 사용되든 사용되지 않든 빌드에 무조건 포함되고 메모리에 적재됩니다.
  4. 안드로이드 빌드시 세팅
    • x86(데스크탑 안드로이드) 를 사용하는 기기는 거의 없습니다.
    • FAT 은 x86과 ARM 을 모두 빌드하므로 용량이 커집니다.
      • FAT을 하지 말고 ARM 대상으로만 빌드합시다.
      • 혹은 x86 과 ARM 을 따로 빌드해서 구글 플레이 콘솔에 멀티 APK 로 올려도 됩니다.
    • Stripping Level의 경우 가능한 높은 수준을 추천합니다. 다만, micro nmscorlib이나 byte code 세팅이 프로그램을 크래쉬할 수 있으니 테스트는 해봅시다.
    • 32비트의 컬러 품질이 필요한지 비교후, 그렇지 않다면 체크 해제합니다.
    • 호환에 문제가 없다면 빌드 용량을 줄이기 위해 기본값인 .NET 2.0 Subset 을 사용합니다.
  5. 클라우드 테스트와, 안드로이드 실제 기기 테스트를 둘다 애용 합시다.
    • 쓸만한 무료 클라우드 테스트로는 구글 플레이 콘솔에서 지원하는 “출시전 보고서” 기능이 있습니다.
    • 즉시 사용 가능한 유료 클라우드 테스트는, 아마존 디바이스팜과 파이어베이스 테스트 랩이 있습니다.

계속 갱신중.

 

 

 

=======================

=======================

=======================

 

 

 

 

 

 

*기타링크관련

http://ndcreplay.nexon.com/NDC2015/sessions/NDC2015_0062.html

 

https://www.slideshare.net/agebreak/141206-42456391

 

https://meetup.toast.com/posts/155 //로딩최적화

 

https://ijemin.com/blog/%EC%9C%A0%EB%8B%88%ED%8B%B0-2d-%EA%B2%8C%EC%9E%84-%EB%B9%8C%EB%93%9C-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%8C%81/

 

 

 

 

=======================

=======================

=======================

 

 

 

 

 

반응형