Bagaimana cara menerapkan dialog percabangan dalam javascript?

11

Saya membuat jenis permainan visual visual yang sangat mendasar dalam JavaScript. Saya seorang pemula, jadi saya hanya melakukan ini untuk bersenang-senang dan belajar, dan karena perencanaan yang buruk saya telah mengalami sedikit masalah ketika Anda sampai ke cabang dalam dialog.

Saat ini saya memegang script untuk game dalam variabel string dan saya memecah setiap adegan dengan tag seperti "# ~" menjadi array yang lebih kecil sehingga script game terlihat seperti ini:

var script = "Hello World!#~How are you today?"
var gameText = script.split("#~");
//gameText[0]= Hello World!

Ini berfungsi baik untuk hal-hal linier, tetapi bagaimana saya harus menangani cabang di pohon dialog? Metode ini tampaknya sangat rumit, karena saya harus tahu persis berapa lama setiap jalur dan kemudian jika saya perlu mengubah apa pun itu akan menjadi sakit kepala.

Bagaimana saya bisa melakukan ini dengan cara yang lebih sederhana? Saya mencoba untuk tetap menggunakan vanilla JavaScript karena saya ingin game bekerja dengan Web Run Time.

The Silent Cartographer
sumber
Video ini dapat memberi Anda beberapa ide: youtube.com/watch?v=XM2t5H7kY6Y
JCM
Saya baru-baru ini harus mengembangkan sesuatu untuk ini menggunakan Node, dan memilih untuk struktur file teks yang sangat dasar. Anda dapat melihat kode yang dihasilkan dan format teks di: github.com/scottbw/dialoguejs Kode tersebut adalah GPL, jangan ragu untuk menggunakannya. Saya yakin tidak akan sulit untuk beradaptasi untuk game JS yang bukan Node - ganti bagian "fs.load ()" dengan Ajax.
Scott Wilson
Lihatlah Ink , bahasa scripting cerita percabangan, yang dikembangkan oleh Inkle Studio . Ada berbagai integrasi Tinta terprogram (Java, Javascript, C #) dan banyak sumber daya pihak ke - 3 . Tinta juga telah digunakan di banyak game komersial. Akhirnya, ada editor desktop, Inky yang dapat memeriksa sintaks dan 'memutar' dialog percabangan Anda.
Big Rich

Jawaban:

8

Jawaban Philipp sudah menunjukkan arah yang benar. Saya hanya berpikir struktur data tidak perlu bertele-tele. Teks yang lebih pendek akan lebih mudah untuk ditulis dan dibaca.

Sekalipun teks yang lebih pendek akan membuat algoritme sedikit lebih rumit, itu layak dilakukan, karena Anda hanya menulis algoritme satu kali, tetapi sebagian besar waktu Anda akan dihabiskan untuk menulis dan memelihara cerita. Karena itu optimalkan untuk mempermudah bagian yang paling Anda habiskan.

var story = [
  { m: "Hi!" },
  { m: "This is my new game." },
  { question: "Do you like it?", answers: [
    { m: "yes", next: "like_yes" },
    { m: "no", next: "like_no" },
  ] },
  { label: "like_yes", m: "I am happy you like my game!", next: "like_end" },
  { label: "like_no", m: "You made me sad!", next: "like_end" },
  { label: "like_end" },
  { m: "OK, let's change the topic" }
];

Beberapa penjelasan untuk desain ini:

Seluruh cerita ditulis dalam satu larik. Anda tidak harus memberikan angka, angka itu diberikan secara otomatis oleh sintaks array: item pertama memiliki indeks 0, item berikutnya memiliki indeks 1, dll.

Dalam kebanyakan kasus, tidak perlu menulis nomor langkah berikut. Saya berasumsi bahwa sebagian besar baris teks bukan cabang. Mari kita membuat "langkah selanjutnya adalah item berikut" sebagai asumsi default, dan hanya membuat catatan ketika sebaliknya.

Untuk lompatan, gunakan label , bukan angka. Kemudian, jika nanti Anda menambahkan atau menghapus beberapa baris, logika cerita akan dipertahankan, dan Anda tidak harus menyesuaikan angka.

Temukan kompromi yang masuk akal antara kejelasan dan kekurangan. Sebagai contoh, saya menyarankan untuk menulis "m" daripada "message", karena itu akan menjadi perintah yang paling sering digunakan, jadi membuatnya singkat akan membuat teks lebih terbaca. Tetapi tidak perlu mempersingkat kata kunci yang tersisa. (Namun, lakukan seperti yang Anda inginkan. Yang penting adalah membuatnya menjadi terbaca untuk Anda . Atau, Anda dapat mendukung "m" dan "pesan" sebagai kata kunci yang valid.)

Algoritma untuk gim harus seperti ini:

function execute_game() {
  var current_line = 0;
  while (current_line < story.length) {
    var current_step = story[current_line];
    if (undefined !== current_step.m) {

      display_message(current_step.m);
      if (undefined !== current_step.next) {
        current_line = find_label(current_step.next);
      } else {
        current_line = current_line + 1;
      }

    } else if (undefined !== current_step.question) {

      // display the question: current_step.question
      // display the answers: current_step.answers
      // choose an answer
      // and change current_line accordingly

    }
  }
}

Ngomong-ngomong, ide-ide ini terinspirasi oleh Ren'Py , yang tidak persis seperti yang Anda inginkan (bukan JavaScript, bukan web), tetapi tetap dapat memberi Anda beberapa ide keren.

Viliam Búr
sumber
Terima kasih atas penjelasan mendalam, saya tidak tahu bahwa array dapat bekerja seperti yang Anda dan Philipp tunjukkan, saya pikir mereka hanya dapat menyimpan String atau angka.
The Silent Cartographer
1
Saya sudah mencoba menerapkan solusi Anda dan itu bekerja dengan cukup baik, meskipun saya pikir di beberapa tempat ({ label: "like_yes"; m: "I am happy you like my game!"; next: "like_end" },)harus memiliki ',' bukan ';'. Juga apa tepatnya yang disebut dalam kurung kurawal? Apakah itu objek dalam array? jika saya ingin info lebih lanjut tentang cara menggunakan ini apa yang akan saya cari?
The Silent Cartographer
Ya, {...}adalah sebuah objek. Dalam JavaScript, objek adalah array asosiatif nilai kunci (dengan beberapa fungsionalitas tambahan, tidak digunakan dalam contoh ini), mirip dengan array dalam PHP, atau Peta di Jawa. Untuk info lebih lanjut, lihat artikel Wikipedia tentang JavaScript dan ECMAScript, dan dokumentasi yang ditautkan dari sana, terutama dokumentasi resmi ECMAScript.
Viliam Búr
1
btw perhatikan bahwa struktur data yang dia rekomendasikan di sini pada dasarnya adalah JSON. Secara pribadi saya akan merekomendasikan semua jalan ke JSON (kebanyakan menambahkan kurung keriting dan "pohon": sekitar seluruh hal; seperti {"pohon": [dll]}) dan kemudian Anda dapat menyimpan pohon dialog Anda dalam file eksternal. Menempatkan data Anda ke file eksternal yang dimuat game Anda jauh lebih fleksibel dan modular (jadi mengapa pendekatan itu merupakan praktik terbaik).
jhocking
5

Saya akan merekomendasikan Anda untuk membuat berbagai acara dialog. Setiap peristiwa adalah objek yang berisi teks yang dikatakan NPC dan berbagai kemungkinan tanggapan pemain, yang pada gilirannya adalah objek dengan teks respons dan indeks acara yang mengikuti respons ini.

var event = []; // create empty array

// create event objects and store them in the array
event[0] = { text: "Hello, how are you?",
             options: [    { response: "Bad", next: 1 },
                           { response: "Good", next: 2 }
                      ]
           };
event[1] = { text: "Why, what's wrong?",
             options: [    { response: "My dog ran away", next: 3},
                           { response: "I broke up with my girlfriend", next: 4}
                      ]
           };
event[2] = { text: "That's nice",

...
Philipp
sumber
2

Anda harus menggunakan pendekatan yang berbeda. JavaScript mendukung array dan objek, jadi mengapa tidak menggunakan satu per entri, menghemat semua pemisahan dan juga membuat teks aktual lebih mudah untuk diedit / dibaca?

Jika Anda mau, Anda dapat melihat beberapa prototipe yang saya buat selama beberapa jam untuk # 1gam . Sumbernya gratis untuk digunakan di bawah GPLv3 (saya baik-baik saja jika Anda tidak menggunakan GPL, jika Anda hanya menggunakannya untuk inspirasi. Tapi beri tahu saya setelah game Anda selesai.). Hanya saja, jangan berharap tulisan yang luar biasa atau semacamnya. ;)

Untuk memberikan penjelasan singkat tentang cara kerja kode, abaikan hal-hal animasi CSS dan hal-hal seperti itu:

  • var data intinya berisi keseluruhan cerita dengan semua pilihan yang memungkinkan, dll.
  • Setiap "lokasi" (atau halaman / entri) diidentifikasi oleh ID. ID pertama dalam daftar adalah start, yang kedua cwait, dll.
  • Setiap lokasi berisi dua elemen wajib: Teks dan teks yang sebenarnya. Tautan untuk keputusan ditulis dalam beberapa markup sederhana yang mengambil formulir [target location:display text].
  • Seluruh "keajaiban" sedang terjadi di dalamnya navigate(): Fungsi ini membuat tautan Markup menjadi klik. Ini sedikit lebih lama, karena saya juga menangani beberapa teks statis untuk mati Berakhir di sana. Bagian penting adalah dua panggilan pertama replace().
  • Entri terakhir opsional menentukan warna latar belakang baru untuk berbaur, mendukung suasana keseluruhan permainan.
  • Alih-alih mendefinisikan warna-warna ini, Anda juga bisa menambahkan tautan yang menunjuk ke lokasi lain (perhatikan ini tidak ditangani oleh kode saya; itu hanya beberapa ide untuk menunjukkan ini):

    'start': ['Waking up', 'You wake...', 'cwait:yell for help', 'cwait: wait a bit', 'clook: look around']

Mario
sumber