Question

Let's say we have a ball that can be moved left and right around the screen. When you click space, the ball should jump.

I got the ball to move left and right in a canvas. However, when the ball is moving left (for example) and I hit the space bar before anything, the ball stops moving left.

Check out my example so far!

I am using the KeyboardJS library to handle my key events:

KeyboardJS.on("left", function () {
    cc();
    x -= xv;
    if (x < r) {
        circle(x + width, y, r);
        if (x + width <= width - r) {
            x = x + width;
        }
    }
    circle(x, y, r);
});

KeyboardJS.on("space", null, function () {
    console.log("space!");
});

How could I get this behavior to stop so that when the space bar is hit, the ball jumps up but at the same time still moves to the left?

Was it helpful?

Solution

One thought added to everyone else's good ideas:

Separate your user input from your drawing.

Keyboarding:

If you’re having problems with KeyboardJS, check out Keydrown: http://jeremyckahn.github.io/keydrown/

Don’t do any drawing when capturing keys…just capture the user’s input of which direction they want the circle to go.

Set up an “direction” variable to hold how many times the user has pressed [left] or [right]:

var direction=0;  

When [left] is pressed:

direction--; 

When [right] is pressed:

direction++;

Direction is a net number. So if the user holds down the left key for 20 strokes and the right key for 15 strokes, direction will be -5 ( -20 + 15 ).

Set up an “altitude” variable to hold how many times the user has pressed [space]:

var altitude=0;

When [space] is pressed:

altitude-=10;

Altitude is a net number also.

Drawing:

Do all your drawing in a separate animation loop. Rather than using javascript’s setInterval, use the new and improved way of creating an imation loop -- requestAnimationFrame.

// set the starting circle positions 

var currentX=canvas.width;
var currentY=canvas.height-r;

function animate(){

    // even as we're executing this current animation loop
    // request another loop for next time

    requestAnimationFrame(animate);

    // change the currentX position by the accumulated direction

    currentX+=direction;
    direction=0;

    // change the currentY position by the accumulated altitude

    currentY+=altitude;
    altitude=0;

    // draw the circle at its current position

    cc();

    circle(currentX,currentY,r);

    // apply gravity to the circle
    // to make it fall if its in the air

    if(currentY<canvas.height-r){
        currentY++;
    }

}

Good Luck with your project!

OTHER TIPS

What the problem is, is that if you press another key after pressing the first key, it will trigger that event, and stop triggering the other keydown event. This can be seen in this simplified example:

addEventListener('keydown',function(e) {console.log(e.keyCode, e.keyIdentifier)});

If you run that script, and then press left and then up, it will first show 37 Left a bunch of times, and then it'll show 32 U+0020 once and stop logging the left keydowns.

This is simply how the browser (and most other basic programs too) work. You can try doing the same thing in for example notepad, if you press the A key first, and then press space, it'll stop adding more As. This also means that you can't rely on key events (or key event libraries) to do this for you.

What you could do though, is make a global object that holds all keys that are pressed. For example:

window.KeysDown = {
    37: false, //Left
    39: false, //Right
    38: false, //Up
    40: false, //Down
    32: false, //Space
};

addEventListener('keydown', function(e) {
     var keyCode = e.keyCode||e.charCode||e.which;
     if (keyCode == 32 && !window.KeysDown[keyCode])
         onSpace();//This will only run when the spacebar is first pressed down.
     if (window.KeysDown.hasOwnProperty(keyCode))
         window.KeysDown[keyCode] = true;
});
addEventListener('keyup', function(e) {
     var keyCode = e.keyCode||e.charCode||e.which;
     if (window.KeysDown.hasOwnProperty(keyCode)) window.KeysDown[keyCode] = false;
});

var interval = setInterval(function() {
    for (var i in window.KeysDown) {
        if (window.KeysDown[i]) switch (i+'') {
            case '37': onLeft(); break;
            //case '38': window.KeysDown[i] && onUp(); break;
            case '39': onRight(); break;
            //case '40': window.KeysDown[i] && onDown(); break;
        }
    }
}, 50);

The syntax window.KeysDown[i] && onLeft() causes the onLeft function only to run if window.KeysDown[i] is true. I hope this solution works for you.

EDIT: I've changed the code to the working one. I've also made a JSFiddle that demonstrates this. The problem in my previous code was that apparently a switch doesn't handle integer values well, so I needed to convert i to a string.

EDIT: I've also added an extra part to the script that makes the onSpace function only run when the spacebar is first pressed down, and so that it won't run again until the spacebar is released and pressed again. I've also updated my JSFiddle to include these changes.

I would create a main function, which runs at a regular interval, and each time it runs, it updates the position of the circle based on what keys are currently down.

The main function can be done like this:

var keyDown = {};

function mainLoop() {
    cc();

    //pseudo code
    if (keyDown["left"]) {
        x -= 5;
    }
    if(keyDown["space"]) {
        y -= 10;
    }

    // redraw circle at new location
    circle(x,y,r);
}

setInterval(mainLoop, 30) //sets the function to be called every 30 milliseconds

// key event handler, first function handles the keydown, second function handles keyup
KeyboardJS.on("left", function() {
    keyDown["left"] = true;
}, function() {
    keyDown["left"] = false;
});

With this example, if the user had the left arrow key and space bar pressed when the mainLoop function ran, then the circle would move to the left 5 pixels, and up 10 pixels.

jsFiddle Demo

You are going to have to manually create a framework for this. KeyboardJS just wasn't cutting it. I guess I kind of set that up here. It uses an Action "class" coupled with key event triggers.

Action "class"

function Actions(){
 this.count = 0;
 this.running = {};
 this.interval = undefined;
}

Actions.prototype.start = function(action){
  if( typeof(this.running[action]) == "undefined"){
     this[action]();
     this.running[action] = action;
     this.count++;
  }
  var me = this;
  if( typeof(this.interval) == "undefined"){
     this.interval = setInterval(function(){
      for( var act in me.running ){
       me[act]();
      }
     },50);
  }
};

Actions.prototype.stop = function(action){
  this.running[action] = void 0;
  delete this.running[action];
  this.count--;
  if( this.count == 0 ){
   clearInterval(this.interval);
   this.interval = void 0;
  };
};

Actions.prototype.left = function(){
 cc();
 x -= xv;
 if (x < r) {
    circle(x + width, y, r);
    if (x + width <= width - r) {
        x = x + width;
    }
 }
 circle(x, y, r);
};
Actions.prototype.right = function(){
 cc();
 x += xv;
 if (x >= width - r) {
    circle((x - r) - (width - r), y, r);
    if ((x - r) - (width - r) > r) {
        x = (x - r) - (width - r);
    }
 }
 circle(x, y, r);
};
Actions.prototype.space = function(){
 cc();
 y -= yv;
 circle(x, y, r);
};

key event triggers

document.onkeydown = checkKeyDown;
function checkKeyDown(e) {
 e = e || window.event;
 if (e.keyCode == '37') {
    // left arrow
    actions.start("left");
 }
 if (e.keyCode == '39') {
    // right arrow
    actions.start("right");
 }
 if (e.keyCode == '32') {
    // space bar
    actions.start("space");
 }
}
document.onkeyup = checkKeyUp;
function checkKeyUp(e) {
 e = e || window.event;

 if (e.keyCode == '37') {
    // left arrow
    actions.stop("left");
 }
 if (e.keyCode == '39') {
    // right arrow
    actions.stop("right");
 }
 if (e.keyCode == '32') {
    // space bar
    actions.stop("space");
 }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top