Saya menghibur diri belakangan ini dengan memprogram game petualangan berbasis teks yang sederhana, dan saya terjebak pada apa yang tampaknya seperti masalah desain yang sangat sederhana.
Untuk memberikan gambaran singkat: permainan dipecah menjadi Room
objek. Masing-masing Room
memiliki daftar Entity
benda yang ada di ruangan itu. Masing-masing Entity
memiliki keadaan peristiwa, yang merupakan peta string-> boolean sederhana, dan daftar tindakan, yang merupakan peta fungsi string->.
Input pengguna berupa [action] [entity]
. The Room
menggunakan nama entitas untuk mengembalikan sesuai Entity
objek, yang kemudian menggunakan nama tindakan untuk menemukan fungsi yang benar, dan mengeksekusinya.
Untuk menghasilkan deskripsi kamar, setiap Room
objek menampilkan string deskripsi sendiri, lalu menambahkan string deskripsi masing-masing Entity
. The Entity
deskripsi dapat berubah berdasarkan keadaan ( "Pintu terbuka", "Pintu tertutup", "Pintu terkunci", dll).
Inilah masalahnya: menggunakan metode ini, jumlah fungsi deskripsi dan tindakan yang perlu saya implementasikan dengan cepat menjadi tidak terkendali. Ruang awal saya sendiri memiliki sekitar 20 fungsi antara 5 entitas.
Saya bisa menggabungkan semua tindakan menjadi satu fungsi dan jika-ada / beralih melalui mereka, tapi itu masih dua fungsi per entitas. Saya juga dapat membuat Entity
sub-kelas spesifik untuk objek umum / umum seperti pintu dan kunci, tetapi itu hanya membuat saya sejauh ini.
EDIT 1: Seperti yang diminta, contoh pseudo-code dari fungsi aksi ini.
string outsideDungeonBushesSearch(currentRoom, thisEntity, player)
if thisEntity["is_searched"] then
return "There was nothing more in the bushes."
else
thisEntity["is_searched"] := true
currentRoom.setEntity("dungeonDoorKey")
return "You found a key in the bushes."
end if
string dungeonDoorKeyUse(currentRoom, thisEntity, player)
if getEntity("outsideDungeonDoor")["is_locked"] then
getEntity("outsideDungeonDoor")["is_locked"] := false
return "You unlocked the door."
else
return "The door is already unlocked."
end if
Fungsi deskripsi bekerja dengan cara yang hampir sama, memeriksa status dan mengembalikan string yang sesuai.
EDIT 2: Merevisi kata-kata saya. Asumsikan bahwa mungkin ada sejumlah besar objek dalam game yang tidak berbagi perilaku umum (respons berbasis negara untuk tindakan tertentu) dengan objek lain. Apakah ada cara saya dapat mendefinisikan perilaku unik ini dengan cara yang lebih bersih dan lebih dapat dipertahankan daripada menulis fungsi khusus untuk setiap tindakan entitas tertentu?
Jawaban:
Alih-alih membuat fungsi terpisah untuk setiap kombinasi kata benda dan kata kerja, Anda harus mengatur arsitektur di mana ada satu antarmuka umum yang semua objek dalam gim implementasikan.
Salah satu pendekatan dari atas kepala saya adalah mendefinisikan objek Entitas yang diperluas oleh semua objek spesifik dalam game Anda. Setiap Entitas akan memiliki tabel (struktur data apa pun yang digunakan bahasa Anda untuk array asosiatif) yang mengaitkan tindakan yang berbeda dengan hasil yang berbeda. Tindakan dalam tabel kemungkinan adalah Strings (mis. "Open") sementara hasil terkait bahkan bisa menjadi fungsi pribadi dalam objek jika bahasa Anda mendukung fungsi kelas satu.
Demikian pula, keadaan objek disimpan di berbagai bidang objek. Jadi misalnya, Anda dapat memiliki array barang di Bush, dan kemudian fungsi yang terkait dengan "pencarian" akan bertindak pada array itu, baik mengembalikan objek yang ditemukan atau string "Tidak ada yang lebih di semak-semak."
Sementara itu, salah satu metode publik adalah sesuatu seperti Entity.actOn (tindakan String) Kemudian dalam metode itu membandingkan tindakan yang dikirimkan dengan tabel tindakan untuk objek itu; jika tindakan itu ada dalam tabel maka kembalikan hasilnya.
Sekarang semua fungsi berbeda yang diperlukan untuk setiap objek akan terkandung di dalam objek, sehingga mudah untuk mengulangi objek itu di ruangan lain (mis. Instantiate objek Door di setiap kamar yang memiliki pintu)
Terakhir, tentukan semua kamar dalam XML atau JSON atau apa pun sehingga Anda dapat memiliki banyak kamar unik tanpa perlu menulis kode terpisah untuk setiap kamar. Muat file data ini saat game dimulai, dan parsing data untuk membuat instance objek yang mengisi game Anda. Sesuatu seperti:
TAMBAHAN: aha, saya baru saja membaca jawaban FxIII dan bit ini mendekati saya:
Meskipun saya tidak setuju bahwa jebakan api yang dipicu adalah sesuatu yang hanya akan terjadi sekali (saya bisa melihat jebakan ini digunakan kembali untuk banyak objek yang berbeda). Saya pikir saya akhirnya mendapatkan apa yang Anda maksudkan tentang entitas yang bereaksi secara unik terhadap input pengguna. Saya mungkin akan membahas hal-hal seperti membuat satu pintu di ruang bawah tanah Anda memiliki perangkap bola api dengan membangun semua entitas saya dengan arsitektur komponen (dijelaskan secara rinci di tempat lain).
Dengan cara ini setiap entitas Door dibangun sebagai satu bundel komponen, dan saya dapat secara fleksibel mencampur dan mencocokkan komponen antara entitas yang berbeda. Sebagai contoh, sebagian besar pintu akan memiliki konfigurasi seperti
tetapi satu pintu dengan perangkap bola api akan menjadi
dan satu-satunya kode unik yang harus saya tulis untuk pintu itu adalah komponen FireballTrap. Itu akan menggunakan komponen Kunci dan Portal yang sama dengan semua pintu lainnya, dan jika saya kemudian memutuskan untuk menggunakan FireballTrap pada peti harta karun atau sesuatu yang sesederhana menambahkan komponen FireballTrap ke peti itu.
Apakah Anda mendefinisikan semua komponen dalam kode yang dikompilasi atau dalam bahasa scripting yang terpisah bukanlah perbedaan besar dalam pikiran saya (baik Anda akan menulis kode di suatu tempat ) tetapi yang penting adalah bahwa Anda dapat secara signifikan mengurangi jumlah kode unik yang perlu Anda tulis. Heck, jika Anda tidak khawatir tentang fleksibilitas untuk perancang tingkat / modder (Anda sendiri yang menulis game ini), Anda bahkan dapat membuat semua entitas mewarisi dari Entitas dan menambahkan komponen dalam konstruktor daripada file konfigurasi atau skrip atau Masa bodo:
sumber
Entity
hanya untuk satu objek mengelompokkan kode bersama tetapi tidak mengurangi jumlah kode yang harus saya tulis. Atau apakah itu perangkap yang tidak terhindarkan dalam hal ini?Entity
dan atribut menentukan status awalnya. Saya menduga tag anak dari entitas berfungsi sebagai parameter untuk tindakan apa pun yang terkait dengan tag, bukan?Masalah dimensi yang Anda tangani cukup normal dan hampir tidak dapat dihindari. Anda ingin menemukan cara untuk mengekspresikan entitas Anda yang bertepatan dan fleksibel .
"Wadah" (semak di jawaban jhocking) adalah cara yang bersamaan tetapi Anda lihat itu tidak cukup fleksibel .
Saya tidak menyarankan Anda untuk mencoba menemukan antarmuka generik dan kemudian menggunakan file konfigurasi untuk menentukan perilaku, karena Anda akan selalu memiliki sensasi yang tidak menyenangkan berada di antara batu (entitas standar dan membosankan, mudah digambarkan) dan tempat yang sulit ( entitas fantastis yang unik tetapi terlalu lama untuk diterapkan).
Saran saya adalah untuk menggunakan sebuah bahasa ditafsirkan perilaku kode.
Pikirkan contoh semak: itu adalah wadah tetapi semak kami perlu memiliki barang-barang tertentu di dalamnya; objek kontainer mungkin memiliki:
Salah satu item ini memiliki tali yang memicu alat yang pada gilirannya menyalakan api membakar semak-semak ... (Anda lihat, saya dapat membaca pikiran Anda sehingga saya tahu barang-barang yang Anda suka).
Anda dapat menggunakan skrip untuk menggambarkan semak ini, bukan file konfigurasi yang meletakkan kode tambahan yang relevan di dalam kait yang Anda jalankan dari program utama Anda setiap kali seseorang mengambil item dari sebuah wadah.
Sekarang Anda memiliki banyak pilihan arsitektur: Anda dapat mendefinisikan alat perilaku sebagai kelas dasar menggunakan bahasa kode Anda atau bahasa scripting (hal-hal seperti wadah, seperti pintu dan sebagainya). The Tujuan dari hal theese adalah untuk membiarkan Anda untuk menggambarkan entitas easely menggabungkan perilaku sederhana dan mengkonfigurasi mereka menggunakan binding pada bahasa scripting .
Semua entitas harus dapat diakses oleh skrip: Anda dapat mengaitkan pengenal untuk setiap entitas dan menempatkannya dalam wadah yang diekspor dalam perluasan skrip bahasa skriping.
Dengan menggunakan strategi scripting, Anda dapat membuat konfigurasi Anda tetap sederhana (tidak ada hal-hal seperti
<item triggerFlamesOnPicking="true">
itu yang akan Anda gunakan hanya sekali) sambil membiarkan Anda mengekspresikan beaviour aneh (yang menyenangkan) menambahkan beberapa baris kodeDalam beberapa kata: skrip sebagai file konfigurasi yang dapat menjalankan kode.
sumber