如果同时按下两个移动键,如何将精灵的方向改为对角线

时间:2016-06-15 03:30:30

标签: javascript canvas onkeydown onkeyup

我正在尝试使用箭头键移动精灵。下面是如果按下两个键,应该导致精灵对角移动的逻辑。但事实并非如此。它只会在一个方向上移动一次。因为每当我按下按钮时,我将每个keyCode保存为对象属性中的布尔值,我认为这不应该是一个问题,但我不能让它工作。我也尝试使用数组存储这些布尔值,并检查这些值。没运气。

我在我的智慧结束,非常感谢一些帮助。我已经在构建2D游戏时搜索了stackoverflow和许多不同的博客,但是我尝试的都没有。

var keyState = {};

window.onkeydown = window.onkeyup = function(e) {
  keyState[e.keyCode] = e.type == 'keydown';
  // checks for up and left
  if (keyState[38] && keyState[37]) {
    player.pos.x -= 2;
    player.pos.y -= 2;
  }
  // checks for up and right
  else if (keyState[38] && keyState[39]) {
    player.pos.x += 2;
    player.pos.y += 2;
  }
  // checks for down and left
  else if (keyState[40] && keyState [37]) {
    player.pos.x -= 2;
    player.pos.y -= 2;
  }
  // checks for down and right
  else if(keyState[40] && keyState [39]) {
    player.pos.x += 2;
    player.pos.y -= 2;
  }
  // checks for up
  else if (keyState[38]) {
    player.animation.y = 64;
    player.pos.y -= 2;
}
};

4 个答案:

答案 0 :(得分:2)

您应该将对象键值显式设置为布尔值,并且应该在单独的keydown / keyup处理程序中切换它们:

let keysdown = {};

window.addEventListener('keydown', function(evt) {
  keysdown[evt.which] = true;

  if (keysdown["38"] === true && keysdown["37"] === true) {
    console.log('up & left');
  } else if (keysdown["38"] === true && keysdown["39"] === true) {
    console.log('up & right');
  } else if (keysdown["40"] === true && keysdown["37"] === true) {
    console.log('down and left');
  } else if (keysdown["40"] === true && keysdown["39"] === true) {
    console.log('down and right');
  } else if (keysdown["38"] === true) {
    console.log('up');
  }
}, false);

window.addEventListener('keyup', function(evt) {
  keysdown[evt.which] = false;
}, false);

这会为我记录所有正确的组合键。

答案 1 :(得分:1)

您需要将移动逻辑移出事件侦听器。让关键事件只记录每个键的当前状态(向上或向下),然后在主循环中检查状态并根据需要移动播放。

由于我不确定您是想让动作保持不变还是仅在按下时我已经包含了使用常量标志MOVE_ONLY_ON_PRESS更改行为的选项,如果仅在首次检测到按下时才进行真正的移动。因此没有错过键击我清除主循环中的键标志。

以下是如何与游戏键盘连接的示例。

// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;

// Player
var player = {};
player.x = 100;
player.y = 100;
const MOVE_SPEED = 2;
const MOVE_ONLY_ON_PRESS = false; // This will toggle constant movement or only on press

// from your code
window.onkeydown = window.onkeyup = function (e) {
    if (MOVE_ONLY_ON_PRESS) {
        if (e.type === 'keydown') {
            keyState[e.keyCode] = true;
        }
    } else {
        keyState[e.keyCode] = e.type == 'keydown';
    }

}

// in the main loop;
function update(timer) {
    // you dont need to test for the combinations (ie up left) when its just simple movement
    if (keyState[KEY_UP]) {
        player.y -= MOVE_SPEED;
        if (MOVE_ONLY_ON_PRESS) { keyState[KEY_UP] = false; }
    } 
    if (keyState[KEY_DOWN]) {
        player.y += MOVE_SPEED;
        if (MOVE_ONLY_ON_PRESS) { keyState[KEY_DOWN] = false; }
    }
    if (keyState[KEY_LEFT]) {
        player.x -= MOVE_SPEED;
        if (MOVE_ONLY_ON_PRESS) { keyState[KEY_LEFT] = false; }
    }
    if (keyState[KEY_RIGHT]) {
        player.x += MOVE_SPEED;
        if (MOVE_ONLY_ON_PRESS) { keyState[KEY_RIGHT] = false; }
    }
    requestAnimationFrame(update);

}
requestAnimationFrame(update);

位字段。

简化箭头键的另一种方法是将数字的前4位设置为对应一个键,每个键一位,然后很容易测试键的组合,因为16种可能的组合中的每一种都有唯一号码。你可以用这种方式映射更多的键,但是当你走到远方时它会变得有点不切实际。

这是设置它们的方法

var arrowBits = 0;  // the value to hold the bits
const KEY_BITS = [4,1,8,2]; // left up right down
const KEY_MASKS = [0b1011,0b1110,0b0111,0b1101]; // left up right down
window.onkeydown = window.onkeyup = function (e) {
    if(e.keyCode >= 37 && e.keyCode <41){
        if(e.type === "keydown"){
            arrowKeys |= KEY_BITS[e.keyCode - 37];
        }else{
            arrowKeys &= KEY_MASKS[e.keyCode - 37];
        }
    }    
}

这些是16种可能组合中的8种

// postfix U,D,L,R for Up down left right
const KEY_U = 1;
const KEY_D = 2;
const KEY_L = 4;
const KEY_R = 8;
const KEY_UL = KEY_U + KEY_L; // up left
const KEY_UR = KEY_U + KEY_R; // up Right
const KEY_DL = KEY_D + KEY_L; // 
const KEY_DR = KEY_D + KEY_R; // 

这是你测试的方式

if ((arrowBits & KEY_UL) === KEY_UL) { // is UP and LEFT only down
if (arrowBits & KEY_UL) {              // is Up left down (down and right may also be down)
if ((arrowBits & KEY_U) === KEY_U) {   // is Up only down
if (arrowBits & KEY_U) {               // is Up down (any other key may be down)
if (!(arrowBits & KEY_U)) {            // is Up up (any other key may be down)
if (!arrowBits) {                      // no keys are down
if (arrowBits) {                       // any one or more keys are down

答案 2 :(得分:0)

我无法告诉你为什么你的代码不起作用。如果我用控制台日志替换播放器动作,它对我有用。

虽然它非常原始。

首先将keystate-manager与移动逻辑分开。

var isKeyDown = (function(alias){
    for(var i=0, a=Object.create(null); i<256;) a[i++]=false;
    for(var k in alias) i=0|alias[k], i>0 && i<256 && Object.defineProperty(a,k,{get:Function("return this["+i+"]")});

    function keyStateHandler(e){
        a[e.which||e.keyCode] = e.type==="keydown"
    }
    addEventListener("keydown", keyStateHandler, false);
    addEventListener("keyup", keyStateHandler, false);

    return a;
})({
    //add some aliases for more readable code
    up: 38,
    left: 37,
    right: 39,
    down: 40
});

一次,因为keydown事件的更新间隔不可靠,因浏览器而异,并且不会与brwoser的更新间隔同步,这将导致闪烁。

现在为您的游戏创建一个主循环,并考虑时间。虽然requestAnimationFrame的目标是一个恒定的帧速率,但你可能会有滞后,而且你不希望看到你的玩家在这些滞后期间以不同的速度移动。

var lastCall = Date.now();
function tick(){
    requestAnimationFrame(tick);

    var now = Date.now(),
        time = (now - lastCall) / 1000;  //time in seconds
        //because it's easier to think in terms like 2px/s 
        //than 0.002px/ms or 0.0333 px/frame
    lastCall = now;

    move(time);
}
tick();

现在的实际运动:

//the speed of your player: it would be better if this would be 
//a property of the player than hardcoded like you did, or global like this
var speed = 2; // 2px/second
function move( time ){
    //accounts for varying framerates
    //opposite directions cancel each other out
    var dx = (isKeyDown.right - isKeyDown.left) * time,
        dy = (isKeyDown.down - isKeyDown.up) * time;

    //taking account for diagonals
    if(dx && dy) dx /= Math.SQRT2, dy /= Math.SQRT2;

    player.pos.x += dx * speed;
    player.pos.y += dy * speed;
}

答案 3 :(得分:0)

基于skyline3000的答案,我试图使其在上下左右也都可以使用。 Appart从添加其他选项起,我不得不将窗口更改为文档才能使其正常工作:

  let keysdown = {};

document.addEventListener('keydown', function (evt) {
    keysdown[evt.which] = true;

    if (keysdown["38"] === true && keysdown["37"] === true) {
        prota.moveUp();
        prota.moveLeft();
        // console.log('up & left');
    } else if (keysdown["38"] === true && keysdown["39"] === true) {
        prota.moveUp();
        prota.moveRight();
        // console.log('up & right');
    } else if (keysdown["40"] === true && keysdown["37"] === true) {
        prota.moveDown();
        prota.moveLeft();
        // console.log('down and left');
    } else if (keysdown["40"] === true && keysdown["39"] === true) {
        prota.moveDown();
        prota.moveRight();
        // console.log('down and right');
    } else if (keysdown["38"] === true) {
        prota.moveUp();
        // console.log('up');
    } else if (keysdown["40"] === true) {
        prota.moveDown();
        // console.log('down');
    } else if (keysdown["39"] === true) {
        prota.moveRight();
        // console.log('right');
    } else if (keysdown["37"] === true) {
        prota.moveLeft();
        // console.log('left');
    }
}, false);

document.addEventListener('keyup', function (evt) {
    keysdown[evt.which] = false;
}, false);