Wie implementiere ich einen Verzweigungsdialog in Javascript?

11

Ich mache eine sehr einfache visuelle neuartige Art von Spiel in JavaScript. Ich bin ein Anfänger, also mache ich das nur zum Spaß und zum Lernen, und aufgrund der schlechten Planung bin ich auf ein kleines Problem gestoßen, wenn Sie zu einer Zweigstelle im Dialog kommen.

Derzeit halte ich das Skript für das Spiel in einer Zeichenfolgenvariablen und teile jede Szene mit einem Tag wie "# ~" in kleinere Arrays auf, sodass das Spieleskript folgendermaßen aussieht:

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

Dies funktioniert gut für lineare Inhalte, aber wie soll ich mit einem Zweig im Dialogbaum umgehen? Diese Methode scheint sehr kompliziert zu sein, da ich genau wissen müsste, wie lang jeder Pfad ist, und wenn ich dann jemals etwas ändern müsste, würde es Kopfschmerzen bereiten.

Wie kann ich das einfacher machen? Ich versuche, mich an Vanille-JavaScript zu halten, da ich möchte, dass das Spiel mit Web Run Time funktioniert.

Der stille Kartograph
quelle
Dieses Video kann Ihnen einige Ideen geben: youtube.com/watch?v=XM2t5H7kY6Y
JCM
Ich musste kürzlich mit Node etwas dafür entwickeln und entschied mich für eine sehr einfache Textdateistruktur. Sie können den resultierenden Code und das Textformat unter folgender Adresse sehen : github.com/scottbw/dialoguejs Der Code ist GPL. Sie können ihn gerne verwenden. Ich bin sicher, es wird nicht schwer sein, sich an ein Nicht-Node-JS-Spiel anzupassen - ersetzen Sie den Teil "fs.load ()" durch Ajax.
Scott Wilson
Schauen Sie sich Ink an , eine von Inkle Studio entwickelte Skriptsprache für verzweigte Geschichten . Es gibt verschiedene programmatische Ink-Integrationen (Java, Javascript, C #) und viele Ressourcen von Drittanbietern . Tinte wurde auch in vielen kommerziellen Spielen verwendet. Schließlich gibt es einen Desktop-Editor, Inky , der die Syntaxprüfung und Verzweigung Ihrer Verzweigungsdialoge durchführen kann.
Big Rich

Antworten:

8

Philipps Antwort zeigt bereits die richtige Richtung. Ich denke nur, dass die Datenstruktur unnötig ausführlich ist. Kürzere Texte wären leichter zu schreiben und zu lesen.

Selbst wenn kürzere Texte den Algorithmus etwas komplexer machen würden, lohnt es sich, dies zu tun, da Sie den Algorithmus nur einmal schreiben, aber die meiste Zeit damit verbringen, die Geschichte zu schreiben und zu pflegen. Optimieren Sie daher, um den Teil zu vereinfachen, für den Sie die meiste Zeit aufwenden.

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

Einige Erklärungen für dieses Design:

Die ganze Geschichte ist in einem Array geschrieben. Sie müssen keine Zahlen angeben, diese werden automatisch von der Array-Syntax bereitgestellt: Das erste Element hat den Index 0, das nächste den Index 1 usw.

In den meisten Fällen ist es nicht erforderlich, die Nummer des folgenden Schritts zu schreiben. Ich gehe davon aus, dass die meisten Textzeilen keine Zweige sind. Machen wir "Der nächste Schritt ist das folgende Element" zu einer Standardannahme und machen wir uns nur Notizen, wenn dies nicht der Fall ist.

Verwenden Sie für Sprünge Beschriftungen , keine Zahlen. Wenn Sie später einige Zeilen hinzufügen oder entfernen, bleibt die Logik der Geschichte erhalten, und Sie müssen die Zahlen nicht anpassen.

Finden Sie einen vernünftigen Kompromiss zwischen Klarheit und Kürze. Zum Beispiel schlage ich vor, "m" anstelle von "message" zu schreiben, da dies der am häufigsten verwendete Befehl aller Zeiten ist. Wenn Sie ihn also kurz machen, wird der Text besser lesbar. Die verbleibenden Schlüsselwörter müssen jedoch nicht gekürzt werden. (Tun Sie jedoch, was Sie möchten. Wichtig ist, dass es für Sie am besten lesbar ist . Alternativ können Sie sowohl "m" als auch "message" als gültige Schlüsselwörter unterstützen.)

Der Algorithmus für das Spiel sollte ungefähr so ​​aussehen:

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

    }
  }
}

Diese Ideen wurden übrigens von Ren'Py inspiriert , was nicht genau das ist, was Sie wollen (nicht JavaScript, nicht Web), aber Ihnen trotzdem einige coole Ideen geben könnte.

Viliam Búr
quelle
Vielen Dank für die ausführliche Erklärung. Ich war mir nicht bewusst, dass Arrays so funktionieren können, wie Sie und Philipp es gezeigt haben. Ich dachte, sie können nur Strings oder Zahlen enthalten.
Der stille Kartograph
1
Ich habe versucht, Ihre Lösung zu implementieren, und sie funktioniert ziemlich gut, obwohl ich denke, dass an einigen Stellen ({ label: "like_yes"; m: "I am happy you like my game!"; next: "like_end" },)ein ',' kein ';' stehen sollte. Und wie heißt das Ding in den geschweiften Klammern genau? Ist das ein Objekt innerhalb des Arrays? Wenn ich weitere Informationen zur Verwendung dieser Informationen wünschen würde, wonach würde ich suchen?
Der stille Kartograph
Ja, {...}ist ein Objekt. In JavaScript ist object ein assoziatives Schlüsselwert-Array (mit einigen zusätzlichen Funktionen, die in diesem Beispiel nicht verwendet werden), ähnlich dem Array in PHP oder Map in Java. Weitere Informationen finden Sie in den Wikipedia-Artikeln zu JavaScript und ECMAScript sowie in der von dort verlinkten Dokumentation, insbesondere in der offiziellen ECMAScript-Dokumentation.
Viliam Búr
1
Übrigens, die Datenstruktur, die er hier empfiehlt, ist im Grunde JSON. Persönlich würde ich empfehlen, den ganzen Weg zu JSON zu gehen (meistens geschweifte Klammern und einen "Baum" hinzufügen: um das Ganze herum; wie {"Baum": [etc]}) und dann können Sie Ihre Dialogbäume in externen Dateien speichern. Das Speichern Ihrer Daten in externen Dateien, die Ihr Spiel lädt, ist viel flexibler und modularer (daher ist dieser Ansatz eine bewährte Methode).
Schockieren
5

Ich würde Ihnen empfehlen, eine Reihe von Dialogereignissen zu erstellen. Jedes Ereignis ist ein Objekt, das den vom NPC angegebenen Text und eine Reihe möglicher Spielerantworten enthält, die wiederum Objekte mit einem Antworttext und dem Index des Ereignisses sind, das auf diese Antwort folgt.

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
quelle
2

Sie sollten einen anderen Ansatz verwenden. JavaScript unterstützt Arrays und Objekte. Warum also nicht eines pro Eintrag verwenden, um die Aufteilung zu sparen und den eigentlichen Text einfacher zu bearbeiten / lesen?

Wenn Sie möchten, können Sie sich einen Prototyp ansehen, den ich in ein paar Stunden für # 1gam hergestellt habe . Die Quelle kann kostenlos unter GPLv3 verwendet werden (ich bin vollkommen in Ordnung, wenn Sie sich nicht an die GPL halten, wenn Sie sie nur als Inspiration verwenden. Aber lassen Sie es mich wissen, sobald Ihr Spiel fertig ist.). Erwarten Sie einfach kein großartiges Schreiben oder ähnliches. ;)

Um eine kurze Erklärung zu geben, wie der Code funktioniert, ignorieren Sie die CSS-Animationssachen und ähnliches:

  • var data enthält im Wesentlichen die ganze Geschichte mit allen möglichen Entscheidungen usw.
  • Jeder "Ort" (oder jede Seite / jeder Eintrag) wird durch eine ID identifiziert. Die erste ID in der Liste ist start, die zweite cwaitusw.
  • Jeder Ort enthält zwei obligatorische Elemente: Eine Beschriftung sowie den eigentlichen Text. Links für Entscheidungen werden in einem einfachen Markup in der Form geschrieben [target location:display text].
  • Die ganze "Magie" geschieht im Inneren navigate(): Diese Funktion macht die Markup-Links klickend. Es ist etwas länger, weil ich dort auch statischen Text für Sackgassen verarbeite. Der wichtige Teil sind die ersten beiden Anrufe an replace().
  • Die optionalen letzten Einträge definieren neue Hintergrundfarben, die gemischt werden sollen, um die allgemeine Stimmung des Spiels zu unterstützen.
  • Anstatt diese Farben zu definieren, können Sie auch Links hinzufügen, die auf andere Orte verweisen (beachten Sie, dass dies nicht von meinem Code behandelt wird; es ist nur eine Idee, dies zu demonstrieren):

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

Mario
quelle