Bagaimana cara menghindari objek dewa GameManager?

51

Saya baru saja membaca jawaban atas pertanyaan tentang penataan kode game . Itu membuat saya bertanya-tanya tentang GameManagerkelas mana-mana , dan seberapa sering itu menjadi masalah di lingkungan produksi. Biarkan saya jelaskan ini.

Pertama, ada prototyping. Tidak ada yang peduli untuk menulis kode yang hebat, kami hanya mencoba menjalankan sesuatu untuk melihat apakah gameplay bertambah.

Lalu ada lampu hijau, dan dalam upaya untuk membersihkan, seseorang menulis a GameManager. Mungkin untuk memegang banyak GameStates, mungkin untuk menyimpan beberapa GameObjects, tidak ada yang besar, sungguh. Manajer yang imut, kecil.

Di dunia damai pra-produksi, permainan ini terbentuk dengan baik. Coders memiliki malam tidur yang tepat dan banyak ide untuk merancang benda dengan Great Design Patterns.

Kemudian produksi dimulai dan segera, tentu saja, ada waktu krisis. Diet seimbang sudah lama berlalu, pelacak kutu ini penuh dengan masalah, orang-orang stres dan permainan harus dirilis kemarin.

Pada titik itu, biasanya, GameManagerini adalah kekacauan yang sangat besar (untuk tetap sopan).

Alasannya sederhana. Setelah semua, ketika menulis sebuah permainan, baik ... semua kode sumber sebenarnya di sini untuk mengelola yang permainan . Sangat mudah untuk hanya menambahkan fitur tambahan kecil ini atau perbaikan bug di GameManager, di mana semuanya sudah tersimpan. Ketika waktu menjadi masalah, tidak ada cara untuk menulis kelas yang terpisah, atau untuk membagi manajer raksasa ini menjadi sub-manajer.

Tentu saja ini adalah anti-pola klasik: objek dewa . Itu adalah hal yang buruk, rasa sakit untuk bergabung, rasa sakit untuk dipertahankan, rasa sakit untuk dipahami, rasa sakit untuk berubah.

Apa yang Anda sarankan untuk mencegah hal ini terjadi?

SUNTING

Saya tahu tergoda untuk menyalahkan penamaan. Tentu saja membuat GameManagerkelas bukan ide yang bagus. Tetapi hal yang sama dapat terjadi pada bersih GameApplicationatau GameStateMachineatau GameSystemyang akhirnya menjadi favorit-lakban pada akhir proyek. Tidak masalah penamaannya, Anda memiliki kelas itu di suatu tempat dalam kode permainan Anda, itu hanya embrio untuk saat ini: Anda belum tahu monster apa yang akan terjadi. Jadi saya tidak mengharapkan jawaban "salahkan nama". Saya ingin cara untuk mencegah hal ini terjadi, sebuah arsitektur pengkodean dan / atau proses yang dapat diikuti oleh tim dengan mengetahui bahwa ini akan terjadi pada titik produksi tertentu. Sayang sekali membuang, katakanlah, satu bulan perbaikan bug dan fitur-fitur menit terakhir, hanya karena semuanya sekarang berada dalam satu manajer besar yang tidak dapat dipelihara.

Laurent Couvidou
sumber
Apakah itu disebut GameManager atau ControllerOfTheGame, atau apa pun yang Anda ingin menyebutnya, maksud saya hindari kelas yang bertugas mengendalikan logika untuk seluruh permainan. Anda tahu Anda melakukannya ketika Anda melakukannya terlepas dari apa yang Anda rencanakan untuk menyebutnya. Saya pikir permainan terakhir saya, saya memiliki levelcontroller yang dimulai hanya untuk menahan tingkat penyimpanan, tetapi jika saya berhenti dan memikirkannya, jelas bahwa objek ini dimaksudkan untuk menangani lebih banyak detail daripada yang seharusnya.
brandon
@brandon Biarkan saya ulangi pertanyaan saya untuk Anda: bagaimana Anda mengontrol logika seluruh permainan tanpa memaparkan diri Anda pada GameManagerefek-yang saya jelaskan di atas?
Laurent Couvidou
Tergantung pada logika. Hal-hal yang jelas seperti pemain, musuh, struktur, dll yang jelas memiliki logikanya sendiri dimasukkan ke dalam kelas masing-masing. Apa yang tampaknya paling Anda tanyakan adalah menangani kondisi permainan. Biasanya akan ada kelas yang melacak keadaan seluruh permainan, tetapi dalam pengalaman saya itu adalah salah satu hal terakhir yang perlu Anda khawatirkan saat membuat prototipe, dan tujuannya harus sangat jelas. Pada dasarnya Anda melakukan perencanaan awal sebelum membuat prototipe atau memulai dari awal ketika Anda memulai produksi untuk menghindari penggunaan kelas sampah yang hanya untuk pengujian.
brandon
Cukup menarik, XNA menciptakan potensi bencana ini untuk Anda. Secara default, kelas Game dibuat untuk mengelola, well, everything. Ini berisi pembaruan utama, menggambar, menginisialisasi, dan memuat metode. Saya sebenarnya sedang dalam proses migrasi ke proyek baru karena permainan saya (tidak terlalu rumit) menjadi terlalu sulit untuk dikelola dengan segala sesuatu yang dijejalkan ke kelas itu. Lebih jauh, tutorial Microsoft tampaknya mendorongnya.
Fibericon

Jawaban:

23

Lalu ada lampu hijau, dan dalam upaya untuk membersihkan, seseorang menulis GameManager. Mungkin untuk memegang banyak GameStates, mungkin untuk menyimpan beberapa GameObjects, tidak ada yang besar, sungguh. Manajer yang imut, kecil.

Anda tahu, ketika saya membaca ini, ada sedikit alarm yang berbunyi di kepala saya. Objek dengan nama "GameManager" tidak akan pernah lucu, atau kecil. Dan seseorang melakukan ini untuk membersihkan kode? Seperti apa itu sebelumnya? OK, bercanda samping: nama kelas harus menjadi indikasi yang jelas tentang apa yang dilakukan kelas, dan ini harus menjadi satu hal (alias: prinsip tanggung jawab tunggal ).

Juga, Anda mungkin masih berakhir dengan objek seperti GameManager, tetapi jelas, itu ada di level yang sangat tinggi, dan itu harusnya berkaitan dengan tugas level tinggi. Mempertahankan katalog objek game? Mungkin. Memfasilitasi komunikasi antar objek game? Tentu. Menghitung tabrakan antar objek? Tidak! Ini juga mengapa nama Manager disukai - terlalu luas, dan memungkinkan banyak penyalahgunaan di bawah spanduk itu.

Aturan praktis praktis tentang ukuran kelas: jika Anda menjalankan beberapa ratus baris kode per kelas, ada sesuatu yang mulai salah. Tanpa terlalu bersemangat, apapun yang berakhir, katakanlah, 300 LOC adalah bau kode bagi saya, dan jika Anda melebihi 1000, lonceng peringatan harus dimatikan. Dengan meyakini bahwa entah bagaimana 1000 baris kode lebih mudah dipahami daripada 4 kelas terstruktur masing-masing dari 250, Anda menipu diri sendiri.

Ketika waktu menjadi masalah, tidak ada cara untuk menulis kelas yang terpisah, atau untuk membagi manajer raksasa ini menjadi sub-manajer.

Saya pikir inilah masalahnya hanya karena masalahnya dibiarkan merambat ke titik di mana semuanya berantakan total. Praktik refactoring benar-benar apa yang Anda cari - Anda harus terus meningkatkan desain kode dengan peningkatan kecil .

Apa yang Anda sarankan untuk mencegah hal ini terjadi?

Masalahnya bukan masalah teknologi, jadi Anda seharusnya tidak mencari perbaikan teknologi untuk itu. Masalahnya adalah ini: ada kecenderungan dalam tim Anda untuk membuat potongan kode monolitik, dan keyakinan bahwa entah bagaimana menguntungkan dalam jangka menengah / panjang untuk bekerja seperti ini. Tampaknya juga tim tersebut kurang memiliki keunggulan arsitektur yang kuat, yang akan mengarahkan arsitektur permainan (atau setidaknya, orang ini terlalu sibuk untuk melakukan tugas ini). Pada dasarnya, satu-satunya jalan keluar adalah membuat anggota tim mengenali bahwa pemikiran ini salah. Tidak ada yang membantu. Kualitas produk akan memburuk, dan tim hanya akan menghabiskan lebih banyak malam memperbaiki barang.

Berita baiknya adalah manfaat langsung dan nyata dari penulisan kode bersih sangat besar, sehingga hampir semua pengembang menyadari manfaatnya dengan sangat cepat. Meyakinkan tim untuk bekerja dengan cara ini untuk sementara waktu, hasilnya akan melakukan sisanya.

Bagian yang sulit adalah mengembangkan rasa untuk apa yang merupakan kode buruk (dan bakat untuk cepat menghasilkan desain yang lebih baik) adalah salah satu keterampilan yang lebih sulit untuk dipelajari dalam pengembangan. Saran saya bergantung pada harapan bahwa Anda memiliki seseorang yang cukup senior dalam tim yang dapat melakukan ini - jauh lebih mudah untuk meyakinkan orang seperti itu.

Edit - Info lebih banyak:

Secara umum, saya tidak berpikir masalah Anda terbatas pada pengembangan game. Pada intinya, ini adalah masalah rekayasa perangkat lunak, maka komentar saya ke arah itu. Apa yang mungkin berbeda adalah sifat industri pengembangan game, apakah lebih banyak hasil dan berorientasi tenggat waktu daripada jenis pengembangan lainnya, saya tidak yakin.

Khusus untuk pengembangan game, jawaban yang diterima untuk pertanyaan ini di StackOverflow mengenai kiat "terutama arsitektur game", mengatakan:

Ikuti prinsip Solid dari desain berorientasi objek ....

Ini pada dasarnya persis apa yang saya katakan. Ketika saya di bawah tekanan, saya juga menemukan diri saya menulis potongan kode besar, tetapi saya telah mengebornya ke kepala saya bahwa itu adalah hutang teknis. Apa yang cenderung bekerja dengan baik bagi saya, adalah menghabiskan paruh pertama (atau tiga perempat) hari itu menghasilkan sejumlah besar kode berkualitas menengah, dan kemudian duduk santai, dan memikirkannya sebentar; lakukan sedikit desain di kepala saya, atau di atas kertas / papan tulis, tentang cara meningkatkan kode sedikit. Seringkali, saya melihat kode berulang, dan saya dapat benar-benar mengurangi total baris kode dengan memecah semuanya, sambil meningkatkan keterbacaan. Kali ini berinvestasi membayar untuk dirinya sendiri begitu cepat, yang menyebutnya sebagai "investasi" terdengar konyol - cukup sering saya akan mengambil bug yang mungkin terbuang setengah hari saya (seminggu kemudian), seandainya saya membiarkannya berlanjut.

  • Memperbaiki hal-hal pada hari yang sama Anda kode mereka.
  • Anda akan senang Anda melakukannya dalam beberapa jam.

Datang untuk benar-benar percaya hal di atas sulit; Saya telah berhasil melakukannya untuk pekerjaan saya sendiri hanya karena saya telah mengalami, berulang kali, efeknya. Meski begitu, masih sulit bagi saya untuk membenarkan memperbaiki kode ketika saya bisa mengeluarkan lebih banyak ... Jadi saya benar-benar mengerti dari mana Anda berasal. Sayangnya, saran ini mungkin terlalu umum, dan tidak mudah dilakukan. Saya sangat percaya akan hal itu! :)

Untuk menjawab contoh spesifik Anda:

Kode Bersih Paman Bob melakukan pekerjaan luar biasa untuk meringkas seperti apa kode kualitas yang baik. Saya kebetulan setuju dengan hampir semua isinya. Jadi, ketika saya memikirkan contoh Anda tentang 30.000 manajer kelas LOC, saya benar-benar tidak setuju dengan bagian "alasan bagus". Saya tidak ingin terdengar ofensif, tetapi berpikir seperti itu akan menyebabkan masalah. Tidak ada alasan bagus untuk memiliki kode sebanyak itu dalam satu file - hampir 1000 halaman teks! Setiap manfaat dari lokalitas (kecepatan eksekusi, atau desain "kesederhanaan") akan segera dibatalkan oleh pengembang yang sepenuhnya macet mencoba menavigasi monster itu, dan itu bahkan sebelum kita membahas penggabungan, dll.

Jika Anda tidak yakin, saran terbaik saya adalah mengambil salinan buku di atas, dan memeriksanya. Menerapkan tipe pemikiran seperti itu untuk mengarahkan orang secara sukarela membuat kode yang bersih dan jelas, yang terstruktur dengan baik.

Daniel B
sumber
Ini bukan cacat yang pernah kulihat. Saya telah melihat ini di beberapa tim, untuk beberapa proyek. Saya telah melihat banyak orang yang berbakat dan terstruktur berjuang dengan ini. Ketika berada di bawah tekanan, 300 baris batas kode bukanlah sesuatu yang dipedulikan oleh produsen Anda. Atau pemain btw. Tentu ini bagus dan manis, tetapi iblis ada dalam perinciannya. Kasus terburuk yang GameManagerpernah saya lihat sejauh ini adalah sekitar 30.000 baris kode, dan saya cukup yakin sebagian besar ada di sini karena alasan yang sangat bagus. Tentu saja ini ekstrem, tetapi hal-hal itu terjadi di dunia nyata. Saya meminta cara untuk membatasi GameManagerefek ini .
Laurent Couvidou
@lorancou - Saya telah menambahkan sedikit info tambahan ke posting, itu terlalu banyak untuk komentar, mudah-mudahan itu memberi Anda sedikit lebih banyak ide, bahkan jika saya tidak dapat mengarahkan Anda ke arsitektur beton apa pun contoh.
Daniel B
Ah, saya tidak membaca buku itu jadi itu saran yang bagus. Oh, dan saya tidak bermaksud bahwa ada alasan bagus untuk menambahkan kode dalam kelas ribuan baris. Maksud saya, semua kode dalam objek dewa semacam itu melakukan sesuatu yang bermanfaat. Saya mungkin menulis itu agak terlalu cepat;)
Laurent Couvidou
@lorancou Hehe, itu bagus ... dengan cara itu ada kegilaan :)
Daniel B
"Praktik refactoring benar-benar apa yang Anda cari - Anda harus terus meningkatkan desain kode dengan peningkatan kecil." Saya pikir Anda punya poin yang sangat kuat di sana. Kekacauan menarik kekacauan, itulah alasan sebenarnya di balik ini, jadi kekacauan harus diperjuangkan dalam jangka panjang. Saya akan menerima jawaban ini, tetapi itu tidak berarti tidak ada nilai di yang lain!
Laurent Couvidou
7

Ini sebenarnya bukan masalah dengan pemrograman game, tetapi dengan pemrograman pada umumnya.

semua kode sumber sebenarnya ada di sini untuk mengelola permainan.

Inilah sebabnya mengapa Anda harus mengerutkan kening pada setiap pembuat kode Berorientasi Objek yang akan menyarankan kelas dengan "Manajer" pada namanya. Agar adil, saya pikir hampir tidak mungkin untuk menghindari "semigod" objek dalam game, tetapi kami hanya menyebutnya "sistem".

Perangkat lunak apa pun cenderung kotor ketika tenggat waktu sudah dekat. Itu bagian dari pekerjaan. Dan pada dasarnya tidak ada yang salah dengan " pemrograman tipe saluran ", terutama ketika Anda harus mengirimkan produk Anda dalam beberapa jam. Anda tidak bisa menghilangkan masalah ini sepenuhnya, Anda bisa menguranginya.

Meskipun jelas, jika Anda tergoda untuk memasukkan sesuatu ke dalam GameManager, itu berarti Anda tidak memiliki basis kode yang sangat sehat. Mungkin ada dua masalah:

  • Kode Anda terlalu rumit. Terlalu banyak pola, optimisasi prematur, tidak ada lagi yang mengerti alurnya. Cobalah untuk menggunakan lebih banyak TDD ketika mengembangkan jika Anda bisa, karena itu solusi yang sangat baik untuk melawan kompleksitas.

  • Kode Anda tidak terstruktur dengan baik. Anda (ab) menggunakan variabel global, kelas tidak digabungkan secara longgar, tidak ada antarmuka apa pun, tidak ada sistem acara, dan sebagainya.

Jika kode tampak baik-baik saja, Anda harus ingat, untuk setiap proyek, "apa yang salah" dan menghindarinya lain kali.

Akhirnya, itu juga bisa menjadi masalah manajemen tim: apakah ada cukup waktu? Apakah semua orang tahu tentang arsitektur yang Anda tuju? Siapa yang mengizinkan komit dengan kelas dengan kata "Manajer" di dalamnya?

Raveline
sumber
Tidak ada yang salah dengan pemrograman lakban, saya berikan itu. Tapi saya cukup yakin - Kelas manajer ada di sekitar mesin game, setidaknya saya sudah sering melihatnya. Mereka berguna selama mereka tidak tumbuh terlalu besar ... Apa yang salah dengan a SceneManageratau a NetworkManager? Saya tidak mengerti mengapa kita harus mencegahnya, atau bagaimana mengganti -Manager dengan -Sistem membantu. Saya mencari saran untuk arsitektur pengganti untuk apa yang biasanya masuk ke GameManager, sesuatu yang kurang rentan kode.
Laurent Couvidou
Apa yang salah dengan SceneManager? Nah, apa perbedaan antara Scene dan SceneManager? Jaringan dan Manajer Jaringan? Pada aspek lain: Saya tidak berpikir ini adalah masalah arsitektur, tetapi jika Anda bisa memberikan contoh nyata dari sesuatu yang ada di GameManager dan tidak ada di sana, akan lebih mudah untuk menyarankan solusi.
Raveline
OK, jadi lupakan soal -Manager. Katakanlah Anda memiliki kelas yang menangani status gim Anda, sebut saja GameStatesCollection. Pada awalnya, Anda hanya akan memiliki beberapa status permainan dan cara untuk beralih di antara mereka. Lalu ada layar pemuatan yang datang dan menyulitkan semua ini. Kemudian, beberapa programmer harus berurusan dengan bug ketika pengguna mengeluarkan disk saat Anda beralih dari Level_A ke Level_B, dan satu-satunya cara untuk memperbaikinya tanpa menghabiskan setengah hari refactoring GameStatesCollection. Boom, objek dewa.
Laurent Couvidou
5

Jadi, membawa satu kemungkinan solusi dari dunia Enterprise-y: apakah Anda sudah memeriksa inversi injeksi kontrol / ketergantungan?

Pada dasarnya, Anda mendapatkan kerangka kerja ini yang merupakan wadah untuk semua hal lain di game Anda. Itu, dan hanya itu, yang tahu bagaimana menghubungkan semuanya dan apa hubungan antara objek-objek Anda. Tak satu pun dari objek Anda instantiate sesuatu di dalam konstruktor mereka sendiri. Daripada menciptakan apa yang mereka butuhkan, mereka meminta apa yang mereka butuhkan dari kerangka kerja IoC; pada saat penciptaan, kerangka kerja IoC "tahu" objek apa yang dibutuhkan untuk memuaskan ketergantungan dan mengisinya. FTW kopling longgar.

Ketika kelas EnemyTreasure Anda meminta wadah untuk objek GameLevel, kerangka kerja tahu contoh apa yang harus diberikan. Bagaimana bisa tahu? Mungkin itu dipakai sebelumnya, mungkin itu meminta beberapa GameLevelFactory terdekat. Intinya adalah, EnemyTreasure tidak tahu dari mana instance GameLevel berasal. Ia hanya tahu bahwa ketergantungannya sekarang terpuaskan dan ia bisa melanjutkan hidupnya.

Oh, saya melihat kelas PlayerSprite Anda mengimplementasikan IUpdateable. Nah itu bagus, karena kerangka kerja secara otomatis berlangganan setiap objek IUpdateable ke Timer, yang merupakan tempat loop permainan utama. Every Timer :: tick () secara otomatis memanggil semua metode IUpdatedable :: update (). Mudah kan?

Secara realistis, Anda tidak memerlukan kerangka bighugeohmygod ini untuk melakukan ini untuk Anda: Anda dapat melakukan bagian penting dari itu sendiri. Intinya adalah bahwa jika Anda menemukan diri Anda membuat objek tipe -Manager maka, kemungkinan besar, Anda tidak mendistribusikan tanggung jawab Anda di antara kelas Anda dengan benar atau kehilangan beberapa kelas di suatu tempat.

drhayes
sumber
Menarik, pertama kali saya mendengar tentang IOC. Ini mungkin terlalu bighugeohmygod untuk pemrograman game, tapi aku akan memeriksanya!
Laurent Couvidou
IoC, Bukankah kita biasa menyebutnya akal sehat?
brice
Diberi setengah kesempatan, seorang insinyur akan membuat kerangka dari apa pun. (= Selain itu, saya tidak menganjurkan menggunakan kerangka kerja, saya menganjurkan pola arsitektur.
drhayes
3

Di masa lalu, saya biasanya berakhir dengan kelas dewa jika saya melakukan hal berikut:

  • duduk di editor game / IDE / notepad sebelum saya menulis diagram kelas (atau hanya sketsa objek utama)
  • Mulailah dengan ide permainan yang sangat samar dan segera kode itu, kemudian mulai iterasi pada ide-ide baru saat mereka datang
  • buat file kelas yang disebut GameManager ketika saya tidak membuat game berbasis stat seperti strategi berbasis giliran di mana GameManager sebenarnya adalah objek domain
  • tidak peduli dengan organisasi kode sejak dini

Ini mungkin kontroversial, yang saya akan tertawa jika itu, tapi saya tidak bisa memikirkan satu waktu bahkan dalam prototipe bahwa Anda tidak harus menulis diagram kelas yang sangat dasar sebelum menulis prototipe yang dapat dimainkan. Saya memang cenderung menguji ide teknologi sebelum merencanakan, tetapi hanya itu. Jadi jika saya ingin membuat portal, saya akan menulis kode yang sangat dasar untuk membuat portal berfungsi, lalu katakan ok waktu yang tepat untuk beberapa perencanaan kecil maka saya dapat bekerja pada prototipe yang dapat dimainkan.

brandon
sumber
1

Saya tahu pertanyaan ini sudah tua sekarang, tetapi saya pikir saya akan menambahkan 2 sen saya.

Saat mendesain game, saya hampir selalu memiliki objek Game. Namun, levelnya sangat tinggi dan tidak benar-benar "melakukan apa pun" itu sendiri.

Misalnya, ini memberi tahu kelas Graphics ke Render, tetapi tidak ada hubungannya dengan proses rendering. Mungkin meminta LevelManager untuk memuat level baru, tetapi tidak ada hubungannya dengan proses pemuatan.

Singkatnya, saya menggunakan objek semi-dewa untuk mengatur penggunaan kelas lain.

OMGtechy
sumber
2
Ini sama sekali tidak seperti dewa - ini disebut abstraksi. :)
Vaughan Hilts