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

How to change sprite's direction to diagonal if two movement keys are pressed simultaneously

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

我已无计可施,非常感谢您的帮助。我搜索了 Whosebug 和许多关于构建 2d 游戏的不同博客,但我尝试的任何东西都不起作用。

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;
}
};

您应该明确地将对象键值设置为布尔值,并且您应该在单独的 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);

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

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

因为我不确定您是希望运动保持恒定还是仅在压力机上运动,所以我已经包含了使用常量标志 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

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

虽然很原始。

首先将按键状态管理器与移动逻辑分开。

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-event 的更新间隔不可靠,因浏览器而异,并且与 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;
}

根据 skyline3000 的回答,我试图让它在下、左和右也能工作。除了添加其他选项外,我不得不将 window 更改为 document 以使其工作:

  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);