如何在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

我最近不得不使用Node为此开发一些东西,并选择了一个非常基本的文本文件结构。您可以在以下位置查看生成的代码和文本格式:github.com/scottbw/dialoguejs该代码为GPL,请随时使用。我敢肯定,适应非Node JS游戏会很困难-用Ajax替换“ fs.load()”部分。
Scott Wilson

查看Ink Studio开发的Ink,这是一种分支故事脚本语言。有各种程序化的Ink集成(Java,Javascript,C#)和许多第三方资源。墨水也已用于许多商业游戏中。最后,有一个桌面编辑器Inky,可以对语法进行检查并“播放”分支对话框。
Big Rich

Answers:


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启发的,Ren'Py并不是您想要的(不是JavaScript,不是Web),但是无论如何都会给您一些很酷的想法。


感谢您的深入解释,我不知道数组可以像您和Philipp所示的那样工作,我认为它们只能容纳字符串或数字。
沉默的制图师

1
我一直在尝试实现您的解决方案,并且效果很好,尽管我认为在某些地方({ label: "like_yes"; m: "I am happy you like my game!"; next: "like_end" },)应该使用“,”而不是“;”。花括号中的东西到底叫什么呢?那是数组中的一个对象吗?如果我想了解更多有关如何使用它的信息,我将搜索什么?
沉默的制图师

是的,{...}是一个对象。在JavaScript中,对象是键值关联数组(具有一些额外的功能,本示例中未使用),类似于PHP中的数组或Java中的Map。有关更多信息,请参阅Wikipedia上有关JavaScript和ECMAScript的文章,以及从那里链接的文档,尤其是官方的ECMAScript文档。
ViliamBúr2013年

1
顺便说一句,他在这里推荐的数据结构基本上是JSON。我个人建议一路使用JSON(通常在整个内容周围加上大括号和“ tree”:类似{“ tree”:[etc]}),然后将对话树存储在外部文件中。将您的数据放入游戏加载的外部文件更加灵活和模块化(因此,这种方法是最佳实践)。
轰动

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支持数组和对象,为什么不为每个条目使用一个数组,从而节省所有分割内容,并使实际文本更易于编辑/阅读?

如果您愿意,可以看一下我在几个小时内为#1gam制作的原型。该源代码可以在GPLv3下免费使用(如果您不遵循GPL,如果您只是将其用作灵感,那我就很好了。一旦游戏结束,请告诉我。)。只是不要指望真棒文字或类似的东西。;)

为了简短说明代码的工作原理,而忽略CSS动画之类的东西:

  • var data 本质上包含整个故事以及所有可能的选择,等等。
  • 每个“位置”(或页面/条目)均由ID标识。列表中的第一个ID是start,第二个cwait,以此类推。
  • 每个位置都包含两个必填元素:标题和实际文本。决策链接以一些简单的标记形式编写[target location:display text]
  • 整个“魔术”正在发生navigate():此功能使标记链接可点击。这会更长一些,因为我还在那里处理一些用于死角的静态文本。重要的是对的前两个调用replace()
  • 可选的最后一项定义了要混合的新背景色,以支持游戏的整体气氛。
  • 除了定义这些颜色,您还可以添加指向其他位置的链接(请注意,这不是我的代码处理的;这只是演示的一种想法):

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.