Saya sedang menulis penembak (seperti 1942, grafis 2D klasik) dan saya ingin menggunakan pendekatan berbasis komponen. Sejauh ini saya memikirkan desain berikut:
Setiap elemen game (airship, proyektil, powerup, musuh) adalah Entitas
Setiap Entitas adalah seperangkat komponen yang dapat ditambahkan atau dihapus pada saat dijalankan. Contohnya adalah Posisi, Sprite, Kesehatan, IA, Kerusakan, BoundingBox dll.
Idenya adalah bahwa Airship, Proyektil, Musuh, Powerup BUKAN kelas permainan. Suatu entitas hanya ditentukan oleh komponen yang dimilikinya (dan yang dapat berubah selama waktu). Jadi pemain Airship dimulai dengan Sprite, Posisi, Kesehatan dan Komponen input. Powerup memiliki Sprite, Position, BoundingBox. Dan seterusnya.
Loop utama mengelola permainan "fisika", yaitu bagaimana komponen saling berinteraksi:
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());
...
Komponen di-hardcode dalam aplikasi C ++ utama. Entitas dapat didefinisikan dalam file XML (bagian IA dalam file lua atau python).
Loop utama tidak terlalu peduli tentang entitas: itu hanya mengelola komponen. Desain perangkat lunak harus memungkinkan untuk:
Diberikan komponen, dapatkan entitas miliknya
Diberikan entitas, dapatkan komponen tipe "tipe"
Untuk semua entitas, lakukan sesuatu
Untuk semua komponen entitas, lakukan sesuatu (misalnya: cerita bersambung)
Saya sedang memikirkan hal-hal berikut:
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;
Dengan desain ini saya bisa mendapatkan # 1, # 2, # 3 (terima kasih untuk meningkatkan :: fusion :: algoritma peta) dan # 4. Semuanya juga O (1) (ok, tidak persis, tapi masih sangat cepat).
Ada juga pendekatan yang lebih "umum":
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
...
};
Pendekatan lain adalah untuk menyingkirkan kelas Entity: setiap tipe Komponen tinggal di daftar sendiri. Jadi ada daftar Sprite, daftar Kesehatan, daftar Kerusakan dll. Saya tahu mereka milik entitas logika yang sama karena id entitas. Ini lebih sederhana, tetapi lebih lambat: komponen-komponen IA pada dasarnya memerlukan akses ke semua komponen entitas lainnya dan yang akan membutuhkan pencarian masing-masing daftar komponen lainnya pada setiap langkah.
Menurut Anda pendekatan mana yang lebih baik? apakah boost :: fusion map cocok untuk digunakan dengan cara itu?