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 have coded this game inspired by The Coding Train's Snake Game and I can't figure a problem out. If the player presses both the right button and the down button, for example, they would just turn to the opposite direction, which would just kill them. I wonder how I would set a cooldown on the keys opposite of eachother. I'm using the p5js library.
I appreciate any help!
this.dir = function(x, y) {
this.xspeed = x;
this.yspeed = y;
}
if (keyCode === UP_ARROW && s.yspeed != 1) {
s.dir(0, -1);
} else if (keyCode === DOWN_ARROW && s.yspeed != -1) {
s.dir(0, 1);
} else if (keyCode === RIGHT_ARROW && s.xspeed != -1) {
s.dir(1, 0);
} else if (keyCode === LEFT_ARROW && s.xspeed != 1) {
s.dir(-1, 0);
}
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
Canvas auto runs when you click on another window and come back.
I have added key listeners and they make the ship move when you click w a s d or up, left, right, down.
It all works fine until you click two buttons like up and right and then click on another tab or window and come back. Then it will just keep moving not listening to your input.
What I think the problem is, is that when you click off the page the canvas never gets to check if the key was left up.But how do I make all the keys act like they were unpressed when you click off the screen?
Also the enemy disappera when you move off screen
This is what happens when buttons are pressed:
Player.prototype.checkKeys = function() //these functions are in the PLayer class
{
if(this.isUpKey == true)//if true
{
if(Player1.drawY >= 0)
{
this.drawY -= this.Speed;
}
}
if(this.isRightKey == true)
{
if(Player1.drawX <= (canvasWidthShips - this.playerWidth))
{
this.drawX += this.Speed;
}
}
if(this.isDownKey == true)
{
if(Player1.drawY <= (canvasHeightShips - this.playerHeight))
{
this.drawY += this.Speed;
}
}
if(this.isLeftKey == true)
{
if(Player1.drawX >= 0)
{
this.drawX -= this.Speed;
}
}
};
And these are my simple functions to check when a key is pressed down, I dont think the error is here but not sure?
function checkKeyDown(e)
{
if (Paused == false)
{
var KeyID = e.KeyCode || e.which;
if (KeyID === 38 || KeyID === 87) //up and w keyboard buttons
{
Player1.isUpKey = true;
e.preventDefault(); //webpage dont scroll when playing
}
if (KeyID === 39 || KeyID === 68) //right and d keyboard buttons
{
Player1.isRightKey = true;
e.preventDefault(); //webpage dont scroll when playing
}
if (KeyID === 40 || KeyID === 83) //down and s keyboard buttons
{
Player1.isDownKey = true;
e.preventDefault(); //webpage dont scroll when playing
}
if (KeyID === 37 || KeyID === 65) //left and a keyboard buttons
{
Player1.isLeftKey = true;
e.preventDefault(); //webpage dont scroll when playing
}
}
else if (Paused == true)
{
Player1.isUpKey = false;
Player1.isDownKey = false;
Player1.isRightKey = false;
Player1.isLeftKey = false;
}
}
function checkKeyUp(e)
{
if (Paused == false)
{
var KeyID = e.KeyCode || e.which;
if (KeyID === 38 || KeyID === 87) //up and w keyboard buttons
{
Player1.isUpKey = false;
e.preventDefault(); //webpage dont scroll when playing
}
if (KeyID === 39 || KeyID === 68) //right and d keyboard buttons
{
Player1.isRightKey = false;
e.preventDefault(); //webpage dont scroll when playing
}
if (KeyID === 40 || KeyID === 83) //down and s keyboard buttons
{
Player1.isDownKey = false;
e.preventDefault(); //webpage dont scroll when playing
}
if (KeyID === 37 || KeyID === 65) //left and a keyboard buttons
{
Player1.isLeftKey = false;
e.preventDefault(); //webpage dont scroll when playing
}
}
else if (Paused == true)
{
Player1.isUpKey = false;
Player1.isDownKey = false;
Player1.isRightKey = false;
Player1.isLeftKey = false;
}
}
Would it be ok to just pause the game? You can listen for onblur on the browser window which gets triggered when it loses focus and then just pause the game in the event handler.
window.onblur = function() {
// Add logic to pause the game here...
Paused = true;
};
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/