Як я реалізую діалог розгалуження в JavaScript?


11

Я роблю дуже базовий візуальний роман типу гри в JavaScript. Я початківець, тому я просто роблю це для розваги та навчання, і через неправильне планування у мене виникли проблеми, коли ти дістаєшся до відділення в діалозі.

В даний час я тримаю скрипт для гри в рядковій змінній і розбиваю кожну сцену тегом, таким як "# ~", на менші масиви, щоб сценарій гри виглядав так:

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

Це чудово підходить для лінійних матеріалів, але як я повинен обробляти гілку в дереві діалогів? Цей метод здається дуже складним, тому що я повинен був би точно знати, скільки триває кожен шлях, і тоді, якщо мені коли-небудь потрібно щось змінити, це буде головний біль.

Як я можу це зробити більш простим способом? Я намагаюся дотримуватися JavaScript ванілі, як я хотів би, щоб гра працювала з Web Run Time.


Це відео може дати вам кілька ідей: youtube.com/watch?v=XM2t5H7kY6Y
JCM

Нещодавно мені довелося щось розробити для цього за допомогою Node, і вибрав дуже базову структуру текстових файлів. Ви можете побачити отриманий код та формат тексту за адресою: github.com/scottbw/dialoguejs Код GPL, не соромтеся ним користуватися. Я впевнений, що не важко буде адаптуватися до гри, що не є Node - замініть частину "fs.load ()" на Ajax.
Скотт Вілсон

Ознайомтеся з чорнилом , мовою сценаріїв розгалуженої історії, розробленою Inkle Studio . Існують різні програмні інтеграції чорнила (Java, Javascript, C #) та багато сторонніх ресурсів . Чорнила також використовувались у багатьох комерційних іграх. Нарешті, є настільний редактор Inky, який може перевірити синтаксис і «відтворити» ваші розгалужувальні діалоги.
Великий багатий

Відповіді:


8

Відповідь Філіпа вже показує правильний напрямок. Я просто думаю, що структура даних непотрібна багатослівна. Коротші тексти було б простіше писати та читати.

Навіть якщо більш короткі тексти роблять алгоритм трохи складнішим, це варто зробити, тому що алгоритм ви пишете лише один раз, але більшість вашого часу буде витрачено на написання та підтримку історії. Тому оптимізуйте для того, щоб полегшити ту частину, на яку будете витрачати більшу частину часу.

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" }
];

Деякі пояснення цього дизайну:

Вся історія написана одним масивом. Вам не потрібно вводити номери, вони надаються автоматично синтаксисом масиву: перший елемент має індекс 0, наступний - індекс 1 тощо.

У більшості випадків не потрібно писати номер наступного кроку. Я припускаю, що більшість рядків тексту не є гілками. Давайте зробимо «наступним кроком наступний пункт» припущення за замовчуванням, і робимо примітки лише тоді, коли це інакше.

Для стрибків використовуйте мітки , а не цифри. Тоді, якщо згодом ви додасте чи вилучите кілька рядків, логіка історії буде збережена, і вам не доведеться коригувати числа.

Знайдіть розумний компроміс між чіткістю та короткістю. Наприклад, я пропоную написати "m" замість "message", тому що це буде найчастіше використовувана команда коли-небудь, тому, якщо скоротити її, зробить текст більш розбірливим. Але не потрібно скорочувати решту ключових слів. (Однак робіть так, як хочете. Важливим є зробити це найбільш розбірливим для вас . Як варіант, ви можете підтримувати і "m", і "message" як дійсні ключові слова.)

Алгоритм гри повинен бути приблизно таким:

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

    }
  }
}

До речі, ці ідеї були натхнені компанією Ren'Py , яка не є саме такою, яку ви хочете (не JavaScript, не веб), але все одно може дати вам круті ідеї.


Дякую за глибоке пояснення, я не знав, що масиви можуть працювати так, як ви та Філіп показали, я думав, що вони можуть містити лише рядки або числа.
Мовчазний картограф

1
Я намагався реалізувати ваше рішення, і це працює досить добре, хоча я думаю, що в деяких місцях ({ label: "like_yes"; m: "I am happy you like my game!"; next: "like_end" },)повинні бути "," не ";". Також як саме називається річ у фігурних брекетах? Це об’єкт у масиві? якби я хотів отримати більше інформації про те, як це використовувати, що б я шукав?
Мовчазний картограф

Так, {...}є об’єктом. У JavaScript об'єкт - це асоціативний масив ключових значень (з деякою додатковою функціональністю, не використовується в цьому прикладі), аналогічний масиву в PHP або Map у Java. Для отримання додаткової інформації дивіться статті Вікіпедії на JavaScript та ECMAScript та пов'язану з нею документацію, особливо офіційну документацію ECMAScript.
Viliam Búr

1
btw зауважте, що структура даних, яку він рекомендує тут, в основному є JSON. Особисто я рекомендую пройти весь шлях до JSON (в основному додайте фігурні дужки та "дерево": навколо всієї справи; наприклад, {"дерево": [тощо]}), а потім ви можете зберігати діалогові дерева у зовнішніх файлах. Введення даних у зовнішні файли, які завантажує ваша гра, набагато гнучкіше і модульніше (тому саме такий підхід є найкращою практикою).
поштовх

5

Я рекомендую вам створити масив діалогових подій. Кожна подія - це об'єкт, що містить текст, який говорить NPC, і масив можливих відповідей гравця, що, в свою чергу, є об'єктами з текстом відповіді та індексом події, що випливає з цієї відповіді.

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",

...

2

Вам слід використовувати інший підхід. JavaScript підтримує масиви та об'єкти, то чому б не використовувати один за запис, заощаджуючи всі розбиття, а також полегшуючи редагування / читання фактичного тексту?

Якщо ви хочете, ви можете ознайомитись з деяким прототипом, який я склав протягом декількох годин протягом # 1гаму . Джерело може бути використане під GPLv3 (я прекрасно, якщо ви не дотримуєтесь GPL, якщо ви просто використовуєте його для натхнення. Але повідомте мене, як тільки ваша гра буде закінчена.) Просто не сподівайтесь на дивовижне написання чи щось подібне. ;)

Щоб дати коротке пояснення того, як працює код, ігноруючи речі анімації CSS та подібні речі:

  • var data по суті містить всю історію з усіма можливими варіантами тощо.
  • Кожне "місцезнаходження" (або сторінка / запис) ідентифікується ідентифікатором. Перший ідентифікатор у списку - startдругий cwaitтощо.
  • Кожне місце містить два обов'язкових елемента: Запис, а також власне текст. Посилання на рішення пишуться у вигляді простої розмітки у формі [target location:display text].
  • Вся "магія" - це відбувається всередині navigate(). Ця функція робить посилання розмітки натисканням. Це трохи довше, бо я також обробляю якийсь статичний текст для мертвих кінців. Важливою частиною є перші два заклики до replace().
  • Необов'язкові останні записи визначають нові кольори тла, які слід поєднувати, підтримуючи загальний настрій гри.
  • Замість визначення цих кольорів ви можете також додати посилання, що вказують на інші місця (зверніть увагу, це не обробляється моїм кодом; це лише якась ідея, щоб продемонструвати це):

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.