0

I followed this question/answer to create a multiple key press detector. It works as intended: you press D and the square moves right, then you press S while pressing the D and the square moves in angle. So far so good. However, if you are pressing D and S at the same time and you unpress the S, the square will stop moving. The expected behaviour is that it keeps moving to the right, since you are still pressing D.

Here's a jsfiddle and the demo I'm making (my web).

So, why is .keyup() making other .keydown() event invalid? This is the offending jQuery code:

// Check if it's a function
function isFunction(functionToCheck) {
    var getType = {};
    return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}

// Store the keys pressed and callback for each key
var keys = {};
var keycalls = {};

// Loop and call all the needed callbacks
function run_keys() {
    $.each(keys, function (index, value) {
        if (value == true) {
            console.log(keys);
            var fun = keycalls[index];
            if (isFunction(fun))
                fun();
        }
    });
}
// Store a key
$(document).keydown(function (e) {
    keys[e.which] = true;
    run_keys();
});
// Delete a key
$(document).keyup(function (e) {
    delete keys[e.which];
    run_keys();
});
// Assign a callback to a key
keyboard = function (key, callback) {
    console.log(callback);
    keycalls[key.toUpperCase().charCodeAt()] = callback;
}
// Assign keys
// Assign w to up action
keyboard("w", function () {
    $(".keyboard").animate({
        'top': "-=5px"
    }, 0);
});
keyboard("a", function () {
    $(".keyboard").animate({
        'left': "-=5px"
    }, 0);
});
keyboard("s", function () {
    $(".keyboard").animate({
        'top': "+=5px"
    }, 0);
});
keyboard("d", function () {
    $(".keyboard").animate({
        'left': "+=5px"
    }, 0);
});

Disclaimer: this is just for fun and learning, not about having a game engine. I know there are many good ones already available.

UPDATE

One of the solutions I've thought is making an independent loop for calling run_keys(), but I'd like to avoid that if possible. It works, however I'm still interested in the problem.

Community
  • 1
  • 1
Francisco Presencia
  • 8,732
  • 6
  • 46
  • 90

1 Answers1

1

I believe your problem is coming from the end of keypress events after the keyup, rather than anything "going wrong", use a setTimout loop instead of calling run_keys at the end of each handler.


Here is an example of a setTimeout loop

(function looper() {
    run_keys();
    window.setTimeout(looper, 100);
}());

It's a bit like setInterval but the delay starts after the invocation, meaning you won't get (the same kind of) cascading errors as is possible with setInterval.

Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Yes, [that works](http://jsfiddle.net/2fX94/2/). But why does a keyup on one key stop the keydown on the other? That's what I still don't understand. – Francisco Presencia Jun 08 '14 at 10:52
  • @FranciscoPresencia it doesn't, it ends the _keypress_ events and I think _jQuery_ routes _keypress_ through _keydown_ to make life easier – Paul S. Jun 08 '14 at 10:52
  • Okay, sorry, so why does a keyup on `S` stop the keypress event in `D`? Is this the expected behaviour? – Francisco Presencia Jun 08 '14 at 10:53
  • @FranciscoPresencia It is standard behaviour; in a textbox, hold down a D and you'll see a line of `d`s appear, tap S and then you'll get a single `s` after your line of `d`s, and the `d` will stop even though you're still holding the key down. It must be defined somewhere but I'm not sure where - a quick look on W3 wasn't helpful, may even be OS level as it happens in e.g. Notepad too – Paul S. Jun 08 '14 at 10:59
  • 1
    On the other hand with `setInterval()` it can see that it's being pressed. I'll leave it as one of those strange quirks and move on. I can see how the setTimeout solves not only my original problem, but the ones with setInterval, so thanks (: – Francisco Presencia Jun 08 '14 at 11:06