Apakah ada kasus di mana global / lajang berguna dalam pengembangan game? [Tutup]

11

Saya tahu bahwa memiliki variabel global atau kelas tunggal menciptakan kasus yang mungkin sulit untuk diuji / dikelola dan saya telah tertangkap basah dalam menggunakan pola-pola tersebut dalam kode tetapi sering kali Anda harus mengirim.

Jadi apakah ada kasus di mana variabel global atau lajang benar-benar berguna dalam pengembangan game?

Ólafur Waage
sumber

Jawaban:

30

Hal-hal ini selalu bermanfaat . Apakah itu solusi tercantik atau teraman adalah masalah lain, tapi saya pikir pengembangan game melibatkan tingkat pragmatisme tertentu.

JasonD
sumber
Sangat dekat dengan mantra saya.
Ólafur Waage
15

Jelas daftar yang tidak lengkap, tapi begini:

Cons

  • Manajemen seumur hidup . Singleton dan global mungkin ingin memulai sebelum sistem utama (mis. Tumpukan) diinisialisasi, tergantung pada bagaimana Anda mengaturnya. Jika Anda pernah ingin merobohkannya (berguna jika Anda melakukan pelacakan kebocoran, misalnya) Anda harus berhati-hati tentang urutan teardown atau mulai masuk ke hal-hal seperti burung hantu phoenix . (Lihat kegagalan pesanan inisialisasi statis .)
  • Kontrol akses . Haruskah rendering hanya memiliki akses const ke data game, sementara pembaruan mendapat non-const? Ini mungkin lebih sulit untuk ditegakkan dengan para lajang dan global.
  • Negara . Seperti yang ditunjukkan Kaj, lebih sulit untuk secara fungsional mendekomposisi dan mentransformasikan lajang untuk bekerja sama sekali dalam arsitektur non-shared-memory. Ini juga memiliki implikasi kinerja potensial untuk jenis lain dari sistem NUMA (mengakses memori yang bukan lokal). Lajang biasanya mewakili keadaan terpusat, dan dengan demikian merupakan antitesis dari transformasi yang dibuat lebih mudah dengan kemurnian.

Baik pro atau kontra

  • Konkurensi . Dalam lingkungan yang bersamaan, lajang dapat menjadi masalah (Anda harus mempertimbangkan masalah ras / reentrancy data) atau berkat (lebih mudah untuk dipusatkan dan alasan tentang penguncian dan manajemen sumber daya). Penggunaan pintar hal-hal seperti utas penyimpanan lokal dapat mengurangi potensi masalah, tetapi biasanya ini bukan masalah yang mudah.
  • Codegen (tergantung pada kompiler, arsitektur, dll): Untuk implementasi singleton create-on-first use yang naif, Anda mungkin mengevaluasi cabang bersyarat ekstra untuk setiap akses. (Ini bisa bertambah.) Beberapa global yang digunakan dalam suatu fungsi dapat mengasapi kumpulan literal . Pendekatan "struct of all globals" dapat menghemat ruang dalam kumpulan literal: hanya satu entri ke dasar struct, dan kemudian offset yang dikodekan dalam instruksi pemuatan. Terakhir, jika Anda menghindari global dan lajang dengan segala cara, Anda biasanya perlu menggunakan setidaknya sedikit memori tambahan (register, stack, atau heap) untuk memberikan pointer, referensi, atau bahkan salinan.

Pro

  • Kesederhanaan . Jika Anda tahu kontra yang tercantum di atas tidak terlalu memengaruhi Anda (mis. Anda bekerja di lingkungan berulir tunggal untuk platform genggam CPU tunggal), Anda menghindari beberapa arsitektur (seperti passing-argumen yang disebutkan sebelumnya). Lajang dan global mungkin lebih mudah dipahami untuk coders yang kurang berpengalaman (meskipun mungkin lebih mudah untuk menggunakannya secara salah).
  • Manajemen seumur hidup (lagi). Jika Anda menggunakan "struct global" atau mekanisme lain selain permintaan create-on-first-request, Anda dapat dengan mudah membaca dan mengontrol kontrol inisialisasi dan kehancuran dengan baik. Anda mengotomatisasi ini pada tingkat tertentu, atau mengelolanya secara manual (harus mengelolanya mungkin pro atau kontra, tergantung pada jumlah global / lajang dan antar-ketergantungan mereka).

Kami banyak menggunakan "struct of singletons global" dalam judul handheld kami. Judul PC dan konsol cenderung kurang mengandalkannya; kami akan beralih lebih banyak ke arsitektur berbasis pesan / event Yang telah dikatakan, judul pc / konsol masih sering menggunakan TextureManager pusat; karena biasanya membungkus satu sumber daya bersama (memori tekstur) ini masuk akal bagi kami.

Jika Anda menjaga API Anda relatif bersih, mungkin tidak terlalu sulit untuk menolak dari (atau ke!) Pola tunggal ketika Anda membutuhkan ...

Leander
sumber
Daftar hebat. Secara pribadi saya hanya menemukan nilai negatif dalam lajang dan global karena masalah yang berasal dari keadaan bersama (kemungkinan bisa berubah). Saya tidak menemukan masalah "codegen" sebagai masalah; Anda juga perlu melewati pointer melalui register atau Anda perlu melakukan beberapa urutan operasi untuk mendapatkannya, saya pikir itu mengubah kode vs ukuran data dengan jelas, tetapi jumlahnya akan hampir sama.
dash-tom-bang
Ya, ulang kodegen, satu-satunya hal yang benar-benar kita lihat membuat perbedaan signifikan adalah beralih dari masing-masing global ke tabel global: ia menyusutkan kumpulan literal, dan karenanya ukuran bagian .teks, beberapa persen. Yang merupakan hal yang sangat baik pada sistem kecil. =) Manfaat kinerja tidak memukul dcache cukup banyak untuk literal hanya manfaat samping yang bagus (walaupun kecil). Satu keuntungan lain dari pindah ke tabel global adalah kemampuan untuk benar-benar dengan mudah memindahkannya ke bagian yang berada dalam memori lebih cepat ...
leander
4

Mereka bisa sangat berguna, terutama selama prototyping atau implementasi eksperimental, tetapi secara umum kami lebih suka melewati referensi untuk manajer seperti struktur, dengan cara itu Anda setidaknya memiliki kontrol atas dari mana mereka diakses. Masalah terbesar dengan global dan lajang (menurut saya) adalah bahwa mereka tidak sangat ramah multithread, dan kode yang menggunakannya jauh lebih sulit untuk port ke memori yang tidak dibagi, seperti SPU.

Kaj
sumber
2

Saya akan mengatakan bahwa desain singleton itu sendiri tidak berguna sama sekali. Variabel global pasti bisa berguna tetapi saya lebih suka melihatnya tersembunyi di balik antarmuka yang ditulis dengan baik sehingga Anda tidak menyadari keberadaannya. Dengan lajang Anda pasti menyadari keberadaan mereka.

Saya sering menggunakan variabel global untuk hal-hal yang perlu diakses sepanjang mesin. Alat kinerja saya adalah salah satu contoh yang baik yang saya gunakan di seluruh mesin untuk menelepon. Panggilannya sederhana; ProbeRegister (), ProbeHit () dan ProbeScoped (). Akses nyata mereka sedikit lebih rumit dan menggunakan variabel global untuk beberapa hal mereka.

Simon
sumber
2

Masalah utama dengan global, dan lajang yang diimplementasikan dengan buruk adalah bug konstruksi & dekonstruksi yang tidak jelas.

Jadi jika Anda bekerja dengan primitif yang tidak memiliki masalah ini atau sangat menyadari masalah dengan sebuah pointer. Kemudian mereka dapat digunakan dengan aman.

Global memiliki tempat mereka, sama seperti gotos dan tidak boleh diberhentikan begitu saja tetapi digunakan dengan hati-hati.

Ada penjelasan yang bagus tentang hal itu di Panduan Gaya Google C ++

Kimau
sumber
Saya tidak setuju bahwa itulah masalah utama; "Jangan gunakan global" lebih jauh dari konstruktor keberadaan. Saya akan mengatakan ada dua masalah utama dengan mereka. Pertama, mereka memperumit alasan tentang suatu program. Jika saya memiliki fungsi yang mengakses global, perilaku fungsi itu tidak hanya bergantung pada argumennya sendiri, tetapi juga pada semua fungsi lain yang mengakses global. Itu lebih banyak untuk dipikirkan. Kedua, bahkan jika Anda dapat menyimpan semuanya di kepala Anda (Anda tidak bisa), mereka masih menghasilkan masalah konkurensi yang lebih rumit.
@Joe kata kuncinya adalah "dependensi." Fungsi yang mengakses global (atau lajang atau negara bagian lain mana pun) memiliki dependensi tersirat pada semua hal itu. Jauh lebih mudah untuk berpikir tentang sedikit kode jika semua dependensinya eksplisit yang adalah apa yang Anda dapatkan ketika daftar dependensi lengkap didokumentasikan dalam daftar argumen.
dash-tom-bang
1

Global berguna saat dengan cepat membuat prototipe sistem yang membutuhkan beberapa keadaan di antara panggilan fungsi. Setelah Anda menetapkan bahwa sistem bekerja, pindahkan status ke dalam kelas dan buat fungsinya menjadi metode kelas itu.

Lajang berguna untuk menyebabkan masalah pada diri sendiri dan orang lain. Semakin banyak kondisi global yang Anda perkenalkan, semakin banyak masalah yang Anda miliki dengan kebenaran kode, pemeliharaan, ekstensibilitas, konkurensi, dll. Hanya saja, jangan lakukan itu.

Kylotan
sumber
1

Saya cenderung merekomendasikan menggunakan semacam wadah DI / IoC dengan manajemen seumur hidup khusus alih-alih lajang (bahkan jika Anda menggunakan manajer seumur hidup "satu contoh"). Setidaknya itu mudah untuk menukar implementasi untuk memfasilitasi pengujian.

Mike Strobel
sumber
Bagi Anda yang tidak tahu, DI adalah "Dependency Injection", dan IoC adalah "Inversion of Control". Tautan: martinfowler.com/articles/injection.html (saya telah membaca tentang ini sebelumnya tetapi tidak pernah melihat akronimnya, jadi saya butuh sedikit pencarian.) +1 untuk menyebutkan transformasi yang luar biasa ini.
Leander
0

Lajang adalah cara yang bagus untuk menyimpan keadaan bersama dalam prototipe awal.

Mereka bukan peluru perak dan datang dengan beberapa masalah, tetapi mereka adalah pola yang sangat berguna untuk negara UI / Logika tertentu.

Misalnya di iOS, Anda menggunakan lajang untuk mendapatkan aplikasi [UIAplikasi dibagikan], di cocos2d Anda dapat menggunakannya untuk mendapatkan referensi ke objek tertentu seperti [CCNotifications sharedManager] dan secara pribadi saya biasanya memulai dengan singleton [Game sharedGame] di mana saya bisa keadaan toko yang dibagi antara banyak komponen yang berbeda.

DFectuoso
sumber
0

Wow, ini menarik bagi saya, karena saya pribadi tidak pernah punya masalah dengan pola tunggal. Proyek saya saat ini adalah mesin game C ++ untuk Nintendo DS, dan saya mengimplementasikan banyak utilitas akses perangkat keras (mis. VRAM Banks, Wifi, dua mesin grafis) sebagai instance tunggal, karena mereka dimaksudkan untuk membungkus global C fungsi di pustaka yang mendasarinya.


sumber
Pertanyaan saya adalah, mengapa menggunakan lajang sama sekali? Saya melihat bahwa orang suka membungkus API dengan lajang atau kelas yang hanya terdiri dari fungsi statis, tetapi mengapa repot-repot dengan kelas sama sekali? Tampaknya lebih mudah bagi saya untuk hanya memiliki panggilan fungsi (dibungkus sesuai keinginan), tetapi kemudian internal mereka mengakses negara "global". Misalnya Log :: GetInstance () -> LogError (...) bisa juga LogError (...) yang secara internal mengakses setiap negara global tanpa memerlukan kode klien untuk mengetahuinya.
dash-tom-bang
0

Hanya ketika Anda memiliki item yang hanya memiliki satu pengontrol tetapi ditangani oleh beberapa modul.

Seperti, misalnya, antarmuka mouse. Atau antarmuka joystick. Atau pemutar musik. Atau pemutar suara. Atau layarnya. Atau pengelola file simpan.

zaratustra
sumber
-1

Global jauh lebih cepat! Jadi itu akan sangat cocok untuk aplikasi intensif kinerja, seperti game.

Singletons adalah global yang lebih baik, IMO, dan dengan demikian alat yang tepat.

Gunakan dengan hemat!

jacmoe
sumber
Jauh lebih cepat dari apa ? Global akan berada di beberapa halaman memori acak jika mereka adalah pointer, dan beberapa halaman memori tetap tapi mungkin jauh jika mereka nilai. Kompiler tidak dapat menggunakan optimisasi lubang intip berguna pada mereka. Dengan ukuran apa pun yang bisa saya pikirkan, global lambat .
Lebih cepat dari getter dan setter dan melewati referensi di sekitar. Tapi tidak banyak. Itu membantu menurunkan ukuran, sehingga akan membantu dalam beberapa sistem. Saya mungkin melebih-lebihkan ketika saya mengatakan 'banyak', tetapi saya sangat skeptis terhadap siapa pun yang mengatakan bahwa Anda tidak boleh menggunakan sesuatu. Anda hanya perlu menggunakan akal sehat Anda. Lagipula, lajang tidak lebih dari anggota kelas statis, dan mereka adalah global.
jacmoe
Tetapi untuk menyelesaikan masalah sinkronisasi dengan global Anda memerlukan getter / setter untuk mereka, jadi itu tidak benar-benar relevan. Masalah dengan (dan manfaat yang jarang dari) keadaan global adalah keadaan global, bukan kekhawatiran tentang kecepatan atau (secara efektif) aspek sintaksis antarmuka.