如何解决Javascript游戏中的按键延迟问题?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何解决Javascript游戏中的按键延迟问题?相关的知识,希望对你有一定的参考价值。

我正在创建一个javascript蛇游戏,当某些键按顺序按得太快时会遇到问题。例如,(在向右移动时)按下向上箭头,然后向左箭头键过快会使我的蛇完全转身并撞向自身,而忽略向上箭头键的按下。是否有任何代码可以确保始终显示任何按键?预先感谢。

let d = "RIGHT";
document.addEventListener("keydown", direction);
function direction(event) 
  let key = event.keyCode;
  if (key == 37 && d != "RIGHT" && d != "LEFT") 
    d = "LEFT";
   else if (key == 38 && d != "DOWN" && d != "UP") 
    d = "UP";
   else if (key == 39 && d != "LEFT" && d != "RIGHT") 
    d = "RIGHT";
   else if (key == 40 && d != "UP" && d != "DOWN") 
    d = "DOWN";
  

在单独的功能中:

if (d == "LEFT") snakeX -= box;
  if (d == "UP") snakeY -= box;
  if (d == "RIGHT") snakeX += box;
  if (d == "DOWN") snakeY += box;

您也可以通过转到https://jssnake.glitch.me/并稍作播放来查看此问题。

答案

我简要调查了您的代码。您每秒渲染10倍,因此,如果您在此间隔内设法按了多个键,则会发生上述问题:

例如,(在向右移动时)按下向上箭头,然后过快的向左箭头键会使我的蛇完全转身并撞向自身,而忽略向上箭头键的按下。

有两种可能的解决方案:

  1. 更快地运行渲染循环,以便在此间隔内没有人可以按下两个键。
  2. 不只存储最后一个键,而是存储自上次渲染调用以来在两次之间按下的所有键。
  3. 避免半圈。

我认为解决方案1不是理想的,应该永不言败。因此,让我们继续第3个(hack),然后继续第2个(正确而干净的方法)。

避免半圈(替代3)

这种小技巧无法解决问题的根源,但是会使蛇的行为表现得很正确。蛇可以向四个方向移动,但始终只能向两个方向旋转。您可以使用两键控件来触发CW / CCW更改,例如

let currentDir = "RIGHT"; //note I renamed your d to currentDir
let nextDir = undefined;
document.addEventListener("keydown", direction);

function direction(event) 
  const key = event.keyCode;
  while (~currentDir) ; //wait until the control function is finished
  switch (currentDir) 
    case "LEFT": nextDir = (key === 37 ? "DOWN" : (key === 39 ? "UP" : nextDir)); break;
    case "UP": nextDir = (key === 37 ? "LEFT" : (key === 39 ? "RIGHT" : nextDir)); break;
    case "RIGHT": nextDir = (key === 37 ? "UP" : (key === 39 ? "DOWN" : nextDir)); break;
    case "DOWN": nextDir = (key === 37 ? "RIGHT" : (key === 39 ? "LEFT" : nextDir)); break;
  


//and later in the movement control function:
currentDir = undefined; //avoid overwriting nextDir during this update,
// i.e. the while-loop inside of direction() will wait
switch (tmp) 
  case "LEFT": snakeX -= box; break;
  case "UP": snakeY -= box; break;
  case "RIGHT": snakeX += box; break;
  case "DOWN": snakeY += box; break;

currentDir = nextDir;
nextDir = undefined;

四键版本将以类似的方式工作,您可以轻松地将其集成到您的代码中。关键是使用currentDirnextDir对,并在两次渲染调用之间的整个0.1s时间内保持currentDir不变。但是您的问题会一直存在。仅当您紧接着彼此按下↑和←时,蛇的航向权才会继续向上。

let currentDir = "RIGHT";
let nextDir = undefined;
document.addEventListener("keydown", direction);

function direction(event) 
  const key = event.keyCode;
  while (~currentDir) ; //wait until the control function is finished
  switch (currentDir) 
    case "LEFT":
    case "RIGHT":
        nextDir = (key === 38 ? "UP" : (key === 40 ? "DOWN" : nextDir)); break;
    case "UP":
    case "DOWN":
        nextDir = (key === 37 ? "LEFT" : (key === 39 ? "RIGHT" : nextDir)); break;
  

键缓冲区(替代2)

正确的解决方案甚至更容易,但是需要一个数组。它将自上次渲染调用以来的所有按键存储在queue中。

keysPressed = [];
document.addEventListener("keydown", event => 
  keysPressed.push(event.keyCode); //enqueues the key pressed

按住两个或三个键,您实际上可以在0.1s间隔内更新蛇的位置,每帧有效旋转一圈。如果您能够使用命令快速填充缓冲区,则可能导致蛇移动延迟。尝试一下有趣的运动可能会很有趣。四键控件的移动功能如下所示:


  if (keysPressed.length > 0 
    const key = keysPresses.shift(); //dequeues the oldest key
    //if there are more keys in the queue, they have to wait until next time
    switch (d) 
      case "LEFT":
      case "RIGHT":
          d = (key === 38 ? "UP" : (key === 40 ? "DOWN" : d)); break;
      case "UP":
      case "DOWN":
          d = (key === 37 ? "LEFT" : (key === 39 ? "RIGHT" : d)); break;
    
  

  switch (d) 
    case "LEFT": snakeX -= box; break;
    case "UP": snakeY -= box; break;
    case "RIGHT": snakeX += box; break;
    case "DOWN": snakeY += box; break;
  

另一答案

这里您需要延迟按键的效果。您可以通过将最后按下的键存储在变量中并仅在蛇准备转弯时才读取键来实现。

let pressedKey;

document.addEventListener("keydown", event => 
    pressedKey = event.keyCode;
);

以上是关于如何解决Javascript游戏中的按键延迟问题?的主要内容,如果未能解决你的问题,请参考以下文章

Javascript中的按键监听器

求助,怎么取消键盘延迟

按键精灵 代码SayString 输入不了游戏登陆框 设置一个游戏登陆账号递增

按键精灵 代码SayString 输入不了游戏登陆框 设置一个游戏登陆账号递增

触摸事件仅在 iOS 9 的左屏幕边缘附近延迟。如何解决?

谈谈网络游戏中的延迟解决方案