Javascript Snake Game (Prevent reverse while maintaining responsiveness) - javascript

I have a snake game that's "finished" except for one pretty serious annoyance.
Previously, I had the following problem.
Say the snake is going right. If the player changed directions multiple times in quick succession, e.g., by pressing down then left, then the snake would go back into itself and reverse direction. Once I added collision detection code, this would cause the game to end and the player to lose because the snake collided with itself by going inward into itself. This problem was made possible because direction could be changed multiple times in one interval, e.g., from right to down then from down to left.
This problem was fixed by adding a bool that would store whether or not direction was allowed to change this interval. If the direction had not been changed yet this interval, then direction was allowed to change. If it had already been changed this interval, then it would have to wait until next interval to change again. This prevented the snake from ever going inward into itself because it guaranteed that the snake's direction could only change once per interval. But this created another problem in that in each interval, every key press after the first would not register.
Now, the problem is that the game no longer feels responsive. If the snake is going to the right, there is no reason that spamming down right in quick succession shouldn't do what the player expects. As it stands, the second right sometimes doesn't register because it was pressed too quickly (on the same interval as down was pressed). So by fixing one big issue in this way, I created another annoyance. How do I get the best of both worlds. How do I stay rid of the possibility to go inward into myself while restoring responsiveness?
I can provide code if necessary, but I thought that the question was pretty clear even in abstraction.

It sounds like the game either acts on the new direction in the next interval or the direction is lost until the user presses again.
Have you tried queuing the next direction? for instance, at every interval, you can check the queue to see if there is a new direction. If there is, change direction. If not, continue in the current direction.
This will probably accomplish the responsiveness you want while still being accurate to the user’s input.
Good luck

I had the exact same problem! Very annoying! I ended up implementing a queue, where recent keypresses are stored in a queue, and for each 'tick' I check if any elements in queue, and if so, shift it once (with Array.shift, which returns the shifted value) and then set the snakes's direction to the direction from the queue, like this:
if (dir_queue.length) {
let d = dir_queue.shift();
dx = d.dx;
dy = d.dy;
}
// move snake
x += dx;
y += dy;
Directions are pushed to the dir. queue in the keydown event handler, like this:
var key = evt.keyCode;
if (key === KEY.UP || key === KEY.DOWN || key === KEY.LEFT || key === KEY.RIGHT) {
switch (key) {
case 37:
dx = dx || -1; dy = 0;
break;
case 38:
dx = 0; dy = dy || -1;
break;
case 39:
dx = dx || 1; dy = 0;
break;
case 40:
dx = 0; dy = dy || 1;
}
// push to dir. queue
dir_queue.push({ dx: dx, dy: dy });
}

Related

Snake Like Game - Automatic Movement in one direction after pressing a button

so I am having troubles with automatic movement
The code I am using is
if (myGameArea.key && myGameArea.key == 40) {
snake.y ++;
}
which is moving but only when the key is hold, is there a way to let the object move in one direction automatically after just pressing the button. Sorry, if that's a stupid question, I really am not an experienced coder.
You should consider giving the snake object another attribute: velocity! Since you are dealing with two dimensions you can define vx and vy as the two components of the velocity, and when a key is pressed you can change the velocity to the appropriate value, for example:
if (myGameArea.key && myGameArea.key==40) {
snake.vx = 0;
snake.vy = 1;
}
As well as this, inside the snake object you should define the position by updating the current position. For this you just add the current velocity.
Hopefully this gets you on the path to a working snake game! If not, let me know and I can try to help out more.
I would use flags, and when you press a key set the flag to true for a while loop. When you press a key, go to a move function and have it move the direction of the flag until another key is pressed or you hit a wall.

Attempting to get two simultaneous joysticks working with touch events

I've been battling with this for two days. I'm using an HTML5/JS game engine called ImpactJS and someone made a very useful plugin to create joystick touch zones for mobile devices. The idea is that you specify a zone on the screen where the joystick is activated when that area is touched.
The code for the plugin I am using is here. I have modified it slightly to add "curPos" (x and y coordinates of the spot that the user is currently touching), otherwise all code is identical. I have been trying to solve this problem myself as well as contacting the original creator, but they seem to be unreachable at this time and I'm getting nowhere.
I'm sure I'm doing something very wrong here, but while I can get the touch zones to work perfectly on their own, every time I try to use both joysticks at the same time they partially overwrite each other.
I have specified two zones as follows when my game initializes:
this.joystick1 = new TouchJoystickZone(0, 0, ig.system.width / 2, ig.system.height);
this.joystick2 = new TouchJoystickZone(ig.system.width / 2, 0, ig.system.width / 2, ig.system.height);
this.joystick1 is responsible for player rotation. this.joystick2 is responsible for player acceleration. In my Player entity I have the following movement code for the two joysticks. Again, this works perfectly when I only have one finger on the screen/one joystick in use:
if( ig.ua.mobile ) {
// ROTATION
if (ig.game.joystick1.touchStart.x > 0 && ig.game.joystick1.touchStart.x < ig.system.width/2) {
if (Math.abs(ig.game.joystick1.delta.x) >= 50 || Math.abs(ig.game.joystick1.delta.y) >= 50) {
this.joystickAngle = ig.game.Controller.toDegrees(ig.game.Controller.joystickAngle());
if (this.angle > this.joystickAngle + 20) {
this.angle -= this.turnSpeed * ig.system.tick;
}
else if (this.angle < this.joystickAngle - 20) {
this.angle += this.turnSpeed * ig.system.tick;
}
else {
this.angle = this.joystickAngle;
}
}
}
// THRUST
if (ig.game.joystick2.touchStart.x > ig.system.width / 2) {
if (ig.game.joystick2.delta.y <= -50) {
this.accel.x = Math.cos(this.angle*Math.PI/180)*this.thrust;
this.accel.y = (Math.sin(this.angle*Math.PI/180)*this.thrust);
this.fuel -= 0.1;
}
else if (ig.game.joystick2.delta.y >= 50) {
this.accel.x = Math.cos(this.angle*Math.PI/180)*-this.thrust;
this.accel.y = (Math.sin(this.angle*Math.PI/180)*-this.thrust);
this.fuel -= 0.1;
}
}
else {
this.accel.x = 0;
this.accel.y = 0;
}
}
As soon as I place a second finger on the screen, however, the first joystick becomes overwritten. I can rotate and then move or move and then rotate and it works fine, but I need to do both at the same time.
I found out that touchStart.x and touchStart.y seems to be being set for both joysticks when I tap to use the other stick and not just the relevant joystick1 or joystick2, even though in the plugin code those coordinates are only meant to be affected if the touch for that joystick is within the specified zone. I believe this is partly what is contributing to the issue. At this stage I've spent hours trying to figure this out and am just as lost as when I started.
Can someone possibly point me in the right direction with using both of these joysticks at the same time?
The joystick script you are using is only looking for the first finger. The following is from lines 47-48
var x = ev.touches[0].pageX - pos.left,
y = ev.touches[0].pageY - pos.top;
The '0' determines which finger to track.
The script would have the be changed to be aware which finger is on which element and then only track that finger. You could do that by either determining which element was pressed first or by location.
JSFiddle Example: http://jsfiddle.net/7WR88/5/
http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
As mentioned by #Dcullen, there may be multiple touches in each event starting at the first touch.
It would be a simple solution to iterate through the ev.touches collection and see if each touch falls into a hotzone. If it falls under hot zone 1, treat it as a touch for joystick 1. If it falls under hotzone 2, treat it as a touch for joystick 2.
This would mean that it doesn't matter in which order the touches appear, because they will always map to the correct joystick if they are near to it.

How can I improve on this JavaScript Collision Detection?

I have a test application where I am creating collison detection between to rectangles using Raphael.js.
I am able to get collison detection working properly, but I have to drag it around slooowly.... the issue arises when I move the mouse too quickly. It seems that it is not refreshing quick enough to detect the draggable rect.
The purple square is the only one that drags.
JS Fiddle
I guess my question is how can I improve detection/fix my issue?
Thanks in advance.
Since move is getting called on each pixel move, you don't have time to do much in the way of calculations to keep it smooth. First, I replaced you function for determining overlap with a more standard one:
var rect_collision = function (x1, y1, size1, x2, y2, size2) {
var a = {top: y1, bottom: y1+size1, left: x1, right: x1+size1};
var b = {top: y2, bottom: y2+size2, left: x2, right: x2+size2};
// this is the general way to figure out if two rects are overlapping
return !(a.left >= b.right || a.right <= b.left ||
a.top >= b.bottom || a.bottom <= b.top);
};
This just checks to see if one rectangle is completely to the left, right, top, or bottom of the other one. If it isn't, then they must be overlapping. Since this just gives a true or false value, I still had to figure out which side the collision occurred on.
To figure that out, I broke the collisions into two components, an x collision and a y collision by pretending that first only dx changed then only dy changed. Once I knew which direction was causing the overlap I could then use the change in direction to determine which side the overlap occurred on. For example, if x caused the collision and the previous dx was greater than the current dx then the collision was on the right side.
// check the x and y directions separately
var x_collide = rect_collision(r2_x, r2_y, rectSize, x, r1_y, rectSize);
// see if we are currently overlapping
if (!x_collide) {
// not colliding, update our x position normally
this.attr({x:x});
this.pdx = dx;
}
else {
// we are, stick the moving rect to the correct side of the stationary one
// based on the drag direction that got us stuck
this.attr({x: this.pdx > dx ? r2_x + rectSize + 1 : r2_x - rectSize - 1});
}
I then added a little bit of extra logic to match the functionality that you had which prevent the user from dragging the rectangle directly through the stationary one. Basically I ended up just seeing if the move would place the moving rectangle directly on the opposite side of the stationary one, and if so, prevent it.
I also cleaned up your checks for the border to get rid of all of the Math.min and Math.max calls since you didn't really need those. That's more of a preference thing though since I doubt there were causing much of the performance issues.
You can see the results at http://jsfiddle.net/X7H9G/3/. I'm not sure if this is the best solution, but it seems to do the job.

How to determine when an animated sprite reaches a point?

I have a 2D animation in Javascript, and I need a sprite to move from one point (x1, y1) to another point (x2, y2) on a set speed and direction. For example,
function update(speedX, speedY){
x1 += speedX;
y1 += speedY;
if("(x1, y1) reach (x2, y2)"){
// do other stuff
}
}
In most cases, speedX and speedY are not equal, nor do they factor evenly into the distance needed to travel for each axis. I calculate the speedX and speedY values using a tangent function to compute the necessary velocity for a given speed and angle.
My question is, is there an algorithm that can do this? I would prefer something efficient since this has to be done 30 times a second, and it's float addition! Thanks!
Usually (I developed some games in the past, nothing special btw) I used to see my sprites as rectangles and then I check for the overlapping of the two sprites using a very simple formula (look at this very good explanation).
Let's suppose you have to check collision between a point and a rectangle. You can apply the same formula, simplified, to check if the point stays between the rectangle area. In the case, read here.
SOLUTION:
Okay, with some work I figured it out. First, I defined a function
function getSign(val) { ... }
that simply returns -1, 0, or 1 depending on whether the number passed in is negative, 0, or positive, respectively. Then, I did the following at the beginning (before any updates happened):
var xSign = getSign(x1-x2);
var ySign = getSign(y1-y2);
Finally, to check whether or not the point is currently at or just past the target (x2, y2), just check with this code:
function update(speedX, speedY){
x1 += speedX;
x2 += speedY;
var curXsign = getSign(x1-x2);
var curYsign = getSign(y1-y2);
if( curXsign != xSign || curYsign != ySign ||
(curXsign == 0 && curYsign == 0)){
// do other stuff
}
}
Basically, if either sign of the relative vector distance between X or Y changes sign, that means that axis went past the point. If both curXsign and curYsign are 0, that means it's right on the target point. Either of these three cases mean that you are on or just went past the target point. Tested and works.
Another possible solution would be to test the distance between the start point and the current location (curDist), and compare it to the distance between the start point and the target point (dist). Once curDist >= dist, you know you are at or past the target point.
It is in the $1F port of the VIC-II:
PRINT PEEK(53279)
:-)

Canvas Game: My snake eats itself

Since my other bug got solved, I'm posting a new question for this bug.
I made a Snake canvas game, but my snake tends to eat itself when you press two buttons at the same time.. I'm not sure how to explain it properly, but this is what happens:
Say my snake is moving towards the left, and I press down + right, it'll eat itself and trigger a game over. Same when it goes to the right: down + left and bam, dead. I can't seem to reproduce the bug when the snake goes up and down though..
This is the code involved with changing directions:
bindEvents = ->
keysToDirections =
37 : LEFT
38 : UP
39 : RIGHT
40 : DOWN
$(document).keydown (e) ->
key = e.which
newDirection = keysToDirections[key]
if newDirection
setDirection newDirection
e.preventDefault()
setDirection = (newDirection) ->
# TODO: there's some bug here; try pressing two buttons at the same time..
switch Snake.direction
when UP, DOWN
allowedDirections = [LEFT, RIGHT]
when LEFT, RIGHT
allowedDirections = [UP, DOWN]
if allowedDirections.indexOf(newDirection) > -1
Snake.direction = newDirection
I thought there was something wrong with the compiled JS because my switch statement doesn't have a break on the last case; however, I tried adding else return to the coffee script file and this didn't change anything. I'm completely lost here so I hope someone will be able to spot where I'm going wrong.
It seems as if it takes the keydown events right, but they get overridden when you press too fast. If that makes sense? Like.. You'd press up when the snake is going right, but then you press left before it actually had a chance to go up and then it just goes left. Chaotic sentence right there, I suggest you play for a while and try to reproduce this if you're as intrigued as I am :(
I have no clue how to debug this properly.. The game loop tends to spam me with messages when I do console.log.
A live demo and link to my github repo can be found here
Thanks in advance.
The problem is that if you push the keys quickly enough, its possible to trigger the event callback multiple times during one frame. Thus, if the snake is going down, it can turn right and then up in the same frame, thus reversing direction and eating itself. I'll suggest two ways to solve this problem:
The first is to set a flag when the direction is changed, i.e.:
if allowedDirections.indexOf(newDirection) > -1 and !turnedThisFrame
Snake.direction = newDirection
turnedThisFrame = true
and then, in your code that runs every frame, set turnedThisFrame to false.
The second is to rework how you deal with keypresses. This is often the approach that I take. Keep a map of which keys are pressed (say, keysDown), and bind a function that sets keysDown[e.which] = true to keydown and another function which sets keysDown[e.which] = false to keyup. Then, you can check which keys are pressed in the code that runs each frame, and act accordingly.
Here's some details on how I implemented the second solution in a current project. The following code appears in my onload handler:
do ->
keysDown = []
$(document).keydown (e) ->
keysDown.push e.which
$(document).keyup (e) ->
keysDown = _.without keysDown, e.which
window.isPressed = (keyCode) -> keyCode in keysDown
The do -> construct is used to create a function and immediately calling it, which has the beneficial effect of keeping keysDown in closure for the handlers and isPressed, while avoiding polluting the main scope of the onload callback.
Then, at the beginning of my tick function (which runs once per frame and handles game logic, drawing the screen, etc.) I would have something like this:
switch Snake.direction
when UP, DOWN
allowedDirections = [LEFT, RIGHT]
when LEFT, RIGHT
allowedDirections = [UP, DOWN]
for direction in allowedDirections
if isPressed(directionToKey[direction])
Snake.direction = newDirection
break
The map directionToKey would just be the opposite of your keysToDirections.
Note that this means that keys listed first in allowedDirections will have priority, i.e. if you are going right and press both up and down in the same frame, up will occur regardless of which was pressed first.
Added advantage to this second method: you don't have to change the key handler callbacks when switching between, say, a menu screen and the game. You just have a different tick function checking for what is pressed.
You must retain the last direction is which the snake actually moved and determine allowedDirection based on that. Pressing a key only represents the intent to move in that direction, but it does not actually move when the key is pressed, but based on the speed of the game, i guess.
Your snake eating itself shows a problem with your hit detection (and hit detection handling) code. If you're hitting the snake, the game should end. A snake is not an apple! Nevermind, apparently I missed the part where the game is ending for you.
If you choose to allow pixel-granularity (I can't see your demo, my work's network is half-down..), you can't really make U turns "safe", like you could with a hex-granularity approach. Not saying your choice is bad, just telling you to pick your battles, some you just can't win.
I had the same problem, but I solved it by implementing a queue; each keypress sets the direction of the snake and that (new) direction is pushed to an array. Then, before I actually move the snake in the game loop, I check if there are any elements in my direction queue. If so, I shift it once via Array.shift. The return value is the first element of the queue and the new direction for my snake. Array.shift also removes that element from the queue, which is exactly what we want. If two keys are pressed almost simultaneously, the first and the second direction change is stored in our queue, and our aforementioned routine takes care of the rest, by first applying the first direction change in the next available tick and then applying the second direction change in the next tick thereafter.
Hope that makes sense? :-)

Categories