ฉันกำลังเขียนปืน (เช่น 1942, กราฟิก 2D แบบคลาสสิก) และฉันต้องการใช้การอนุมัติตามส่วนประกอบ จนถึงตอนนี้ฉันคิดถึงการออกแบบต่อไปนี้:
องค์ประกอบแต่ละเกม (เรือเหาะขีปนาวุธเพิ่มพลังศัตรู) เป็นเอนทิตี
แต่ละหน่วยงานคือชุดของส่วนประกอบที่สามารถเพิ่มหรือลบได้ในเวลาทำงาน ตัวอย่างเช่นตำแหน่ง, Sprite, Health, IA, Damage, BoundingBox เป็นต้น
แนวคิดคือ Airship, Projectile, Enemy, Powerup ไม่ใช่คลาสเกม เอนทิตีถูกกำหนดโดยส่วนประกอบที่เป็นเจ้าของเท่านั้น (และสามารถเปลี่ยนแปลงได้ในช่วงเวลา) ดังนั้นผู้เล่นเรือเหาะเริ่มต้นด้วย Sprite, ตำแหน่ง, สุขภาพและส่วนประกอบอินพุต Powerup มี 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 ในไฟล์ lua หรือ python)
ลูปหลักไม่ได้สนใจอะไรมากมายเกี่ยวกับเอนทิตี: มันจัดการเฉพาะส่วนประกอบ การออกแบบซอฟต์แวร์ควรอนุญาตให้:
ให้ส่วนประกอบรับเอนทิตี้ของมัน
ให้เอนทิตี้รับส่วนประกอบประเภท "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 :: algorithm algorithm) และ # 4 ทุกอย่างก็โอ (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: Component แต่ละประเภทมีชีวิตอยู่ในรายการของตัวเอง ดังนั้นจึงมีรายการ Sprite, รายการสุขภาพ, รายการความเสียหายเป็นต้นฉันรู้ว่าพวกเขาอยู่ในเอนทิตีตรรกะเดียวกันเพราะรหัสนิติบุคคล สิ่งนี้ง่ายกว่า แต่ช้ากว่า: ส่วนประกอบของ IA นั้นจำเป็นต้องเข้าถึงส่วนประกอบของเอนทิตีอื่น ๆ ทั้งหมดและจะต้องค้นหารายการของส่วนประกอบอื่น ๆ ในแต่ละขั้นตอน
คุณคิดว่าการอนุมัติแบบไหนดีกว่ากัน? คือ :: แผนที่ฟิวชั่นที่เหมาะที่จะใช้ในทางนั้น?