메쉬 병합
메쉬병합 (Mesh Combine)으로 DrawCall을 줄인다.
에셋스토어에 mesh bake가 있으니 다운받아서 사용하자.
=> 병합할 mesh들을 쭉 드래그하거나 한곳에 몰아넣어 우클릭 후 mesh Bake를 누르면 Baked Mesh라는 새 오브젝트가 나온다. 기존의 오브젝트는 끄거나 삭제해주자.
- 베이킹을 할려면 메쉬가 있어야 하는데, 위 방법을 쓰면 메쉬필터가 공백으로 되어 있고 UV가 깨져서 베이킹이 깨지게 된다. 이를 해결할려면 ProBuilder를 다운받고 새로만든 Baked MEsh를 Probuilderize를 해주면 메쉬가 새로 만들어진다. 이후 오브젝트를 static으로 만든 후 오브젝트에 붙어 있는 스크립트 버튼이나 Probuilder 윈도우 창에서 UV generate를 해주면 된다. 이후 베이킹까지 하면 최적화가 아주 크게 된다.
코루틴 캐싱
코루틴을 사용할 때 가비지가 자주 생성된다. 코루틴을 자주 사용한다면 꼭 최적화를 해주자.
일단 StartCoroutine을 호출하는 순간 가비지가 생성된다, 이는 따로 방법이 없으므로 StartCoroutine을 적게 사용해야 할 것이다.
두 번째로, YieldInstruction(코루틴에서 yield구문에 사용되는 값)에서 new를 통해 인스턴스를 생성할 때 가비지가 만들어진다. 따라서 이를 캐싱해준다면 가비지가 생성되지 않을 것이다.
일단 리턴할 yield 값이 없을 때는 yield return0이 아니라 yield return null을 사용한다. (0을 리턴하면 0의 값은 변수가 박싱처리되기 때문에 가비지가 만들어짐)
while문에서 yield를 돌리는 등의 상황과 같이 new가 무수히 생성될 때, WaitForSeconds waitfor = new WaitforSeconds(1f);를 만들고 while에는 yield return waitfor을 넘겨주는 식으로 캐싱을 해준다.
더 자세한 내용은 여기를 참고하자.
https://ejonghyuck.github.io/blog/2016-12-12/unity-coroutine-optimization/
추가) 코루틴 팁
함수가 끝나고 실행되고 싶을 때, ex) first()가 끝나고 second()가 실행되길 원하는 경우.
IEnumerator delay(){
yield return StartCoroutine(first());
yield return StartCoroutine(second());
}
으로 하면 first가 실행되고 끝난 후 second가 실행됨.
시간초로 구별하고프면 그냥 WaitForSeconds(n)을 쓰면 됨.
배치와 드로우콜
라이트 맵 구글링 (정적 오브젝트들은 미리 라이트를 구워, 실시간 라이팅 계산을 줄인다. 이거만 잘 써도 굉장한 도움이 된다)
(레이어별로 빛을 받을 물체와 빛 계산을 하지 않을 물체를 나눈다)
+) 좀 더 조사할 것
resolution
UHD로 맞추면 60프레임을 못낸다. (2020년 표준)
게임은 HD해상도로 맞춘다 (1280 * 720)
모바일에선 큰 차이 없지만, 패드 등의 대화면으로 넘어가면 HD만으로는 조금 흐리게 나오므로 옵션으로 FHD까지 허용가능토록 한다.
참고 ) FHD => 1920 * 1080
FrameRate
pc에서는 딱히 생각하지 않지만, 모바일에서는 매우 중요하다. (배터리 + 발열)
60프레임으로 가면 발열이 꾸준히 증가한다. => 일정 온도에 도달하면 기기에서 cpu를 다운시킨다.
(rpg의 경우) 프레임을 30까지 낮추자. (45까지도 괜찮다. 발열이 천천히증가하며, 리미트까지 올라가지는 않는다.)
프레임 조절 2가지 방법
- setresolution => 스크립트 프레임조절
- playerSetting => fixdDPI => 화면스크린에 따라 조절
전체 해상도 720P 기준 비율 적용, Frame rate 동적 적용 (인트로에서는 30, 배틀에서는 60 과 같은)
=> 발열량이 정말 크게 감소한다.
활성화/비활성화 관련
ui가 나왔을 때 캐릭터가 안보인다면, 그냥 캐릭터를 비활성화 시키자. 그런 식으로 용량을 줄인다. (UI레이어 최적화, UI로직 최적화)
수 많은 오브젝트를 사용할 때, Object Pooling으로 구현한다. 유니티에서 Destroy + Instantiate를 사용하는 것 보다, SetActive로 구현하는 것이 훨씬 메모리 효율이 좋다.
Scene 이동도 역시 동일한데, 특정한 Scene이 아니면 한 scene에 레벨을 몰아 넣어 SetActive와 position이동으로 구현하는게 좋다고 한다. (모바일 기준, 오픈월드 제외)
디자인 패턴
싱글톤 패턴, MVC 패턴 등 적극 활용하자
UI 관련
- 이미지 파일들을 사용할 때, 그 크기 (Default의 Max Size)를 이미지의 최소크기 (n x n)형태에서 n보다 크면서 선택범위중 가장 작게 해주자.
- 캔버스는 분할한다. 유니티는 UI에 변화가 생길 때 캔버스 전체를 다시 그리기 때문에, 비효율적인 성능 낭비가 발생한다. 따라서 큰 틀 단위로 캔버스를 분할하도록 하자.
- 스프라이트 렌더러와 이미지의 동작 방식을 이해하고 사용처를 명확히 알아두자. sprite renderer의 경우는 자기 자신에 대한 메쉬를 독단적으로 렌더링 한다. 따라서 주로 애니메이션 등(ex. 2d 캐릭터의 움직)에서 사용된다. 반면 image의 경우는 canvas와 함께 렌더링된다. 주로 UI 위에서 상세한 position value를 요구할 때 사용된다.
함수 사용 관련
GetComponent, Find 등 많은 계산을 요구하는 함수는 가급적 사용을 지양한다.