Unity DOTS/Dots Custom Manual

UNITY DOTS - 유니티의 데이터 지향에 대해 간단하게 알아보자

개양반 2022. 7. 25.

그 동안 Unity는 C# 객체지향 환경에서 동작했다. 객체지향이란 프로그램 구현에 필요한 객체를 파악하고 각각의 객체들의 역할이 무엇인지를 정의하여 객체들 간의 상호작용을 통해 객체를 기준으로 프로그램을 설계하는 것을 말합니다. 객체지향은 프로그램을 작성하는 사람에게는 편리했지만 프로그램 입장에서는 불편한 설계였습니다.

객체지향은 게임오브젝트의 정보가 메모리의 여기저기에 중구난방으로 저장되어 CPU가 메모리에서 데이터를 읽어올때 비효율적이었습니다.

객체지향 설계는 메모리 여기저기에 저장된다

 

그래서 등장한 것이 데이터 지향 방식입니다. 데이터 지향 방식의 설계는 제가 알기로는 Sparse Set과 Archetype 2가지의 방식이 존재하는데 유니티에서는 Archetype 방식을 채택했습니다. 

유니티가 선택한 Archetype 데이터 지향방식은 Entity를 구성하는 Compoent를 Archetype으로 묶고 그것을 메모리의 Chunk에 저장하여 CPU가 Entity의 정보를 불러올때 빠르게 읽어올 수 있도록 설계합니다.

ComponentData조합을 Archetype을 만들어 메모리의 청크에 저장하므로 CPU가 메모리에서 정보를 읽어오기 편하다.

 

 


유니티에서는 어떤 방식으로 DOTS가 동작하는가?

유니티의 데이터 지향 방식은 ECS 또는 DOTS 라고 부르는데 Entity, Component, System 으로 이뤄져있습니다.

 Entity  여러 ComponentData를 담으며 프로그램이 식별하는 ID를 가집니다. 
 기존 객체지향 설계에서 다양한 Compoent를 담았던 GameObject와 비슷합니다.
 

Component  데이터 자체로 구성되는 데이터입니다. Component에는 로직이 포함되지 않습니다.
 



System  Component Data를 현재 상태에서 다음 상태로 변환하는 논리를 가지고 있습니다.
 예로 Translation과 RotationSpeed_ForEach라는 ComponetData 조합을 가진 Entity를 식별하여
 이동시키는 로직을 System을 통해 만들 수 있습니다.  

 시스템은 특정 Component를 가진 Entity를 식별해서 로직을 만들 수 있다.

 

AcheType 이 만들어지는 과정

Entity에 여럿 ComponentData를 연결하면 해당 조합을 하나의 Archetype으로 만듭니다. 

예로 오크전사를 만든다고 과정하겠습니다.

오크전사와 관련된 데이터(능력치, 경험치 등)을 Component로 만들어 Entity에 연결하면 프로그램은 오크전사와 관련된 데이터를 하나의 Archetype 으로 만듭니다. 

오크전사의 Entity에 변화를 줘서 ArchetypeB와 동일하게 구성한다면 오크전사 Entity는 ArchetpyeB로 이동합니다.

 

동일한 Component를 가진 Entity는 동일한 Archetype을 가집니다. 이는 상대적으로 숫자가 적은 ArcheType을 검색해서 원하는 다수의 Entites를 찾을 수 있다는 의미입니다.

ArcheType은 Memory Chunk에 저장됩니다.  만약 Entity에 Component를 추가하거나, 제거하면 다른 Chunk로 이동시킵니다.

 

System 으로 읽고, 쓰기 작성 (로직)

위에서 ComponentData를 작성해서 Archetype을 만들고 해당 Archetype이 Chunk에 저장했다면 이번에는 데이터를 처리하는 방법에 대해 알아볼 것이다.

유니티의 데이터 지향 설계는 System을 통해 로직을 작성한다.

System은 OnUpdate라는 반복문에서 쿼리를 통해 특정Achetpye을 가진 Entities를 검색하고 메인스레드에서 처리하거나 멀티스레드에서 병렬로 처리 할 수 있습니다. 

System에서 Query를 통해 검색한 Entities 를 읽고 쓰는 방법에는 SystemBase.Entities.ForEach, IJobEntity, IJobEntityBatch, Manual iteration 이 있습니다.

 SystemBase.Entities.ForEach Query를 통해 검색한 Entities를 엔터티별로 처리하는 가장 간단하고 효율적인 방법입니다.
 IJobEntity  위 SystemBase.Entities.ForEach와 비슷하게 검색한 Entities를 Entity별로 처리합니다. 차이점은 SystemBase.Entities.ForEach와 달리 IJobEntity는 여러 시스템에서 사용할 수 있다는 점입니다.
 IJobEntityBatch   청크를 이용해서 읽고, 쓰기를 합니다.
 동일한 컴포넌트 조합을 가진 entity는 하나의 Archetype를 공유하고 Archetype은 청크에
 저장됩니다.
 이런 청크를 이용해서 읽고 쓰기를 진행하면 빠르게 다수의 Entites의 데이터를 변경하거나
 읽을 수 있습니다.

 다만, SystemBase.Entities.ForEach와 IJobEntity 비해 코드가 복잡해지므로 필요한
 경우에만 사용해야 합니다.
 Manual iteration   이전 방법이 충분하지 않은 경우 엔터티 또는 청크를 수동으로 반복할 수 있습니다.
 예를 들어 IJobParallelFor와 같은 작업을 사용하여 처리하려는 엔터티 또는 엔터티 청크가 포함된 NativeArray를 반복할 수 있습니다.

(대충 이런게 있다 정도로만..)

 

마무리

오랜만에 글을 작성하는 것도 있고 큰 개념을 간단하게 정리하려니깐 오히러 내용이 어려워진 느낌도 있다. 

데이터 지향 설계가 무엇이고 유니티가 채택한 Archetype이 무엇인지와 유니티에서 Archetype이 만들어지고 Chunk에 저장된다는 점을 중점으로 파악하셨기를 바래본다.

댓글

💲 추천 글