Comment implémenter le dialogue de branchement en javascript?

11

Je fais un type de jeu roman visuel très basique en JavaScript. Je suis un débutant, donc je fais juste cela pour le plaisir et l'apprentissage, et en raison d'une mauvaise planification, j'ai rencontré un peu de problème lorsque vous arrivez dans une branche en dialogue.

Actuellement, je tiens le script du jeu dans une variable de chaîne et je décompose chaque scène avec une balise telle que "# ~" en plus petits tableaux afin que le script du jeu ressemble à ceci:

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

Cela fonctionne très bien pour les choses linéaires, mais comment dois-je gérer une branche dans l'arborescence de dialogue? Cette méthode semble très compliquée, car je devrais savoir exactement combien de temps chaque chemin est et si jamais j'avais besoin de changer quoi que ce soit, ce serait un casse-tête.

Comment puis-je le faire de manière plus simple? J'essaie de m'en tenir au JavaScript vanille car j'aimerais que le jeu fonctionne avec Web Run Time.

Le cartographe silencieux
la source
Cette vidéo peut vous donner quelques idées: youtube.com/watch?v=XM2t5H7kY6Y
JCM
J'ai récemment dû développer quelque chose pour cela en utilisant Node, et j'ai opté pour une structure de fichier texte très basique. Vous pouvez voir le code et le format de texte obtenus sur: github.com/scottbw/dialoguejs Le code est GPL, n'hésitez pas à l'utiliser. Je suis sûr qu'il ne sera pas difficile de s'adapter à un jeu JS non-Node - remplacez la partie "fs.load ()" par Ajax.
Scott Wilson
Découvrez Ink , un langage de script d'histoires de branchement, développé par Inkle Studio . Il existe différentes intégrations programmatiques Ink (Java, Javascript, C #) et de nombreuses ressources tierces . L'encre a également été utilisée dans de nombreux jeux commerciaux. Enfin, il y a un éditeur de bureau, Inky qui peut vérifier la syntaxe et «jouer» vos dialogues de branchement.
Big Rich

Réponses:

8

La réponse de Philipp montre déjà la bonne direction. Je pense simplement que la structure des données est inutilement verbeuse. Des textes plus courts seraient plus faciles à écrire et à lire.

Même si des textes plus courts rendraient l'algorithme un peu plus complexe, cela vaut la peine, car vous n'écrivez l'algorithme qu'une seule fois, mais la plupart de votre temps sera consacré à l'écriture et à la maintenance de l'histoire. Par conséquent, optimisez pour faciliter la partie que vous passerez le plus de temps à faire.

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

Quelques explications pour cette conception:

L'histoire entière est écrite dans un tableau. Vous n'avez pas à fournir de nombres, ils sont fournis automatiquement par la syntaxe du tableau: le premier élément a l'index 0, le suivant a l'index 1, etc.

Dans la plupart des cas, il n'est pas nécessaire d'écrire le numéro de l'étape suivante. Je suppose que la plupart des lignes de texte ne sont pas des branches. Faisons "l'étape suivante est l'élément suivant" une hypothèse par défaut, et ne prenons des notes que dans le cas contraire.

Pour les sauts, utilisez des étiquettes , pas des chiffres. Ensuite, si vous ajoutez ou supprimez plus tard quelques lignes, la logique de l'histoire sera préservée et vous n'aurez pas à ajuster les nombres.

Trouvez un compromis raisonnable entre clarté et brièveté. Par exemple, je suggère d'écrire "m" au lieu de "message", car ce sera la commande la plus fréquemment utilisée, donc la rendre courte rendra le texte plus lisible. Mais il n'est pas nécessaire de raccourcir les mots clés restants. (Cependant, faites comme vous le souhaitez. L'important est de le rendre plus lisible pour vous . Alternativement, vous pouvez prendre en charge "m" et "message" comme mots clés valides.)

L'algorithme du jeu devrait ressembler à ceci:

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

    }
  }
}

Soit dit en passant, ces idées ont été inspirées par Ren'Py , qui n'est pas exactement ce que vous voulez (ni JavaScript, ni Web), mais pourrait vous donner quand même quelques idées intéressantes.

Viliam Búr
la source
Merci pour l'explication approfondie, je ne savais pas que les tableaux pouvaient fonctionner comme Philipp et vous l'avez montré, je pensais qu'ils ne pouvaient contenir que des chaînes ou des nombres.
The Silent Cartographer
1
J'ai essayé d'implémenter votre solution et cela fonctionne assez bien, même si je pense que dans certains endroits, il ({ label: "like_yes"; m: "I am happy you like my game!"; next: "like_end" },)devrait y avoir un ',' pas un ';'. De plus, comment s'appelle exactement la chose entre les accolades? Est-ce un objet dans le tableau? si je voulais plus d'informations sur comment l'utiliser, que rechercherais-je?
The Silent Cartographer
Oui, {...}c'est un objet. En JavaScript, objet est un tableau associatif clé-valeur (avec des fonctionnalités supplémentaires, non utilisées dans cet exemple), similaire au tableau en PHP ou Map en Java. Pour plus d'informations, consultez les articles Wikipedia sur JavaScript et ECMAScript, et la documentation liée à partir de là, en particulier la documentation officielle ECMAScript.
Viliam Búr
1
Notez que la structure de données qu'il recommande ici est essentiellement JSON. Personnellement, je recommanderais d'aller jusqu'au JSON (ajoutez principalement des accolades et un "arbre": autour de tout; comme {"arbre": [etc]}), puis vous pouvez stocker vos arbres de dialogue dans des fichiers externes. Placer vos données dans des fichiers externes que votre jeu charge est beaucoup plus flexible et modulaire (c'est pourquoi cette approche est une meilleure pratique).
jhocking
5

Je vous recommande de créer un tableau d'événements de dialogue. Chaque événement est un objet contenant le texte dit par le PNJ et un tableau de réponses possibles des joueurs, qui sont à leur tour des objets avec un texte de réponse et l'index de l'événement qui suit cette réponse.

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
la source
2

Vous devez utiliser une approche différente. JavaScript prend en charge les tableaux et les objets, alors pourquoi ne pas en utiliser un par entrée, vous épargnant tout le fractionnement et facilitant également la modification / lecture du texte réel?

Si vous le souhaitez, vous pouvez jeter un œil à un prototype que j'ai réalisé pendant quelques heures pour # 1gam . La source est gratuite pour être utilisée sous GPLv3 (je vais parfaitement bien si vous ne vous en tenez pas à la GPL, si vous l'utilisez simplement comme source d'inspiration. Mais faites-le moi savoir une fois votre jeu terminé.). Ne vous attendez pas à une écriture géniale ou quelque chose comme ça. ;)

Pour donner une courte explication sur le fonctionnement du code, en ignorant les choses d'animation CSS et des trucs comme ça:

  • var data contient essentiellement toute l'histoire avec tous les choix possibles, etc.
  • Chaque "emplacement" (ou page / entrée) est identifié par un identifiant. Le premier ID de la liste est startle second cwait, etc.
  • Chaque emplacement contient deux éléments obligatoires: une légende ainsi que le texte réel. Les liens pour les décisions sont écrits dans un balisage simple prenant la forme [target location:display text].
  • Toute la «magie» se produit à l'intérieur navigate(): cette fonction rend les liens de balisage cliquables. C'est un peu plus long, car je gère également du texte statique pour les impasses là-bas. La partie importante est les deux premiers appels à replace().
  • Les dernières entrées facultatives définissent de nouvelles couleurs d'arrière-plan pour se fondre, soutenant l'ambiance générale du jeu.
  • Au lieu de définir ces couleurs, vous pouvez également ajouter des liens pointant vers d'autres emplacements (notez que cela n'est pas géré par mon code; c'est juste une idée pour le démontrer):

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

Mario
la source