DOTS
15편
하나를 위해 ECS 를 지불할 이유는 없다
적 1만은 ECS 이고 플레이어는 GameObject 입니다. 무엇을 옮기고 무엇을 두는지는 개수와 이터레이션 빈도가 정합니다.
5,330만 삼각형이 118 드로우 콜로 접힌다
최적화가 잘 되면 프로파일에서 시뮬레이션이 사라집니다. 그리고 병목이 렌더로 옮겨갑니다. Entities Graphics 와 GPU Resident Drawer 는 같은 뿌리입니다.
에러 0개로 틀리는 법
이 프로젝트에서 가장 오래 잡은 버그들은 전부 컴파일을 통과했습니다. 하나는 런타임 데모까지 통과했습니다.
결과가 같은 두 API 사이에서 33퍼센트가 갈린다
1만 마리가 동시에 죽을 때 DeadTag 를 AddComponent 로 붙이느냐 비트로 켜느냐. 코드는 몇 줄 차이인데 프레임 스파이크가 31.71ms 와 42.26ms 로 갈렸습니다.
브릿지는 표면적이 좁을수록 좋다
플레이어 체력을 ECS 에 넘길 뻔했는데 결국 bool 한 비트로 끝났습니다. 필드를 추가하는 기준은 반대편이 그걸 읽느냐입니다.
수신자가 살아 있으면 버퍼, 사라지면 큐
데미지는 적의 버퍼에 담고 사망은 월드 전역 큐에 담습니다. 주인공이 파괴되기 때문입니다. 그리고 값을 담을지 참조를 담을지도 같은 이유로 갈립니다.
엔티티는 물건만이 아니다
공격을 요청 엔티티로 모델링해 뒀더니, 나중에 광역기를 붙일 때 숫자 두 개만 바꾸면 됐습니다. 시스템 코드는 한 줄도 안 고쳤습니다.
방향이 다르면 통로의 모양도 다르다
GameObject 세계와 ECS 세계를 잇는 통로 세 개를 뚫었는데 셋의 모양이 전부 달랐습니다. 방향과 건수가 모양을 정합니다.
1만 마리 이웃 탐색을 6.26ms 로, 그리고 기울기가 0 이 된다는 것
셀 크기를 질의 반경에 맞추면 엔티티당 검사 수가 상수가 됩니다. 19.60ms 가 6.26ms 가 됐는데, 진짜 결과는 그 배수가 아닙니다.
잡의 시그니처가 곧 쿼리다
IJobEntity 는 Execute 파라미터를 보고 쿼리를 만듭니다. 그리고 잡의 필드와 파라미터는 각각 1만 개가 공유하는 값과 1만 번 반복되는 대상입니다.
캡슐 하나가 엔티티가 되기까지
SubScene 에 캡슐을 놓으면 커스텀 코드 없이 엔티티가 되어 GPU 인스턴싱으로 그려집니다. 그리고 Baker 는 자기 엔티티만 건드릴 수 있습니다.
C# 을 쓰는데 결과는 네이티브다
Burst 는 IL 을 LLVM 으로 넘겨 네이티브 코드를 뽑습니다. 대신 쓸 수 있는 C# 이 부분집합으로 줄고, 그 제약이 곧 최적화의 조건입니다.
스레드를 직접 만들지 않고 일을 맡긴다
잡 시스템은 워커 스레드를 미리 띄워 두고 일감을 나눠 줍니다. 스레드를 안 만드는 것이 핵심이고, 안전 장치가 나머지 절반입니다.
엔티티는 번호표이고 데이터는 청크에 줄지어 있다
ECS 의 세 조각이 각각 무엇인지, 아키타입과 청크가 왜 필요한지, 쿼리가 엔티티를 하나씩 뒤지지 않는 이유까지.
1만 개를 한 번 도는 것과 1개를 1만 번 부르는 것
적 1만 마리를 움직이는 코드가 느린 이유는 계산이 무거워서가 아니라 데이터가 흩어져 있어서입니다. ECS 는 그걸 붙여 놓는 것에서 시작합니다.