如何在按下时只触发一次按键?

How to make a key fire only once when pressed?

我正在制作一个简单的 JavaScript 游戏 (Space Invaders 风格)(自上而下 space 射击游戏)并且我'我试图让我的角色每按一次 'space' 键就发射一颗子弹。我该怎么做?

我尝试了多种方法,设置标志,使用 onkeypress 而不是 keydown,Google 搜索(也遇到过类似的问题但没有帮助:Javascript onkeydown event fire only once?

下面是我尝试过的一种解决方案的示例。

document.onkeydown = function(e) 
{
      if(e.keyCode == 32 && space == false)
      {
           space = true;         
      }
}

document.onkeyup = function(e)
{
     if(e.keyCode == 32) space = false;
}

window.requestAnimationFrame(gameLoop);

function gameLoop(timeStamp) 
{      if(space === true)
       {   
          p.shoot();
       }

       window.requestAnimationFrame(gameLoop);
}

预期结果:密钥仅触发一次。 实际结果:密钥被触发多次。

这里有两个事件,每次按下三个键,即使被触发也是三个事件 1. keydown(当用户按下键但尚未释放键时) 2. keypress(在按键事件后触发) 3. keyup(当用户松开按键时) 如果您只想为每个按键触发一次 如果实现所有这三种方法,将触发所有三种方法。 另外:为了安全起见,你可以写 e.preventDefault().

按照@JaromandaX的建议,space = false;p.shoot();之后会给你想要的结果:

重现问题:

var space = false;
var element = document.querySelector('input');

element.onkeydown = function(e) {
  if (!space && e.keyCode == 32) {
    space = true;
    console.log('event fired');
  }
};

element.onkeyup = function(e) {
  if (e.keyCode == 32)
    space = false;
}


window.requestAnimationFrame(gameLoop);

function gameLoop(timeStamp) {
  if (space == true) {
    console.log('shoot');
  }
  window.requestAnimationFrame(gameLoop);
}
<input />

编辑:添加了另一个 isShot 变量来跟踪射击和 space 键在 requestAnimationFrame 事件中按下:

var space = false;
var element = document.querySelector('input');
var isShot = false;

element.onkeydown = function(e) {
  if (!space && e.keyCode == 32) {
    space = true;
    console.log('event fired');
  }
};

element.onkeyup = function(e) {
  if (e.keyCode == 32) {
     space = isShot = false;
  }
}


window.requestAnimationFrame(gameLoop);

function gameLoop(timeStamp) {
  if (space && !isShot) {
    console.log('shoot');
    isShot = true;
  }
  window.requestAnimationFrame(gameLoop);
}
<input />

一颗子弹实际上有 3 种状态 - 而且,考虑到在经典 Space 入侵者中,玩家一次只能发射一颗子弹,这让事情变得相对简单

子弹可以

  • NONE - 不存在
  • FIRED - 即需要创建一个
  • 存在 - 正在飞行

处理的代码也比较简单

var BulletState = {
    NONE: 0,
    FIRED: 1,
    EXISTS: 2
};
var bullet = BulletState.NONE;

document.onkeydown = function(e) {
    if (e.keyCode == 32 && bullet === BulletState.NONE) {
        bullet = BulletState.FIRED;
    }
}

window.requestAnimationFrame(gameLoop);


function gameLoop(timeStamp) {
    if (bullet === BulletState.FIRED) {
        bullet = BulletState.EXISTS;
        p.shoot(); // p.shoot needs to set bullet = BulletState.NONE when the bullet expires
    }
    window.requestAnimationFrame(gameLoop);
}

document.onkeyup = function(e)
{
     // not required at all
}

edit: To fire a bullet every press of spacebar (allowing multiple bullets)

var space = false;
var fireBullet = 0;

document.onkeydown = function(e) {
    if (e.keyCode == 32 && !e.repeat && !space) {
        fireBullet++;
        space = true;
    }
}

window.requestAnimationFrame(gameLoop);


function gameLoop(timeStamp) {
    while (fireBullet) {
        p.shoot();
        fireBullet--;
    }
    window.requestAnimationFrame(gameLoop);
}

document.onkeyup = function(e)
{
    space = false;
}

我使用 fireBullet++ 和 fireBullet-- 因为我想有人可以在 16 毫秒(单帧)内按下和释放并按下空格键是合理的:p

你也可以

function gameLoop(timeStamp) {
    if (fireBullet) {
        p.shoot();
        fireBullet--;
    }
    window.requestAnimationFrame(gameLoop);
}

这样你仍然可以处理多发子弹,但每帧只发射一发 - 这真的取决于你想如何处理扳机手指非常快的人 :p