Menulis Mesin Petualangan Teks Multiplayer Di Node.js (Bagian 1)

Diterbitkan: 2022-03-10
Ringkasan cepat Pernah mendengar tentang petualangan teks? Dalam rangkaian artikel ini, Fernando Doglio menjelaskan proses bagaimana membuat keseluruhan mesin yang memungkinkan Anda memainkan petualangan teks apa pun yang Anda dan teman Anda nikmati. Itu benar, kami akan sedikit membumbuinya dengan menambahkan multipemain ke genre petualangan teks!

Petualangan teks adalah salah satu bentuk pertama dari permainan peran digital di luar sana, saat permainan tidak memiliki grafik dan yang Anda miliki hanyalah imajinasi Anda sendiri dan deskripsi yang Anda baca di layar hitam monitor CRT Anda.

Jika kita ingin bernostalgia, mungkin nama Petualangan Gua Kolosal (atau Petualangan saja, seperti namanya) membunyikan lonceng. Itu adalah game petualangan teks pertama yang pernah dibuat.

Gambar petualangan teks aktual dari masa lalu
Gambar petualangan teks aktual dari masa lalu. (Pratinjau besar)

Gambar di atas adalah cara Anda sebenarnya melihat game ini, jauh dari game petualangan AAA teratas kami saat ini. Dikatakan demikian, mereka menyenangkan untuk dimainkan dan akan mencuri ratusan jam waktu Anda, saat Anda duduk di depan teks itu, sendirian, mencoba mencari cara untuk mengalahkannya.

Maklum, petualangan teks telah digantikan selama bertahun-tahun oleh game yang menghadirkan visual yang lebih baik (walaupun, dapat dikatakan bahwa banyak dari mereka telah mengorbankan cerita untuk grafis) dan, terutama dalam beberapa tahun terakhir, peningkatan kemampuan untuk berkolaborasi dengan orang lain. teman dan bermain bersama. Fitur khusus ini adalah salah satu yang tidak dimiliki oleh petualangan teks asli, dan salah satu yang ingin saya bawa kembali dalam artikel ini.

Bagian Lain Dari Seri Ini

  • Bagian 2: Desain Server Mesin Game
  • Bagian 3: Membuat Klien Terminal
  • Bagian 4: Menambahkan Obrolan Ke Game Kami

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Tujuan kita

Inti dari upaya ini, seperti yang mungkin sudah Anda duga dari judul artikel ini, adalah untuk membuat mesin petualangan teks yang memungkinkan Anda untuk berbagi petualangan dengan teman-teman, memungkinkan Anda untuk berkolaborasi dengan mereka seperti yang Anda lakukan selama a Dungeons & Dragons game (di mana, seperti halnya petualangan teks lama, tidak ada grafik untuk dilihat).

Dalam membuat mesin, server obrolan dan klien cukup banyak bekerja. Dalam artikel ini, saya akan menunjukkan kepada Anda fase desain, menjelaskan hal-hal seperti arsitektur di balik mesin, bagaimana klien akan berinteraksi dengan server, dan apa aturan permainan ini nantinya.

Hanya untuk memberi Anda beberapa bantuan visual tentang seperti apa tampilannya, inilah tujuan saya:

Gambar rangka umum untuk UI akhir klien game
Gambar rangka umum untuk UI akhir klien game (Pratinjau besar)

Itu adalah tujuan kami. Setelah kami sampai di sana, Anda akan memiliki tangkapan layar alih-alih maket cepat dan kotor. Jadi, mari kita ikuti prosesnya. Hal pertama yang akan kita bahas adalah desain semuanya. Kemudian, kita akan membahas alat paling relevan yang akan saya gunakan untuk mengkode ini. Terakhir, saya akan menunjukkan kepada Anda beberapa bit kode yang paling relevan (tentu saja dengan tautan ke repositori lengkap).

Semoga, pada akhirnya, Anda akan menemukan diri Anda membuat petualangan teks baru untuk mencobanya bersama teman-teman!

Fase Desain

Untuk fase desain, saya akan membahas cetak biru kami secara keseluruhan. Saya akan mencoba yang terbaik untuk tidak membuat Anda bosan sampai mati, tetapi pada saat yang sama, saya pikir penting untuk menunjukkan beberapa hal di balik layar yang perlu terjadi sebelum meletakkan baris kode pertama Anda.

Empat komponen yang ingin saya bahas di sini dengan jumlah detail yang layak adalah:

  • Mesin
    Ini akan menjadi server game utama. Aturan permainan akan diterapkan di sini, dan itu akan menyediakan antarmuka agnostik teknologi untuk semua jenis klien untuk dikonsumsi. Kami akan menerapkan klien terminal, tetapi Anda dapat melakukan hal yang sama dengan klien browser web atau jenis lain yang Anda inginkan.
  • Server obrolan
    Karena cukup kompleks untuk memiliki artikel sendiri, layanan ini juga akan memiliki modul sendiri. Server obrolan akan membiarkan pemain berkomunikasi satu sama lain selama pertandingan.
  • Klien
    Seperti yang dinyatakan sebelumnya, ini akan menjadi klien terminal, yang idealnya akan terlihat mirip dengan mockup sebelumnya. Ini akan menggunakan layanan yang disediakan oleh mesin dan server obrolan.
  • Game (file JSON)
    Akhirnya, saya akan membahas definisi game yang sebenarnya. Inti dari ini adalah untuk membuat mesin yang dapat menjalankan game apa pun, selama file game Anda sesuai dengan persyaratan mesin. Jadi, meskipun ini tidak memerlukan pengkodean, saya akan menjelaskan bagaimana saya akan menyusun file petualangan untuk menulis petualangan kita sendiri di masa depan.

Mesin

Mesin game, atau server game, akan menjadi REST API dan akan menyediakan semua fungsionalitas yang diperlukan.

Saya memilih REST API hanya karena — untuk jenis game ini — penundaan yang ditambahkan oleh HTTP dan sifatnya yang tidak sinkron tidak akan menimbulkan masalah. Namun, kami harus menempuh rute yang berbeda untuk server obrolan. Namun sebelum kita mulai mendefinisikan titik akhir untuk API kita, kita perlu mendefinisikan kemampuan mesin tersebut. Jadi, mari kita lakukan.

Fitur Keterangan
Bergabunglah dengan permainan Seorang pemain akan dapat bergabung dengan permainan dengan menentukan ID permainan.
Buat permainan baru Seorang pemain juga dapat membuat instance game baru. Mesin harus mengembalikan ID, sehingga orang lain dapat menggunakannya untuk bergabung.
Adegan kembali Fitur ini harus mengembalikan adegan saat ini di mana pesta berada. Pada dasarnya, ini akan mengembalikan deskripsi, dengan semua informasi terkait (kemungkinan tindakan, objek di dalamnya, dll.).
Berinteraksi dengan adegan Ini akan menjadi salah satu yang paling kompleks, karena akan mengambil perintah dari klien dan melakukan tindakan itu — hal-hal seperti memindahkan, mendorong, mengambil, melihat, membaca, dan lain-lain.
Periksa inventaris Meskipun ini adalah cara untuk berinteraksi dengan permainan, itu tidak secara langsung berhubungan dengan adegan. Jadi, memeriksa inventaris untuk setiap pemain akan dianggap sebagai tindakan yang berbeda.

Sepatah Kata Tentang Gerakan

Kami membutuhkan cara untuk mengukur jarak dalam permainan karena bergerak melalui petualangan adalah salah satu tindakan inti yang dapat dilakukan pemain. Kami akan menggunakan angka ini sebagai ukuran waktu, hanya untuk menyederhanakan gameplay. Mengukur waktu dengan jam sebenarnya mungkin bukan yang terbaik, mengingat jenis permainan ini memiliki aksi berbasis giliran, seperti pertarungan. Sebagai gantinya, kami akan menggunakan jarak untuk mengukur waktu (artinya jarak 8 akan membutuhkan lebih banyak waktu untuk melintasi daripada salah satu dari 2, sehingga memungkinkan kami untuk melakukan hal-hal seperti menambahkan efek ke pemain yang bertahan untuk jumlah "titik jarak" yang ditentukan. ).

Aspek penting lainnya yang perlu dipertimbangkan tentang pergerakan adalah kita tidak bermain sendirian. Demi kesederhanaan, mesin tidak akan membiarkan pemain membagi partai (meskipun itu bisa menjadi peningkatan yang menarik untuk masa depan). Versi awal modul ini hanya akan membiarkan semua orang bergerak ke mana pun mayoritas partai memutuskan. Jadi, pemindahan harus dilakukan secara musyawarah, artinya setiap tindakan pemindahan akan menunggu permintaan mayoritas pihak sebelum dilakukan.

Tempur

Pertempuran adalah aspek lain yang sangat penting dari jenis permainan ini, dan salah satu yang harus kami pertimbangkan untuk ditambahkan ke mesin kami; jika tidak, kita akan kehilangan beberapa kesenangan.

Ini bukan sesuatu yang perlu diciptakan kembali, jujur. Pertempuran partai berbasis giliran telah ada selama beberapa dekade, jadi kami hanya akan menerapkan versi mekanik itu. Kami akan menggabungkannya dengan konsep "inisiatif" Dungeons & Dragons, menggulirkan nomor acak untuk menjaga pertempuran sedikit lebih dinamis.

Dengan kata lain, urutan di mana setiap orang yang terlibat dalam pertarungan dapat memilih tindakan mereka akan diacak, dan itu termasuk musuh.

Akhirnya (walaupun saya akan membahas ini lebih detail di bawah), Anda akan memiliki item yang dapat Anda ambil dengan nomor "kerusakan" yang ditetapkan. Ini adalah item yang dapat Anda gunakan selama pertempuran; apa pun yang tidak memiliki properti itu akan menyebabkan 0 kerusakan pada musuh Anda. Kami mungkin akan menambahkan pesan saat Anda mencoba menggunakan objek tersebut untuk bertarung, sehingga Anda tahu bahwa apa yang Anda coba lakukan tidak masuk akal.

Interaksi Klien-Server

Sekarang mari kita lihat bagaimana klien tertentu akan berinteraksi dengan server kita menggunakan fungsionalitas yang telah ditentukan sebelumnya (belum memikirkan titik akhir, tetapi kita akan sampai di sana sebentar lagi):

(Pratinjau besar)

Interaksi awal antara klien dan server (dari sudut pandang server) adalah awal dari permainan baru, dan langkah-langkahnya adalah sebagai berikut:

  1. Buat permainan baru .
    Klien meminta pembuatan game baru dari server.
  2. Buat ruang obrolan .
    Meskipun namanya tidak menentukannya, server tidak hanya membuat ruang obrolan di server obrolan, tetapi juga mengatur semua yang diperlukan untuk memungkinkan sekelompok pemain bermain melalui petualangan.
  3. Kembalikan data meta game .
    Setelah permainan dibuat oleh server dan ruang obrolan tersedia untuk para pemain, klien akan membutuhkan informasi itu untuk permintaan selanjutnya. Ini sebagian besar akan menjadi satu set ID yang dapat digunakan klien untuk mengidentifikasi diri mereka sendiri dan game saat ini yang ingin mereka ikuti (lebih lanjut tentang itu dalam satu detik).
  4. Bagikan ID game secara manual .
    Langkah ini harus dilakukan oleh para pemain itu sendiri. Kami dapat membuat semacam mekanisme berbagi, tetapi saya akan meninggalkannya di daftar keinginan untuk perbaikan di masa mendatang.
  5. Bergabunglah dengan permainan .
    Yang satu ini cukup mudah. Setelah setiap orang memiliki ID game, mereka akan bergabung dalam petualangan menggunakan aplikasi klien mereka.
  6. Bergabunglah dengan ruang obrolan mereka .
    Terakhir, aplikasi klien pemain akan menggunakan metadata game untuk bergabung dengan ruang obrolan petualangan mereka. Ini adalah langkah terakhir yang diperlukan sebelum pertandingan. Setelah ini semua selesai, maka para pemain siap untuk mulai berpetualang!
Urutan tindakan untuk game yang ada
Urutan tindakan untuk game yang ada (Pratinjau besar)

Setelah semua prasyarat terpenuhi, pemain dapat mulai memainkan petualangan, berbagi pemikiran mereka melalui obrolan pesta, dan memajukan cerita. Diagram di atas menunjukkan empat langkah yang diperlukan untuk itu.

Langkah-langkah berikut akan berjalan sebagai bagian dari game loop, artinya akan diulang terus-menerus hingga game berakhir.

  1. Adegan permintaan .
    Aplikasi klien akan meminta metadata untuk adegan saat ini. Ini adalah langkah pertama dalam setiap iterasi loop.
  2. Kembalikan data meta .
    Server akan, pada gilirannya, mengirim kembali metadata untuk adegan saat ini. Informasi ini akan mencakup hal-hal seperti deskripsi umum, objek yang ditemukan di dalamnya, dan bagaimana mereka berhubungan satu sama lain.
  3. Kirim perintah .
    Di sinilah kesenangan dimulai. Ini adalah masukan utama dari pemain. Ini akan berisi tindakan yang ingin mereka lakukan dan, opsional, target tindakan itu (misalnya, meniup lilin, mengambil batu, dan sebagainya).
  4. Kembalikan reaksi ke perintah yang dikirim .
    Ini bisa saja menjadi langkah kedua, tetapi untuk kejelasan, saya menambahkannya sebagai langkah tambahan. Perbedaan utama adalah bahwa langkah kedua dapat dianggap sebagai awal dari loop ini, sedangkan yang ini memperhitungkan bahwa Anda sudah bermain, dan, dengan demikian, server perlu memahami siapa yang akan terpengaruh oleh tindakan ini (baik pemain tunggal atau semua pemain).

Sebagai langkah tambahan, meskipun bukan bagian dari alur, server akan memberi tahu klien tentang pembaruan status yang relevan bagi mereka.

Alasan untuk langkah ekstra berulang ini adalah karena pembaruan yang dapat diterima pemain dari tindakan pemain lain. Ingat persyaratan untuk pindah dari satu tempat ke tempat lain; seperti yang saya katakan sebelumnya, setelah mayoritas pemain telah memilih arah, maka semua pemain akan bergerak (tidak diperlukan input dari semua pemain).

Bagian yang menarik di sini adalah bahwa HTTP (kami telah menyebutkan bahwa server akan menjadi REST API) tidak mengizinkan jenis perilaku ini. Jadi, pilihan kami adalah:

  1. melakukan polling setiap X jumlah detik dari klien,
  2. menggunakan semacam sistem notifikasi yang bekerja secara paralel dengan koneksi client-server.

Dalam pengalaman saya, saya cenderung memilih opsi 2. Bahkan, saya akan (dan akan untuk artikel ini) menggunakan Redis untuk perilaku semacam ini.

Diagram berikut menunjukkan ketergantungan antar layanan.

Interaksi antara aplikasi klien dan mesin game
Interaksi antara aplikasi klien dan mesin game (Pratinjau besar)

Server Obrolan

Detail desain modul ini akan saya tinggalkan untuk tahap pengembangan (yang bukan merupakan bagian dari artikel ini). Karena itu, ada hal-hal yang bisa kita putuskan.

Satu hal yang dapat kami definisikan adalah kumpulan batasan untuk server, yang akan menyederhanakan pekerjaan kami. Dan jika kami memainkan kartu kami dengan benar, kami mungkin berakhir dengan layanan yang menyediakan antarmuka yang kuat, sehingga memungkinkan kami, pada akhirnya, memperluas atau bahkan mengubah implementasi untuk memberikan lebih sedikit batasan tanpa memengaruhi permainan sama sekali.

  • Hanya akan ada satu kamar per pesta.
    Kami tidak akan membiarkan subgrup dibuat. Ini berjalan seiring dengan tidak membiarkan partai terpecah. Mungkin setelah kami menerapkan peningkatan itu, memungkinkan pembuatan subgrup dan ruang obrolan khusus akan menjadi ide yang bagus.
  • Tidak akan ada pesan pribadi.
    Ini murni untuk tujuan penyederhanaan, tetapi memiliki obrolan grup sudah cukup baik; kami tidak membutuhkan pesan pribadi sekarang. Ingatlah bahwa setiap kali Anda mengerjakan produk minimum yang layak, cobalah untuk menghindari fitur yang tidak perlu; itu adalah jalan yang berbahaya dan sulit untuk disingkirkan.
  • Kami tidak akan mempertahankan pesan.
    Dengan kata lain, jika Anda meninggalkan pesta, Anda akan kehilangan pesan. Ini akan sangat menyederhanakan tugas kita, karena kita tidak perlu berurusan dengan jenis penyimpanan data apa pun, kita juga tidak perlu membuang waktu untuk memutuskan struktur data terbaik untuk menyimpan dan memulihkan pesan lama. Semuanya akan tersimpan dalam memori, dan akan tetap ada selama ruang obrolan aktif. Setelah ditutup, kami hanya akan mengucapkan selamat tinggal kepada mereka!
  • Komunikasi akan dilakukan melalui soket .
    Sayangnya, klien kami harus menangani saluran komunikasi ganda: saluran RESTful untuk mesin game dan soket untuk server obrolan. Ini mungkin sedikit meningkatkan kerumitan klien, tetapi pada saat yang sama, itu akan menggunakan metode komunikasi terbaik untuk setiap modul. (Tidak ada gunanya memaksa REST di server obrolan kami atau memaksa soket di server game kami. Pendekatan itu akan meningkatkan kompleksitas kode sisi server, yang juga menangani logika bisnis, jadi mari fokus pada sisi itu untuk sekarang.)

Itu saja untuk server obrolan. Bagaimanapun, itu tidak akan rumit, setidaknya tidak pada awalnya. Ada lebih banyak hal yang harus dilakukan ketika saatnya untuk mulai mengkodekannya, tetapi untuk artikel ini, ini adalah informasi yang lebih dari cukup.

Klien

Ini adalah modul terakhir yang membutuhkan pengkodean, dan ini akan menjadi yang terbodoh dari semuanya. Sebagai aturan praktis, saya lebih suka klien saya bodoh dan server saya pintar. Dengan begitu, membuat klien baru untuk server menjadi lebih mudah.

Agar kita berada di halaman yang sama, inilah arsitektur tingkat tinggi yang seharusnya kita dapatkan.

Arsitektur tingkat tinggi akhir dari seluruh pengembangan
Arsitektur tingkat tinggi akhir dari seluruh pengembangan (Pratinjau besar)

Klien ClI sederhana kami tidak akan mengimplementasikan sesuatu yang sangat kompleks. Faktanya, bagian paling rumit yang harus kita tangani adalah UI yang sebenarnya, karena ini adalah antarmuka berbasis teks.

Karena itu, fungsionalitas yang harus diterapkan oleh aplikasi klien adalah sebagai berikut:

  1. Buat permainan baru .
    Karena saya ingin membuat semuanya sesederhana mungkin, ini hanya akan dilakukan melalui antarmuka CLI. UI yang sebenarnya hanya akan digunakan setelah bergabung dengan game, yang membawa kita ke poin berikutnya.
  2. Bergabunglah dengan permainan yang ada .
    Mengingat kode permainan yang dikembalikan dari poin sebelumnya, pemain dapat menggunakannya untuk bergabung. Sekali lagi, ini adalah sesuatu yang harus dapat Anda lakukan tanpa UI, jadi fungsi ini akan menjadi bagian dari proses yang diperlukan untuk mulai menggunakan UI teks.
  3. Mengurai file definisi permainan .
    Kami akan membahas ini sedikit, tetapi klien harus dapat memahami file-file ini untuk mengetahui apa yang harus ditampilkan dan mengetahui cara menggunakan data itu.
  4. Berinteraksi dengan petualangan.
    Pada dasarnya, ini memberi pemain kemampuan untuk berinteraksi dengan lingkungan yang dijelaskan pada waktu tertentu.
  5. Pertahankan inventaris untuk setiap pemain .
    Setiap instance klien akan berisi daftar item dalam memori. Daftar ini akan dicadangkan.
  6. Mendukung obrolan .
    Aplikasi klien juga perlu terhubung ke server obrolan dan memasukkan pengguna ke ruang obrolan pesta.

Lebih lanjut tentang struktur dan desain internal klien nanti. Sementara itu, mari selesaikan tahap desain dengan persiapan terakhir: file game.

Game: File JSON

Di sinilah menjadi menarik karena sampai sekarang, saya telah membahas definisi dasar layanan mikro. Beberapa dari mereka mungkin berbicara REST, dan yang lain mungkin bekerja dengan soket, tetapi pada dasarnya, semuanya sama: Anda mendefinisikannya, Anda mengkodekannya, dan mereka menyediakan layanan.

Untuk komponen khusus ini, saya tidak berencana mengkodekan apa pun, namun kita perlu mendesainnya. Pada dasarnya, kami menerapkan semacam protokol untuk mendefinisikan permainan kami, adegan di dalamnya dan semua yang ada di dalamnya.

Jika Anda memikirkannya, petualangan teks, pada intinya, pada dasarnya adalah sekumpulan kamar yang terhubung satu sama lain, dan di dalamnya ada "hal" yang dapat Anda interaksikan, semuanya diikat bersama dengan, semoga, cerita yang layak. Sekarang, mesin kami tidak akan mengurus bagian terakhir itu; bagian itu terserah Anda. Tapi selebihnya, masih ada harapan.

Sekarang, kembali ke himpunan ruangan yang saling berhubungan, yang bagi saya terdengar seperti grafik, dan jika kita juga menambahkan konsep jarak atau kecepatan gerakan yang saya sebutkan sebelumnya, kita memiliki grafik berbobot. Dan itu hanyalah sekumpulan node yang memiliki bobot (atau hanya sebuah angka — jangan khawatir tentang apa namanya) yang mewakili jalur di antara mereka. Berikut adalah visualnya (Saya suka belajar dengan melihat, jadi lihat saja gambarnya, oke?):

Contoh grafik berbobot
Contoh grafik berbobot (Pratinjau besar)

Itu adalah grafik berbobot — itu saja. Dan saya yakin Anda sudah mengetahuinya, tetapi demi kelengkapan, izinkan saya menunjukkan kepada Anda bagaimana Anda akan melakukannya setelah mesin kami siap.

Setelah Anda mulai menyiapkan petualangan, Anda akan membuat peta Anda (seperti yang Anda lihat di sebelah kiri gambar di bawah). Dan kemudian Anda akan menerjemahkannya ke dalam grafik berbobot, seperti yang Anda lihat di sebelah kanan gambar. Mesin kami akan dapat mengambilnya dan membiarkan Anda melewatinya dengan urutan yang benar.

Contoh grafik untuk penjara bawah tanah yang diberikan
Contoh grafik untuk dungeon tertentu (Pratinjau besar)

Dengan grafik berbobot di atas, kita bisa memastikan pemain tidak bisa pergi dari pintu masuk sampai ke sayap kiri. Mereka harus melalui node di antara keduanya, dan hal itu akan menghabiskan waktu, yang dapat kita ukur menggunakan bobot dari koneksi.

Sekarang, ke bagian "menyenangkan". Mari kita lihat bagaimana grafik akan terlihat dalam format JSON. Bersabarlah dengan saya di sini; JSON ini akan berisi banyak informasi, tetapi saya akan membahasnya sebanyak mungkin:

 { "graph": [ { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } }, { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } , { "id": "bigroom", "name": "Big room", "south": { "node": "1stroom", "distance": 1}, "north": { "node": "bossroom", "distance": 2}, "east": { "node": "rightwing", "distance": 3} , "west": { "node": "leftwing", "distance": 3} }, { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} } { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} } { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } } ], "game": { "win-condition": { "source": "finalboss", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } }, "lose-condition": { "source": "player", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } } }, "rooms": { "entrance": { "description": { "default": "You're at the entrance of the dungeon. There are two lit torches on each wall (one on your right and one on your left). You see only one path: ahead." }, "items": [ { "id": "littorch1", "name": "Lit torch on the right", "triggers": [ { "action": "grab", //grab Lit torch on the right "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" }, { "id": "littorch2", "name": "Lit torch on the left", "triggers": [ { "action": "grab", //grab Lit torch on the left "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" } ] }, "1stroom": { "description": { "default": "You're in a very dark room. There are no windows and no source of light, other than the one at the entrance. You get the feeling you're not alone here.", "conditionals": { "has light": "The room you find yourself in appears to be empty, aside from a single chair in the right corner. There appears to be only one way out: deeper into the dungeon." } }, "items": [ { "id": "chair", "name": "Wooden chair", "details": "It's a wooden chair, nothing fancy about it. It appears to have been sitting here, untouched, for a while now.", "subitems": [ { "id": "woodenleg", "name": "Wooden leg", "triggeractions": [ { "action": "break", "target": "chair"}, //break { "action": "throw", "target": "chair"} //throw ], "destination": "inventory", "damage": 2 } ] } ] }, "bigroom": { "description": { "default": "You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you." }, "exits": { "north": { "id": "bossdoor", "name": "Big double door", "status": "locked", "details": "A aig, wooden double door. It seems like something big usually comes through here."} }, "items": [] }, "leftwing": { "description": { "default": "Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.", "conditionals": { "has light": "You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow." } }, "items": [ { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10} ] }, "rightwing": { "description": { "default": "This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk." }, "items": [ { "id": "key", "name": "Golden key", "details": "A small golden key. What use could you have for it?", "destination": "inventory", "triggers": [{ "action": "use", //use on north exit (contextual) "target": { "room": "bigroom", "exit": "north" }, "effect": { "statusUpdate": "unlocked", "target": { "room": "bigroom", "exit": "north" } } } ] } ] }, "bossroom": { "description": { "default": "You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you." }, "npcs": [ { "id": "finalboss", "name": "Hulking Ogre", "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.", "stats": { "hp": 10, "damage": 3 } } ] } } } { "graph": [ { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } }, { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } , { "id": "bigroom", "name": "Big room", "south": { "node": "1stroom", "distance": 1}, "north": { "node": "bossroom", "distance": 2}, "east": { "node": "rightwing", "distance": 3} , "west": { "node": "leftwing", "distance": 3} }, { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} } { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} } { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } } ], "game": { "win-condition": { "source": "finalboss", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } }, "lose-condition": { "source": "player", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } } }, "rooms": { "entrance": { "description": { "default": "You're at the entrance of the dungeon. There are two lit torches on each wall (one on your right and one on your left). You see only one path: ahead." }, "items": [ { "id": "littorch1", "name": "Lit torch on the right", "triggers": [ { "action": "grab", //grab Lit torch on the right "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" }, { "id": "littorch2", "name": "Lit torch on the left", "triggers": [ { "action": "grab", //grab Lit torch on the left "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" } ] }, "1stroom": { "description": { "default": "You're in a very dark room. There are no windows and no source of light, other than the one at the entrance. You get the feeling you're not alone here.", "conditionals": { "has light": "The room you find yourself in appears to be empty, aside from a single chair in the right corner. There appears to be only one way out: deeper into the dungeon." } }, "items": [ { "id": "chair", "name": "Wooden chair", "details": "It's a wooden chair, nothing fancy about it. It appears to have been sitting here, untouched, for a while now.", "subitems": [ { "id": "woodenleg", "name": "Wooden leg", "triggeractions": [ { "action": "break", "target": "chair"}, //break { "action": "throw", "target": "chair"} //throw ], "destination": "inventory", "damage": 2 } ] } ] }, "bigroom": { "description": { "default": "You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you." }, "exits": { "north": { "id": "bossdoor", "name": "Big double door", "status": "locked", "details": "A aig, wooden double door. It seems like something big usually comes through here."} }, "items": [] }, "leftwing": { "description": { "default": "Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.", "conditionals": { "has light": "You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow." } }, "items": [ { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10} ] }, "rightwing": { "description": { "default": "This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk." }, "items": [ { "id": "key", "name": "Golden key", "details": "A small golden key. What use could you have for it?", "destination": "inventory", "triggers": [{ "action": "use", //use on north exit (contextual) "target": { "room": "bigroom", "exit": "north" }, "effect": { "statusUpdate": "unlocked", "target": { "room": "bigroom", "exit": "north" } } } ] } ] }, "bossroom": { "description": { "default": "You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you." }, "npcs": [ { "id": "finalboss", "name": "Hulking Ogre", "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.", "stats": { "hp": 10, "damage": 3 } } ] } } } { "graph": [ { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } }, { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } , { "id": "bigroom", "name": "Big room", "south": { "node": "1stroom", "distance": 1}, "north": { "node": "bossroom", "distance": 2}, "east": { "node": "rightwing", "distance": 3} , "west": { "node": "leftwing", "distance": 3} }, { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} } { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} } { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } } ], "game": { "win-condition": { "source": "finalboss", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } }, "lose-condition": { "source": "player", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } } }, "rooms": { "entrance": { "description": { "default": "You're at the entrance of the dungeon. There are two lit torches on each wall (one on your right and one on your left). You see only one path: ahead." }, "items": [ { "id": "littorch1", "name": "Lit torch on the right", "triggers": [ { "action": "grab", //grab Lit torch on the right "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" }, { "id": "littorch2", "name": "Lit torch on the left", "triggers": [ { "action": "grab", //grab Lit torch on the left "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" } ] }, "1stroom": { "description": { "default": "You're in a very dark room. There are no windows and no source of light, other than the one at the entrance. You get the feeling you're not alone here.", "conditionals": { "has light": "The room you find yourself in appears to be empty, aside from a single chair in the right corner. There appears to be only one way out: deeper into the dungeon." } }, "items": [ { "id": "chair", "name": "Wooden chair", "details": "It's a wooden chair, nothing fancy about it. It appears to have been sitting here, untouched, for a while now.", "subitems": [ { "id": "woodenleg", "name": "Wooden leg", "triggeractions": [ { "action": "break", "target": "chair"}, //break { "action": "throw", "target": "chair"} //throw ], "destination": "inventory", "damage": 2 } ] } ] }, "bigroom": { "description": { "default": "You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you." }, "exits": { "north": { "id": "bossdoor", "name": "Big double door", "status": "locked", "details": "A aig, wooden double door. It seems like something big usually comes through here."} }, "items": [] }, "leftwing": { "description": { "default": "Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.", "conditionals": { "has light": "You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow." } }, "items": [ { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10} ] }, "rightwing": { "description": { "default": "This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk." }, "items": [ { "id": "key", "name": "Golden key", "details": "A small golden key. What use could you have for it?", "destination": "inventory", "triggers": [{ "action": "use", //use on north exit (contextual) "target": { "room": "bigroom", "exit": "north" }, "effect": { "statusUpdate": "unlocked", "target": { "room": "bigroom", "exit": "north" } } } ] } ] }, "bossroom": { "description": { "default": "You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you." }, "npcs": [ { "id": "finalboss", "name": "Hulking Ogre", "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.", "stats": { "hp": 10, "damage": 3 } } ] } } } { "graph": [ { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } }, { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } , { "id": "bigroom", "name": "Big room", "south": { "node": "1stroom", "distance": 1}, "north": { "node": "bossroom", "distance": 2}, "east": { "node": "rightwing", "distance": 3} , "west": { "node": "leftwing", "distance": 3} }, { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} } { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} } { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } } ], "game": { "win-condition": { "source": "finalboss", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } }, "lose-condition": { "source": "player", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } } }, "rooms": { "entrance": { "description": { "default": "You're at the entrance of the dungeon. There are two lit torches on each wall (one on your right and one on your left). You see only one path: ahead." }, "items": [ { "id": "littorch1", "name": "Lit torch on the right", "triggers": [ { "action": "grab", //grab Lit torch on the right "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" }, { "id": "littorch2", "name": "Lit torch on the left", "triggers": [ { "action": "grab", //grab Lit torch on the left "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" } ] }, "1stroom": { "description": { "default": "You're in a very dark room. There are no windows and no source of light, other than the one at the entrance. You get the feeling you're not alone here.", "conditionals": { "has light": "The room you find yourself in appears to be empty, aside from a single chair in the right corner. There appears to be only one way out: deeper into the dungeon." } }, "items": [ { "id": "chair", "name": "Wooden chair", "details": "It's a wooden chair, nothing fancy about it. It appears to have been sitting here, untouched, for a while now.", "subitems": [ { "id": "woodenleg", "name": "Wooden leg", "triggeractions": [ { "action": "break", "target": "chair"}, //break { "action": "throw", "target": "chair"} //throw ], "destination": "inventory", "damage": 2 } ] } ] }, "bigroom": { "description": { "default": "You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you." }, "exits": { "north": { "id": "bossdoor", "name": "Big double door", "status": "locked", "details": "A aig, wooden double door. It seems like something big usually comes through here."} }, "items": [] }, "leftwing": { "description": { "default": "Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.", "conditionals": { "has light": "You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow." } }, "items": [ { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10} ] }, "rightwing": { "description": { "default": "This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk." }, "items": [ { "id": "key", "name": "Golden key", "details": "A small golden key. What use could you have for it?", "destination": "inventory", "triggers": [{ "action": "use", //use on north exit (contextual) "target": { "room": "bigroom", "exit": "north" }, "effect": { "statusUpdate": "unlocked", "target": { "room": "bigroom", "exit": "north" } } } ] } ] }, "bossroom": { "description": { "default": "You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you." }, "npcs": [ { "id": "finalboss", "name": "Hulking Ogre", "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.", "stats": { "hp": 10, "damage": 3 } } ] } } }

Saya tahu ini terlihat seperti banyak, tetapi jika Anda meringkasnya menjadi deskripsi permainan yang sederhana, Anda memiliki ruang bawah tanah yang terdiri dari enam kamar, masing-masing saling berhubungan satu sama lain, seperti yang ditunjukkan pada diagram di atas.

Tugas Anda adalah menelusurinya dan menjelajahinya. Anda akan menemukan ada dua tempat berbeda di mana Anda dapat menemukan senjata (baik di dapur atau di ruangan gelap, dengan memecahkan kursi). Anda juga akan dihadapkan dengan pintu yang terkunci; jadi, setelah Anda menemukan kuncinya (terletak di dalam ruangan seperti kantor), Anda akan dapat membukanya dan melawan bos dengan senjata apa pun yang telah Anda kumpulkan.

Anda akan menang dengan membunuhnya atau kalah dengan terbunuh olehnya.

Sekarang mari kita masuk ke gambaran yang lebih rinci dari seluruh struktur JSON dan tiga bagiannya.

Grafik

Yang ini akan berisi hubungan antara node. Pada dasarnya, bagian ini langsung diterjemahkan ke dalam grafik yang kita lihat sebelumnya.

Struktur untuk bagian ini cukup sederhana. Ini adalah daftar node, di mana setiap node terdiri dari atribut berikut:

  • ID yang secara unik mengidentifikasi node di antara semua node lainnya dalam game;
  • sebuah nama, yang pada dasarnya adalah versi ID yang dapat dibaca manusia;
  • satu set link ke node lain. Hal ini dibuktikan dengan adanya empat kemungkinan kunci: utara”, selatan, timur, dan barat. Kami akhirnya bisa menambahkan arah lebih lanjut dengan menambahkan kombinasi dari empat ini. Setiap link berisi ID dari node terkait dan jarak (atau bobot) dari relasi tersebut.

Permainan

Bagian ini akan berisi pengaturan dan ketentuan umum. Secara khusus, dalam contoh di atas, bagian ini berisi ketentuan menang dan kalah. Dengan kata lain, dengan dua kondisi itu, kami akan memberi tahu engine kapan game bisa berakhir.

Untuk mempermudah, saya hanya menambahkan dua kondisi:

  • Anda juga menang dengan membunuh bos,
  • atau kalah dengan terbunuh.

Kamar

Di sinilah sebagian besar dari 163 baris berasal, dan ini adalah bagian yang paling rumit. Di sinilah kami akan menjelaskan semua ruangan dalam petualangan kami dan semua yang ada di dalamnya.

Akan ada kunci untuk setiap ruangan, menggunakan ID yang telah kita tentukan sebelumnya. Dan setiap ruangan akan memiliki deskripsi, daftar item, daftar pintu keluar (atau pintu) dan daftar karakter yang tidak dapat dimainkan (NPC). Dari properti tersebut, satu-satunya yang harus wajib adalah deskripsi, karena itu diperlukan agar mesin memberi tahu Anda apa yang Anda lihat. Sisanya hanya akan ada di sana jika ada sesuatu untuk ditunjukkan.

Mari kita lihat apa yang bisa dilakukan properti ini untuk game kita.

Deskripsi

Item ini tidak sesederhana yang dibayangkan, karena pandangan Anda tentang sebuah ruangan dapat berubah tergantung pada keadaan yang berbeda. Jika, misalnya, Anda melihat deskripsi ruangan pertama, Anda akan melihat bahwa, secara default, Anda tidak dapat melihat apa pun, kecuali tentu saja, Anda membawa obor yang menyala.

Jadi, mengambil item dan menggunakannya dapat memicu kondisi global yang akan memengaruhi bagian lain dari game.

Barang-barang

Ini mewakili semua hal” yang dapat Anda temukan di dalam sebuah ruangan. Setiap item memiliki ID dan nama yang sama dengan node di bagian grafik.

Mereka juga akan memiliki properti "tujuan", yang menunjukkan di mana barang itu harus disimpan, setelah diambil. Ini relevan karena Anda hanya dapat memiliki satu item di tangan Anda, sedangkan Anda akan dapat memiliki sebanyak yang Anda inginkan di inventaris Anda.

Terakhir, beberapa item ini mungkin memicu tindakan lain atau pembaruan status, tergantung pada apa yang diputuskan pemain untuk dilakukan dengan item tersebut. Salah satu contohnya adalah obor yang menyala dari pintu masuk. Jika Anda mengambil salah satunya, Anda akan memicu pembaruan status dalam game, yang pada gilirannya akan membuat game menampilkan deskripsi berbeda dari ruangan berikutnya.

Item juga dapat memiliki "subitem", yang ikut bermain setelah item asli dihancurkan (melalui tindakan "break", misalnya). Sebuah item dapat dipecah menjadi beberapa item, dan itu didefinisikan dalam elemen "subitem".

Pada dasarnya, elemen ini hanyalah larik item baru, yang juga berisi serangkaian tindakan yang dapat memicu pembuatannya. Ini pada dasarnya membuka kemungkinan untuk membuat subitem yang berbeda berdasarkan tindakan yang Anda lakukan pada item asli.

Akhirnya, beberapa item akan memiliki properti "kerusakan". Jadi, jika kamu menggunakan item untuk memukul NPC, nilai itu akan digunakan untuk mengurangi nyawa dari mereka.

Pintu Keluar

Ini hanyalah sekumpulan properti yang menunjukkan arah keluar dan propertinya (deskripsi, jika Anda ingin memeriksanya, namanya dan, dalam beberapa kasus, statusnya).

Keluar adalah entitas yang terpisah dari item karena mesin perlu memahami jika Anda benar-benar dapat melintasinya berdasarkan statusnya. Pintu keluar yang dikunci tidak akan membiarkan Anda melewatinya kecuali Anda mengetahui cara mengubah statusnya menjadi tidak terkunci.

NPC

Akhirnya, NPC akan menjadi bagian dari daftar lain. Mereka pada dasarnya adalah item dengan statistik yang akan digunakan mesin untuk memahami bagaimana masing-masing harus berperilaku. Yang telah kami definisikan dalam contoh kami adalah "hp", yang merupakan singkatan dari poin kesehatan, dan "kerusakan", yang, seperti halnya senjata, adalah jumlah yang akan dikurangi setiap pukulan dari kesehatan pemain.

Itu saja untuk penjara bawah tanah yang saya buat. Banyak, ya, dan di masa depan saya mungkin mempertimbangkan untuk membuat semacam editor level, untuk menyederhanakan pembuatan file JSON. Tapi untuk saat ini, itu tidak perlu.

Jika Anda belum menyadarinya, manfaat utama dari mendefinisikan game kami dalam file seperti ini adalah kami dapat mengganti file JSON seperti yang Anda lakukan pada kartrid di era Super Nintendo. Cukup muat file baru dan mulai petualangan baru. Mudah!

Pikiran Penutup

Terima kasih telah membaca sejauh ini. Saya harap Anda menikmati proses desain yang saya lalui untuk mewujudkan ide. Ingat, bagaimanapun, bahwa saya mengarangnya saat saya pergi, jadi kita mungkin akan menyadari nanti bahwa sesuatu yang kita definisikan hari ini tidak akan berhasil, dalam hal ini kita harus mundur dan memperbaikinya.

Saya yakin ada banyak cara untuk meningkatkan ide yang disajikan di sini dan untuk membuat mesin yang hebat. Tapi itu akan membutuhkan lebih banyak kata daripada yang bisa saya masukkan ke dalam artikel tanpa membuatnya membosankan bagi semua orang, jadi kita akan berhenti di situ untuk saat ini.

Bagian Lain Dari Seri Ini

  • Bagian 2: Desain Server Mesin Game
  • Bagian 3: Membuat Klien Terminal
  • Bagian 4: Menambahkan Obrolan Ke Game Kami