0

I'm working on an RPG in Javascript and am setting up controls for the main player, but I am having some issues with the keyEvent handling. This code controls player movement and animation. Where things go wrong, I believe, is that if the game registers a keydown while there is already another keydown, and then that first key goes up, the sprite pauses (the isMoving property basically stops movement and animation when it is false).

// For example:
// I hold W. Sprite begins moving up.
// While holding W, I begin to hold D. Sprite begins moving right.
// I release W. Sprite should keep going right, but pauses momentarily when the
// keyup is registered

var move = function(e) {

    xavier.isMoving = true;

    switch(e.keyCode)
    { 
        case 87: xavier.direction = 'up';    break; //W
        case 65: xavier.direction = 'left';  break; //A
        case 68: xavier.direction = 'right'; break; //D
        case 83: xavier.direction = 'down';  break; //S 
    };
};
var wait = function(e) {
    xavier.isMoving = false;
};    
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);

Is there a better way to set up controls so that my game can handle multiple keys simultaneously?

Also, the sprite moves when mac command key is pushed. Not sure why.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
hgstuffinx
  • 33
  • 5
  • call `wait` only if direction is the same – Dimava Jun 23 '16 at 17:40
  • Use the keydown and keyup events just to log the keys that are down. In the main loop check for the key states and take the appropriate action there. IO events should not be used for game logic as you will not be able to keep them in sync with the main render/update event. – Blindman67 Jun 23 '16 at 17:59

4 Answers4

2

You need to disassociate the key events from the animation or main game loop. THe keydown, keyup events are IO events and should not handle game logic, they can come in at any rate, you may have many between frames and applying logic on each is just a waste of processing, and worse if you have more logic involved the player may miss logic events because the key events have proceeded without the main loop processing the new positions. The same goes for the mouse and touch events, you should only record the events and handle them within the game.

// define keys and an array to keep key states
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;


// create a logging function
const keyEventLogger =  function (e) {  keyState[e.keyCode] = e.type == 'keydown';}
window.addEventListener("keydown", keyEventLogger);
window.addEventListener("keyup", keyEventLogger);


// define something to move
var player = {y : 100, x: 100};
const MOVE_SPEED = 2;

// in the main loop;
function update(timer) {
    if (keyState[KEY_UP]) {
        player.y -= MOVE_SPEED;
    } 
    if (keyState[KEY_DOWN]) {
        player.y += MOVE_SPEED;
    }
    if (keyState[KEY_LEFT]) {
        player.x -= MOVE_SPEED;
    }
    if (keyState[KEY_RIGHT]) {
        player.x += MOVE_SPEED;
    }
    requestAnimationFrame(update);

}
requestAnimationFrame(update);
Blindman67
  • 51,134
  • 11
  • 73
  • 136
1

Of course there is.

You need to have an array, and 2 event listeners, one for keydown and one for keyup.

Whenever someone presses a button, set the element with the id same as the keyCode of the button to true. If your array is called keys, then this would look something like this:

keys[e.keyCode] = true;

Then, whenever the user realeses a button, set the element with the same id as the keyCode of that button to false:

keys[e.keyCode] = false;

Later, you can check, if the element with the same id as the button you want a specific event to happen after you press it is true.

Bálint
  • 4,009
  • 2
  • 16
  • 27
  • 1
    Never use delete as it will kill the optimisation of the array and all associated code. Just set the array item as false, undefined, null or whatever to indicate it is not on. BUT never `delete` – Blindman67 Jun 23 '16 at 17:55
0

var move = function(e) {

    xavier.isMoving = true;

    switch(e.keyCode)
    { 
        case 87: xavier.direction.up = true;    break; //W
        case 65: xavier.direction.left = true;  break; //A
        case 68: xavier.direction.right = true; break; //D
        case 83: xavier.direction.down = true;  break; //S 
    };
};
var wait = function(e) {
    switch(e.keyCode)
    { 
        case 87: xavier.direction.up = true;    break; //W
        case 65: xavier.direction.left = true;  break; //A
        case 68: xavier.direction.right = true; break; //D
        case 83: xavier.direction.down = true;  break; //S 
    };
    xavier.isMoving = xavier.direction.up ||
      xavier.direction.left || xavier.direction.right ||
      xavier.direction.down;
};    
window.addEventListener("keydown", move, false);
window.addEventListener("keyup", wait, false);
Dimava
  • 7,654
  • 1
  • 9
  • 24
0

Here is a solution that keeps track of the keys that are down (directions) and comes back to another key that is down (direction) when a second key is released. Also the two functions are so similar, that they are combined into one:

var xavier = {};
xavier.isMoving = false;
xavier.stack = [];
xavier.direction = '';
xavier.directions = {
    87: 'up',    //W
    65: 'left',  //A
    68: 'right', //D
    83: 'down',  //S 
};

var move = function(e) {
    // get direction from mapping: key -> direction
    var direction = xavier.directions[e.keyCode];
    if (!direction) return; // not a move-key
    var i = xavier.stack.indexOf(direction);
    if (e.type === 'keydown' && i === -1) {
        // If this key was not yet down, but is pressed, 
        // then add the direction to the list
        xavier.stack.push(direction);
    } else if (e.type === 'keyup' && i !== -1) {
        // If this key was down, but is released, 
        // then remove the direction from the list
        xavier.stack.splice(i, 1);
    }
    xavier.direction = xavier.stack.length ? xavier.stack[xavier.stack.length-1]
                                           : '';
    xavier.isMoving = xavier.direction !== '';
    // Test for this snippet only:
    document.body.textContent = xavier.direction;
};

window.addEventListener("keydown", move, false);
window.addEventListener("keyup", move, false);
Click here (to focus) and press/release ASQW keys

Note that in theory this supports any number of keys to be down together, but in practice keyboards are often limited to the number that can be down. Once this limit is crossed, no key events are generated.

trincot
  • 317,000
  • 35
  • 244
  • 286