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:
void Update(UpdateContext updateContext, MultiInputState currentInput)
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:
void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)
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:
virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)
Dan kemudian, setelah mengetahui apa yang akan menggambar, secara otomatis memanggil:
virtual void Draw(DrawContext drawContext, int tag)
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:
public class DrawableGameComponent
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
public int UpdateOrder { get; set; }
public int DrawOrder { get; set; }
}
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:
public class Actor
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
}
List<Actor> actors = new List<Actor>();
foreach(var actor in actors)
actor.Update(gameTime);
foreach(var actor in actors)
actor.Draw(gameTime);
(Sangat mudah untuk menambahkan dalam penyortiran sesuai DrawOrder
dan UpdateOrder
. Salah satu cara sederhana adalah mempertahankan dua daftar, dan menggunakannya List<T>.Sort()
. Ini juga mudah untuk menangani Load
/ 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 seluruh UpdateContext
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 digunakanServices
saat membuat jenis konten khusus untuk ContentManager
.)