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


12

เราเริ่มต้นจากขั้นพื้นฐานวิธีการระบบส่วนประกอบที่มีหน่วยงาน

ขอสร้างassemblages (ระยะที่ได้มาจากนี้บทความ) เพียงออกของข้อมูลเกี่ยวกับชนิดของชิ้นส่วน มันจะทำแบบไดนามิกที่รันไทม์เช่นเดียวกับที่เราจะเพิ่ม / ลบส่วนประกอบไปยังเอนทิตีทีละคน แต่ขอเพียงแค่ตั้งชื่อได้อย่างแม่นยำมากขึ้นเพราะมันเป็นเพียงเกี่ยวกับข้อมูลประเภท

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

ตอนนี้สำหรับแนวคิดหลัก: เมื่อใดก็ตามที่มีการสร้างเอนทิตีมันจะถูกกำหนดให้กับวัตถุที่เรียกว่าbucket bucketlageซึ่งหมายความว่าเอนทิตีทั้งหมดของลายเซ็นเดียวกันจะอยู่ในคอนเทนเนอร์เดียวกัน (เช่นใน std :: vector)

ตอนนี้ระบบเพียงทำซ้ำผ่านทุกกลุ่มที่สนใจและทำงานของพวกเขา

วิธีนี้มีข้อดีดังนี้

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

ป้อนคำอธิบายรูปภาพที่นี่

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

นอกจากนี้ยังมีปัญหาเกี่ยวกับการทำให้ตัวชี้ใช้ไม่ได้เมื่อมีการจัดสรรเวกเตอร์ใหม่ซึ่งสามารถแก้ไขได้โดยการแนะนำโครงสร้างเช่น:

struct assemblage_bucket {
    struct entity_watcher {
        assemblage_bucket* owner;
        entity_id real_index_in_vector;
    };

    std::unordered_map<entity_id, std::vector<entity_watcher*>> subscribers;

    //...
};

ดังนั้นเมื่อมีเหตุผลบางอย่างในตรรกะเกมของเราเราต้องการติดตามเอนทิตีที่สร้างขึ้นใหม่ภายในที่เก็บข้อมูลเราลงทะเบียนเอนทิตีวอลเนอร์และเมื่อเอนทิตีจะต้องเป็นมาตรฐาน :: move'd ระหว่างการลบ ของพวกเขาreal_index_in_vectorให้เป็นค่าใหม่ ส่วนใหญ่เวลานี้กำหนดให้มีการค้นหาพจนานุกรมเพียงครั้งเดียวสำหรับการลบเอนทิตีทุกอัน

มีข้อเสียเพิ่มเติมสำหรับแนวทางนี้หรือไม่?

ทำไมถึงไม่มีวิธีการแก้ปัญหาที่กล่าวถึงแม้จะค่อนข้างชัดเจน?

แก้ไข : ฉันแก้ไขคำถามเพื่อ "ตอบคำตอบ" เนื่องจากความคิดเห็นไม่เพียงพอ

คุณสูญเสียลักษณะไดนามิกของส่วนประกอบที่เสียบได้ซึ่งสร้างขึ้นโดยเฉพาะเพื่อหลีกเลี่ยงการสร้างคลาสแบบสแตติก

ฉันไม่. บางทีฉันไม่ได้อธิบายอย่างชัดเจนพอ:

auto signature = world.get_signature(entity_id); // this would just return entity_id.bucket_owner->bucket_signature or so
signature.add(foo_component);
signature.remove(bar_component);
world.delete_entity(entity_id); // entity_id would hold information about its bucket owner
world.create_entity(signature); // automatically assigns new entity to an existing or a new bucket

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

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

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


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

คำตอบ:


7

คุณได้ออกแบบระบบวัตถุสแตติกเป็นหลักด้วยตัวจัดสรรพูลและคลาสแบบไดนามิก

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

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

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


ฉันพูดว่า: เราไม่สามารถเปลี่ยนลายเซ็นของเอนทิตี้ของและฉันหมายความว่าเราไม่สามารถแก้ไขได้โดยตรงในสถานที่ แต่ยังเราสามารถรับแอสเซมบลีที่มีอยู่เป็นสำเนาโลคอลทำการเปลี่ยนแปลงและอัปโหลดอีกครั้งเป็นนิติบุคคลใหม่ การดำเนินการค่อนข้างถูกตามที่ฉันได้แสดงไว้ในคำถาม อีกครั้ง - มีคลาส "ฝากข้อมูล" หนึ่งคลาสเท่านั้น "Assemblages" / "Signatures" / "ลองตั้งชื่อ แต่เราต้องการ" สามารถสร้างแบบไดนามิกที่รันไทม์ในวิธีมาตรฐานฉันจะไปไกลเท่าที่คิดของเอนทิตีเป็น "ลายเซ็น"
Patryk Czachurski

และฉันบอกว่าคุณไม่จำเป็นต้องจัดการกับการทำให้เป็นจริง "การสร้างเอนทิตีใหม่" อาจหมายถึงการทำลายการจัดการที่มีอยู่ทั้งหมดไปยังเอนทิตีขึ้นอยู่กับการทำงานของระบบการจัดการของคุณ สายของคุณถ้าพวกเขาถูกพอหรือไม่ ฉันพบว่ามันเป็นเพียงความเจ็บปวดในก้นที่จะต้องจัดการกับ
Sean Middleditch

เอาล่ะตอนนี้ฉันมีประเด็นเกี่ยวกับเรื่องนี้แล้ว อย่างไรก็ตามฉันคิดว่าแม้ว่าการเพิ่ม / ลบจะมีราคาแพงกว่าเล็กน้อย แต่ก็มีบางครั้งที่บางครั้งมันก็มีค่าที่ทำให้กระบวนการเข้าถึงส่วนประกอบนั้นง่ายขึ้นซึ่งจะเกิดขึ้นตามเวลาจริง ดังนั้นค่าใช้จ่ายของ "การเปลี่ยนแปลง" จึงไม่อาจเพิกเฉยได้ เกี่ยวกับตัวอย่าง AI ของคุณมันยังไม่คุ้มค่ากับระบบบางระบบที่ต้องการข้อมูลจากหลาย ๆ องค์ประกอบหรือไม่
Patryk Czachurski

ประเด็นของฉันคือว่า AI เป็นสถานที่ซึ่งวิธีการของคุณดีกว่า แต่สำหรับองค์ประกอบอื่น ๆ มันไม่จำเป็นต้องเป็นเช่นนั้น
Sean Middleditch

4

สิ่งที่คุณทำคือวัตถุ C ++ ที่ออกแบบใหม่ เหตุผลที่ทำให้รู้สึกชัดเจนคือถ้าคุณแทนที่คำว่า "เอนทิตี้" กับ "คลาส" และ "ส่วนประกอบ" กับ "สมาชิก" นี่คือการออกแบบ OOP มาตรฐานโดยใช้มิกซ์อิน

1) คุณสูญเสียลักษณะไดนามิกของคอมโพเนนต์แบบเสียบได้ซึ่งสร้างขึ้นโดยเฉพาะเพื่อหลีกเลี่ยงการสร้างคลาสแบบสแตติก

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

3) การออกแบบนี้เปลี่ยนเป็นสไตล์คลาส C ++ เพราะคุณคิดว่าเอนทิตีเป็นวัตถุที่เชื่อมโยงกันเมื่อในการออกแบบส่วนประกอบ + ระบบเอนทิตีเป็นเพียงแท็ก / ID เพื่อให้การทำงานภายในเข้าใจได้สำหรับมนุษย์

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

5) ขั้นตอนถัดไปตรรกะในพา ธ นี้คือการลบระบบและใส่รหัสนั้นลงในเอนทิตีโดยตรงซึ่งมีข้อมูลทั้งหมดที่จำเป็นในการทำงาน เราทุกคนสามารถเห็นสิ่งที่มีความหมาย =)


2) บางทีฉันไม่เข้าใจแคชอย่างสมบูรณ์ แต่สมมติว่ามีระบบที่ทำงานร่วมกับคอมโพเนนต์ 10 รายการ ในวิธีการมาตรฐานการประมวลผลแต่ละเอนทิตีหมายถึงการเข้าถึง RAM 10 ครั้งเนื่องจากส่วนประกอบกระจัดกระจายในสถานที่สุ่มในหน่วยความจำแม้ว่าจะมีการใช้พูล - เนื่องจากส่วนประกอบต่าง ๆ เป็นของพูลที่แตกต่างกัน จะเป็น "สำคัญ" หรือไม่ที่จะแคชทั้งเอนทิตีพร้อมกันและประมวลผลส่วนประกอบทั้งหมดโดยไม่พลาดแคชเพียงครั้งเดียวโดยไม่ต้องทำการค้นหาพจนานุกรม นอกจากนี้ฉันได้ทำการแก้ไขเพื่อให้ครอบคลุมจุด 1)
Patryk Czachurski

@Sean Middleditch มีคำอธิบายที่ดีเกี่ยวกับการแบ่งแคชในคำตอบของเขา
Patrick Hughes

3) พวกเขาไม่ใช่วัตถุที่เชื่อมโยงกันในทางใดทางหนึ่ง เกี่ยวกับองค์ประกอบ A อยู่ถัดจากองค์ประกอบ B ในหน่วยความจำมันเป็นเพียง "หน่วยความจำเชื่อมโยงกัน" ไม่ใช่ "ตรรกะเชื่อมโยงกัน" ตามที่จอห์นได้ชี้ให้เห็น ในการสร้างถังสามารถสลับองค์ประกอบในลายเซ็นตามลำดับที่ต้องการและหลักการจะยังคงอยู่ 4) อาจเป็นเรื่องง่ายพอ ๆ กันในการ "ติดตาม" หากเรามีสิ่งที่เป็นนามธรรมมากพอ - สิ่งที่เรากำลังพูดถึงเป็นเพียงรูปแบบการจัดเก็บที่ให้มาพร้อมกับตัววนซ้ำและบางทีแผนที่ออฟเซตไบต์อาจทำให้การประมวลผลง่าย
Patryk Czachurski

5) และฉันไม่คิดว่าอะไรในความคิดนี้ชี้ไปที่ทิศทางนี้ ไม่ใช่ว่าฉันไม่ต้องการเห็นด้วยกับคุณฉันแค่อยากรู้ว่าการสนทนานี้อาจนำไปสู่อย่างไรแม้ว่ามันอาจจะนำไปสู่ ​​"วัด" หรือการเพิ่มประสิทธิภาพก่อนวัยอันควรที่รู้จักกันดี :)
Patryk Czachurski

@PatrykCzachurski แต่ระบบของคุณไม่สามารถใช้งานได้กับ 10 องค์ประกอบ
user253751

3

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

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

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


ฉันต้องยอมรับว่าฉันไม่ค่อยเข้าใจคำตอบของคุณ "การเชื่อมโยงตรรกะ" หมายความว่าอะไร เกี่ยวกับปัญหาในการโต้ตอบฉันทำการแก้ไข
Patryk Czachurski

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