【问题标题】:How to suspend main loop processing in JavaScript until a key is pressed?如何在 JavaScript 中暂停主循环处理,直到按下一个键?
【发布时间】:2014-05-24 08:34:40
【问题描述】:

我正在将一个旧的终端模式文本游戏转换为 JavaScript。

游戏转换很简单,几乎完成了。但是 UI 的一部分——获取用户输入——被证明是非常困难的,以至于我向 StackOverflow 上的专家寻求帮助。我已经详尽地完成了 SO 并找到了类似的答案,但没有直接适用的答案。

我的目标之一是保留游戏 1970 年代的输入风格:

  1. 所有文本都显示在一个屏幕上(没有弹出对话框或孤立/可见的输入框)。
  2. 通过显示提示信息,然后等待玩家输入响应来获得用户输入。
  3. 用户响应的单个字符显示在屏幕上。
  4. 可以按 Backspace 键以相反的顺序删除输入的字符。
  5. 可输入的字符数有限。
  6. 在用户按下 Return 键之前,所有其他游戏处理都会暂停。
  7. 按下 Return 键时,所有输入的字符都以字符串形式返回给调用函数。
  8. 当调用函数获得结果字符串时,主游戏代码中的处理继续。

我已经成功地弄清楚了第 1 步和第 3-5 步。我在 HTML 中创建一个 Input 元素,在 CSS 中将其移出屏幕,然后将焦点设置到该元素。当一个键被按下时,如果它是一个字母数字,我使用 appendChild() 将它作为文本节点添加到内容元素,以便它显示在主屏幕上;如果是退格键,我删除尾部子节点以从屏幕上删除字符;如果是 Return 键,我将输入字段中的文本复制到局部变量中,以将其返回给调用程序并清除输入字段。

经过一番努力,在内容区域中打印和退格字符的代码正在运行。我一直想不通的是如何暂停其他处理,直到用户键入一些键并按回车键。

在过去的三天里,我一直在搜寻 StackOverflow 和我能找到的所有其他网站,这些网站都提到了这种事情,并尝试了我自己能想到的每一种信号组合。提示对话框或可见输入框不符合要求 #1。一个简单的 while 循环确实会停止处理,但它也会阻止按键被识别。计时器和间隔有丢失按键的风险。而且我宁愿避免使用 yield() 以支持旧版浏览器。 (我想尽可能保持跨浏览器的兼容性。)

类似的问题已经生成了引用异步(通过回调)的答案,但这正是我不想要的——我希望主游戏代码停止运行,但仍然允许检测到按键,直到用户按下Return 键,之后可以继续主游戏循环。

有没有办法做到这一点?如果在 JavaScript 中没有相对干净的方法来模拟提示对话框的模态文本条目,我愿意重新考虑主游戏循环的结构。

注意:如果提供了示例代码,那么直接 HTML/CSS/JavaScript 中的示例对我来说会比 jQuery 更有帮助。谢谢!


编辑:正在工作的程序部分是为按键创建一个侦听器,并向内容区域显示字母数字字符:

form.addEventListener("keydown", function (e) {
    if (e.keyCode == 13) {
        istr = inputBox.value; // save user's text as a string
        inputBox.value = ""; // clear input element
    }
    else if (e.keyCode == 8)
        backspace(); // remove last char from content area
    else if (e.keyCode >= 48 && e.keyCode <= 90)
        printCharST(String.fromCharCode(e.keyCode)); // put char in content area
    inputBox.focus(); // bring focus back to input textbox
}, false);

这不是我要询问的代码的一部分。 (我将它包含在这里是为了清楚我确实有代码来检测按键并对其进行操作。)

我遇到问题的部分是调用程序 - 我如何让它暂停直到用户按下 Return 键,然后我将他们的字符串放入 istr 并将其返回给一直在等待用户的调用者输入?简单地告诉调用者重复轮询istr(或按键侦听器设置的任何其他标志变量)直到它不为空会消耗大量 CPU 以防止按键被识别。

我很乐意提供任何其他信息来帮助阐明我想要实现的目标。另外,如果有任何具体关于如何更好地格式化这个问题的方法,我很乐意修改它。

【问题讨论】:

  • 格式化你的问题,让它看起来不错
  • 你为什么不使用像pauseGame这样的全局变量。如果您正在等待输入,请将其设为 1。
  • @MADTerry,在我的键监听器中,一旦用户按下 Return 键,我将使用全局变量来保存完整的字符串。这与标志具有相同的功能。但是设置一个标志对用户按键时需要暂停的游戏代码没有帮助(直到他们按下 Return 键)。
  • 对不起,你不能,不是我们目前所知的 Javascript。

标签: javascript input suspend


【解决方案1】:

最简单、最快、最容易实现的方法是使用...回调处理程序。与其解释它,不如在实际中看到它要容易得多:

  • 创建一个 onkeydown 处理程序(有关如何执行此操作,请参阅此答案:https://stackoverflow.com/a/4929676/2167545)
  • 在该 onkeydown 处理程序中创建一个函数,该函数将获取最近单击的键并将其附加到输入区域。
  • 让该函数返回一个布尔值,表示键是“返回”(true) 还是不是 (false)
  • 如果该函数返回 true,则将当前输入交给解释器并单步执行您的游戏
    • 这将涉及打印更多文本、更新敌人、对话等。

之所以有效,是因为您所描述的游戏的性质。每次玩家有选择时,游戏必须停止并等待输入。这些暂停中的每一个都会自然地将游戏分成具有明确定义的操作的部分。例如,在对话中,这些操作将管理 NPC 对玩家输入的响应的创建。这个相同的例子可以扩展到战斗场景、描述、导航等。你甚至可以添加超时(如果你小心的话),这样玩家必须快速准确地输入才能继续。如果您在提示符和返回键之间暂停除用户输入之外的所有操作,这些超时将是不可能的。

编辑:问题似乎仍然存在于您显示的事件处理程序中。目前,它所做的只是监听输入并将该输入分配给一个值。这意味着您的代码中某处有一个主游戏循环,它正在检查每个滴答声的istr 的值,然后对其进行响应。这种设计对您的目的没有用,而且效率低下。

鉴于您尝试编写的游戏的性质,将会有很长一段时间不活动,游戏只是在等待用户完成输入响应。当用户按回车键时,随后会有一段短暂的活动。主游戏循环方法太多了。更简单的设计可以增加代码的简单性和健壮性。我上面指定的设计比游戏循环方法要好得多,因为它的控制流与上一段中的描述相匹配。

Example implementation

编辑:如果您有一个游戏循环,因为您需要在用户键入时对输入进行一些操作,那么也可以轻松添加。小提琴已更新为 cmets 和一个示例扩展点。

【讨论】:

  • 谢谢!我在那里提供了关于我的问题的其他信息,包括显示我已经在使用监听器来监听按键事件并处理它们的代码。我遇到的困难不在于调用代码,而是调用代码:当侦听器代码等待不仅仅是 while() 循环的按键时,它会做什么?
  • 非常感谢@Trenton Maki -- 这与我不情愿地想的一致。我需要重组流程,而不是直接转换,以便主循环是用户输入代码,并且有条件地执行游戏代码的正确部分。这是仅仅获得用户输入的一个奇怪的反转! (因为调用者总是知道要返回的文本类型。)但这似乎是唯一的选择。您的代码示例是该方法的绝佳模板——非常感谢您的帮助。
  • 为什么需要一个主循环?您可以只使用 JS 的事件处理并完全废弃循环。我的例子应该已经说明了这一点。
  • 我使用“循环”一词是因为游戏不是线性体验。游戏的核心是让玩家为选项输入一个数字。然后显示文本,生成随机数,并要求用户进行各种附加输入。一旦处理完毕,如果游戏还没有结束,游戏就会返回到要求玩家从命令列表中输入另一个数字。这就是我所说的“循环”——这也是为什么纯事件处理不适合这个特定游戏的原因。但我的任务是重新考虑这种结构,感谢您的建议,我现在可以做到。
  • 这很有趣。我从未听说过基于真人文本的游戏。肯定需要一个主游戏循环,但您仍然应该能够使用事件进行用户输入。您还可以创建一个 playerInput 事件,该事件在用户每次完成输入时触发。这样,您的其余代码就可以利用主游戏循环和回调处理程序。这可能会有所帮助:sitepoint.com/javascript-custom-events 以及对观察者模式的良好理解:en.wikipedia.org/wiki/Observer_pattern。祝你游戏好运!
猜你喜欢
  • 1970-01-01
  • 2021-05-26
  • 2017-07-03
  • 2022-01-06
  • 1970-01-01
  • 2011-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多