คำตอบคือใช้อาร์เรย์หรือ std :: vector เสมอ ประเภทเช่นรายการที่เชื่อมโยงหรือแผนที่ std :: มักจะน่ากลัวอย่างยิ่งในเกมและแน่นอนรวมถึงกรณีเช่นชุดของวัตถุเกม
คุณควรเก็บวัตถุเหล่านั้น (ไม่ใช่ตัวชี้ไปยังวัตถุเหล่านั้น) ในอาร์เรย์ / เวกเตอร์
คุณต้องการหน่วยความจำต่อเนื่อง คุณต้องการมันจริงๆ การวนซ้ำข้อมูลในหน่วยความจำที่ไม่ต่อเนื่องกันนั้นจะทำให้แคชหายไปโดยทั่วไปและจะลบความสามารถของคอมไพเลอร์และซีพียูเพื่อทำการแคชล่วงหน้าอย่างมีประสิทธิภาพ เพียงอย่างเดียวนี้สามารถฆ่าประสิทธิภาพ
คุณต้องการหลีกเลี่ยงการจัดสรรหน่วยความจำและการจัดสรรคืน มันช้ามากแม้จะมีตัวจัดสรรหน่วยความจำที่รวดเร็ว ฉันเคยเห็นเกมได้รับ 10x FPS ชนเพียงแค่ลบการจัดสรรหน่วยความจำไม่กี่ร้อยแต่ละเฟรม ดูเหมือนจะไม่เลวร้ายขนาดนั้น แต่ก็เป็นได้
สุดท้ายโครงสร้างข้อมูลส่วนใหญ่ที่คุณสนใจเกี่ยวกับการจัดการวัตถุเกมสามารถนำไปใช้กับอาเรย์หรือเวกเตอร์ได้อย่างมีประสิทธิภาพมากกว่าที่มีต้นไม้หรือรายการ
ตัวอย่างเช่นในการนำวัตถุเกมออกคุณสามารถใช้ swap-and-pop ดำเนินการได้อย่างง่ายดายด้วยบางสิ่งเช่น:
std::swap(objects[index], objects.back());
objects.pop_back();
นอกจากนี้คุณยังสามารถทำเครื่องหมายว่าวัตถุถูกลบและวางดัชนีของพวกเขาในรายการฟรีในครั้งต่อไปที่คุณต้องสร้างวัตถุใหม่ แต่การทำ swap-and-pop นั้นดีกว่า มันช่วยให้คุณทำง่าย ๆ สำหรับการวนลูปมากกว่าวัตถุที่มีชีวิตทั้งหมดโดยไม่มีการแยกสาขาออกจากลูป สำหรับการบูรณาการของกระสุนฟิสิกส์และสิ่งที่คล้ายกันนี่อาจเป็นการเพิ่มประสิทธิภาพที่สำคัญ
ที่สำคัญคุณสามารถค้นหาวัตถุด้วยการค้นหาตารางอย่างง่าย ๆ จากความเสถียรที่ไม่เหมือนใครคือการใช้โครงสร้างแผนที่สล็อต
วัตถุในเกมของคุณมีดัชนีในชุดหลัก พวกเขาสามารถค้นหาได้อย่างมีประสิทธิภาพด้วยดัชนีนี้ (เร็วกว่าแผนที่หรือแม้แต่ตารางแฮช) อย่างไรก็ตามดัชนีไม่เสถียรเนื่องจากการสลับและป๊อปเมื่อเอาวัตถุออก
แมปสล็อตต้องการเลเยอร์ทางอ้อมสองชั้น แต่ทั้งคู่เป็นการค้นหาอาร์เรย์อย่างง่ายพร้อมดัชนีคงที่ พวกเขามีความรวดเร็ว เร็วจริงๆ.
แนวคิดพื้นฐานคือคุณมีสามอาร์เรย์: รายการวัตถุหลักของคุณรายการทางอ้อมของคุณและรายการฟรีสำหรับรายการทางอ้อม รายการวัตถุหลักของคุณมีวัตถุจริงของคุณที่แต่ละวัตถุรู้รหัสเฉพาะของตนเอง ID ที่ไม่ซ้ำกันประกอบด้วยดัชนีและแท็กเวอร์ชัน รายการทางอ้อมเป็นเพียงอาร์เรย์ของดัชนีไปยังรายการวัตถุหลัก รายการอิสระคือสแต็กของดัชนีลงในรายการทางอ้อม
เมื่อคุณสร้างวัตถุในรายการหลักคุณจะพบรายการที่ไม่ได้ใช้ในรายการทางอ้อม (ใช้รายการฟรี) รายการในรายการทางอ้อมชี้ไปยังรายการที่ไม่ได้ใช้ในรายการหลัก คุณกำหนดค่าเริ่มต้นวัตถุของคุณในตำแหน่งนั้นและตั้งค่า ID เฉพาะให้กับดัชนีของรายการรายการทางอ้อมที่คุณเลือกและแท็กเวอร์ชันที่มีอยู่ในองค์ประกอบรายการหลักบวกหนึ่ง
เมื่อคุณทำลายวัตถุคุณทำ swap-and-pop ตามปกติ แต่คุณต้องเพิ่มหมายเลขรุ่นด้วย คุณยังเพิ่มดัชนีรายการทางอ้อม (ส่วนหนึ่งของ ID เฉพาะของวัตถุ) ไปยังรายการว่าง เมื่อย้ายวัตถุซึ่งเป็นส่วนหนึ่งของ swap-and-pop คุณจะต้องอัปเดตรายการในรายการการอ้อมไปยังตำแหน่งใหม่
ตัวอย่างรหัสหลอก:
Object:
int index
int version
other data
SlotMap:
Object objects[]
int slots[]
int freelist[]
int count
Get(id):
index = indirection[id.index]
if objects[index].version = id.version:
return &objects[index]
else:
return null
CreateObject():
index = freelist.pop()
objects[count].index = id
objects[count].version += 1
indirection[index] = count
Object* object = &objects[count].object
object.initialize()
count += 1
return object
Remove(id):
index = indirection[id.index]
if objects[index].version = id.version:
objects[index].version += 1
objects[count - 1].version += 1
swap(objects[index].data, objects[count - 1].data)
เลเยอร์ทางอ้อมช่วยให้คุณมีตัวระบุที่มีความเสถียร (ดัชนีลงในเลเยอร์ทางอ้อมที่รายการไม่ย้าย) สำหรับทรัพยากรที่สามารถย้ายในระหว่างการบดอัด (รายการวัตถุหลัก)
แท็กเวอร์ชันช่วยให้คุณสามารถเก็บ ID ไปยังวัตถุที่อาจถูกลบ ตัวอย่างเช่นคุณมี ID (10,1) วัตถุที่มีดัชนี 10 ถูกลบ (เช่นสัญลักษณ์แสดงหัวข้อย่อยของคุณกระทบกับวัตถุและถูกทำลาย) วัตถุที่อยู่ในตำแหน่งของหน่วยความจำในรายการวัตถุหลักนั้นมีหมายเลขรุ่นของมันกระแทกให้ (10,2) หากคุณพยายามค้นหา (10,1) อีกครั้งจาก stale ID การค้นหาจะส่งคืนออบเจ็กต์นั้นผ่านดัชนี 10 แต่จะเห็นว่าหมายเลขเวอร์ชันนั้นเปลี่ยนไปดังนั้น ID จะไม่ถูกต้องอีกต่อไป
นี่คือโครงสร้างข้อมูลที่เร็วที่สุดที่คุณสามารถทำได้ด้วยรหัสที่เสถียรซึ่งอนุญาตให้วัตถุย้ายในหน่วยความจำซึ่งเป็นสิ่งสำคัญสำหรับการใช้งานข้อมูลและการเชื่อมโยงแคช นี่เร็วกว่าการใช้งานตารางแฮชที่เป็นไปได้ ตารางแฮชอย่างน้อยที่สุดต้องคำนวณแฮช (คำแนะนำมากกว่าการค้นหาตาราง) จากนั้นจะต้องทำตามเชนแฮช (อาจเป็นรายการที่เชื่อมโยงในกรณีที่น่ากลัวของ std :: unordered_map หรือรายการ open-addressed ใน การใช้ตารางแฮชที่ไม่โง่ใด ๆ และจากนั้นจะต้องทำการเปรียบเทียบค่ากับแต่ละคีย์ (ไม่แพงกว่า แต่เป็นไปได้น้อยกว่าแพงกว่าการตรวจสอบแท็กเวอร์ชัน) ตารางแฮชที่ดีมาก (ไม่ใช่หนึ่งในการนำไปใช้งานของ STL ใด ๆ เนื่องจาก STL สั่งให้ตารางแฮชที่ปรับให้เหมาะกับกรณีการใช้งานที่แตกต่างจากเกมที่คุณเกี่ยวกับรายการวัตถุเกม) อาจบันทึกในทิศทางเดียว
มีการปรับปรุงต่าง ๆ ที่คุณสามารถทำได้กับอัลกอริทึมพื้นฐาน การใช้บางอย่างเช่น std :: deque สำหรับรายการวัตถุหลักตัวอย่างเช่น การเพิ่มทางอ้อมอีกชั้นหนึ่ง แต่อนุญาตให้วัตถุถูกแทรกลงในรายการแบบเต็มโดยไม่ทำให้ตัวชี้ชั่วคราวที่คุณได้รับมาจาก slotmap เสียหาย
นอกจากนี้คุณยังสามารถหลีกเลี่ยงการจัดเก็บดัชนีภายในวัตถุได้เนื่องจากดัชนีสามารถคำนวณได้จากที่อยู่หน่วยความจำของวัตถุ (นี่ - วัตถุ) และจำเป็นต้องดียิ่งขึ้นเมื่อนำวัตถุออกในกรณีที่คุณมี ID ของวัตถุอยู่แล้ว ดัชนี) เป็นพารามิเตอร์
ขอโทษที่เขียนขึ้น; ฉันไม่รู้สึกว่ามันเป็นคำอธิบายที่ชัดเจนที่สุด มันสายและยากที่จะอธิบายโดยไม่ต้องใช้เวลามากกว่าที่ฉันมีในตัวอย่างโค้ด