2021.09.26 - [이론/설계] - ECS (Entity Component System)
2023.02.04 - [유니티/DOTS] - DOTS 1.0 - 2 (생성, MonoBehavour 연계)
데이터구조
월드(World)
- 엔티티의 콜렉션임. 엔티티의 id는 한 월드 안에서만 유니크함.
- 월드마다 EntitiyManager를 가짐.
- 월드마다 System들의 Set을 가진다.
structural change
- 메모리 청크를 재조직하는걸 structural change라고 함.
- 이거 비싸다 그리고 메인스레드에서만 됨.
다음 경우에 structural change 임
- 엔티티를 지우거나 만든다 . (추가할때는 청크 새로 만들기/ 지울때 : 청크사이에 갭이 있으면 마지막걸로 채움(list의 remove가 아니다) . 청크가 비어버리면 할당해제.
- 컴포넌트를 더하거나 뺌 (엔티티 지우고 다시만드는거랑 똑같겠지 )
- Shared 컴포넌트의 값을 설정함. (같은 Shared 컴포넌트 쓰는 애들끼리 청크로 묶으니까 당연)
Sync Point
- 싱크포인트는 일단 스케쥴된 모든 잡들이 완료될때까지 기다리는 지점을 의미.
- 당연히 성능에 좋지않음.
- Structural change가 주 원인이다.
- structural change가 일어나면 기존 레퍼런스 같은것들도 다 훼손됨. (Dynamic buffer)
- 그래서 이걸 피하려면 entity command buffer를 써야함. 이건 structural change를 일으키는 애들을 모았다가 한번에 처리하는 용도
컴포넌트 종류
Unmanaged
- IComponentData를 상속한 구조체가 해당된다. 지정할 수 있는 데이터 타입에 제한이 있다.
Managed
- IComponentData를 상속한 클래스가 해당된다. 저장할 수 있는 데이터 타입에 제한이 없다.
- 대신 Jobs안에서 사용할 수 없고 버스트 컴파일도 안 됨. GC에 의해 영향받음. 직렬화를 위해 인자 없는 생성자가 필요하다.
- 외부 리소스에 참조하는 경우라면 ICloneable이랑 IDisposable을 구현해주는게 좋다. (entity가 복사되는 경우를 위해서)
- 이 컴포넌트는 청크에 저장되지 않음. 대신에 World안에있는 거대한 array안에 다 보관함.
- 그러면 chunk는 이런 array의 indices를 저장함.
- 당연히 index 룩업을 하기 때문에 umanaged에 비해서 느림. (jobs를 못 쓰는거나 버스트 컴파일 사용 못함으로 인한 성능 저하는 덤)
Shared
- 말그대로 엔티티들이 공유하는 컴포넌트. 최적화를 위해 쓴다.
- 다른 공유 컴포넌트를 쓰는 엔티티끼리는 같은 청크에 있지 않는다. 즉, 어떤 청크가 있다면 얘네는 다 같은 공유 컴포넌트를 쓴다.
- managed unmanaged 둘 다 가능 managed면 그냥 managed랑 똑같은 장단점 존재
Tag
- 말 그대로 엔티티에 태그를 부여하는 용도.
- 그냥 아무것도 없는 IComponent 상속 struct만들면 됨 . 얘네는 청크에 저장도 안 됨.
Cleanup
- 이것도 일종의 태그라고 볼 수 있음.
- ICleanupComponentData를 상속한 빈 struct를 만들면 된다.
- 이게 붙어있는 애들의 엔티티를 삭제하려고 하면 엔티티를 지우는 대신 일단 non-cleanup 컴포넌트를 다 지운다. 그 다음cleanup컴포넌트까지 지워줘야 엔티티가 지워진다.
- 삭제될 때 cleanup이 필요한 애들한테 붙여주면 유용하다.
DynamicBuffer
- 크기 변경이 가능한 배열처럼 동작하는 컴포넌트.
- Length랑 Capacity가 존재한다.
- Capacity초기값은 보통 128Byte가 되도록 맞춰짐.
- 처음에는 다이나믹 버퍼에 있는 데이터들을 청크에 직접 저장한다. 그러다가 사이즈가 커지면 데이터를 복사해서 청크 밖에 저장한다. 한 번 밖으로 나온 데이터는 다시 청크 안으로 안 들어간다.
- 따라서 이런 경우가 생기면 청크에 낭비되는 공간이 생긴다.
- 그러니까 웬만하면 Capacity를 초과하는 일이 없는 게 좋다.
- 만약 처음부터 Capacity가 클 것으로 예상되면 InternalBufferCapcity 어트리뷰트를 0으로 설정해서 첨부터 청크 밖에 저장하게 하는 게 좋다.
Chunk
- 청크컴포넌트는 chuck마다 하나의 value를 저장한다. 목적은? 최적화
- 공유 컴포넌트와 비슷하다고 할 수 있는데 조금 다르다.
- 청크컴포넌트는 청크에 속함. (엔티티가 아니라)
- 청크 컴포넌트 세팅하는건 structural change가 아님
- unamanged만 됨
- 엔티티의 아키타입이 바뀌거나 공유 컴포넌트가 바뀌면 다른 청크로 엔티티를 옮기는데 이때도 청크 컴포넌트 값은 안 바뀐다.
- 공유 컴포넌트와 다르게 유니티가 복제하지 않음.
Enableable
- 예네는 그냥 enable/disable만 스위치해주는 컴포넌트. 추가/제거보다 낫다. 왜냐? structural change가 없으니까~
- IEnableableComponent를 구현하기만 하면 된당~ 이거 추가한다고 아키타입이 바뀌지는 않는다. 이말인 즉슨 entity command buffer를 쓸 필요도 없단 말.
- 그치만 race를 방지하기위 해 메인 스레드에서만 enable값을 바꾼다.
Singleton Component
- 월드당 하나만 있는 컴포넌트이다.
- 당연히 여러 스레드에서 동시 접근하는 것을 주의해야 한다.
'게임엔진 > DOTS' 카테고리의 다른 글
Unity.Physics에 가속도와 힘 구현해보기 - 2 (1) | 2023.03.12 |
---|---|
ECS - RequireForUpdate / Dependency (0) | 2023.03.05 |
Unity.Physics에 가속도와 힘 구현해보기 - 1 (1) | 2023.02.18 |
Physics 1.0 써보기 (1) | 2023.02.10 |
DOTS 1.0 - 2 (생성, MonoBehavour 연계) (0) | 2023.02.04 |