Saya telah membaca di banyak tempat bahwa DrawableGameComponents harus disimpan untuk hal-hal seperti "level" atau semacam manajer alih-alih menggunakannya, misalnya, untuk karakter atau ubin (Seperti yang dikatakan orang ini di sini ). Tapi saya tidak mengerti mengapa demikian. Saya membaca posting ini dan itu masuk akal bagi saya, tetapi ini adalah minoritas.
Saya biasanya tidak akan terlalu memperhatikan hal-hal seperti ini, tetapi dalam hal ini saya ingin tahu mengapa mayoritas orang percaya ini bukan jalan yang harus ditempuh. Mungkin saya melewatkan sesuatu.
xna
game-component
Kenji Kina
sumber
sumber
DrawableGameComponent
kunci Anda menjadi satu set tanda tangan metode dan model data tertentu yang dipertahankan. Ini biasanya merupakan pilihan yang salah pada awalnya, dan kunci-in secara signifikan menghambat evolusi kode di masa mendatang. "Tetapi izinkan saya memberi tahu Anda apa yang terjadi di sini ...DrawableGameComponent
adalah bagian dari inti XNA API (sekali lagi: sebagian besar karena alasan historis). Pemrogram pemula menggunakannya karena "ada" dan "berfungsi" dan mereka tidak tahu yang lebih baik. Mereka melihat jawaban saya mengatakan bahwa mereka " salah " dan - karena sifat psikologi manusia - menolaknya dan memilih "sisi" yang lain. Yang kebetulan adalah argumentasi non-jawaban VeraShackle; yang kebetulan mengambil momentum karena saya tidak segera menyebutnya (lihat komentar itu). Saya merasa bahwa bantahan akhirnya masih cukup.Jawaban:
"Komponen Game" adalah bagian paling awal dari desain XNA 1.0. Mereka seharusnya bekerja seperti kontrol untuk WinForms. Idenya adalah Anda dapat menyeret-dan-menjatuhkannya ke dalam game Anda menggunakan GUI (semacam bit untuk menambahkan kontrol non-visual, seperti timer, dll) dan mengatur properti pada mereka.
Jadi Anda dapat memiliki komponen seperti mungkin Kamera atau ... penghitung FPS? ... atau ...?
Kamu melihat? Sayangnya ide ini tidak benar-benar berfungsi untuk game dunia nyata. Jenis komponen yang Anda inginkan untuk gim tidak benar-benar dapat digunakan kembali. Anda mungkin memiliki komponen "pemain", tetapi itu akan sangat berbeda dengan komponen "pemain" dari setiap permainan lainnya.
Saya membayangkan itu karena alasan ini, dan karena kurangnya waktu, GUI ditarik sebelum XNA 1.0.
Tetapi API masih dapat digunakan ... Inilah mengapa Anda tidak harus menggunakannya:
Pada dasarnya itu adalah apa yang paling mudah untuk menulis dan membaca dan men-debug dan memelihara sebagai pengembang game. Melakukan sesuatu seperti ini di kode pemuatan Anda:
Jauh lebih tidak jelas daripada hanya melakukan ini:
Terutama ketika, dalam game dunia nyata, Anda mungkin berakhir dengan sesuatu seperti ini:
(Memang Anda bisa berbagi kamera dan spriteBatch menggunakan
Services
arsitektur yang sama . Tapi itu juga ide yang buruk untuk alasan yang sama.)Arsitektur ini - atau ketiadaan - jauh lebih ringan dan fleksibel!
Saya dapat dengan mudah mengubah urutan pengundian atau menambahkan beberapa viewports atau menambahkan efek target render, hanya dengan mengubah beberapa baris. Untuk melakukannya di sistem lain mengharuskan saya untuk berpikir tentang apa yang dilakukan semua komponen - menyebar ke banyak file - dan dalam urutan apa.
Ini semacam perbedaan antara pemrograman deklaratif vs imperatif. Ternyata, untuk pengembangan game, pemrograman imperatif jauh lebih disukai.
sumber
DrawableGameComponent
.player.DrawOrder = 100;
metode ini daripada yang penting untuk menggambar. Saya sedang menyelesaikan permainan Flixel sekarang, yang menarik objek sesuai urutan Anda menambahkannya ke tempat kejadian. Sistem FlxGroup mengurangi beberapa hal ini, tetapi memiliki semacam penyortiran Z-index akan lebih baik.Hmmm. Saya mendapat tantangan yang satu ini demi sedikit keseimbangan di sini. Saya khawatir.
Tampaknya ada 5 tuduhan dalam balasan Andrew, jadi saya akan mencoba dan menangani masing-masing secara bergantian:
1) Komponen adalah desain yang ketinggalan zaman dan ditinggalkan.
Ide pola desain komponen sudah ada jauh sebelum XNA - pada dasarnya itu hanyalah keputusan untuk membuat objek, bukan objek permainan yang melengkung, rumah dari perilaku Update and Draw mereka sendiri. Untuk objek dalam koleksi komponennya, gim akan memanggil metode ini (dan beberapa lainnya) secara otomatis pada waktu yang tepat - yang krusial objek gim itu sendiri tidak harus 'mengetahui' kode ini. Jika Anda ingin menggunakan kembali kelas gim Anda di gim yang berbeda (dan dengan demikian obyek gim yang berbeda), penggandaan objek ini secara fundamental masih merupakan ide bagus. Melihat sekilas demo XNA4.0 terbaru (yang diproduksi secara profesional) dari AppHub menegaskan kesan saya bahwa Microsoft masih (cukup benar menurut saya) di belakang ini.
2) Andrew tidak dapat memikirkan lebih dari 2 kelas permainan yang dapat digunakan kembali.
Saya bisa. Sebenarnya saya bisa memikirkan banyak! Saya baru saja memeriksa lib bersama saya saat ini, dan terdiri lebih dari 80 komponen dan layanan yang dapat digunakan kembali . Untuk memberi Anda rasa, Anda bisa memiliki:
Setiap ide gim tertentu membutuhkan setidaknya 5-10 hal dari set ini - dijamin - dan mereka dapat berfungsi penuh dalam gim yang sama sekali baru dengan satu baris kode.
3) Mengontrol urutan undian dengan urutan panggilan undian eksplisit lebih disukai daripada menggunakan properti DrawOrder komponen.
Yah, pada dasarnya saya setuju dengan Andrew yang satu ini. TETAPI, jika Anda membiarkan semua properti DrawOrder komponen Anda sebagai default, Anda masih dapat secara eksplisit mengontrol urutan pengundiannya dengan urutan di mana Anda menambahkannya ke koleksi. Ini benar-benar identik dalam istilah 'visibilitas' dengan urutan eksplisit panggilan draw manual mereka.
4) Memanggil banyak metode Draw objek sendiri lebih 'ringan' daripada memanggil mereka dengan metode framework yang ada.
Mengapa? Ditambah lagi, dalam semua hal kecuali proyek yang paling sepele, logika Pembaruan dan Kode Draw Anda akan benar-benar menaungi metode yang Anda panggil dalam hal kinerja yang baik.
5) Menempatkan kode Anda dalam satu file lebih disukai, ketika debugging, daripada memilikinya di file objek individu.
Yah, pertama, ketika saya sedang debug di Visual Studio lokasi breakpoint dalam hal file pada disk hampir tidak terlihat hari ini - IDE berurusan dengan lokasi tanpa drama. Tetapi juga pertimbangkan sejenak bahwa Anda memiliki masalah dengan gambar Skybox Anda. Apakah pergi ke metode Draw Skybox benar-benar lebih berat daripada menemukan kode menggambar skybox dalam metode master draw (mungkin sangat panjang) dalam objek Game? Tidak, tidak juga - sebenarnya saya berpendapat bahwa itu sangat berlawanan.
Jadi, dalam ringkasan - mohon perhatikan argumen Andrew di sini. Saya tidak percaya mereka benar-benar memberikan alasan substantif untuk menulis kode permainan terjerat. Sisi buruk dari pola komponen yang dipisahkan (digunakan secara bijaksana) sangat besar.
sumber
Drawable
)GameComponent
untuk memberikan arsitektur game Anda, karena hilangnya kontrol eksplisit atas urutan pengundian / pembaruan (yang Anda sepakati di # 3) kekakuan antarmuka mereka (yang Anda tidak alamat).Saya sedikit tidak setuju dengan Andrew, tetapi saya juga berpikir ada waktu dan tempat untuk semuanya. Saya tidak akan menggunakan
Drawable(GameComponent)
untuk sesuatu yang sederhana seperti Sprite atau Musuh. Namun, saya akan menggunakannya untukSpriteManager
atauEnemyManager
.Kembali ke apa yang dikatakan Andrew tentang menggambar dan memperbarui ketertiban, saya pikir
Sprite.DrawOrder
akan sangat membingungkan bagi banyak sprite. Selain itu, relatif mudah untuk mengaturDrawOrder
danUpdateOrder
untuk sejumlah kecil (atau masuk akal) Manajer yang(Drawable)GameComponents
.Saya pikir Microsoft akan menyingkirkan mereka jika mereka benar-benar kaku dan tidak ada yang menggunakannya. Tetapi mereka tidak melakukannya karena mereka bekerja dengan sangat baik, jika perannya benar. Saya sendiri menggunakannya untuk mengembangkan
Game.Services
yang diperbarui di latar belakang.sumber
Saya juga memikirkan hal ini, dan terlintas di benak saya: Misalnya, untuk mencuri contoh di tautan Anda, dalam permainan RTS Anda bisa menjadikan setiap unit komponen komponen permainan. Baik.
Tetapi Anda juga bisa membuat komponen UnitManager, yang menyimpan daftar unit, menggambar dan memperbaruinya sesuai kebutuhan. Saya kira alasan Anda ingin membuat unit Anda menjadi komponen game di tempat pertama adalah untuk mengkotak-kotakkan kode, jadi apakah solusi ini cukup mencapai tujuan itu?
sumber
Dalam komentar, saya memposting versi kental dari jawaban lama saya:
Menggunakan
DrawableGameComponent
mengunci Anda ke dalam satu set tanda tangan metode dan model data tertentu yang dipertahankan. Ini biasanya merupakan pilihan yang salah pada awalnya, dan penguncian secara signifikan menghambat evolusi kode di masa depan.Di sini saya akan mencoba mengilustrasikan mengapa hal ini terjadi dengan memposting beberapa kode dari proyek saya saat ini.
Objek root yang mempertahankan keadaan dunia game memiliki
Update
metode yang terlihat seperti ini:Ada sejumlah titik masuk ke
Update
, tergantung pada apakah game berjalan di jaringan atau tidak. Mungkin, misalnya, untuk keadaan gim digulung kembali ke titik waktu sebelumnya dan kemudian disimulasikan kembali.Ada juga beberapa metode tambahan seperti pembaruan, seperti:
Dalam kondisi permainan ada daftar aktor yang akan diperbarui. Tapi ini lebih dari sekadar
Update
panggilan. Ada melewati tambahan sebelum dan sesudah untuk menangani fisika. Ada juga beberapa barang mewah untuk pemijahan yang ditangguhkan / penghancuran aktor, serta transisi tingkat.Di luar kondisi permainan, ada sistem UI yang perlu dikelola secara mandiri. Tetapi beberapa layar di UI juga harus dapat berjalan dalam konteks jaringan.
Untuk menggambar, karena sifat permainannya, ada sistem penyortiran dan pemusnahan khusus yang mengambil input dari metode ini:
Dan kemudian, setelah mengetahui apa yang akan menggambar, secara otomatis memanggil:
The
tag
parameter diperlukan karena beberapa pelaku dapat memegang aktor lain - dan begitu perlu untuk dapat menarik baik di atas dan di bawah aktor diadakan. Sistem penyortiran memastikan bahwa ini terjadi dalam urutan yang benar.Ada juga sistem menu untuk menggambar. Dan sistem hierarkis untuk menggambar UI, sekitar HUD, di sekitar game.
Sekarang bandingkan persyaratan itu dengan yang
DrawableGameComponent
menyediakan:Tidak ada cara yang masuk akal untuk menggunakan sistem menggunakan
DrawableGameComponent
sistem yang saya jelaskan di atas. (Dan itu bukan persyaratan yang tidak biasa untuk permainan kompleksitas yang bahkan sederhana).Juga, sangat penting untuk dicatat bahwa persyaratan itu tidak muncul begitu saja pada awal proyek. Mereka berkembang selama berbulan-bulan pekerjaan pembangunan.
Apa yang biasanya dilakukan orang, jika mereka memulai
DrawableGameComponent
rute, adalah mereka mulai membangun dari peretasan:Alih-alih menambahkan metode, mereka melakukan beberapa down-casting jelek ke tipe basis mereka sendiri (mengapa tidak menggunakannya secara langsung?).
Alih-alih mengganti kode untuk menyortir dan memohon
Draw
/Update
, mereka melakukan beberapa hal yang sangat lambat untuk mengubah nomor pemesanan on-the-fly.Dan yang terburuk: Alih-alih menambahkan parameter ke metode, mereka mulai "melewati" "parameter" di lokasi memori yang bukan tumpukan (penggunaan
Services
untuk ini populer). Ini berbelit-belit, rawan kesalahan, lambat, rapuh, jarang aman, dan cenderung menjadi masalah jika Anda menambahkan jaringan, membuat alat eksternal, dan sebagainya.Ini juga membuat kode digunakan kembali lebih sulit , karena sekarang dependensi Anda tersembunyi di dalam "komponen", alih-alih diterbitkan pada batas API.
Atau, mereka hampir melihat betapa gilanya ini semua, dan mulai melakukan "manajer"
DrawableGameComponent
untuk mengatasi masalah ini. Kecuali mereka masih memiliki masalah ini di batas "manajer".Biasanya "manajer" ini dapat dengan mudah diganti dengan serangkaian panggilan metode dalam urutan yang diinginkan. Ada lagi yang terlalu rumit.
Tentu saja, setelah Anda mulai menggunakan peretasan itu, maka dibutuhkan kerja nyata untuk membatalkan peretasan itu setelah Anda menyadari bahwa Anda membutuhkan lebih dari yang
DrawableGameComponent
disediakan. Anda menjadi " terkunci ". Dan godaan seringkali adalah untuk terus menumpuk di atas peretasan - yang dalam praktiknya akan merongrong Anda dan membatasi jenis permainan yang dapat Anda buat untuk yang relatif sederhana.Banyak orang tampaknya mencapai titik ini dan memiliki reaksi mendalam - mungkin karena mereka menggunakan
DrawableGameComponent
dan tidak mau menerima menjadi "salah". Jadi mereka mengaitkan pada fakta bahwa setidaknya mungkin untuk membuatnya "berfungsi".Tentu saja bisa dibuat untuk "bekerja"! Kami adalah programmer - membuat segala sesuatunya bekerja di bawah pengekangan yang tidak biasa adalah tugas kami. Tetapi pengekangan ini tidak perlu, berguna atau rasional ...
Apa yang terutama flabbergasting tentang adalah bahwa itu adalah menakjubkan sepele untuk sisi-langkah seluruh masalah ini. Di sini, saya bahkan akan memberi Anda kode:
(Sangat mudah untuk menambahkan dalam penyortiran sesuai
DrawOrder
danUpdateOrder
. Salah satu cara sederhana adalah mempertahankan dua daftar, dan menggunakannyaList<T>.Sort()
. Ini juga mudah untuk menanganiLoad
/UnloadContent
.)Hal ini tidak penting apa kode yang benar-benar adalah . Yang penting adalah saya bisa dengan sepele memodifikasinya .
Ketika saya menyadari bahwa saya memerlukan sistem pengaturan waktu yang berbeda
gameTime
, atau saya memerlukan lebih banyak parameter (atau seluruhUpdateContext
objek dengan sekitar 15 hal di dalamnya), atau saya memerlukan urutan operasi yang sama sekali berbeda untuk menggambar, atau saya memerlukan beberapa metode tambahan untuk berbagai pembaruan pembaruan, saya bisa langsung saja melakukannya .Dan saya bisa segera melakukannya. Saya tidak perlu bingung tentang cara mengambil
DrawableGameComponent
kode saya. Atau lebih buruk: meretasnya dalam beberapa cara yang akan membuatnya lebih sulit saat berikutnya kebutuhan semacam itu muncul.Sebenarnya hanya ada satu skenario di mana itu adalah pilihan yang baik untuk digunakan
DrawableGameComponent
: dan itu adalah jika Anda benar-benar membuat dan menerbitkan middleware gaya-tarik-dan-jatuhkan-komponen untuk XNA. Yang merupakan tujuan aslinya. Yang - saya akan tambahkan - tidak ada yang melakukan!(Demikian pula, itu hanya masuk akal untuk digunakan
Services
saat membuat jenis konten khusus untukContentManager
.)sumber
DrawableGameComponent
komponen tertentu, terutama untuk hal-hal seperti layar menu (menu, banyak tombol, opsi dan hal-hal lain), atau overlay FPS / debug kamu menyebutkan. Dalam kasus ini, Anda biasanya tidak perlu kontrol berbutir halus atas urutan undian dan Anda berakhir dengan lebih sedikit kode yang perlu Anda tulis secara manual (yang merupakan hal yang baik, kecuali jika Anda perlu menulis kode itu). Tapi saya kira itu skenario seret-dan-jatuhkan yang Anda sebutkan.