2016-06-15 16 views
1

我正在嘗試使用箭頭鍵移動精靈。如果按下兩個鍵,下面是應該使精靈沿對角線移動的邏輯。但是,情況並非如此。它只會一次向一個方向移動。由於我每次按下按鈕時都將每個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; 
} 
}; 

回答

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 
+0

好的角度。但在我的情況下,我需要檢查組合。這是因爲我需要改變每個方向被按下時精靈片的哪一部分被使用。但是,如果我只使用四個不同的方向,這將很好。我可能只需添加一個if語句來檢查是否同時按下多個keystate的倒數,然後使用一個else塊來運行你寫的內容。 – Streamer

+0

@Azurasky我通常會將箭頭鍵映射到單個值設置,將位0-3設置爲映射向上,向下,向左,向右。然後,我可以檢查16個可能的組合中的一個,或者只是屏蔽一下,看看是否按下了鍵。例如,向上位0設置'keyMap | = 1'來清除'keyMap&= 0b1110',如果我想測試UP,LEFT(位0,2)'if(keyMap === 0b0101){'或if keyMap === 5)'(0b是二進制數前綴,0x是十六進制前綴,方便用於位域) – Blindman67

+0

哦,這個東西很難消化。我正在看一些關於位域的文檔(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators),剛纔瞭解到調用.toString(2 )在一個數字上返回它的二進制表示。編輯完成後,你的答案從好到好!我在學習這麼多東西。 – Streamer

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

此記錄所有正確的鍵組合我。

+0

它的工作原理。不過,你的回答並不能真正幫助我理解爲什麼我的代碼不起作用。 – Streamer

+0

@Azurasky。提出分析的努力!檢查工作代碼以瞭解其工作原理。這將幫助你理解你的代碼不工作的原因。 ;-) – markE

+0

@markE邏輯錯誤。關鍵事件每按一次鍵就會觸發一次,因此按鍵「down」,然後「left」將觸發兩個事件,邏輯將在第一個事件向下移動,第二個事件向下移動並離開,然後按鍵將觸發兩次,釋放按鍵'向下'會增加第二個動作(因爲它仍然是下跌的),而最終版本什麼都不會做。 – Blindman67

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