I am making a classic snake remake in JavaScript just for weekend fun, and I ran into this problem that if I press buttons quite fast - the snake (caterpillar in my case) is able to change direction to opposite and run into itself.
The way to recreate this situation is as follows:
direction is for example 'left'
press up(or down) and press right quickly after
now the caterpillar goes backwards. And my goal is it should make U-turn
I made the checks for opposite dir, but this doesn't prevent this
update = function() {
if (cat.direction != 'right' && key[37] === true) {
cat.direction = 'left';
}
if (cat.direction != 'left' && key[39] === true) {
cat.direction = 'right';
}
if (cat.direction != 'down' && key[38] === true) {
cat.direction = 'up';
}
if (cat.direction != 'up' && key[40] === true) {
cat.direction = 'down';
}
};
the full code
I was using normal addEventListener for the key listening, but I changed it for another approach (found somewhere), where I do update on keys very often and caterpillar moving is happening only from time to time, as I thought it might be a problem to directly associate drawing, changing direction and moving in the same time interval. I hope I am understandable, sorry if something isn't clear - I would be happy to provide more info if so.
One solution is to not process more than one key per move, but to allow more responsiveness you could implement a key buffer, instead of maintaining the key states as you do know. You would only collect arrow key presses in that buffer, and not push any repetitions of the same key into it.
Here are the relevant changes to the code:
Initialise the key buffer:
var keyBuffer = [];
Push arrow keys into the buffer when pressed:
var keyDown = function(e) {
var keyCode = e.which ? e.which : e.keyCode;
// *** Queue the arrow key presses
if (keyCode >= 37 && keyCode <= 40 &&
keyCode !== keyBuffer[keyBuffer.length-1] && ) {
keyBuffer.push(keyCode);
}
};
Process one key from the buffer at a time:
var update = function() {
// *** Extract next key from buffer, and only treat that one
// -- could be `undefined`, but that is not a problem:
var key = keyBuffer.shift();
if(cat.direction != 'right' && key === 37){
cat.direction = 'left';
} else if(cat.direction != 'left' && key === 39){
cat.direction = 'right';
} else if(cat.direction != 'down' && key === 38){
cat.direction = 'up';
} else if(cat.direction != 'up' && key === 40){
cat.direction = 'down';
}
};
Only process next key when about to move:
function loop() {
board.resetCanvas();
if(counter > 1000){
update(); // ***only process buffered keys when moving
cat.move();
counter = 0;
}
cat.draw();
counter += 5*cat.multiplier;
};
That's it. See fiddle below:
var canvas = document.getElementById("board");
var context = canvas.getContext("2d", {alpha:false});
var pieceSideLength = canvas.width / 40;
var key = [];
var keyBuffer = [];
window.addEventListener('keyup', function(e) {
this.keyUp.call(this, e);
}, false);
window.addEventListener('keydown', function(e) {
this.keyDown.call(this, e);
}, false);
function Piece(x,y){
this.x = x;
this.y = y;
}
board = {
leftBound: 0,
rightBound: canvas.width / pieceSideLength,
topBound: 0,
bottomBound: canvas.height / pieceSideLength,
drawPiece: function(x, y, color){
context.fillStyle = color;
context.fillRect(x*pieceSideLength,y*pieceSideLength,pieceSideLength,pieceSideLength);
context.strokeStyle = 'white';
context.strokeRect(x*pieceSideLength,y*pieceSideLength,pieceSideLength,pieceSideLength);
},
resetCanvas: function(){
context.clearRect(0,0,canvas.width,canvas.height);
}
};
//cat as for caterpillar
cat = {
x: canvas.width/pieceSideLength/2, //initial x
y: canvas.height/pieceSideLength/2, //initial y
pieces: [],
direction: 'up',
color: '#5da03c',
shouldGrow: false,
multiplier: 5,
init: function(){
cat.pieces.push(new Piece(this.x, this.y));
},
move: function(){
if(cat.pieces.length <= 10){
cat.shouldGrow = true;
}
var newX = cat.pieces[cat.pieces.length-1].x;
var newY = cat.pieces[cat.pieces.length-1].y;
if(cat.direction=='up'){
cat.makeNewHeadAt(newX,newY-1);
}
if(cat.direction=='down'){
cat.makeNewHeadAt(newX,newY+1);
}
if(cat.direction=='left'){
cat.makeNewHeadAt(newX-1,newY);
}
if(cat.direction=='right'){
cat.makeNewHeadAt(newX+1,newY);
}
cat.grow();
},
makeNewHeadAt: function(x,y){
cat.pieces.push(new Piece(x,y));
},
grow: function(){
if(cat.shouldGrow == false){
cat.pieces.shift();
} else {
cat.shouldGrow = false;
}
},
draw: function(){
for(i=0;i<cat.pieces.length;i++){
var p = cat.pieces[i];
board.drawPiece(p.x,p.y,cat.color);
}
}
};
cat.init();
update = function() {
// *** Extract next key from buffer, and only treat that one
// -- could be `undefined`, but that is not a problem:
var key = keyBuffer.shift();
if(cat.direction != 'right' && key === 37){
cat.direction = 'left';
} else if(cat.direction != 'left' && key === 39){
cat.direction = 'right';
} else if(cat.direction != 'down' && key === 38){
cat.direction = 'up';
} else if(cat.direction != 'up' && key === 40){
cat.direction = 'down';
}
};
keyUp = function(e) {
var keyCode = e.which ? e.which : e.keyCode;
this.key[keyCode] = false;
};
keyDown = function(e) {
var keyCode = e.which ? e.which : e.keyCode;
// *** Queue the key presses
if (keyCode >= 37 && keyCode <= 40 &&
keyCode !== keyBuffer[keyBuffer.length-1]) {
keyBuffer.push(keyCode);
}
this.key[keyCode] = true;
};
var counter = 0;
function loop() {
board.resetCanvas();
if(counter > 1000){
update(); // ***only process buffered keys when moving
cat.move();
counter = 0;
}
cat.draw();
counter += 5*cat.multiplier;
};
setInterval(loop, 1);
body { margin: 0px }
<div>
<canvas id="board" width="300" height="200" style="display: block; margin: 0 auto; background-color: #553300; border-style: solid; border-color: green;"></canvas>
</div>
Limiting the buffer size
You can limit the buffer size by replacing this:
keyBuffer.push(keyCode);
with:
keyBuffer = keyBuffer.slice(-2).concat(keyCode);
This will limit the size to 3. Adjust the slice argument as desired.
You can keep track of whether the snake has 'moved'. If you receive keyboard input, don't react to another keypress until the snake has moved. This way you're only allowing 1 key for each movement, so you can't change direction and run into yourself.
Modified example: link
update = function() {
if (moved = true) {
if(cat.direction != 'right' && key[37] === true){
and so forth
Related
I am working with keyboard events in Javascript. My goal is to be able to move images on a canvas with the keyboard arrows, but there is a problem: when I press two keys simultaneously and then release one of them, the key I left pressed does not work, it crashes and therefore the image It does not move. I'm already using the keyup and keydown events and I managed to find a way to detect when two keys are pressed at the same time, however I can't solve this problem. Can anyone help me? Thank you.
this is what i tried
function keyPressed(event) {
keysPressed[event.key] = true;
switch(event.key) {
case 'ArrowLeft':
playerPetSelected.speedX = -5;
break;
case 'ArrowRight':
playerPetSelected.speedX = 5;
break;
case 'ArrowDown':
playerPetSelected.speedY = 5;
break;
case 'ArrowUp':
playerPetSelected.speedY = -5;
break;
}
if (keysPressed['ArrowLeft'] && event.key == 'ArrowDown') {
playerPetSelected.speedX = -3.5;
playerPetSelected.speedY = 3.5;
}
if (keysPressed['ArrowDown'] && event.key == 'ArrowLeft') {
playerPetSelected.speedY = 3.5;
playerPetSelected.speedX = -3.5;
}
if (keysPressed['ArrowLeft'] && event.key == 'ArrowUp') {
playerPetSelected.speedX = -3.5;
playerPetSelected.speedY = -3.5;
}
if (keysPressed['ArrowUp'] && event.key == 'ArrowLeft') {
playerPetSelected.speedY = -3.5;
playerPetSelected.speedX = -3.5;
}
if (keysPressed['ArrowUp'] && event.key == 'ArrowRight') {
playerPetSelected.speedY = -3.5;
playerPetSelected.speedX = 3.5;
}
if (keysPressed['ArrowRight'] && event.key == 'ArrowUp') {
playerPetSelected.speedX = 3.5;
playerPetSelected.speedY = -3.5;
}
if (keysPressed['ArrowDown'] && event.key == 'ArrowRight') {
playerPetSelected.speedY = 3.5;
playerPetSelected.speedX = 3.5;
}
if (keysPressed['ArrowRight'] && event.key == 'ArrowDown') {
playerPetSelected.speedX = 3.5;
playerPetSelected.speedY = 3.5;
}
}
function stopMovement(event) {
keysPressed[event.key] = false;
playerPetSelected.speedX = 0;
playerPetSelected.speedY = 0;
}
The parts where I modify the 'playerPetSelected' object are so that the images can be moved on the canvas.
I was hoping I wouldn't have this problem anymore because here I was able to find a way to detect when two keys are pressed at the same time, but it only helped me to manipulate the speed of movement on the canvas when two keys are pressed simultaneously.
Here is how I would do it... Using an object for the speed in x/y. Look how the conditions are easier to read. That handles all possible arrow combinations.
window.addEventListener("keydown", keyPressed)
window.addEventListener("keyup", keyUnpressed)
let keysPressed = {}
function keyPressed(event) {
keysPressed[event.key] = true
getActiveKeys()
}
function keyUnpressed(event) {
keysPressed[event.key] = false
getActiveKeys()
}
function getActiveKeys() {
const activeKeys = Object.keys(keysPressed).map((key) => keysPressed[key] ? key : null).filter((key) => key)
const speed = {
x: 0,
y: 0
}
if(keysPressed["ArrowUp"]){
speed.y = 5
}
if(keysPressed["ArrowDown"]){
speed.y = -5
}
if(keysPressed["ArrowLeft"]){
speed.x = -5
}
if(keysPressed["ArrowRight"]){
speed.x = 5
}
// Gas and break conditions
if(keysPressed["ArrowUp"] && keysPressed["ArrowDown"]){
speed.y = 0
}
if(keysPressed["ArrowLeft"] && keysPressed["ArrowRight"]){
speed.x = 0
}
// Diagonal condition
if(speed.x && speed.y){
speed.x *= 0.7
speed.y *= 0.7
}
document.querySelector("#activeKeys").innerText = JSON.stringify(activeKeys)
document.querySelector("#xSpeed").innerText = speed.x
document.querySelector("#ySpeed").innerText = speed.y
}
<div>Keys pressed: <span id="activeKeys">[]</span></div>
<div>Speed X: <span id="xSpeed">0</span></div>
<div>Speed Y: <span id="ySpeed">0</span></div>
Run that snippet in full page mode, else the page will scroll. Or look at CodePen
I have an SVG
<svg id='mainSvg' viewBox='0 0 1920 1080'>
<rect id='rectangle' x='800' y='800'/>
</svg>
and I am moving the rectangle inside the SVG with javascript with left and right arrow keys and A and D keys.
that is working perfectly but I have a problem that the rectangle can go outside of the view-box of the SVG and I want the rectangle to stop when it touches the left or right corner. this is my code.
function keyDown(event) {
if (event.key === 'ArrowLeft' || event.key === 'a') {
leftKey = true;
aKey = true;
} else if (event.key === 'ArrowRight' || event.key === 'd') {
rightKey = true;
dKey = true;
}
}
function keyUp(event) {
if (event.key === 'ArrowLeft' || event.key === 'a') {
leftKey = false;
aKey = false;
} else if (event.key === 'ArrowRight' || event.key === 'd') {
rightKey = false;
dKey = false;
}
}
function move() {
let xPosition = parseInt(rectangle.getAttribute('x'));
const movmentSpeed = 10;
//if right arrow key,d is pressed
if (rightKey) {
rectangle.setAttribute('x', xPosition + movmentSpeed);
//if right arrow key,a is pressed
} else if (leftKey) {
rectangle.setAttribute('x', xPosition - movmentSpeed)
if(xPosition === 0){
leftKey = false;
}
}
setTimeout(move, 10);
}
on the last function move() I tried saying if(Xposition ===0){leftKey = false} that works briefly but if the user keeps pressing the left arrow then the rectangle will start moving again and go out of the viewBox.
Also, the arrow keys have a value of false by default
Any ideas on how to fix this?
I am trying to move a sprite using arrow keys. Below is the logic that SHOULD cause the sprite to move diagonally if two keys are pressed. However, that is not the case. It will only move in one direction a a time. Since I am saving each keyCode as a Boolean within an object property whenever I press a button, I would think that this should not be an issue, but I can't get it to work. I have also tried using an array to store these Booleans, and checking those values instead. No luck.
I am at my wit's end and would greatly appreciate some help. I've scoured stackoverflow and many different blogs on building 2d games, but but nothing that I try works.
var keyState = {};
window.onkeydown = window.onkeyup = function(e) {
keyState[e.keyCode] = e.type == 'keydown';
// checks for up and left
if (keyState[38] && keyState[37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for up and right
else if (keyState[38] && keyState[39]) {
player.pos.x += 2;
player.pos.y += 2;
}
// checks for down and left
else if (keyState[40] && keyState [37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for down and right
else if(keyState[40] && keyState [39]) {
player.pos.x += 2;
player.pos.y -= 2;
}
// checks for up
else if (keyState[38]) {
player.animation.y = 64;
player.pos.y -= 2;
}
};
You should explicitly set the object key values as booleans and you should toggle them in separate keydown/keyup handlers:
let keysdown = {};
window.addEventListener('keydown', function(evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
console.log('down and right');
} else if (keysdown["38"] === true) {
console.log('up');
}
}, false);
window.addEventListener('keyup', function(evt) {
keysdown[evt.which] = false;
}, false);
This logs all the correct key combinations for me.
You need to move the movement logic out of the event listener. Have the key event just log the current state of each key (up or down) then in the main loop check the state and move the play as needed.
As I am unsure if you want the movement to be constant or only on the press I have included the option to change the behaviour with the constant flag MOVE_ONLY_ON_PRESS if true movement only when a press is first detected. So that no key strokes are missed I clear the key flag in the main loop.
The following is an example of how to interface with the keyboard for a game.
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
// Player
var player = {};
player.x = 100;
player.y = 100;
const MOVE_SPEED = 2;
const MOVE_ONLY_ON_PRESS = false; // This will toggle constant movement or only on press
// from your code
window.onkeydown = window.onkeyup = function (e) {
if (MOVE_ONLY_ON_PRESS) {
if (e.type === 'keydown') {
keyState[e.keyCode] = true;
}
} else {
keyState[e.keyCode] = e.type == 'keydown';
}
}
// in the main loop;
function update(timer) {
// you dont need to test for the combinations (ie up left) when its just simple movement
if (keyState[KEY_UP]) {
player.y -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_UP] = false; }
}
if (keyState[KEY_DOWN]) {
player.y += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_DOWN] = false; }
}
if (keyState[KEY_LEFT]) {
player.x -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_LEFT] = false; }
}
if (keyState[KEY_RIGHT]) {
player.x += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_RIGHT] = false; }
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
Bit fields.
Another way to simplify the arrow keys is to set the first 4 bits of a number to correspond to a key, one bit per key, then it is easy to test for combinations of keys as each of the 16 possible combinations has a unique number. You Could map many more keys this way but it becomes a little impractical when you go to far.
This is how to set them
var arrowBits = 0; // the value to hold the bits
const KEY_BITS = [4,1,8,2]; // left up right down
const KEY_MASKS = [0b1011,0b1110,0b0111,0b1101]; // left up right down
window.onkeydown = window.onkeyup = function (e) {
if(e.keyCode >= 37 && e.keyCode <41){
if(e.type === "keydown"){
arrowKeys |= KEY_BITS[e.keyCode - 37];
}else{
arrowKeys &= KEY_MASKS[e.keyCode - 37];
}
}
}
These are 8 of the 16 possible combinations
// postfix U,D,L,R for Up down left right
const KEY_U = 1;
const KEY_D = 2;
const KEY_L = 4;
const KEY_R = 8;
const KEY_UL = KEY_U + KEY_L; // up left
const KEY_UR = KEY_U + KEY_R; // up Right
const KEY_DL = KEY_D + KEY_L; //
const KEY_DR = KEY_D + KEY_R; //
This is how you test for then
if ((arrowBits & KEY_UL) === KEY_UL) { // is UP and LEFT only down
if (arrowBits & KEY_UL) { // is Up left down (down and right may also be down)
if ((arrowBits & KEY_U) === KEY_U) { // is Up only down
if (arrowBits & KEY_U) { // is Up down (any other key may be down)
if (!(arrowBits & KEY_U)) { // is Up up (any other key may be down)
if (!arrowBits) { // no keys are down
if (arrowBits) { // any one or more keys are down
I cannot tell you why your code doesn't work. If I replace the player-movements with console-logs, it works for me.
Although it's pretty primitive.
First separate the keystate-manager from the movement-logic.
var isKeyDown = (function(alias){
for(var i=0, a=Object.create(null); i<256;) a[i++]=false;
for(var k in alias) i=0|alias[k], i>0 && i<256 && Object.defineProperty(a,k,{get:Function("return this["+i+"]")});
function keyStateHandler(e){
a[e.which||e.keyCode] = e.type==="keydown"
}
addEventListener("keydown", keyStateHandler, false);
addEventListener("keyup", keyStateHandler, false);
return a;
})({
//add some aliases for more readable code
up: 38,
left: 37,
right: 39,
down: 40
});
once, because the update-interval of the keydown-event is not reliable, varies between browsers, and is not sync with the update-interval of the brwoser wich will cause flickering.
Now create a main loop for your game, and take account for the timing. Although requestAnimationFrame aims for a constant framerate you may have lags, and you don't want to see your player move at different speeds during these lags.
var lastCall = Date.now();
function tick(){
requestAnimationFrame(tick);
var now = Date.now(),
time = (now - lastCall) / 1000; //time in seconds
//because it's easier to think in terms like 2px/s
//than 0.002px/ms or 0.0333 px/frame
lastCall = now;
move(time);
}
tick();
now the actual movement:
//the speed of your player: it would be better if this would be
//a property of the player than hardcoded like you did, or global like this
var speed = 2; // 2px/second
function move( time ){
//accounts for varying framerates
//opposite directions cancel each other out
var dx = (isKeyDown.right - isKeyDown.left) * time,
dy = (isKeyDown.down - isKeyDown.up) * time;
//taking account for diagonals
if(dx && dy) dx /= Math.SQRT2, dy /= Math.SQRT2;
player.pos.x += dx * speed;
player.pos.y += dy * speed;
}
Based on skyline3000 answer I tried to make it work also in down, left and right. Appart from adding the other options I had to change window to document to make it work:
let keysdown = {};
document.addEventListener('keydown', function (evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
prota.moveUp();
prota.moveLeft();
// console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
prota.moveUp();
prota.moveRight();
// console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
prota.moveDown();
prota.moveLeft();
// console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
prota.moveDown();
prota.moveRight();
// console.log('down and right');
} else if (keysdown["38"] === true) {
prota.moveUp();
// console.log('up');
} else if (keysdown["40"] === true) {
prota.moveDown();
// console.log('down');
} else if (keysdown["39"] === true) {
prota.moveRight();
// console.log('right');
} else if (keysdown["37"] === true) {
prota.moveLeft();
// console.log('left');
}
}, false);
document.addEventListener('keyup', function (evt) {
keysdown[evt.which] = false;
}, false);
I put in the entire thing just in case
I went into chrome and looked for errors, and it wasnt working either so I checked, apparently the first line has a problem. I kept the first 6 lines and deleted all else then it worked fine, so I went to SublimeText2 and searched for every ) and } in the code.
var canvasBg = document.getElementById('canvasBg');
var ctxBg = canvasBg.getContext('2d');
var canvasJet = document.getElementById('canvasJet');
var ctxJet = canvasJet.getContext('2d');
var jet1;
var fps = 17;
var drawInterval;
var imgSprite = new Image();
imgSprite.src = 'SpriteSheet.png'
imgSprite.addEventListener('load',init,false);
function init() {
drawBg();
startDrawing();
jet1 = new Jet();
document.addEventListener('keydown',checkKeyDown,false);
document.addEventListener('keyup',checKeyUp,false);
}
function draw() {
jet1.draw();
}
function startDrawing() {
stopDrawing();
drawInterval = setInterval(draw,fps);
}
function stopDrawing() {
clearInterval(setInterval);
}
Jet.prototype.draw = function() {
clearCtxJet();
ctxJet.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,this.drawX,this.drawY,this.width,this.height);
};
function Jet() {
this.srcX = 0;
this.srcY = 0;
this.drawX = 200;
this.drawY = 200;
this.width = 96;
this.height = 30;
}
function drawJet() {
}
function drawBg() {
ctxBg.drawImage(imgSprite,96,0,800,500,0,0,800,500)
}
function clearCtxBg() {
ctxBg.clearRect(0,0,800,500);
}
function clearCtxJet() {
ctxJet.clearRect(0,0,800,500);
}
function checkKeyDown(e) {
var keyID = (e.keyCode) ? e.keyCode : e.which;
if (keyID === 38) { // 38 is up key
alert('up arrow was pressed');
e.preventDeafault();
}
if (keyID === 39) { // 39 is right key
e.preventDeafault();
}
if (keyID === 40) { // 40 is down key
e.preventDeafault();
}
if (keyID === 37) { // 37 is left key
e.preventDeafault();
}
function checkKeyup(e) {
var keyID = (e.keyCode) ? e.keyCode : e.which;
if (keyID === 38) { // 38 is up key
alert('up arrow was pressed');
e.preventDeafault();
}
if (keyID === 39) { // 39 is right key
e.preventDeafault();
}
if (keyID === 40) { // 40 is down key
e.preventDeafault();
}
if (keyID === 37) { // 37 is left key
e.preventDeafault();
}
}
I assume you're showing only parts of the code so there's no way of knowing whether this is the actual error, but both checkKeyup and checkKeydown are missing closing braces.
I recommend installing Package Control for Sublime Text 2 and then using it to install SublimeLinter which will be able to check your code for you and point out missing semicolons and braces.
It would be nice to see full code in http://jsfiddle.net
So here is my snake script -
http://jsfiddle.net/6bKHc/24/
I started to create a snake game, but basically move(top), move(bottom) e.c. is not working. Any ideas why? I understand that I can't pass the elements to variable like this, so maybe you could show me how to do that correctly?
Ok cleared syntax and took a look at errors this should get you started
$(document).keydown(function(event){
var move, inter;
inter = setInterval(move = function() {
var dir = $(".snake").data('dir');
var snake = $('.snake');
if(dir == 'top') {
snake.stop().animate({"top": "+=5px"});
}
if(dir == 'bottom') {
snake.stop().animate({"top": "-=5px"});
}
if(dir == 'left') {
snake.stop().animate({"left": "+=5px"});
}
if(dir == 'right') {
snake.stop().animate({"top": "-=5px"});
}
}, 500);
if(event.which == 40) {
$(".snake").data('dir','top');
} else if(event.which == 39) {
$(".snake").data('dir','left');
} else if(event.which == 37) {
$(".snake").data('dir','right');
} else if(event.which == 38) {
$(".snake").data('dir','bottom');
}; });
fiddle
Couple of obvious additions to make:
boundary checking
smoothness of animation.
To make it work you will have to moodify your code like follows :
$(document).keydown(function() {
var inter;
return function(event) {
var move, prevDirection;
clearInterval(inter);
inter = setInterval(move = function(direction) {
var value, prop;
switch (direction || prevDirection) {
case "top":
prop = "top";
value = -5;
break;
case "bottom":
prop = "top";
value = 5;
break;
case "left":
prop = "left";
value = -5;
break;
case "right":
prop = "left";
value = 5;
break;
}
if (direction) prevDirection = direction;
$(".snake").css(prop, $(".snake").position()[prop] + value);
}, 500);
if (event.which == 40) {
move('bottom');
} else if (event.which == 39) {
move('right');
} else if (event.which == 37) {
move('left');
} else if (event.which == 38) {
move('top')
};
}
}());
http://jsfiddle.net/6bKHc/42/