简体   繁体   English

如何防止Javascript中的多个按键

[英]How to prevent a multiple keypresses in Javascript

I'm beginner at JS and I'm working on a project to create a game, which is in my case a Snake-game. 我是JS的初学者,我正在开发一个创建游戏的项目,在我的例子中是一个Snake游戏。 Everything is going fine, except that if I quickly press multiple keys at once, the Snake dies, because (i think) it's a collision. 一切都很顺利,除非我快速按下多个键,Snake会死,因为(我认为)这是一次碰撞。 That's why I want to disable multiple keypresses somehow, to try if it solves the problem. 这就是为什么我想以某种方式禁用多个按键,尝试解决问题。 My code: 我的代码:

var Snake = function()
{
  //this is the direction table; UP, RIGHT, DOWN, LEFT
  this.directions = [[0, -1], [1, 0], [0, 1], [-1, 0]];
}

function onKeyDown(event)
{
  if (gameover)
    tryNewGame();
  else 
  {
    if (event.keyCode == 37 || event.keyCode == 65)
    {
      if (snake.direction != 1)  
        snake.direction = 3;
    } 
    else if (event.keyCode == 38 || event.keyCode == 87) 
    {
      if (snake.direction != 2) 
        snake.direction = 0;
    } 
    else if (event.keyCode == 39 || event.keyCode == 68) 
    {
      if (snake.direction != 3) 
        snake.direction = 1;
    } 
    else if (event.keyCode == 40 || event.keyCode == 83) 
    {
      if (snake.direction != 0)
        snake.direction = 2;
    }   
  }
}

you can use the setTimeout function on your key press functionality like this... 你可以在你的按键功能上使用setTimeout功能......

EDIT: 2000ms is just an example time. 编辑:2000毫秒只是一个例子。 You can make it 500ms for every 1/2 second or any other number of ms you want 您可以每1/2秒或任何其他ms数使其达到500毫秒

setTimeout(
function onKeyDown(event)
{
  if (gameover)
    tryNewGame();
  else 
  {
    if (event.keyCode == 37 || event.keyCode == 65)
    {
      if (snake.direction != 1)  
        snake.direction = 3;
    } 
    else if (event.keyCode == 38 || event.keyCode == 87) 
    {
      if (snake.direction != 2) 
        snake.direction = 0;
    } 
    else if (event.keyCode == 39 || event.keyCode == 68) 
    {
      if (snake.direction != 3) 
        snake.direction = 1;
    } 
    else if (event.keyCode == 40 || event.keyCode == 83) 
    {
      if (snake.direction != 0)
        snake.direction = 2;
    }   
  }
},
2000ms
);

I always recommend debounced or throttled function for such use case. 对于这种用例,我总是建议使用去抖限制功能。 Hands down, it is the best. 放下手,这是最好的。

Lodash has a decent implementation of both _.debounce and _.throttle . Lodash实现了_.debounce_.throttle Also make sure you read this article to fully understand the concepts. 另外,请务必阅读本文以充分理解这些概念。

Basic idea is after 1st call, you "freeze" the event handler for a short period of time. 基本想法是在第一次调用之后,您将“冻结”事件处理程序一小段时间。 It's still called and receive events, just not having any effect. 它仍然被调用并接收事件,只是没有任何效果。

The problem is probably that the direction changes twice before the snake's shape is updated, and so the first of those two direction changes is actually ignored. 问题可能是在蛇的形状更新之前方向改变了两次,因此实际上忽略了这两个方向改变中的第一个。

A way to overcome this, is to buffer the direction changes in a queue (implemented as array). 克服这个问题的一种方法是缓冲队列中的方向更改(实现为数组)。

So in your key event handler you would not do this: 所以在你的关键事件处理程序中你不会这样做:

if (snake.direction != 1)  
    snake.direction = 3;

But instead: 但反而:

if ((snake.queue.length ? snake.queue[0] : snake.direction) != 1) 
    snake.queue.unshift(3);

This queue should be initialised in the Snake constructor: 应该在Snake构造函数中初始化此队列:

this.queue = [];

Then when you update the snake's position (at a time interval), you would consume that queue if there is something in it: 然后,当你更新蛇的位置(以一个时间间隔)时,如果其中有东西,你将使用该队列:

if (snake.queue.length)
    snake.direction = snake.queue.pop();
// Now apply this direction:
//   ... this would be code you already have...

You could set a maximum to this queue size, as it can get awkward if the user keeps pressing keys faster than the snake updates. 您可以将此最大值设置为此队列大小,因为如果用户持续按键比蛇更新更快,则可能会变得很麻烦。

As mentioned here, throttling the event would work well. 如上所述,限制事件将很有效。 Here's a simple class that could serve as your throttler: 这是一个可以作为你的节流器的简单类:

class CooldownTimer {
  constructor(time) {
    this.cooldownTimeout = null
    this.cooldownTime = time
    this.startedAt = null
  }

  isReady = () => {
    return !this.cooldownTimeout
  }

  start = () => {
    if (!this.cooldownTimeout) {
      clearTimeout(this.cooldownTimeout)
    }

    this.startedAt = Date.now()
    this.cooldownTimeout = setTimeout(() => {
      this.cooldownTimeout = null
    }, this.cooldownTime)
  }
}

You would have to define a CooldownTimer somewhere, probably where you bind your events: 您必须在某处定义CooldownTimer,可能是您绑定事件的地方:

let keyPressCooldown = new CooldownTimer(200)

Then you can use it in your event code: 然后您可以在事件代码中使用它:

function onKeyPress(event) {
  if (keyPressCooldown.isReady()) {
    console.log('key pressed')
    keyPressCooldown.start() // Do not forget to start the cooldown here
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM