Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to move an element via arrow keys continuously/smoothly?

I'm making a primitive game with Javascipt for fun. I have set up functions to move my character around when the arrow keys are pressed as follows:

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

}

and it works, but not the way I'd like it to. I find that if you hold down an arrow key, it will move once, wait a second, then continuously move until you let go. I think what is causing this is Windows' built in function to repeat a pressed button at a speed set in Control Panel after a specified delay.

I would prefer that the character move immediately and continuously without the delay. I am not sure how to go about this. I'd also like to be able to define how quickly to move again if the button is still pressed (it moves ten pixels at a time, and if it repeated too quickly it would fly across the screen).

I know that Javascript is not really meant for game programming, but hopefully someone can think of a way around this! Thanks!

like image 438
Eric David Sartor Avatar asked Dec 24 '22 21:12

Eric David Sartor


1 Answers

This is a standard issue in lots of coding languages that rely on keyboard interrupts to control game characters, and isn't particular to JavaScript. The way around this is to trap the keys separately from your character's movement, and not rely on the keyboard event to redraw/update your game. Instead use a continual game loop that can, and will quite often, do nothing (many times a second) — until something in your game changes. Which seems a strange thing to do, but it is the way that many games are designed and built — even in 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);

A live example (also with up and down movement):

    /// 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>

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

The above is just the basics, there are a number of optimisations you can make. Nor is the above the be-all-and-end-all of controlling a game character, as sadly, keyboard events in browsers are rather unpredictable (has nothing really to do with JavaScript) and there are a number of issues you will find if you start trying to make more complicated control systems i.e. certain keys cannot be pressed simultaneously without problems arising (especially control characters like CMD or CTRL).

One day it would be great if JavaScript had a Keyboard object, one which behaved just as the keys object above. Meaning you can test for the status of any key currently pressed, at any time, without having to rely on an event model. It may happen at some yet-unknown-future-point, but we'll have to wait and see.

If it does happen, I'd like to suggest a feature:

Keyboard.isPressed(Keyboard.ANY)

This would help in an untold number of game intro/menu screens (why do I have to press any key before I see the menu? Are we dreaming of arcade machines?). Not to mention the above would help out all those still waiting for the existence of what they deem a missing key.

like image 61
Pebbl Avatar answered Apr 12 '23 22:04

Pebbl