Jawabannya selalu menggunakan array atau std :: vector. Jenis seperti daftar yang ditautkan atau std :: map biasanya benar-benar menghebohkan dalam gim, dan itu pasti termasuk case seperti koleksi objek gim.
Anda harus menyimpan objek itu sendiri (bukan pointer ke mereka) dalam array / vektor.
Anda ingin memori yang berdekatan. Anda benar-benar menginginkannya. Iterasi atas data apa pun dalam memori yang tidak bersebelahan memaksakan banyak kesalahan cache secara umum dan menghilangkan kemampuan kompiler dan CPU untuk melakukan prefetching cache yang efektif. Ini saja dapat mematikan kinerja.
Anda juga ingin menghindari alokasi dan deallokasi memori. Mereka sangat lambat, bahkan dengan pengalokasi memori cepat. Saya telah melihat game mendapatkan 10x FPS bump dengan hanya menghapus alokasi memori beberapa ratus setiap frame. Kelihatannya tidak seburuk itu, tapi bisa juga.
Terakhir, sebagian besar struktur data yang Anda pedulikan untuk mengelola objek game bisa jauh lebih efisien diimplemetasikan pada array atau vektor dari pada dengan pohon atau daftar.
Misalnya, untuk menghapus objek game, Anda dapat menggunakan swap-and-pop. Mudah diimplementasikan dengan sesuatu seperti:
std::swap(objects[index], objects.back());
objects.pop_back();
Anda juga bisa menandai objek sebagai dihapus dan meletakkan indeks mereka pada daftar gratis untuk waktu berikutnya Anda perlu membuat objek baru, tetapi melakukan swap-and-pop lebih baik. Ini memungkinkan Anda melakukan loop sederhana untuk semua objek hidup tanpa bercabang selain dari loop itu sendiri. Untuk integrasi fisika peluru dan sejenisnya, ini bisa menjadi dorongan kinerja yang signifikan.
Lebih penting lagi, Anda dapat menemukan objek dengan sepasang tabel lookup sederhana dari stabil unik menggunakan struktur peta slot.
Objek gim Anda memiliki indeks di larik utamanya. Mereka bisa sangat efisien mencari hanya dengan indeks ini (jauh lebih cepat daripada peta atau bahkan tabel hash). Namun, indeksnya tidak stabil karena swap dan pop ketika menghapus objek.
Peta slot membutuhkan dua lapisan tipuan, tetapi keduanya adalah pencarian array sederhana dengan indeks konstan. Mereka cepat . Sangat cepat.
Ide dasarnya adalah bahwa Anda memiliki tiga array: daftar objek utama Anda, daftar tipuan Anda, dan daftar gratis untuk daftar tipuan. Daftar objek utama Anda berisi objek aktual Anda, di mana setiap objek tahu ID uniknya sendiri. ID unik terdiri dari indeks dan tag versi. Daftar tipuan hanyalah array indeks ke daftar objek utama. Daftar gratis adalah setumpuk indeks ke dalam daftar tipuan.
Ketika Anda membuat objek di daftar utama, Anda menemukan entri yang tidak digunakan dalam daftar tipuan (menggunakan daftar gratis). Entri dalam daftar tipuan menunjuk ke entri yang tidak digunakan dalam daftar utama. Anda menginisialisasi objek Anda di lokasi itu, dan menetapkan ID uniknya ke indeks entri daftar tipuan yang Anda pilih dan tag versi yang ada di elemen daftar utama, ditambah satu.
Saat Anda menghancurkan objek, Anda melakukan swap-and-pop seperti biasa, tetapi Anda juga menambah nomor versi. Anda kemudian juga menambahkan indeks daftar tipuan (bagian dari ID unik objek) ke daftar gratis. Saat memindahkan objek sebagai bagian dari swap-and-pop, Anda juga memperbarui entri dalam daftar tipuan ke lokasi baru.
Contoh pseudo-code:
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)
Lapisan tipuan memungkinkan Anda untuk memiliki pengidentifikasi stabil (indeks ke lapisan tipuan, di mana entri tidak bergerak) untuk sumber daya yang dapat bergerak selama pemadatan (daftar objek utama).
Tag versi memungkinkan Anda untuk menyimpan ID ke objek yang mungkin dihapus. Misalnya, Anda memiliki id (10,1). Objek dengan indeks 10 dihapus (misalnya, peluru Anda mengenai objek dan dihancurkan). Objek di lokasi memori itu dalam daftar objek utama kemudian memiliki nomor versinya bertemu, memberikannya (10,2). Jika Anda mencoba mencari lagi (10,1) dari ID yang sudah basi, pencarian akan mengembalikan objek tersebut melalui indeks 10, tetapi dapat melihat bahwa nomor versi telah berubah, sehingga ID tersebut tidak lagi valid.
Ini adalah struktur data tercepat mutlak yang dapat Anda miliki dengan ID stabil yang memungkinkan objek bergerak dalam memori, yang penting untuk lokalitas data dan koherensi cache. Ini lebih cepat daripada implementasi tabel hash yang dimungkinkan; tabel hash paling tidak perlu menghitung hash (lebih banyak instruksi daripada pencarian tabel) dan kemudian harus mengikuti rantai hash (baik daftar yang ditautkan dalam kasus mengerikan std :: unordered_map, atau daftar yang ditangani secara terbuka di implementasi tabel hash yang tidak bodoh), dan kemudian harus melakukan perbandingan nilai pada masing-masing kunci (tidak lebih mahal, tetapi mungkin lebih murah, daripada pemeriksaan tag versi). Tabel hash yang sangat baik (bukan yang ada dalam implementasi STL, karena STL mengamanatkan tabel hash yang dioptimalkan untuk berbagai kasus penggunaan dibandingkan dengan gim yang Anda gambarkan untuk daftar objek game) mungkin menghemat satu tipuan,
Ada berbagai peningkatan yang dapat Anda lakukan pada algoritme dasar. Menggunakan sesuatu seperti std :: deque untuk daftar objek utama, misalnya; satu lapisan tipuan tambahan, tetapi memungkinkan objek untuk dimasukkan ke dalam daftar lengkap tanpa membatalkan pointer sementara yang telah Anda peroleh dari slotmap.
Anda juga dapat menghindari menyimpan indeks di dalam objek, karena indeks dapat dihitung dari alamat memori objek (ini - objek), dan bahkan lebih baik hanya diperlukan ketika menghapus objek yang dalam hal ini Anda sudah memiliki id objek (dan karenanya index) sebagai parameter.
Permintaan maaf untuk penulisan; Saya tidak merasa itu deskripsi yang paling jelas. Sudah terlambat dan sulit untuk dijelaskan tanpa menghabiskan lebih banyak waktu daripada yang saya miliki pada sampel kode.