Saya sangat baru dalam pengembangan game, tetapi tidak untuk pemrograman.
Saya (lagi-lagi) bermain-main dengan game tipe Pong menggunakan canvas
elemen JavaScript .
Saya telah membuat Paddle
objek yang memiliki properti berikut ...
width
height
x
y
colour
Saya juga punya Pong
objek yang memiliki properti seperti ...
width
height
backgroundColour
draw()
.
The draw()
Metode saat ini ulang canvas
dan yang mana pertanyaan muncul.
Haruskah Paddle
objek memiliki draw()
metode yang bertanggung jawab untuk menggambar nya, atau harus draw()
dari Pong
objek bertanggung jawab untuk menggambar aktor nya (saya berasumsi bahwa adalah istilah yang benar, perbaiki saya jika saya salah).
Saya pikir itu akan menguntungkan bagi Paddle
untuk menggambar sendiri, karena saya instantiate dua objek, Player
dan Enemy
. Jika tidak ada di Pong
's draw()
, saya harus menulis kode yang sama dua kali.
Apa praktik terbaik di sini?
Terima kasih.
sumber
Jawaban:
Membuat aktor menggambar sendiri bukanlah desain yang baik, karena dua alasan utama:
1) itu melanggar prinsip tanggung jawab tunggal , karena aktor-aktor itu mungkin memiliki pekerjaan lain yang harus dilakukan sebelum Anda memasukkan kode render ke dalamnya.
2) itu membuat ekstensi sulit; jika setiap jenis aktor mengimplementasikan gambarnya sendiri, dan Anda perlu mengubah cara Anda menggambar secara umum , Anda mungkin harus memodifikasi banyak kode. Menghindari penggunaan warisan secara berlebihan dapat meringankan hal ini sampai batas tertentu, tetapi tidak sepenuhnya.
Lebih baik bagi penyaji Anda untuk menangani gambar. Lagi pula, itulah artinya menjadi penyaji. Metode menggambar renderer harus mengambil objek "deskripsi render", yang berisi semua yang Anda butuhkan untuk membuat sesuatu. Referensi ke (mungkin dibagi) data geometri, transformasi spesifik-instance atau sifat material seperti warna, dan lain-lain. Itu kemudian menarik itu, dan tidak peduli apa yang membuat deskripsi seharusnya "menjadi."
Aktor Anda kemudian dapat berpegang pada deskripsi render yang mereka buat sendiri. Karena aktor biasanya adalah tipe pemrosesan logika, mereka dapat mendorong perubahan status ke deskripsi render sesuai kebutuhan - misalnya, saat aktor mengalami kerusakan, aktor dapat mengatur warna deskripsi rendernya menjadi merah untuk mengindikasikan hal ini.
Kemudian Anda dapat dengan mudah mengulangi setiap aktor yang terlihat, meminta deskripsi render mereka ke dalam renderer, dan membiarkannya melakukan hal tersebut (pada dasarnya; Anda dapat menggeneralisasi ini lebih jauh).
sumber
actor.draw(renderer,x,y)
daripadarenderer.draw(actor.getAttribs(),x,y)
.struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }
lebih atau kurang untuk renderer saya. Dengan begitu renderer tidak peduli dengan data yang diwakilinya, itu hanya memprosesnya. Berdasarkan informasi ini, saya kemudian dapat memutuskan apakah akan mengurutkan urutan panggilan undian untuk mengurangi panggilan GPU secara keseluruhan.The Pola Pengunjung dapat berguna di sini.
Apa yang dapat Anda lakukan adalah memiliki antarmuka Renderer yang tahu cara menggambar setiap objek dan metode "gambar sendiri" di setiap aktor yang menentukan metode penyaji (spesifik) mana yang harus dipanggil, misalnya
Dengan cara ini masih mudah untuk port ke perpustakaan grafis atau kerangka kerja lain (saya pernah mem-porting sebuah game dari Swing / Java2D ke LWJGL dan sangat senang saya telah menggunakan pola ini alih-alih
Graphics2D
berkeliling.)Ada keuntungan lain: renderer dapat diuji secara terpisah dari kode aktor apa pun
sumber
Dalam contoh Anda, Anda ingin memiliki objek Pong mengimplementasikan draw () dan membuat sendiri.
Meskipun Anda tidak akan melihat adanya keuntungan signifikan dalam proyek sebesar ini, umumnya memisahkan logika game dan representasi visualnya (rendering) adalah kegiatan yang bermanfaat.
Maksud saya ini, Anda akan memiliki objek game yang dapat diperbarui () tetapi mereka tidak tahu bagaimana mereka dirender, mereka hanya peduli dengan simulasi.
Maka Anda akan memiliki kelas PongRenderer () yang memiliki referensi ke objek Pong, dan kemudian bertanggung jawab untuk memberikan kelas Pong (), ini bisa melibatkan rendering Paddles, atau memiliki kelas PaddleRenderer untuk mengurusnya.
Pemisahan masalah dalam hal ini adalah hal yang cukup alami yang berarti kelas Anda bisa kurang membengkak, dan lebih mudah untuk memodifikasi bagaimana hal-hal yang diberikan, itu tidak lagi harus mengikuti hierarki yang dilakukan simulasi Anda.
sumber
Jika Anda melakukannya
Pong
di draw (), Anda tidak perlu menduplikasi kode - cukup panggil fungsiPaddle
draw () untuk setiap dayung Anda. Jadi, dalamPong
undian Anda () Anda akansumber
Setiap objek harus mengandung fungsi draw-nya sendiri yang harus dipanggil oleh kode pembaruan game atau kode draw. Seperti
Ini bukan kode tetapi hanya untuk menunjukkan, bagaimana menggambar objek harus dilakukan.
sumber