Bir nişancı yazıyorum (1942, klasik 2D grafikler gibi) ve bileşen tabanlı bir yaklaşım kullanmak istiyorum. Şimdiye kadar aşağıdaki tasarımı düşündüm:
Her oyun öğesi (zeplin, mermi, güçlendirme, düşman) bir Varlıktır
Her Varlık, çalışma zamanında eklenebilen veya çıkarılabilen bir bileşenler kümesidir. Örnekler, Konum, Sprite, Sağlık, IA, Hasar, Sınırlayıcı Kutu vb.
Fikir, Zeplin, Mermi, Düşman, Güçlendirme oyun sınıfları DEĞİLDİR. Bir varlık yalnızca sahip olduğu bileşenler tarafından tanımlanır (ve bu süre içinde değişebilir). Böylece Airship oyuncusu Sprite, Position, Health ve Input bileşenleri ile başlar. Bir powerup Sprite, Position, BoundingBox'a sahiptir. Ve bunun gibi.
Ana döngü oyunu "fizik", yani bileşenlerin birbirleriyle nasıl etkileşime girdiğini yönetir:
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());
...
Bileşenler ana C ++ uygulamasında sabit kodlanmıştır. Varlıklar bir XML dosyasında tanımlanabilir (lua veya python dosyasındaki IA bölümü).
Ana döngü, varlıkları çok fazla önemsemez: sadece bileşenleri yönetir. Yazılım tasarımı şunları yapmalıdır:
Bir bileşen verildiğinde, ait olduğu varlığı alın
Bir varlık verildiğinde, "type" türündeki bileşeni alın
Tüm varlıklar için bir şeyler yapın
Tüm varlığın bileşeni için bir şeyler yapın (örneğin: seri hale getirme)
Aşağıdakileri düşünüyordum:
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;
Bu tasarım ile # 1, # 2, # 3 (boost :: fusion :: map algoritmaları sayesinde) ve # 4 elde edebilirim. Ayrıca her şey O (1) (Tamam, tam olarak değil, ama yine de çok hızlı).
Daha "ortak" bir yaklaşım da vardır:
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
...
};
Başka bir yaklaşım Entity sınıfından kurtulmaktır: her bir Bileşen türü kendi listesinde yaşar. Yani bir Sprite listesi, bir Sağlık listesi, Hasar listesi vb. Varlıkları biliyorum çünkü aynı mantık varlığına aitler. Bu daha basit, ancak daha yavaş: IA bileşenlerinin temel olarak diğer tüm varlık bileşenlerine erişmesi gerekir ve bu da her adımda birbirlerinin bileşenlerinin aranmasını gerektirir.
Hangi yaklaşımın daha iyi olduğunu düşünüyorsunuz? boost :: fusion haritası bu şekilde kullanılmaya uygun mu?