1942, 클래식 2D 그래픽과 같은 슈팅 게임을 작성 중이며 구성 요소 기반 접근 방식을 사용하고 싶습니다. 지금까지 나는 다음과 같은 디자인에 대해 생각했습니다.
각 게임 요소 (비행선, 발사체, 파워 업, 적)는 개체입니다
각 엔터티는 런타임에 추가하거나 제거 할 수있는 구성 요소 집합입니다. 예를 들면 Position, Sprite, Health, IA, Damage, BoundingBox 등이 있습니다.
비행선, 발사체, 적, 파워 업은 게임 클래스가 아닙니다. 엔터티는 소유 한 구성 요소에 의해서만 정의되며 시간에 따라 변경 될 수 있습니다. 비행선 플레이어는 스프라이트, 위치, 상태 및 입력 구성 요소로 시작합니다. 파워 업에는 Sprite, Position, BoundingBox가 있습니다. 등등.
메인 루프는 게임 "물리", 즉 구성 요소가 서로 상호 작용하는 방식을 관리합니다.
foreach(entity (let it be entity1) with a Damage component)
foreach(entity (let it be entity2) with a Health component)
if(the entity1.BoundingBox collides with entity2.BoundingBox)
{
entity2.Health.decrease(entity1.Damage.amount());
}
foreach(entity with a IA component)
entity.IA.update();
foreach(entity with a Sprite component)
draw(entity.Sprite.surface());
...
구성 요소는 기본 C ++ 응용 프로그램에서 하드 코딩됩니다. 엔티티는 XML 파일 (루아 또는 파이썬 파일의 IA 부분)에서 정의 할 수 있습니다.
메인 루프는 엔터티에 대해 큰 관심을 갖지 않으며 구성 요소 만 관리합니다. 소프트웨어 설계는 다음을 허용해야합니다.
컴포넌트가 주어지면, 속한 엔티티를 얻는다
엔터티가 주어지면 "type"유형의 구성 요소를 가져옵니다.
모든 엔티티에 대해 무언가를 수행하십시오.
모든 엔티티의 구성 요소에 대해 무언가를 수행하십시오 (예 : 직렬화).
나는 다음에 대해 생각하고 있었다.
class Entity;
class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
class Sprite : public Component {...};
class Position : public Component {...};
class IA : public Component {... virtual void update() = 0; };
// I don't remember exactly the boost::fusion map syntax right now, sorry.
class Entity
{
int id; // entity id
boost::fusion::map< pair<Sprite, Sprite*>, pair<Position, Position*> > components;
template <class C> bool has_component() { return components.at<C>() != 0; }
template <class C> C* get_component() { return components.at<C>(); }
template <class C> void add_component(C* c) { components.at<C>() = c; }
template <class C> void remove_component(C* c) { components.at<C>() = 0; }
void serialize(filestream, op) { /* Serialize all componets*/ }
...
};
std::list<Entity*> entity_list;
이 디자인을 사용하면 # 1, # 2, # 3 (부스트 :: fusion :: map 알고리즘) 및 # 4를 얻을 수 있습니다. 또한 모든 것이 O (1)입니다 (정확하지는 않지만 여전히 빠릅니다).
보다 일반적인 접근 방식도 있습니다.
class Entity;
class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
class Sprite : public Component { static const int type_id = 0; };
class Position : public Component { static const int type_id = 1; };
class Entity
{
int id; // entity id
std::vector<Component*> components;
bool has_component() { return components[i] != 0; }
template <class C> C* get_component() { return dynamic_cast<C> components[C::id](); } // It's actually quite safe
...
};
또 다른 접근법은 Entity 클래스를 제거하는 것입니다. 각 구성 요소 유형은 자체 목록에 있습니다. Sprite 목록, Health 목록, Damage 목록 등이 있습니다. 엔터티 ID로 인해 동일한 로직 엔터티에 속한다는 것을 알고 있습니다. IA 구성 요소는 기본적으로 다른 모든 엔터티의 구성 요소에 액세스해야하며 각 단계에서 서로 다른 구성 요소 목록을 검색해야합니다.
어떤 접근법이 더 낫다고 생각합니까? boost :: fusion map은 그런 식으로 사용하기에 적합합니까?