Untuk setiap game yang saya buat, saya akhirnya menempatkan semua objek game saya (peluru, mobil, pemain) dalam daftar array tunggal, yang saya lewati untuk menggambar dan memperbarui. Kode pembaruan untuk setiap entitas disimpan di dalam kelasnya.
Saya bertanya-tanya, apakah ini cara yang tepat untuk menyelesaikan berbagai hal, atau apakah cara yang lebih cepat dan lebih efisien?
software-engineering
Derek
sumber
sumber
Jawaban:
Tidak ada cara yang benar. Apa yang Anda gambarkan karya, dan mungkin cukup cepat sehingga tidak masalah karena Anda tampaknya bertanya karena Anda khawatir tentang desain dan bukan karena itu menyebabkan Anda masalah kinerja. Jadi itu adalah suatu cara yang benar, yakin.
Anda tentu bisa melakukannya secara berbeda, misalnya dengan membuat daftar yang homogen untuk setiap jenis objek atau dengan memastikan semua yang ada di daftar heterogen Anda dikelompokkan bersama sehingga Anda telah meningkatkan koherensi untuk kode yang ada dalam cache atau untuk memungkinkan pembaruan paralel. Apakah Anda akan melihat peningkatan kinerja yang cukup dengan melakukan hal ini sulit untuk dikatakan tanpa mengetahui ruang lingkup dan skala gim Anda.
Anda juga dapat memfaktorkan tanggung jawab entitas Anda lebih lanjut - sepertinya entitas memperbarui dan membuat diri mereka sendiri saat ini - untuk mengikuti Prinsip Tanggung Jawab Tunggal dengan lebih baik. Ini dapat meningkatkan rawatan dan fleksibilitas antarmuka individual Anda dengan memisahkannya satu sama lain. Tetapi sekali lagi, apakah Anda akan melihat manfaat dari kerja ekstra yang akan menyulitkan saya untuk mengatakan mengetahui apa yang saya ketahui tentang game Anda (yang pada dasarnya tidak ada artinya).
Saya akan merekomendasikan untuk tidak terlalu menekankan tentang hal itu, dan tetap di belakang kepala Anda bahwa itu mungkin menjadi area yang potensial untuk perbaikan jika Anda pernah melihat masalah kinerja atau pemeliharaan.
sumber
Seperti yang dikatakan orang lain, jika cukup cepat, maka cukup cepat. Jika Anda bertanya-tanya apakah ada cara yang lebih baik, pertanyaan untuk bertanya pada diri sendiri adalah
Apakah saya menemukan diri saya mengulangi semua objek tetapi hanya beroperasi pada subset dari mereka?
Jika Anda melakukannya, maka itu merupakan indikasi bahwa Anda mungkin ingin memiliki beberapa daftar yang hanya menyimpan referensi yang relevan. Misalnya jika Anda mengulang-ulang setiap objek game untuk merendernya tetapi hanya sebagian objek yang harus dirender, mungkin ada baiknya menyimpan daftar yang dapat diurai terpisah hanya untuk objek-objek yang perlu dirender.
Ini akan mengurangi jumlah objek yang Anda lewati dan melewatkan pemeriksaan yang tidak perlu karena Anda sudah tahu semua objek dalam daftar akan diproses.
Anda harus membuat profil untuk melihat apakah Anda mendapatkan banyak keuntungan kinerja.
sumber
Itu hampir seluruhnya tergantung pada kebutuhan gim spesifik Anda . Ini dapat bekerja dengan sempurna untuk gim yang sederhana, tetapi gagal pada sistem yang lebih kompleks. Jika ini berfungsi untuk gim Anda, jangan khawatir tentang hal itu atau coba merekayasa secara berlebihan hingga muncul kebutuhan.
Adapun mengapa pendekatan sederhana mungkin gagal dalam beberapa situasi, sulit untuk meringkas bahwa dalam satu posting, karena kemungkinan tidak terbatas dan sepenuhnya bergantung pada permainan itu sendiri. Jawaban lain telah menyebutkan misalnya bahwa Anda mungkin ingin mengelompokkan objek berbagi tanggung jawab bersama ke dalam daftar terpisah.
Itu salah satu perubahan paling umum yang mungkin Anda lakukan tergantung pada desain gim Anda. Saya akan mengambil kesempatan untuk menggambarkan beberapa contoh lain (lebih kompleks), tetapi ingat masih ada banyak alasan dan solusi yang mungkin.
Sebagai permulaan, saya akan menunjukkan bahwa memperbarui objek game Anda dan merendernya mungkin memiliki persyaratan yang berbeda di beberapa game, dan karena itu perlu ditangani secara terpisah. Beberapa kemungkinan masalah dengan memperbarui dan merender objek game:
Memperbarui Objek Game
Berikut adalah kutipan dari Arsitektur Mesin Game yang saya sarankan baca:
Dengan kata lain, dalam beberapa permainan dimungkinkan objek bergantung satu sama lain dan memerlukan urutan pembaruan tertentu. Dalam skenario ini, Anda mungkin perlu merancang struktur yang lebih kompleks daripada daftar untuk menyimpan objek dan saling ketergantungannya.
Rendering Objek Game
Dalam beberapa permainan, adegan dan objek membuat hierarki simpul orangtua-anak dan harus dirender dalam urutan yang benar dan dengan transformasi relatif terhadap orangtua mereka. Dalam kasus tersebut, Anda mungkin memerlukan struktur yang lebih kompleks seperti grafik adegan (struktur pohon) alih-alih satu daftar:
Alasan lain mungkin misalnya, menggunakan struktur data partisi spasial untuk mengatur objek Anda dengan cara yang meningkatkan efisiensi melakukan pemunculan frustum tampilan.
sumber
Jawabannya adalah "ya, ini baik-baik saja." Ketika saya mengerjakan simulasi besar di mana kinerja tidak bisa dinegosiasikan, saya terkejut melihat semuanya dalam satu susunan global besar (BESAR dan FAT). Kemudian saya bekerja pada sistem OO dan harus membandingkan keduanya. Meskipun itu sangat jelek, versi "satu larik untuk mengatur semuanya" dalam single-threaded plain C lama yang dikompilasi dalam debug beberapa kali lebih cepat daripada sistem OO multithreaded "cantik" yang dikompilasi -O2 dalam C ++. Saya pikir sedikit itu ada hubungannya dengan lokalitas cache yang sangat baik (dalam ruang dan waktu) dalam versi array besar-lemak.
Jika Anda menginginkan kinerja, satu array global C besar yang gemuk akan menjadi batas atas (dari pengalaman).
sumber
Pada dasarnya yang ingin Anda lakukan adalah membuat daftar sesuai kebutuhan Anda. Jika Anda menggambar ulang objek medan sekaligus, daftar hanya benda itu bisa berguna. Tetapi untuk ini layak sementara Anda akan membutuhkan puluhan ribu dari mereka, dan 10.000 hal yang tidak terrain. Kalau tidak membaca dengan teliti daftar segala sesuatu dan menggunakan "jika" untuk mengeluarkan objek medan cukup cepat.
Saya selalu membutuhkan satu daftar master, terlepas dari berapa banyak daftar lain yang saya miliki. Biasanya saya membuatnya menjadi semacam Peta, atau tabel, atau kamus, sehingga saya dapat secara acak mengakses setiap item. Tetapi daftar sederhana jauh lebih sederhana; jika Anda tidak mengakses objek game secara acak, Anda tidak memerlukan Peta. Dan pencarian sekuensial sesekali melalui beberapa ribu entri untuk menemukan yang tepat tidak akan lama. Jadi saya katakan, apa pun yang Anda lakukan, rencanakan untuk tetap dengan daftar master. Pertimbangkan menggunakan Peta jika terlalu besar. (Meskipun jika Anda dapat menggunakan indeks alih-alih kunci, Anda dapat tetap menggunakan array dan memiliki sesuatu yang jauh lebih baik daripada Peta. Saya selalu berakhir dengan kesenjangan besar antara nomor indeks, jadi saya harus menyerah.)
Sepatah kata tentang array: mereka luar biasa cepat. Tetapi ketika mereka bangun sekitar 100.000 elemen, mereka mulai memberikan pengumpul sampah yang cocok. Jika Anda dapat mengalokasikan ruang sekali dan tidak menyentuhnya sesudahnya, baik-baik saja. Tetapi jika Anda terus memperluas array, Anda terus mengalokasikan dan membebaskan banyak memori dan cenderung untuk permainan Anda menggantung selama beberapa saat sekaligus dan bahkan gagal dengan kesalahan memori.
Jadi lebih cepat dan lebih efisien? Yakin. Peta untuk akses acak. Untuk daftar yang sangat besar, Daftar Tertaut akan mengalahkan array jika dan ketika Anda tidak membutuhkan akses acak. Beberapa peta / daftar untuk mengatur objek dengan berbagai cara, sesuai kebutuhan Anda. Anda dapat melakukan multi-utas pembaruan.
Tapi itu semua banyak pekerjaan. Array tunggal itu cepat dan sederhana. Anda dapat menambahkan daftar dan hal-hal lain hanya saat dibutuhkan, dan Anda mungkin tidak akan membutuhkannya. Sadarilah apa yang salah jika game Anda menjadi besar. Anda mungkin ingin memiliki versi uji dengan objek 10 kali lebih banyak daripada versi nyata untuk memberi Anda gambaran tentang kapan Anda menuju masalah. (Hanya lakukan ini jika permainan Anda adalah mendapatkan besar.) (Dan jangan mencoba multi-threading tanpa memberikan banyak pemikiran. Segala sesuatu yang lain adalah mudah untuk memulai dengan dan Anda dapat melakukan sebanyak atau sesedikit yang Anda seperti dan mundur kapan saja. Dengan multi-threading Anda akan membayar harga yang besar untuk masuk, iuran untuk tetap tinggi, dan sulit untuk kembali.)
Dengan kata lain, tetap lakukan apa yang Anda lakukan. Batas terbesar Anda mungkin adalah waktu Anda sendiri, dan Anda memanfaatkan sebaik mungkin hal itu.
sumber
Saya telah menggunakan metode yang agak mirip untuk permainan sederhana. Menyimpan segala sesuatu dalam satu array sangat berguna ketika kondisi berikut ini benar:
Bagaimanapun, saya mengimplementasikan solusi ini sebagai array berukuran tetap yang berisi sebuah
gameObject_ptr
struct. Struct ini berisi pointer ke objek game itu sendiri, dan nilai boolean yang mewakili apakah objek itu "hidup" atau tidak.Tujuan boolean adalah untuk mempercepat operasi pembuatan dan penghapusan. Alih-alih menghancurkan objek game ketika mereka dihapus dari simulasi, saya hanya akan mengatur bendera "hidup"
false
.Saat melakukan perhitungan pada objek, kode akan mengulang melalui array, melakukan perhitungan pada objek yang ditandai sebagai "hidup", dan hanya objek yang ditandai "hidup" yang diberikan.
Optimalisasi sederhana lainnya adalah mengatur array berdasarkan tipe objek. Semua peluru, misalnya, bisa hidup di n sel terakhir dari array. Kemudian, dengan menyimpan int dengan nilai indeks atau pointer ke elemen array, tipe tertentu dapat dirujuk dengan biaya yang lebih rendah.
Tidak perlu dikatakan lagi bahwa solusi ini tidak baik untuk gim dengan jumlah data yang besar, karena lariknya akan terlalu besar. Ini juga tidak cocok untuk game dengan batasan memori yang melarang benda yang tidak terpakai dari mengambil memori. Intinya adalah bahwa jika Anda memiliki batas atas yang diketahui tentang jumlah objek game yang akan Anda miliki pada satu titik waktu, tidak ada yang salah dengan menyimpannya dalam array statis.
Ini posting pertama saya! Saya harap ini menjawab pertanyaan Anda!
sumber
Itu dapat diterima, meskipun tidak biasa. Istilah seperti "lebih cepat" dan "lebih efisien" relatif; biasanya Anda akan mengatur objek permainan ke dalam kategori yang terpisah (jadi satu daftar untuk peluru, satu daftar untuk musuh, dll.) untuk membuat pemrograman bekerja lebih teratur (dan dengan demikian lebih efisien) tetapi tidak seperti komputer akan berputar melalui semua objek lebih cepat .
sumber
Anda mengatakan array dan objek tunggal.
Jadi (tanpa kode Anda untuk melihat) saya kira mereka semua terkait dengan 'uberGameObject'.
Jadi mungkin dua masalah.
1) Anda mungkin memiliki objek 'dewa' - mengerjakan banyak hal, tidak benar-benar tersegmentasi berdasarkan apa yang dilakukannya atau apa.
2) Anda mengulangi daftar ini dan mengetik untuk mengetik? atau unboxing masing-masing. Ini memiliki penalti performa yang besar.
pertimbangkan untuk memecah berbagai jenis (peluru, orang jahat, dll) ke dalam daftar mereka yang sangat diketik.
sumber
update()
).Saya dapat mengusulkan beberapa kompromi antara daftar tunggal umum dan daftar terpisah untuk setiap jenis objek.
Simpan referensi ke satu objek dalam beberapa daftar. Daftar ini ditentukan oleh perilaku dasar. Misalnya,
MarineSoldier
objek akan dirujuk dalamPhysicalObjList
,GraphicalObjList
,UserControlObjList
,HumanObjList
. Jadi, ketika Anda memproses fisika, Anda mengulanginyaPhysicalObjList
, ketika proses perusakan dari racun -HumanObjList
, jika Prajurit mati - singkirkanHumanObjList
tapi tetap masukPhysicalObjList
. Untuk membuat mekanisme ini berfungsi, Anda perlu mengatur penyisipan / menghapus objek saat dibuat / dihapus.Keuntungan dari pendekatan:
AllObjectsList
dengan casting saat pembaruan)MegaAirHumanUnderwaterObject
akan dimasukkan ke daftar yang sesuai alih-alih membuat daftar baru untuk tipenya)PS: Sedangkan untuk memperbarui sendiri, Anda akan menemui masalah ketika beberapa objek berinteraksi dan masing-masing tergantung pada yang lain. Untuk mengatasi ini, kita perlu memengaruhi objek secara eksternal, bukan di dalam pembaruan diri.
sumber