Loop game "Optimal" untuk penggulung sisi 2D

14

Apakah mungkin untuk menggambarkan tata letak "optimal" (dalam hal kinerja) untuk loop permainan penggulung sisi 2D? Dalam konteks ini "loop game" mengambil input pengguna, memperbarui status objek game dan menggambar objek game.

Misalnya memiliki kelas dasar GameObject dengan hierarki warisan yang dalam bisa bagus untuk pemeliharaan ... Anda dapat melakukan sesuatu seperti berikut:

foreach(GameObject g in gameObjects) g.update();

Namun saya pikir pendekatan ini dapat menciptakan masalah kinerja.

Di sisi lain, semua data dan fungsi objek game bisa bersifat global. Yang akan menjadi sakit kepala pemeliharaan tetapi mungkin lebih dekat ke loop game yang berkinerja optimal.

Adakah pikiran? Saya tertarik pada aplikasi praktis dari struktur loop game yang hampir optimal ... bahkan jika saya mendapatkan sakit kepala pemeliharaan dengan imbalan kinerja yang luar biasa.

MrDatabase
sumber

Jawaban:

45

Misalnya memiliki kelas dasar GameObject dengan hierarki warisan yang dalam bisa bagus untuk pemeliharaan ...

Sebenarnya, hierarki yang dalam umumnya lebih buruk untuk perawatan daripada yang dangkal, dan gaya arsitektur modern untuk objek game cenderung mengarah ke pendekatan berbasis agregasi yang dangkal .

Namun saya pikir pendekatan ini dapat menciptakan masalah kinerja. Di sisi lain, semua data dan fungsi objek game bisa bersifat global. Yang akan menjadi sakit kepala pemeliharaan tetapi mungkin lebih dekat ke loop game yang berkinerja optimal.

Loop yang Anda tunjukkan berpotensi memiliki masalah kinerja, tetapi tidak, seperti yang tersirat oleh pernyataan Anda selanjutnya, karena Anda memiliki data instan dan fungsi anggota di GameObjectkelas. Alih-alih, masalahnya adalah Anda jika Anda memperlakukan setiap objek dalam game sama persis, Anda mungkin tidak mengelompokkan objek-objek tersebut secara cerdas - mereka kemungkinan tersebar secara acak di seluruh daftar itu. Maka, berpotensi setiap panggilan ke metode pembaruan untuk objek itu (apakah metode itu adalah fungsi global atau tidak, dan apakah objek memiliki data contoh atau "data global" yang melayang-layang di beberapa tabel tempat Anda mengindeks atau apa pun) berbeda dari panggilan pembaruan di iterasi loop terakhir.

Ini dapat menambah tekanan pada sistem karena Anda mungkin perlu halaman memori dengan fungsi yang relevan masuk dan keluar dari memori dan mengisi ulang cache instruksi lebih sering, menghasilkan loop yang lebih lambat. Apakah ini dapat diamati atau tidak dengan mata telanjang (atau bahkan dalam profiler) tergantung pada apa yang dianggap sebagai "objek game", berapa banyak dari mereka yang ada rata-rata, dan apa lagi yang terjadi dalam aplikasi Anda.

Sistem objek berorientasi komponen adalah tren populer saat ini, memanfaatkan filosofi bahwa agregasi lebih disukai daripada warisan . Sistem seperti itu berpotensi memungkinkan Anda untuk membagi "perbarui" logika komponen (di mana "komponen" secara kasar didefinisikan sebagai beberapa unit fungsi, seperti benda yang mewakili bagian yang disimulasikan secara fisik dari beberapa objek, yang diproses oleh sistem fisika ) ke beberapa utas - dibedakan berdasarkan jenis komponen - jika mungkin dan diinginkan, yang dapat memperoleh peningkatan kinerja. Paling tidak Anda dapat mengatur komponen sedemikian rupa sehingga semua komponen dari pembaruan jenis yang diberikan bersama , memanfaatkan cache CPU secara optimal. Contoh dari sistem berorientasi komponen seperti itu dibahas dalam utas ini .

Sistem seperti ini seringkali sangat terpisah, yang merupakan keuntungan bagi pemeliharaan juga.

Desain berorientasi data adalah pendekatan terkait - ini tentang mengarahkan diri Anda di sekitar data yang dibutuhkan objek sebagai prioritas pertama, sehingga data dapat diproses secara efektif dalam jumlah besar (misalnya). Ini umumnya berarti organisasi yang mencoba untuk menjaga data yang digunakan untuk cluster tujuan yang sama bersama-sama dan dioperasikan sekaligus. Ini pada dasarnya tidak kompatibel dengan desain OO, dan Anda dapat menemukan beberapa obrolan tentang masalah ini di sini di GDSE dalam pertanyaan ini .

Akibatnya, pendekatan yang lebih optimal untuk loop game akan, bukan yang asli Anda

foreach(GameObject g in gameObjects) g.update();

sesuatu yang lebih seperti

ProcessUserInput();
UpdatePhysicsForAllObjects();
UpdateScriptsForAllObjects();
UpdateRenderDataForAllObjects();
RenderEverything();

Dalam dunia seperti itu, masing-masing GameObjectmungkin memiliki pointer atau referensi sendiri PhysicsDataatau Scriptatau RenderData, untuk kasus-kasus di mana Anda mungkin perlu untuk berinteraksi dengan objek secara individual, tetapi sebenarnya PhysicsData, Scripts, RenderData, dan sebagainya semua akan dimiliki oleh subsistem masing-masing (simulator fisika, lingkungan hosting skrip, renderer) dan diproses secara massal seperti yang ditunjukkan di atas.

Sangat penting untuk dicatat bahwa pendekatan ini bukan peluru ajaib dan tidak akan selalu menghasilkan peningkatan kinerja (meskipun umumnya desain yang lebih baik daripada pohon warisan yang mendalam). Pada dasarnya Anda mungkin akan melihat pada dasarnya tidak ada perbedaan kinerja jika Anda memiliki sangat sedikit objek, atau sangat banyak objek yang tidak dapat Anda pisahkan dengan pembaruan secara efektif.

Sayangnya, tidak ada loop ajaib seperti itu yang paling optimal - setiap permainan berbeda dan mungkin memerlukan penyetelan kinerja dengan cara yang berbeda. Jadi sangat penting untuk mengukur (profil) hal-hal sebelum Anda secara membabi buta mengambil saran dari beberapa pria acak di internet.

Komunitas
sumber
5
Tuan yang baik, ini adalah jawaban yang bagus.
Raveline
2

Gaya pemrograman game-objek baik-baik saja untuk proyek yang lebih kecil. Ketika proyek menjadi sangat besar Anda akan berakhir dengan hierarki warisan yang mendalam dan basis game-objek akan menjadi sangat berat!

Sebagai contoh ... Misalkan Anda mulai membuat basis objek-Game dengan atribut minimum mengatakan Posisi (untuk x dan y), displayObject (ini merujuk pada gambar jika elemen gambarnya), lebar, tinggi dll ...

Sekarang nanti jika kita perlu menambahkan sub kelas yang akan memiliki status animasi, maka Anda mungkin memindahkan 'kode kontrol animasi' (katakanlah CurrentAnimationId, Jumlah frame untuk animasi ini, dll.) Ke GameObjectBase dengan anggapan bahwa sebagian besar objek akan memiliki animasi.
Nanti jika Anda ingin menambahkan tubuh kaku yang statis (tidak memiliki animasi apa pun), Anda perlu memeriksa 'kode kendali animasi' di fungsi pembaruan GameObject Base (walaupun ada tanda centang apakah animasi ada atau tidak. ..itu penting!) Bertentangan dengan ini jika Anda mengikuti Struktur berbasis Komponen, Anda akan memiliki 'Komponen Kontrol animasi' yang melekat pada objek Anda. Jika diperlukan Anda akan melampirkannya. Untuk tubuh statis Anda tidak akan memasang komponen itu. Ini cara, kita dapat menghindari pemeriksaan yang tidak perlu.

Jadi, pemilihan memilih gaya tergantung pada ukuran proyek. Struktur objek Game hanya mudah dikuasai untuk proyek yang lebih kecil. Semoga ini bisa membantu sedikit.

Ayyappa
sumber
@ Josh Petrie - Benar-benar jawaban yang bagus!
Ayyappa
@Josh Petrie - Jawaban Sungguh Bagus dengan tautan yang berguna! (Sry tidak tahu bagaimana menempatkan komentar ini di sebelah pos Anda: P)
Ayyappa
Anda dapat mengklik tautan "tambahkan komentar" di bawah jawaban saya, biasanya, tetapi itu membutuhkan reputasi lebih dari yang Anda miliki saat ini (lihat gamedev.stackexchange.com/privileges/comment ). Dan terimakasih!