기본 시스템 구성 요소 엔터티 접근 방식 부터 시작합니다 .
구성 요소 유형에 대한 정보만으로 어셈블리 ( 이 기사 에서 파생 된 용어)를 작성해 봅시다 . 구성 요소를 하나씩 요소에 하나씩 추가 / 제거하는 것처럼 런타임에 동적으로 수행되지만 형식 정보에 관한 것이므로 더 정확하게 이름을 지정합시다.
그런 다음 모든 어셈블리 를 지정하는 엔터티 를 구성 합니다. 엔터티를 만든 후에는 해당 어셈블리를 변경할 수 없습니다. 즉, 직접 수정할 수는 없지만 기존 엔터티의 서명을 로컬 복사본 (콘텐츠와 함께)으로 가져 와서 적절히 변경하고 새 엔터티를 만들 수 있습니다. 그것의.
이제 핵심 개념 : 엔터티가 생성 될 때마다 어셈블리 버킷 이라는 객체에 할당됩니다. 이는 동일한 서명 의 모든 엔터티가 동일한 컨테이너에 있음을 의미합니다 (예 : std :: vector).
이제 시스템 은 관심있는 모든 버킷을 반복하고 작업을 수행합니다.
이 방법에는 몇 가지 장점이 있습니다.
- 구성 요소는 몇 개의 (정확하게 : 버킷 수) 연속적인 메모리 청크에 저장됩니다. 이는 메모리 친 화성을 향상시키고 전체 게임 상태를 덤프하는 것이 더 쉽습니다.
- 시스템은 선형 방식으로 구성 요소를 처리하므로 캐시 일관성이 향상됩니다-사전 및 임의 메모리 점프
- 새로운 엔티티를 생성하는 것은 어셈블리를 버킷에 매핑하고 필요한 컴포넌트를 벡터로 푸시하는 것만 큼 쉽습니다.
- 순서는 중요하지 않기 때문에 엔티티를 삭제하는 것은 std :: move를 호출하여 마지막 요소를 삭제 된 요소로 바꾸는 것만 큼 쉽습니다.
완전히 다른 서명을 가진 엔터티가 많으면 캐시 일관성의 이점이 줄어들지 만 대부분의 응용 프로그램에서 발생할 것이라고 생각하지 않습니다.
벡터가 재 할당되면 포인터 무효화에 문제가 있습니다. 이는 다음과 같은 구조를 도입하여 해결할 수 있습니다.
struct assemblage_bucket {
struct entity_watcher {
assemblage_bucket* owner;
entity_id real_index_in_vector;
};
std::unordered_map<entity_id, std::vector<entity_watcher*>> subscribers;
//...
};
따라서 게임 로직에서 어떤 이유로 든 새로 생성 된 엔티티를 추적하고, 버킷 내에서 entity_watcher를 등록하고 , 엔티티를 제거하는 동안 std :: move'd를 지정하면 감시자를 찾아 업데이트합니다. 그들의 real_index_in_vector
새로운 가치로. 대부분의 경우 이것은 모든 엔티티 삭제에 대해 하나의 사전 검색 만 수행합니다.
이 접근법에 더 이상 단점이 있습니까?
꽤 명백하지만 솔루션이 언급되지 않은 이유는 무엇입니까?
편집 : 의견이 충분하지 않기 때문에 "답변에 답하기"위해 질문을 편집하고 있습니다.
정적 클래스 구성에서 벗어나기 위해 특별히 작성된 플러그 가능 구성 요소의 동적 특성을 잃게됩니다.
난 아니야 어쩌면 나는 충분히 명확하게 설명하지 않았을 수도 있습니다.
auto signature = world.get_signature(entity_id); // this would just return entity_id.bucket_owner->bucket_signature or so
signature.add(foo_component);
signature.remove(bar_component);
world.delete_entity(entity_id); // entity_id would hold information about its bucket owner
world.create_entity(signature); // automatically assigns new entity to an existing or a new bucket
기존 엔터티의 서명을 가져 와서 수정하고 새 엔터티로 다시 업로드하는 것만 큼 간단합니다. 플러그, 동적 특성 ? 물론이야. 여기서는 하나의 "조립"과 하나의 "버킷"클래스 만 있음 을 강조하고 싶습니다 . 버킷은 데이터 중심이며 런타임시 최적의 수량으로 생성됩니다.
유효한 대상을 포함 할 수있는 모든 버킷을 거쳐야합니다. 외부 데이터 구조가 없으면 충돌 감지도 마찬가지로 어려울 수 있습니다.
이것이 우리가 앞서 언급 한 외부 데이터 구조를 갖는 이유 입니다. 해결 방법은 다음 버킷으로 이동할 시점을 감지하는 반복자를 System 클래스에 도입하는 것만 큼 간단합니다. 점프는 논리 순수 투명하다.