Pemisahan menggambar dan logika dalam game

11

Saya seorang pengembang yang baru saja mulai main-main dengan pengembangan game. Saya seorang. Net guy, jadi saya telah mengacaukan XNA dan sekarang saya bermain-main dengan Cocos2d untuk iPhone. Pertanyaan saya sebenarnya lebih umum.

Katakanlah saya sedang membangun game Pong sederhana. Saya akan memiliki Ballkelas dan Paddlekelas. Berasal dari perkembangan dunia bisnis, insting pertama saya adalah tidak memiliki gambar atau kode penanganan input di salah satu dari kelas-kelas ini.

//pseudo code
class Ball
{
   Vector2D position;
   Vector2D velocity;
   Color color;
   void Move(){}
}

Tidak ada di kelas bola yang menangani input, atau berurusan dengan menggambar. Saya kemudian akan memiliki kelas lain, kelas saya Game, atau saya Scene.m(dalam Cocos2d) yang akan baru Bola, dan selama loop permainan, itu akan memanipulasi bola sesuai kebutuhan.

Masalahnya adalah, dalam banyak tutorial untuk XNA dan Cocos2d, saya melihat pola seperti ini:

//pseudo code
class Ball : SomeUpdatableComponent
{
   Vector2D position;
   Vector2D velocity;
   Color color;
   void Update(){}
   void Draw(){}
   void HandleInput(){}
}

Pertanyaan saya adalah, apakah ini benar? Apakah ini pola yang digunakan orang dalam pengembangan game? Entah bagaimana itu bertentangan dengan semua yang saya terbiasa, agar kelas Ball saya melakukan segalanya. Selanjutnya, dalam contoh kedua ini, di mana saya Balltahu cara bergerak, bagaimana saya menangani deteksi tabrakan dengan Paddle? Apakah Ballperlu memiliki pengetahuan tentang itu Paddle? Dalam contoh pertama saya, Gamekelas akan memiliki referensi untuk kedua Balldan Paddle, dan kemudian mengirimkan keduanya ke beberapa CollisionDetectionmanajer atau sesuatu, tetapi bagaimana saya menangani kompleksitas berbagai komponen, jika masing-masing komponen individu melakukan semuanya dengan sendirinya? (Saya harap saya masuk akal .....)

BFree
sumber
2
Sayangnya itulah yang sebenarnya dilakukan dalam banyak game. Saya tidak akan menganggap itu sebagai praktik terbaik. Banyak tutorial yang Anda baca dapat ditujukan untuk pemula, dan karena itu mereka tidak akan mulai menjelaskan model / tampilan / pengontrol atau pola kuk kepada pembaca.
tenpn
@tenpn, komentar Anda tampaknya menunjukkan bahwa Anda tidak berpikir ini adalah praktik yang baik, saya bertanya-tanya apakah Anda dapat menjelaskan lebih lanjut? Saya selalu berpikir itu adalah pengaturan yang paling cocok karena metode-metode yang berisi kode yang mungkin sangat spesifik untuk setiap jenis; tetapi saya sendiri memiliki sedikit pengalaman sehingga saya akan tertarik untuk mendengar pikiran Anda.
sebf
1
@ SDF itu tidak berfungsi jika Anda ke dalam desain berorientasi data, dan itu tidak berfungsi jika Anda suka desain berorientasi objek. Lihat Pangeran Pangeran SOLID Pangeran Bob: butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
tenpn
@tenpn. Artikel dan dokumen yang ditautkan di dalamnya sangat menarik, terima kasih!
sebf

Jawaban:

10

Pertanyaan saya adalah, apakah ini benar? Apakah ini pola yang digunakan orang dalam pengembangan game?

Pengembang game cenderung menggunakan apa pun yang berfungsi. Itu tidak ketat, tapi hei, itu kapal.

Entah bagaimana itu bertentangan dengan semua yang biasa saya lakukan, agar kelas Ball saya melakukan segalanya.

Jadi, idiom yang lebih umum untuk apa yang Anda miliki di sana adalah handleMessages / update / draw. Dalam sistem yang menggunakan banyak pesan (yang memiliki pro dan kontra seperti yang Anda harapkan), entitas game mengambil pesan apa pun yang dipedulikannya, melakukan logika pada pesan-pesan itu, dan kemudian menggambarnya sendiri.

Perhatikan bahwa panggilan draw () mungkin sebenarnya tidak berarti bahwa entitas memanggil putPixel atau apa pun di dalamnya; dalam beberapa kasus, itu bisa berarti memperbarui data yang kemudian disurvei oleh kode yang bertanggung jawab untuk menggambar. Dalam kasus yang lebih maju, ia "menggambar" dirinya sendiri dengan memanggil metode yang diekspos oleh renderer. Dalam teknologi yang saya kerjakan saat ini, objek akan menggambar dirinya sendiri menggunakan panggilan renderer dan kemudian setiap frame thread rendering mengatur semua panggilan yang dilakukan oleh semua objek, mengoptimalkan untuk hal-hal seperti perubahan keadaan, dan kemudian menarik semuanya keluar (semua hal ini terjadi pada utas yang terpisah dari logika game, jadi kami mendapatkan rendering asinkron).

Pendelegasian tanggung jawab tergantung pada programmer; untuk permainan pong sederhana, masuk akal bahwa setiap objek menangani gambar sendiri (lengkap dengan panggilan tingkat rendah) sepenuhnya. Ini masalah gaya dan kebijaksanaan.

Selanjutnya, dalam contoh kedua ini, di mana Bola saya tahu cara bergerak, bagaimana saya menangani deteksi tabrakan dengan Paddle? Apakah Bola harus memiliki pengetahuan tentang Dayung?

Jadi, salah satu cara alami untuk menyelesaikan masalah di sini adalah meminta setiap objek mengambil setiap objek lainnya sebagai semacam parameter untuk memeriksa tabrakan. Ini berskala buruk, dan dapat memiliki hasil yang aneh.

Mintalah masing-masing kelas menerapkan semacam antarmuka Collidable dan kemudian memiliki sedikit kode tingkat tinggi yang baru saja melewati semua objek Collidable dalam fase pembaruan permainan Anda dan menangani tabrakan, mengatur posisi objek sesuai kebutuhan.

Sekali lagi, Anda hanya perlu mengembangkan rasa (atau mengambil keputusan) tentang bagaimana tanggung jawab untuk hal-hal yang berbeda dalam permainan Anda perlu didelegasikan. Rendering adalah aktivitas soliter dan dapat ditangani oleh suatu objek dalam ruang hampa; tabrakan dan fisika umumnya tidak bisa.

Dalam contoh pertama saya, kelas Game akan memiliki referensi untuk Ball dan Dayung, dan kemudian mengirimkan keduanya ke beberapa manajer CollisionDetection atau sesuatu, tetapi bagaimana cara saya menangani kompleksitas berbagai komponen, jika masing-masing komponen individu tidak semuanya sendiri?

Lihat penjelasan di atas untuk penjelajahan ini. Jika Anda menggunakan bahasa yang mendukung antarmuka dengan mudah (yaitu, Java, C #), ini mudah. Jika Anda menggunakan C ++, ini bisa ... menarik. Saya lebih suka menggunakan pesan untuk mengatasi masalah ini (kelas menangani pesan yang mereka bisa, abaikan yang lain), beberapa orang lain menyukai komposisi komponen.

Sekali lagi, apa pun yang berhasil dan termudah untuk Anda.

ChrisE
sumber
2
Oh, dan selalu ingat: YAGNI (Anda tidak akan membutuhkannya) sangat penting di sini. Anda dapat membuang banyak waktu untuk mencoba membuat sistem fleksibel yang ideal untuk menangani setiap masalah yang mungkin terjadi, dan kemudian tidak pernah menyelesaikan apa pun. Maksud saya, jika Anda benar-benar menginginkan tulang punggung multipemain yang baik untuk semua hasil yang mungkin, Anda akan menggunakan CORBA, bukan? :)
ChrisE
5

Baru-baru ini saya membuat game Space Invadors sederhana menggunakan 'sistem entitas'. Ini adalah pola yang memisahkan atribut dan perilaku dengan sangat baik. Butuh beberapa iterasi untuk sepenuhnya memahaminya, tetapi begitu Anda mendapatkan beberapa komponen yang dirancang, menjadi sangat mudah untuk membuat objek baru menggunakan komponen yang ada.

Anda harus membaca ini:

http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

Itu sering diperbarui oleh orang yang sangat berpengetahuan. Ini juga satu-satunya diskusi sistem entitas dengan contoh kode konkret.

Iterasi saya berjalan sebagai berikut:

Iterasi pertama memiliki objek "EntitySystem" yang seperti yang dijelaskan Adam; Namun komponen saya masih memiliki metode - komponen saya 'dapat diajak' memiliki metode pengecatan (), dan komponen posisi saya memiliki metode pemindahan () dan lain-lain. Ketika saya mulai menyempurnakan entitas, saya menyadari bahwa saya perlu mulai menyampaikan pesan di antara komponen dan memesan pelaksanaan pembaruan komponen .... terlalu berantakan.

Jadi, saya kembali dan membaca kembali blog T-Machines. Ada banyak informasi di utas komentar - dan di dalamnya ia sangat menekankan bahwa komponen tidak memiliki perilaku - perilaku disediakan oleh sistem entitas. Dengan cara ini Anda tidak perlu mengirimkan pesan antara komponen dan memesan pembaruan komponen karena pemesanan ditentukan oleh urutan global pelaksanaan sistem. Baik. Mungkin itu terlalu abstrak.

Pokoknya untuk iterasi # 2 inilah yang saya dapatkan dari blog:

EntityManager - bertindak sebagai komponen "basis data", yang dapat ditanyakan untuk entitas yang berisi jenis komponen tertentu. Ini bahkan dapat didukung oleh database dalam-memori untuk akses cepat ... lihat t-machine bagian 5 untuk info lebih lanjut.

EntitySystem - Setiap sistem pada dasarnya hanyalah sebuah metode yang beroperasi pada set entites. Setiap sistem akan menggunakan komponen x, y dan z suatu entitas untuk menyelesaikan pekerjaannya. Jadi Anda akan meminta manajer untuk entitas dengan komponen x, y dan z kemudian meneruskan hasilnya ke sistem.

Entity - hanya id, seperti panjang. Entitas adalah apa yang dikelompokkan sekelompok contoh komponen bersama menjadi 'entitas'.

Komponen - seperangkat bidang .... tidak ada perilaku! ketika Anda mulai menambahkan perilaku itu mulai menjadi berantakan ... bahkan dalam permainan Space Invadors yang sederhana.

Sunting : omong-omong, 'dt' adalah waktu delta sejak doa loop utama terakhir

Jadi loop Invadors utama saya adalah ini:

Collection<Entity> entitiesWithGuns = manager.getEntitiesWith(Gun.class);
Collection<Entity> entitiesWithDamagable =
manager.getEntitiesWith(BulletDamagable.class);
Collection<Entity> entitiesWithInvadorDamagable = manager.getEntitiesWith(InvadorDamagable.class);

keyboardShipControllerSystem.update(entitiesWithGuns, dt);
touchInputSystem.update(entitiesWithGuns, dt);
Collection<Entity> entitiesWithInvadorMovement = manager.getEntitiesWith(InvadorMovement.class);
invadorMovementSystem.update(entitiesWithInvadorMovement);

Collection<Entity> entitiesWithVelocity = manager.getEntitiesWith(Velocity.class);
movementSystem.move(entitiesWithVelocity, dt);
gunSystem.update(entitiesWithGuns, System.currentTimeMillis());
Collection<Entity> entitiesWithPositionAndForm = manager.getEntitiesWith(Position.class, Form.class);
collisionSystem.checkCollisions(entitiesWithPositionAndForm);

Awalnya terlihat agak aneh, tetapi sangat fleksibel. Ini juga sangat mudah untuk dioptimalkan; untuk tipe komponen yang berbeda, Anda dapat memiliki datastore dukungan yang berbeda untuk mempercepat pengambilan. Untuk kelas 'form' Anda dapat membuatnya didukung dengan quadtree untuk mempercepat akses untuk deteksi tabrakan.

Aku seperti kamu; Saya adalah pengembang berpengalaman tetapi tidak memiliki pengalaman menulis game. Saya menghabiskan beberapa waktu meneliti pola dev, dan yang ini menarik perhatian saya. Ini sama sekali bukan satu-satunya cara untuk melakukan sesuatu, tetapi saya merasa sangat intuitif dan kuat. Saya percaya pola ini secara resmi dibahas dalam buku 6 dari seri "Game Programming Gems" - http://www.amazon.com/Game-Programming-Gems/dp/1584500492 . Saya sendiri belum membaca buku-buku itu tetapi saya dengar itu adalah referensi de-facto untuk pemrograman game.

homebrew
sumber
1

Tidak ada aturan eksplisit yang harus Anda ikuti saat memprogram game. Saya menemukan kedua pola tersebut dapat diterima, selama Anda mengikuti pola desain yang sama di seluruh gim Anda. Anda akan terkejut bahwa ada lebih banyak pola desain untuk gim, beberapa di antaranya bahkan tidak di atas OOP.

Sekarang, ke preferensi pribadi saya, saya lebih suka memisahkan kode dengan perilaku (satu kelas / utas untuk menggambar, yang lain untuk memperbarui), sambil memiliki objek minimalis. Saya merasa lebih mudah untuk memparalelkan, dan saya sering menemukan diri saya mengerjakan lebih sedikit file pada saat yang sama. Saya akan mengatakan ini lebih merupakan cara prosedural dalam melakukan sesuatu.

Tetapi jika Anda berasal dari dunia bisnis, Anda mungkin lebih nyaman menulis kelas yang tahu bagaimana melakukan segalanya untuk diri mereka sendiri.

Sekali lagi, saya sarankan Anda menulis apa yang menurut Anda paling alami bagi Anda.


sumber
Saya pikir Anda salah mengerti pertanyaan saya. Saya mengatakan bahwa saya TIDAK suka memiliki kelas yang melakukan semuanya sendiri. Saya jatuh seperti bola seharusnya hanya representasi logis dari bola, namun seharusnya tidak tahu apa-apa tentang permainan, penanganan input dll ...
BFree
Agar adil, dalam pemrograman bisnis jika Anda mendidih, ada dua kereta: objek bisnis yang memuat, bertahan dan melakukan logika pada diri mereka sendiri, dan DTO + AppLogic + Lapisan Repositori / Persitansi ...
Nate
1

objek 'Bola' memiliki atribut & perilaku. Saya merasa masuk akal untuk memasukkan atribut unik & perilaku objek di kelas mereka sendiri. Cara pembaruan objek Bola berbeda dari cara dayung akan memperbarui sehingga masuk akal bahwa ini adalah perilaku unik yang menjamin dimasukkannya kelas. Sama dengan menggambar. Sering ada perbedaan unik dalam cara menggambar dayung vs bola dan saya merasa lebih mudah untuk memodifikasi metode menggambar objek individu agar sesuai dengan kebutuhan ini daripada mengerjakan evaluasi bersyarat di kelas lain untuk menyelesaikannya.

Pong adalah gim yang relatif sederhana dalam hal jumlah objek & perilaku, tetapi ketika Anda masuk ke lusinan atau ratusan objek gim unik, Anda mungkin menemukan contoh kedua Anda sedikit lebih mudah untuk memodulasi segala sesuatu.

Kebanyakan orang memang menggunakan kelas input yang hasilnya tersedia untuk semua objek game baik melalui Layanan atau kelas statis.

Steve H.
sumber
0

Saya pikir apa yang mungkin Anda gunakan di dunia bisnis adalah memisahkan abstraksi dan implementasi.

Di dunia dev permainan, Anda biasanya ingin berpikir dalam hal kecepatan. Semakin banyak objek yang Anda miliki, semakin banyak konteks akan terjadi, yang dapat memperlambat segalanya tergantung pada beban saat ini.

Ini hanya spekulasi saya karena saya seorang pengembang game wannabe tetapi seorang pengembang profesional.

Joey Green
sumber
0

Tidak, ini tidak 'benar', atau 'salah', itu hanya penyederhanaan.

Beberapa kode bisnis persis sama, kecuali Anda bekerja dengan sistem yang memisahkan presentasi dan logika secara paksa.

Di sini, contoh VB.NET, di mana "logika bisnis" (menambahkan nomor) ditulis dalam pengendali event GUI - http://www.homeandlearn.co.uk/net/nets1p12.html

bagaimana cara saya menangani kompleksitas berbagai komponen, jika masing-masing komponen mengerjakan semuanya sendiri?

Jika dan ketika itu menjadi kompleks, maka Anda dapat memfaktorkannya.

class Ball
{
    GraphicalModel ballVisual;
    void Draw()
    {
        ballVisual.Draw();
    }
};
Kylotan
sumber
0

Dari pengalaman saya dalam pengembangan, tidak ada cara yang benar atau salah untuk melakukan sesuatu, kecuali ada praktik yang diterima dalam kelompok yang bekerja dengan Anda. Saya telah bertemu dengan oposisi dari pengembang yang enggan memisahkan perilaku, kulit dll. Dan saya yakin mereka akan mengatakan hal yang sama tentang reservasi saya untuk menulis kelas yang mencakup semua yang ada di bawah matahari. Ingat Anda tidak hanya harus menulisnya, tetapi juga dapat membaca kode Anda besok dan di masa mendatang, debug itu dan mungkin membangunnya di iterasi berikutnya. Anda harus memilih jalur yang sesuai untuk Anda (dan tim). Memilih rute yang digunakan orang lain (dan kontra-intuitif untuk Anda) akan memperlambat Anda.

kosmoknot
sumber