I want to implement character movement in my tile game such that it does not spam the server with key events. Currently I have throttled the amount of events sent by using setInterval.
Problems with my idea:
If the player does not tap a key at the right time, the movement might not work
Continuously tapping a key(right arrow for example), would only send key events when the setInterval is picking up the event. This makes it look buggy.
Good things with my code:
It works perfectly, if all you are doing is holding the key.
You can move diagonally by holding two keys at a time. The code stores which keys are being pressed.
Things that might help:
Here is a stripped down basic example of my code: http://jsfiddle.net/empXx/66/
var canvas = document.getElementById('c'),
ctx = canvas.getContext("2d"),
tileSize = 10;
var holdingUp = false;
var holdingDown = false;
var holdingLeft = false;
var holdingRight = false;
var cx = 150;
var cy = 150;
$(document).keydown(function(e) {
if(e.which==37) { holdingLeft = true; }
if(e.which==38) { holdingUp = true; }
if(e.which==39) { holdingRight = true; }
if(e.which==40) { holdingDown = true; }
});
$(document).keyup(function(e) {
if(e.which==37) { holdingLeft = false; }
if(e.which==38) { holdingUp = false; }
if(e.which==39) { holdingRight = false; }
if(e.which==40) { holdingDown = false; }
});
ctx.fillRect(150, 150, tileSize, tileSize);
setInterval(function() {
if(holdingUp) {
cls();
ctx.fillRect(cx, cy - tileSize, tileSize, tileSize);
cy -= tileSize;
} else if(holdingDown) {
cls();
ctx.fillRect(cx, cy + tileSize, tileSize, tileSize);
cy += tileSize;
}
if(holdingLeft) {
cls();
ctx.fillRect(cx - tileSize, cy, tileSize, tileSize);
cx -= tileSize;
} else if(holdingRight) {
cls();
ctx.fillRect(cx + tileSize, cy, tileSize, tileSize);
cx += tileSize;
}
}, 120);
function cls() { ctx.clearRect(0, 0, canvas.width, canvas.height); }
How could I make this game interaction smoother, and more enjoyable for the user, while keeping down the spam?
Thanks to John S for the original fix. I altered the code a bit to make it even better. Currently if a user is holding both right and left, the player will not move. This is a simpler solution than handling key precedence depending on the order they where pressed. I also made it so that the upCount/downCount etc, cannot be altered when holdingUp & holdingDown are active. Same for left right motions.
Simplified jsfiddle with bare basics here
The production game with this code implemented + anti spam/cheat detection is here
JSFiddle code, as required by SO:
var canvas = document.getElementById('c'),
ctx = canvas.getContext("2d"),
tileSize = 10;
var holdingUp = false;
var holdingDown = false;
var holdingLeft = false;
var holdingRight = false;
var cx = 150;
var cy = 150;
var upCount = 0;
var downCount = 0;
var leftCount = 0;
var rightCount = 0;
$(document).keydown(function(e) {
if(e.which==37 && !holdingLeft) { holdingLeft = true; leftCount++; }
if(e.which==38 && !holdingUp) { holdingUp = true; upCount++; }
if(e.which==39 && !holdingRight) { holdingRight = true; rightCount++; }
if(e.which==40 && !holdingDown) { holdingDown = true; downCount++; }
});
$(document).keyup(function(e) {
if(e.which==37) { holdingLeft = false; }
if(e.which==38) { holdingUp = false; }
if(e.which==39) { holdingRight = false; }
if(e.which==40) { holdingDown = false; }
});
ctx.fillRect(150, 150, tileSize, tileSize);
setInterval(function() {
if(!(holdingUp && holdingDown)) {
if((upCount > 0) || holdingUp) {
if (upCount > 0) { upCount--; }
cls();
ctx.fillRect(cx, cy - tileSize, tileSize, tileSize);
cy -= tileSize;
} else if((downCount > 0) || holdingDown) {
if (downCount > 0) { downCount--; }
cls();
ctx.fillRect(cx, cy + tileSize, tileSize, tileSize);
cy += tileSize;
}
} else {
downCount = upCount = 0;
}
if(!(holdingLeft && holdingRight)) {
if((leftCount > 0) || holdingLeft) {
if (leftCount > 0) { leftCount--; }
cls();
ctx.fillRect(cx - tileSize, cy, tileSize, tileSize);
cx -= tileSize;
} else if((rightCount > 0) || holdingRight) {
if (rightCount > 0) { rightCount--; }
cls();
ctx.fillRect(cx + tileSize, cy, tileSize, tileSize);
cx += tileSize;
}
} else {
leftCount = rightCount = 0;
}
}, 120);
function cls() { ctx.clearRect(0, 0, canvas.width, canvas.height); }