Saran untuk arsitektur game / pola desain

16

Saya telah mengerjakan RPG 2d untuk sementara waktu sekarang, dan saya menyadari bahwa saya telah membuat beberapa keputusan desain yang buruk. Ada beberapa hal yang membuat saya bermasalah, jadi saya bertanya-tanya desain seperti apa yang digunakan orang lain untuk mengatasinya, atau akan digunakan.

Untuk sedikit latar belakang, saya mulai mengerjakannya di waktu senggang saya musim panas lalu. Saya awalnya membuat game dalam C #, tetapi sekitar 3 bulan yang lalu, memutuskan untuk beralih ke C ++. Saya ingin mendapatkan pegangan yang baik pada C ++ karena sudah lama sejak saya menggunakannya dengan berat, dan menemukan proyek yang menarik seperti ini akan menjadi motivator yang baik. Saya telah menggunakan boost library secara luas dan telah menggunakan SFML untuk grafik dan FMOD untuk audio.

Saya memiliki sedikit kode yang ditulis, tetapi saya mempertimbangkan untuk menghapusnya dan memulai kembali.

Inilah bidang utama yang menjadi perhatian saya dan saya ingin mendapatkan beberapa pendapat tentang cara yang tepat orang lain menyelesaikannya atau akan menyelesaikannya.

1. Dependensi Siklikal Ketika saya melakukan permainan dalam C #, saya tidak benar-benar perlu khawatir tentang ini karena itu bukan masalah di sana. Pindah ke C ++, ini telah menjadi masalah yang cukup besar dan membuat saya berpikir saya mungkin telah merancang hal-hal yang salah. Saya tidak dapat membayangkan bagaimana cara memisahkan kelas saya dan masih membiarkan mereka melakukan apa yang saya inginkan. Berikut adalah beberapa contoh rantai ketergantungan:

Saya memiliki kelas efek status. Kelas memiliki sejumlah metode (Terapkan / Tidak Berlaku, Centang, dll.) Untuk menerapkan efeknya terhadap karakter. Contohnya,

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

Fungsi ini akan dipanggil setiap kali karakter yang ditimbulkan oleh efek status berubah. Ini akan digunakan untuk mengimplementasikan efek seperti Regen, Poison, dll. Namun, itu juga memperkenalkan dependensi pada kelas BaseCharacter dan kelas BattleField. Secara alami, kelas BaseCharacter perlu melacak efek status apa yang saat ini aktif padanya sehingga itu merupakan ketergantungan siklus. Battlefield perlu melacak pihak-pihak yang bertikai, dan kelas partai memiliki daftar Karakter BaseChak yang memperkenalkan ketergantungan siklus lainnya.

2 - Acara

Dalam C # I menggunakan banyak delegasi untuk mengaitkan peristiwa dengan karakter, medan perang dll. (Misalnya, ada delegasi untuk kapan kesehatan karakter berubah, ketika stat berubah, ketika efek status ditambahkan / dihapus, dll. .) dan medan perang / komponen grafis akan menghubungkan ke delegasi tersebut untuk menegakkan efeknya. Di C ++, saya melakukan sesuatu yang serupa. Jelas tidak ada yang setara langsung dengan delegasi C #, jadi alih-alih saya membuat sesuatu seperti ini:

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

dan di kelas karakter saya

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

setiap kali stat karakter berubah, saya akan beralih dan memanggil setiap StatChangeFunction di peta. Sementara itu berhasil, saya khawatir ini adalah pendekatan yang buruk untuk melakukan sesuatu.

3 - Grafik

Ini hal besar. Ini tidak terkait dengan perpustakaan grafis yang saya gunakan, tetapi lebih merupakan hal konseptual. Dalam C #, saya menambahkan grafik dengan banyak kelas saya yang saya tahu adalah ide yang buruk. Ingin melakukannya terpisah kali ini saya mencoba pendekatan yang berbeda.

Untuk mengimplementasikan grafik saya, saya membayangkan semua grafik yang terkait dalam game sebagai serangkaian layar. Yaitu ada layar judul, layar status karakter, layar peta, layar persediaan, layar pertempuran, layar GUI pertempuran, dan pada dasarnya saya bisa menumpuk layar ini di atas satu sama lain yang diperlukan untuk membuat grafik permainan. Apa pun layar aktif yang memiliki input game.

Saya merancang manajer layar yang akan mendorong dan memunculkan layar berdasarkan input pengguna.

Misalnya, jika Anda berada di layar peta (pengendali input / visualizer untuk Peta Ubin) dan menekan tombol mulai, itu akan mengeluarkan panggilan ke manajer layar untuk menekan layar Menu Utama di atas layar peta dan menandai peta layar untuk tidak ditarik / diperbarui. Pemain akan menavigasi di sekitar menu, yang akan mengeluarkan lebih banyak perintah ke manajer layar yang sesuai untuk mendorong layar baru ke tumpukan layar, lalu pop mereka ketika pengguna mengubah layar / membatalkan. Akhirnya ketika pemain keluar dari menu utama, saya mematikannya dan kembali ke layar peta, berkomentar itu akan ditarik / diperbarui dan pergi dari sana.

Layar pertempuran akan lebih kompleks. Saya memiliki layar untuk bertindak sebagai latar belakang, layar untuk memvisualisasikan masing-masing pihak dalam pertempuran, dan layar untuk memvisualisasikan UI untuk pertempuran. UI akan terhubung ke peristiwa karakter dan menggunakannya untuk menentukan kapan harus memperbarui / menggambar ulang komponen UI. Akhirnya, setiap serangan yang memiliki skrip animasi yang tersedia akan memanggil lapisan tambahan untuk menghidupkannya sendiri sebelum keluar dari tumpukan layar. Dalam hal ini, setiap layer secara konsisten ditandai sebagai drawable dan updatable dan saya mendapatkan setumpuk layar yang menangani grafik pertempuran saya.

Meskipun saya belum bisa membuat pengelola layar bekerja dengan sempurna, saya pikir saya bisa melakukannya dengan beberapa waktu. Pertanyaan saya tentang itu, apakah ini pendekatan yang berharga sama sekali? Jika itu desain yang buruk, saya ingin tahu sekarang sebelum saya menginvestasikan terlalu banyak waktu untuk membuat semua layar yang saya butuhkan. Bagaimana Anda membangun grafik untuk gim Anda?

pengguna127817
sumber

Jawaban:

15

Secara keseluruhan saya tidak akan mengatakan apa pun yang Anda daftarkan harus menyebabkan Anda menghapus sistem dan memulai kembali. Ini adalah sesuatu yang ingin dilakukan oleh setiap programmer sekitar 50-75% dari jalannya proyek apa pun yang mereka kerjakan, tetapi itu mengarah pada siklus pengembangan yang tidak pernah berakhir dan tidak pernah menyelesaikan apa pun. Jadi, untuk itu, beberapa umpan balik pada setiap bagian.

  1. Ini bisa menjadi masalah tetapi biasanya lebih mengganggu daripada yang lain. Apakah Anda menggunakan #pragma sekali atau #ifndef MY_HEADER_FILE_H #define MY_HEADER_FILE_H ... #endif di bagian atas atau di sekitar file .h Anda masing-masing? Dengan cara ini file .h hanya ada sekali dalam setiap cakupan? Jika ya, rekomendasi saya kemudian menghapus semua #incangan pernyataan dan kompilasi, menambahkan yang diperlukan untuk mengkompilasi game lagi.

  2. Saya penggemar jenis sistem ini dan tidak melihat ada yang salah dengan itu. Apa yang dimaksud dengan Peristiwa dalam C # biasanya diganti dengan Sistem Kejadian atau Sistem Pesan (dapat mencari pertanyaan di sini untuk hal-hal itu untuk menemukan informasi lebih lanjut). Kuncinya di sini adalah untuk menjaga ini seminimal mungkin ketika hal-hal Perlu terjadi, yang sudah terdengar seperti Anda lakukan sehingga tidak boleh ada sedikit kekhawatiran di sini.

  3. Ini juga tampaknya berada di jalur yang benar bagi saya dan apa yang saya lakukan untuk mesin saya sendiri, baik secara pribadi maupun profesional. Ini membuat sistem menu menjadi sistem keadaan yang memiliki menu root (sebelum permainan dimulai) atau pemain HUD sebagai layar 'root' ditampilkan, tergantung pada bagaimana Anda mengaturnya.

Singkatnya, saya melihat tidak ada yang me-restart layak dalam apa yang Anda hadapi. Anda mungkin ingin penggantian sistem Acara yang lebih formal di ujung jalan tetapi itu akan datang pada waktunya. Cyclic termasuk adalah rintangan yang harus selalu dilompati oleh semua programmer C / C ++, dan bekerja untuk memisahkan grafik semua tampak seperti logis 'langkah selanjutnya'.

Semoga ini membantu!

James
sumber
#ifdef tidak membantu melingkar termasuk masalah.
Bebek Komunis
Hanya menutupi dasar saya dengan mengharapkan bahwa berada di sana sebelum melacak siklus termasuk. Dapat berupa ketel ikan lainnya saat Anda memiliki beberapa simbol yang didefinisikan sebagai lawan dari file yang perlu menyertakan file yang menyertakan dirinya sendiri. (meskipun dari apa yang ia jelaskan jika menyertakannya dalam file .CPP dan bukan file .H ia harus baik-baik saja dengan dua objek dasar saling mengetahui tentang satu sama lain)
James
Terima kasih atas sarannya :) Senang mengetahui saya berada di jalur yang benar
user127817
4

Ketergantungan siklus Anda seharusnya tidak menjadi masalah selama Anda meneruskan mendeklarasikan kelas tempat Anda dapat dalam file header dan benar-benar # memasukkannya dalam file .cpp (atau apa pun).

Untuk sistem acara, dua saran:

1) Jika Anda ingin mempertahankan pola yang Anda gunakan sekarang, pertimbangkan beralih ke boost :: unordered_map alih-alih std :: map. Memetakan dengan string sebagai kunci lambat, terutama karena .NET melakukan beberapa hal baik di bawah tenda untuk membantu mempercepatnya. Menggunakan unordered_map memilah string sehingga perbandingan umumnya lebih cepat.

2) Pertimbangkan untuk beralih ke sesuatu yang lebih kuat seperti dorongan :: sinyal. Jika Anda melakukannya, Anda dapat melakukan hal-hal bagus seperti membuat objek game Anda dapat dilacak dengan berasal dari boost :: signal :: dilacak, dan biarkan destructor mengurus pembersihan semuanya alih-alih harus membatalkan pendaftaran secara manual dari sistem acara. Anda juga dapat memiliki beberapa sinyal yang menunjuk ke setiap slot (atau sebaliknya, saya tidak ingat nomenklatur yang tepat) sehingga sangat mirip dengan melakukan +=di delegatedalam C #. Masalah terbesar dengan boost :: sinyal adalah harus dikompilasi, bukan hanya header, jadi tergantung pada platform Anda, mungkin sulit untuk bangkit dan berjalan.

Tetrad
sumber