如何通过箭头键连续/平滑地移动元素?

时间:2015-03-18 09:50:44

标签: javascript onkeydown

我正在和Javascipt一起制作原始游戏以获得乐趣。我设置了按下箭头键时移动角色的功能,如下所示:

document.getElementById("character").style.top = 0;
document.getElementById("character").style.left = 0;

document.body.onkeydown = function() {
var e = event.keyCode,
    charTop = parseInt(document.getElementById("character").style.top),
    charLeft = parseInt(document.getElementById("character").style.left);

    if (e == 40) { //down function
        document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) + 10 + "px";
    } else if (e == 37) { //left function
        document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) - 10 + "px";
    } else if (e == 39) { //right function
        document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) + 10 + "px";
    } else if (e == 38) { //up function
        document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) - 10 + "px";
    }

}

它有效,但不是我喜欢的方式。我发现如果按住箭头键,它会移动一次,等待一秒钟,然后继续移动直到你松开。我认为造成这种情况的原因是Windows'内置功能,在指定的延迟后以控制面板中设置的速度重复按下按钮。

我希望角色能够立即连续不断地移动。我不知道该如何解决这个问题。我还希望能够定义如果仍然按下按钮再次移动的速度(它一次移动十个像素,如果重复得太快,它将飞越屏幕)。

我知道Javascript并不是真正意义上的游戏编程,但希望有人可以想办法解决这个问题!谢谢!

3 个答案:

答案 0 :(得分:6)

这是许多编码语言中的标准问题,这些编码语言依赖于键盘中断来控制游戏角色,并不是JavaScript特有的。解决这个问题的方法是将键与角色的移动分开捕捉,而不是依靠键盘事件来重绘/更新你的游戏。相反,使用一个连续的游戏循环,它可以并且经常不做任何事情(很多次一秒) - 直到游戏中的某些内容发生变化。这似乎是一件奇怪的事情,但它是许多游戏的设计和构建方式 - 即使在JavaScript中也是如此;)

/// store key codes and currently pressed ones
var keys = {};
    keys.LEFT = 37;
    keys.RIGHT = 39;

/// store reference to character's position and element
var character = {
  x: 100,
  y: 100,
  element: document.getElementById("character")
};

/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup = 
document.body.onkeydown = function(e){
  var kc = e.keyCode || e.which;
  keys[kc] = e.type == 'keydown';
};

/// character movement update
var moveCharacter = function(dx, dy){
  character.x += dx||0;
  character.y += dy||0;
  character.element.style.left = character.x + 'px';
  character.element.style.top = character.y + 'px';
};

/// character control
var detectCharacterMovement = function(){
  if ( keys[keys.LEFT] ) {
    moveCharacter(-1);
  }
  if ( keys[keys.RIGHT] ) {
    moveCharacter(1);
  }
};

/// game loop
setInterval(function(){
  detectCharacterMovement();
}, 1000/24);

一个实例(也有上下移动):



    /// store key codes and currently pressed ones
    var keys = {};
        keys.UP = 38;
        keys.LEFT = 37;
        keys.RIGHT = 39;
        keys.DOWN = 40;

    /// store reference to character's position and element
    var character = {
      x: 100,
      y: 100,
      speedMultiplier: 2,
      element: document.getElementById("character")
    };

    /// key detection (better to use addEventListener, but this will do)
    document.body.onkeyup = 
    document.body.onkeydown = function(e){
      /// prevent default browser handling of keypresses
      if (e.preventDefault) { 
        e.preventDefault();
      }
      else {
        e.returnValue = false; 
      }
      var kc = e.keyCode || e.which;
      keys[kc] = e.type == 'keydown';
    };

    /// character movement update
    var moveCharacter = function(dx, dy){
      character.x += (dx||0) * character.speedMultiplier;
      character.y += (dy||0) * character.speedMultiplier;
      character.element.style.left = character.x + 'px';
      character.element.style.top = character.y + 'px';
    };

    /// character control
    var detectCharacterMovement = function(){
      if ( keys[keys.LEFT] ) {
        moveCharacter(-1, 0);
      }
      if ( keys[keys.RIGHT] ) {
        moveCharacter(1, 0);
      }
      if ( keys[keys.UP] ) {
        moveCharacter(0, -1);
      }
      if ( keys[keys.DOWN] ) {
        moveCharacter(0, 1);
      }
    };

    /// update current position on screen
    moveCharacter();

    /// game loop
    setInterval(function(){
      detectCharacterMovement();
    }, 1000/24);

#character {
    position: absolute;
    width: 42px;
    height: 42px;
    background: red;
    border-radius: 50%;
}

<div id="character"></div>
&#13;
&#13;
&#13;

http://jsfiddle.net/7a106ck7/1/

以上只是基础知识,您可以进行一些优化。上面也不是控制游戏角色的全部内容,遗憾的是,浏览器中的键盘事件是不可预测的(与JavaScript没有任何关系),如果有任何问题,你会发现你开始尝试制作更复杂的控制系统,即不能同时按下某些键而不会出现问题(尤其是控制字符,如CMD或CTRL)。

有一天,如果JavaScript有一个Keyboard对象,那就像上面的keys对象一样。这意味着您可以随时测试当前按下的任何键的状态,而无需依赖事件模型。它可能发生在一些尚未知的未来点,但我们必须拭目以待。

如果确实发生了这种情况,我想建议一个功能:

Keyboard.isPressed(Keyboard.ANY)

这将有助于无数的游戏介绍/菜单屏幕(为什么我必须在看到菜单之前按任意键?我们是否梦想使用街机?)。更不用说上述内容将有助于所有那些仍在等待他们认为缺少密钥的人的存在。

答案 1 :(得分:1)

尝试使用onkeyup而不是onkeydown,onkeyup在释放密钥后获取值

 document.getElementById("character").style.top = 0;
 document.getElementById("character").style.left = 0;

document.body.onkeyup = function() {
var e = event.keyCode,
    charTop = parseInt(document.getElementById("character").style.top),
    charLeft = parseInt(document.getElementById("character").style.left);

    if (e == 40) { //down function
        document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) + 10 + "px";
    } else if (e == 37) { //left function
        document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) - 10 + "px";
    } else if (e == 39) { //right function
        document.getElementById("character").style.left = (parseInt(document.getElementById("character").style.left)) + 10 + "px";
    } else if (e == 38) { //up function
        document.getElementById("character").style.top = (parseInt(document.getElementById("character").style.top)) - 10 + "px";
    }

}

答案 2 :(得分:0)

创建一个全局HOP变量,用于定义角色的移动速度。

onkeydown你创建了一个:

var inter = setInterval(function(){
 //apply movements here
}, HOP);

然后在关键字上停止移动:

clearInterval(inter);

setInterval()将每HOP调用一次(以毫秒为单位)。这将循环,直到您检测到用户已释放密钥。

要确保您不创建多个setIntervals,请确保全局初始化 var inter = -1,每次清除它时都要将其设置回 -1 即可。这样,当检测到keydown时,只需检查 inter是否&gt; -1 即可。如果是,那就什么都不做。

Simple demo here

<强> HTML:

<div id="container"></div>

<强> JS:

var container = document.getElementById('container');
var p = document.createElement('p');
var inter = -1;
var HOP = 50; //miliseconds

window.onkeydown = function(){
    if(inter == -1){
        inter = setInterval(function(){
            var p_temp = p.cloneNode();
            p_temp.innerHTML = "Key is down";
            container.appendChild(p_temp);
        }, HOP);
    }
};

window.onkeyup = function(){
    clearInterval(inter);
    inter = -1;
};