ฉันจะสนับสนุนการสื่อสารระหว่างส่วนประกอบกับวัตถุได้อย่างปลอดภัยและด้วยการจัดเก็บส่วนประกอบที่เป็นมิตรกับแคชได้อย่างไร?


9

ฉันกำลังสร้างเกมที่ใช้วัตถุเกมตามองค์ประกอบและฉันมีเวลายากที่จะใช้วิธีการสำหรับแต่ละองค์ประกอบในการสื่อสารกับวัตถุเกม แทนที่จะอธิบายทุกอย่างพร้อมกันฉันจะอธิบายแต่ละส่วนของโค้ดตัวอย่างที่เกี่ยวข้อง:

class GameObjectManager {
    public:
        //Updates all the game objects
        void update(Time dt);

        //Sends a message to all game objects
        void sendMessage(Message m);

    private:
        //Vector of all the game objects
        std::vector<GameObject> gameObjects;

        //vectors of the different types of components
        std::vector<InputComponent> input;
        std::vector<PhysicsComponent> ai;
        ...
        std::vector<RenderComponent> render;
}

การGameObjectManagerเก็บวัตถุเกมทั้งหมดและส่วนประกอบของพวกเขา นอกจากนี้ยังรับผิดชอบในการปรับปรุงวัตถุของเกม มันทำได้โดยการอัพเดทเวกเตอร์ส่วนประกอบตามลำดับเฉพาะ ฉันใช้เวกเตอร์แทนอาร์เรย์เพื่อให้แทบจะไม่ จำกัด จำนวนวัตถุเกมที่สามารถมีอยู่ได้ในครั้งเดียว

class GameObject {
    public:
        //Sends a message to the components in this game object
        void sendMessage(Message m);

    private:
        //id to keep track of components in the manager
        const int id;

        //Pointers to components in the game object manager
        std::vector<Component*> components;
}

GameObjectชั้นรู้ว่าสิ่งที่ส่วนประกอบและสามารถส่งข้อความให้กับพวกเขา

class Component {
    public:
        //Receives messages and acts accordingly
        virtual void handleMessage(Message m) = 0;

        virtual void update(Time dt) = 0;

    protected:
        //Calls GameObject's sendMessage
        void sendMessageToObject(Message m);

        //Calls GameObjectManager's sendMessage
        void sendMessageToWorld(Message m);
}

Componentระดับบริสุทธิ์เสมือนเพื่อให้ชั้นเรียนสำหรับประเภทที่แตกต่างกันของส่วนประกอบที่สามารถใช้วิธีการจัดการกับข้อความและการปรับปรุง นอกจากนี้ยังสามารถส่งข้อความได้

ตอนนี้ปัญหาที่เกิดขึ้นเกี่ยวกับวิธีส่วนประกอบสามารถเรียกsendMessageฟังก์ชั่นในและGameObject GameObjectManagerฉันคิดหาวิธีแก้ปัญหาสองวิธี

  1. ให้ชี้ไปของมันComponentGameObject

อย่างไรก็ตามเนื่องจากวัตถุของเกมอยู่ในเวกเตอร์พอยน์เตอร์อาจกลายเป็นโมฆะได้อย่างรวดเร็ว (เช่นเดียวกันกับที่พูดในเวกเตอร์GameObjectแต่หวังว่าการแก้ปัญหานี้จะสามารถแก้ปัญหานี้ได้) ฉันสามารถวางวัตถุของเกมในอาร์เรย์ได้ แต่จากนั้นฉันจะต้องส่งผ่านจำนวนที่กำหนดเองซึ่งอาจสูงและไม่จำเป็นโดยไม่จำเป็น

  1. ให้ชี้ไปที่ComponentGameObjectManager

อย่างไรก็ตามฉันไม่ต้องการให้ส่วนประกอบสามารถเรียกใช้ฟังก์ชั่นอัพเดทของผู้จัดการได้ ฉันเป็นคนเดียวที่ทำงานในโครงการนี้ แต่ฉันไม่ต้องการให้นิสัยในการเขียนรหัสที่อาจเป็นอันตราย

ฉันจะแก้ไขปัญหานี้ในขณะที่รักษารหัสของฉันให้ปลอดภัยและเป็นมิตรกับแคชได้อย่างไร

คำตอบ:


6

รูปแบบการสื่อสารของคุณดูดีและตัวเลือกที่ใช้ได้ก็ต่อเมื่อคุณสามารถเก็บตัวชี้เหล่านั้นอย่างปลอดภัย คุณสามารถแก้ปัญหานั้นได้โดยเลือกโครงสร้างข้อมูลที่แตกต่างกันสำหรับการจัดเก็บส่วนประกอบ

A std::vector<T>เป็นตัวเลือกแรกที่สมเหตุสมผล อย่างไรก็ตามพฤติกรรมการตรวจสอบความถูกต้องตัววนซ้ำของคอนเทนเนอร์เป็นปัญหา สิ่งที่คุณต้องการคือโครงสร้างข้อมูลที่รวดเร็วและแคชสอดคล้องกันเพื่อทำซ้ำและยังรักษาความเสถียรของตัววนซ้ำเมื่อแทรกหรือลบรายการ

คุณสามารถสร้างโครงสร้างข้อมูลดังกล่าว มันประกอบไปด้วยการเชื่อมโยงรายชื่อของหน้า แต่ละหน้ามีความจุคงที่และเก็บรายการทั้งหมดไว้ในอาร์เรย์เดียว การนับจะใช้เพื่อระบุจำนวนรายการในอาร์เรย์ที่ใช้งานอยู่ หน้านอกจากนี้ยังมีรายการฟรี (อนุญาตให้นำมาใช้ใหม่ของรายการเคลียร์) และรายชื่อข้าม (ช่วยให้คุณสามารถข้ามมากกว่ารายการเคลียร์ขณะ iterating

กล่าวอีกนัยหนึ่งมีแนวคิดคล้าย:

struct Page {
   int count;
   int capacity;           // Optional if every page is a fixed size.
   T * m_storage;
   bool * m_skip;          // Skip list; can be bit-compressed.
   std::stack<int> m_free; // Can be replaced with a specialized stack.

   Page * next;
   Page * prior;           // Optional, allows reverse iteration
};

ฉันเรียกหนังสือเล่มนี้ว่าโครงสร้างข้อมูลโดยไม่ตั้งใจ (เพราะเป็นชุดของหน้าที่คุณทำซ้ำ) แต่โครงสร้างนั้นมีชื่ออื่น ๆ

Matthew Bentley เรียกมันว่า "อาณานิคม" การนำไปใช้ของ Matthew ใช้เขตการข้ามนับข้าม (ขอโทษสำหรับการเชื่อมโยง MediaFire แต่เป็นวิธีที่เบนท์ลีย์เองเป็นเจ้าของเอกสาร) ซึ่งดีกว่ารายการข้ามแบบบูลีนทั่วไปในโครงสร้างเหล่านี้ ห้องสมุดของเบนท์ลีย์เป็นส่วนหัวเท่านั้นและง่ายต่อการไปยังโปรเจ็กต์ C ++ ใด ๆ ดังนั้นฉันขอแนะนำให้คุณใช้เพียงแค่นั้นกับการม้วนของคุณเอง มีรายละเอียดปลีกย่อยและการปรับแต่งมากมายที่ฉันคัดสรรมาที่นี่

เนื่องจากโครงสร้างข้อมูลนี้ไม่ย้ายรายการเมื่อมีการเพิ่มพอยน์เตอร์และตัววนซ้ำไปยังรายการนั้นจะยังคงใช้ได้จนกว่าจะลบรายการนั้นเอง (หรือล้างคอนเทนเนอร์เอง) เนื่องจากมันจัดเก็บรายการที่ได้รับการจัดสรรอย่างต่อเนื่องการวนซ้ำนั้นรวดเร็วและส่วนใหญ่เป็นแคชที่สอดคล้องกัน การแทรกและการลบมีความสมเหตุสมผล

มันไม่สมบูรณ์แบบ เป็นไปได้ที่จะทำลายการเชื่อมโยงกันแคชกับรูปแบบการใช้งานที่เกี่ยวข้องกับการลบอย่างมากจากจุดสุ่มที่มีประสิทธิภาพในภาชนะและจากนั้นวนซ้ำบนภาชนะนั้นก่อนที่ส่วนแทรกที่ตามมาจะมีรายการทดแทน หากคุณอยู่ในสถานการณ์นั้นบ่อยครั้งคุณจะข้ามพื้นที่หน่วยความจำขนาดใหญ่ในเวลาเดียวกัน อย่างไรก็ตามในทางปฏิบัติฉันคิดว่าคอนเทนเนอร์นี้เป็นตัวเลือกที่เหมาะสมสำหรับสถานการณ์ของคุณ

วิธีอื่นที่ฉันจะทิ้งไว้สำหรับคำตอบอื่น ๆ ที่จะครอบคลุมอาจรวมถึงวิธีการจัดการตามหรือการเรียงลำดับโครงสร้างแผนที่สล็อต (ที่คุณมีอาร์เรย์เชื่อมโยงของ "คีย์" จำนวนเต็ม "ค่าจำนวนเต็ม" ค่าเป็นดัชนี ในอาเรย์สำรองซึ่งช่วยให้คุณสามารถวนซ้ำเวกเตอร์โดยยังคงเข้าถึงได้โดย "ดัชนี" โดยมีส่วนเสริมพิเศษ)


Hi! มีทรัพยากรใดบ้างที่ฉันสามารถเรียนรู้เพิ่มเติมเกี่ยวกับทางเลือก "อาณานิคม" ที่คุณพูดถึงในย่อหน้าสุดท้าย? พวกเขาใช้งานได้ทุกที่หรือไม่? ฉันค้นคว้าหัวข้อนี้มาระยะหนึ่งแล้วและสนใจจริงๆ
Rinat Veliakhmedov

5

การเป็น 'แคชที่เป็นมิตร' เป็นเกมใหญ่ที่น่าจับตามอง ดูเหมือนว่านี่จะเป็นการเพิ่มประสิทธิภาพก่อนวัยอันควรสำหรับฉัน


วิธีหนึ่งในการแก้ไขปัญหานี้โดยไม่ต้อง 'เป็นมิตรกับแคช' คือการสร้างวัตถุของคุณบนฮีปแทนที่จะเป็นแบบกองซ้อน: ใช้newตัวชี้และ (สมาร์ท) สำหรับวัตถุของคุณ ด้วยวิธีนี้คุณจะสามารถอ้างอิงวัตถุของคุณและการอ้างอิงของพวกเขาจะไม่ถูกยกเลิก

สำหรับโซลูชันที่เป็นมิตรต่อแคชคุณสามารถจัดการการจัดสรร / การจัดสรรออบเจ็กต์ด้วยตนเองและใช้จุดจับกับออบเจ็กต์เหล่านี้

โดยทั่วไปเมื่อเริ่มต้นโปรแกรมของคุณวัตถุจะสงวนหน่วยความจำอันหนึ่งไว้บนฮีป (ลองเรียกมันว่า MemMan) จากนั้นเมื่อคุณต้องการสร้างส่วนประกอบคุณบอก MemMan ว่าคุณต้องการส่วนประกอบที่มีขนาด X เป็น ' จะสำรองไว้ให้คุณสร้างหมายเลขอ้างอิงและเก็บไว้ภายในซึ่งในการจัดสรรนั้นเป็นวัตถุสำหรับหมายเลขอ้างอิงนั้น มันจะคืนจุดจับและสิ่งเดียวที่คุณจะเก็บไว้เกี่ยวกับวัตถุนั้นจะไม่มีตัวชี้ไปยังตำแหน่งในหน่วยความจำ

เมื่อคุณต้องการส่วนประกอบคุณจะต้องให้ MemMan เข้าถึงวัตถุนี้ซึ่งจะทำให้คุณยินดี แต่อย่าอ้างอิงถึงเพราะ ....

หนึ่งในงานของ MemMan คือการเก็บวัตถุไว้ใกล้กันในหน่วยความจำ เมื่อทุกๆเฟรมของเกมไม่กี่ครั้งคุณสามารถบอกให้ MemMan จัดเรียงวัตถุในหน่วยความจำได้ (หรืออาจทำโดยอัตโนมัติเมื่อคุณสร้าง / ลบวัตถุ) มันจะอัปเดตแผนที่ตำแหน่งที่จับไปยังหน่วยความจำ ที่จับของคุณจะถูกต้องเสมอ แต่ถ้าคุณเก็บการอ้างอิงไปยังพื้นที่หน่วยความจำ ( ตัวชี้หรือการอ้างอิง ) คุณจะพบเพียงความสิ้นหวังและความอ้างว้าง

ตำราบอกว่าวิธีการจัดการหน่วยความจำของคุณมีข้อดีอย่างน้อย 2 ข้อ:

  1. แคชหายน้อยลงเนื่องจากวัตถุอยู่ใกล้กันในหน่วยความจำและ
  2. มันจะช่วยลดจำนวนของหน่วยความจำ de / จัดสรรโทรคุณจะทำให้ระบบปฏิบัติการซึ่งมีการกล่าวว่าจะใช้บางเวลา

โปรดจำไว้ว่าวิธีที่คุณใช้ MemMan และวิธีที่คุณจัดระเบียบหน่วยความจำภายในนั้นขึ้นอยู่กับว่าคุณจะใช้ส่วนประกอบของคุณอย่างไร หากคุณวนซ้ำตามประเภทขององค์ประกอบเหล่านั้นคุณจะต้องเก็บส่วนประกอบตามประเภทหากคุณวนซ้ำตามองค์ประกอบของเกมคุณจะต้องหาวิธีเพื่อให้แน่ใจว่าพวกเขาอยู่ใกล้กับ อีกอันอิงจากสิ่งนั้น ฯลฯ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.