Apakah saya di jalur yang benar dengan arsitektur komponen ini?


9

Saya baru-baru ini memutuskan untuk mengubah arsitektur game saya untuk menyingkirkan hierarki kelas yang dalam dan menggantinya dengan komponen yang dapat dikonfigurasi. Hirarki pertama yang saya ganti adalah hirarki Item dan saya ingin beberapa saran untuk mengetahui apakah saya berada di jalur yang benar.

Sebelumnya, saya memiliki hierarki yang berbunyi seperti ini:

Item -> Equipment -> Weapon
                  -> Armor
                  -> Accessory
     -> SyntehsisItem
     -> BattleUseItem -> HealingItem
                      -> ThrowingItem -> ThrowsAsAttackItem

Tak perlu dikatakan itu mulai menjadi berantakan dan ini bukan solusi mudah untuk item yang perlu beberapa jenis (yaitu beberapa peralatan digunakan dalam sintesis barang, beberapa peralatan dapat dibuang, dll.)

Saya kemudian mencoba untuk memperbaiki dan menempatkan fungsionalitas ke dalam kelas item dasar. Tapi kemudian saya memperhatikan bahwa Item memiliki banyak data yang tidak terpakai / berlebihan. Sekarang saya mencoba melakukan komponen seperti arsitektur, setidaknya untuk item saya sebelum mencoba melakukannya ke kelas permainan saya yang lain.

Inilah yang sedang saya pikirkan untuk pengaturan komponen:

Saya memiliki kelas item dasar yang memiliki slot untuk berbagai komponen (yaitu slot komponen peralatan, slot komponen penyembuhan, dll. Serta peta untuk komponen sewenang-wenang) jadi sesuatu seperti ini:

class Item
{
    //Basic item properties (name, ID, etc.) excluded
    EquipmentComponent* equipmentComponent;
    HealingComponent* healingComponent;
    SynthesisComponent* synthesisComponent;
    ThrowComponent* throwComponent;
    boost::unordered_map<std::string, std::pair<bool, ItemComponent*> > AdditionalComponents;
} 

Semua komponen item akan diwarisi dari kelas ItemComponent dasar, dan masing-masing tipe Komponen bertanggung jawab untuk memberi tahu mesin cara mengimplementasikan fungsi itu. yaitu HealingComponent memberi tahu mekanika pertempuran cara mengonsumsi item sebagai item penyembuhan, sedangkan ThrowComponent memberi tahu mesin pertempuran cara memperlakukan item sebagai item yang bisa dibuang.

Peta ini digunakan untuk menyimpan komponen acak yang bukan komponen inti. Saya memasangkannya dengan bool untuk menunjukkan apakah Wadah Item harus mengelola ItemComponent atau jika dikelola oleh sumber eksternal.

Ide saya di sini adalah bahwa saya mendefinisikan komponen inti yang digunakan oleh mesin gim saya di muka, dan pabrik item saya akan menetapkan komponen yang benar-benar dimiliki oleh item tersebut, jika tidak maka itu adalah nol. Peta akan mengandung komponen arbitrer yang umumnya akan ditambahkan / dikonsumsi oleh file skrip.

Pertanyaan saya adalah, apakah ini desain yang bagus? Jika tidak, bagaimana cara meningkatkannya? Saya mempertimbangkan untuk mengelompokkan semua komponen ke dalam peta, tetapi menggunakan pengindeksan string tampaknya tidak diperlukan untuk komponen item inti

Jawaban:


8

Sepertinya ini langkah pertama yang sangat masuk akal.

Anda memilih kombinasi generalitas (peta "komponen tambahan") dan kinerja pencarian (anggota hard-coded), yang mungkin sedikit pra-optimasi - poin Anda mengenai ketidakefisienan umum berbasis string look-up dibuat dengan baik, tetapi Anda dapat mengurangi itu dengan memilih untuk mengindeks komponen dengan sesuatu yang lebih cepat untuk hash. Salah satu pendekatan mungkin memberi masing-masing jenis komponen ID jenis unik (dasarnya Anda menerapkan RTTI khusus ringan ) dan indeks berdasarkan itu.

Apa pun yang terjadi, saya akan mengingatkan Anda untuk mengekspos API publik untuk objek Item yang memungkinkan Anda meminta komponen apa pun - komponen yang dikode keras dan yang tambahan - secara seragam. Ini akan membuatnya lebih mudah untuk mengubah representasi atau keseimbangan komponen hardcoded / non-hardcode tanpa harus memperbaiki semua komponen item klien.

Anda mungkin juga mempertimbangkan untuk memberikan versi tanpa-op "dummy" dari masing-masing komponen yang dikodekan dan memastikan bahwa mereka selalu ditugaskan - Anda kemudian dapat menggunakan anggota referensi alih-alih pointer, dan Anda tidak perlu memeriksa penunjuk NULL sebelum berinteraksi dengan salah satu kelas komponen hard-coded. Anda masih akan dikenakan biaya pengiriman dinamis untuk berinteraksi dengan anggota komponen itu, tetapi itu akan terjadi bahkan dengan anggota penunjuk. Ini lebih merupakan masalah kebersihan kode karena dampak kinerja akan diabaikan dalam semua kemungkinan.

Saya tidak berpikir itu ide bagus untuk memiliki dua jenis cakupan seumur hidup (dengan kata lain, saya tidak berpikir bool yang Anda miliki di peta komponen tambahan adalah ide bagus). Ini memperumit sistem dan menyiratkan bahwa perusakan dan pelepasan sumber daya tidak akan menjadi sangat deterministik. API untuk komponen Anda akan jauh lebih jelas jika Anda memilih satu strategi manajemen seumur hidup atau yang lain - baik entitas mengelola masa pakai komponen, atau subsistem yang menyadari komponen melakukannya (saya lebih suka yang terakhir karena lebih cocok dengan komponen tempel). pendekatan, yang akan saya bahas selanjutnya).

Kelemahan besar yang saya lihat dengan pendekatan Anda adalah bahwa Anda menggumpal semua komponen bersama-sama dalam objek "entitas", yang sebenarnya tidak selalu merupakan desain terbaik. Dari jawaban terkait saya untuk pertanyaan berbasis komponen lain:

Pendekatan Anda menggunakan peta besar komponen dan panggilan pembaruan () di objek game cukup sub-optimal (dan perangkap umum bagi mereka yang pertama kali membangun sistem semacam ini). Itu membuat koherensi cache yang sangat buruk selama pembaruan dan tidak memungkinkan Anda untuk mengambil keuntungan dari konkurensi dan kecenderungan menuju proses gaya-SIMD dari kumpulan data besar atau perilaku sekaligus. Sering kali lebih baik menggunakan desain di mana objek game tidak memperbarui komponennya, tetapi subsistem yang bertanggung jawab atas komponen itu sendiri memperbarui semuanya sekaligus.

Anda pada dasarnya mengambil pendekatan yang sama dengan menyimpan komponen dalam entitas item (yang, sekali lagi, langkah pertama yang sepenuhnya dapat diterima). Apa yang Anda temukan adalah bahwa sebagian besar akses ke komponen yang Anda khawatirkan tentang kinerja hanya untuk memperbaruinya, dan jika Anda memilih untuk menggunakan pendekatan yang lebih tempel ke organisasi komponen, di mana komponen disimpan dalam cache-koheren , struktur data yang efisien (untuk domain mereka) oleh subsistem yang paling memahami kebutuhan mereka, Anda dapat mencapai kinerja pembaruan yang jauh lebih baik dan lebih dapat diparalelkan.

Tapi saya tunjukkan ini hanya sebagai sesuatu untuk dipertimbangkan sebagai arah masa depan - Anda tentu tidak ingin terlalu berlebihan dalam rekayasa ini; Anda dapat melakukan transisi bertahap melalui refactoring yang konstan atau Anda mungkin menemukan implementasi Anda saat ini memenuhi kebutuhan Anda dengan sempurna dan tidak perlu mengulanginya.


1
+1 untuk menyarankan menyingkirkan objek Item. Meskipun akan lebih banyak bekerja di muka, pada akhirnya akan menghasilkan sistem komponen yang lebih baik.
James

Saya punya beberapa pertanyaan lebih lanjut, tidak yakin apakah saya harus memulai topiuc baru, jadi akan coba di bawah sini dulu: Untuk kelas item saya, tidak ada metode yang saya akan panggil evert frame (atau bahkan tutup). Untuk subsistem grafik saya, saya akan mengikuti saran Anda dan menyimpan semua objek untuk diperbarui di bawah sistem. Pertanyaan lain yang saya miliki adalah bagaimana cara menangani pemeriksaan komponen? Seperti misalnya, saya ingin melihat apakah saya dapat menggunakan item sebagai X, jadi tentu saja saya akan memeriksa untuk melihat apakah item memiliki komponen yang diperlukan untuk melakukan X. Apakah ini cara yang tepat untuk melakukan ini? Terima kasih lagi untuk jawabannya
user127817
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.