Canvas Game: My Serpent Eats Itself

Since my other error was resolved, I am posting a new question for this error.

I made a game with Snake canvas, but my snake usually eats itself when you press two buttons at the same time. I'm not sure how to explain it correctly, but here's what happens:

Say that my snake moves to the left, and I push down + to the right, it eats itself and starts the game. The same thing when it goes to the right: down + left and bam, dead. I cannot reproduce the error when the snake goes up and down, though ..

This is the code associated 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 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 something was wrong with compiled JS, because my switch statement does not have a break in the last case ; however, I tried adding else return to the coffee script file, and that didn't change anything. I am completely lost here, so I hope someone can figure out where I am going wrong.

It seems like it is accepting keydown events correctly, but they become overridden when you click too fast. If that makes sense? Like .. You would push up when the snake goes to the right, but then you press left before there really is a chance to rise, and then it just leaves. A chaotic sentence right there, I suggest you play for a while and try to reproduce it if you are as intrigued as I am: (

I do not know how to properly debug this. The game loop tends to spam me with messages when I do console.log .

Below is a live demo and a link to my github repository here

Thanks in advance.

+7
source share
4 answers

The problem is that if you press the keys fast enough, you can trigger a multiple call of the event within one frame. Thus, if the snake goes down, it can turn to the right, and then up into the same frame, thereby changing direction and eating itself. I suggest two ways to solve this problem:

The first is to set the flag when changing direction, that is:

 if allowedDirections.indexOf(newDirection) > -1 and !turnedThisFrame Snake.direction = newDirection turnedThisFrame = true 

and then in your code that fires each frame, set turnedThisFrame to false .

Secondly, rework how you deal with keystrokes. This is often the approach that I take. Save the map that the keys are pressed on (say keysDown ), and attach a function that sets keysDown[e.which] = true to keydown and another function that sets keysDown[e.which] = false to keyup. Then you can check which keys are pressed in the code that launches each frame and act accordingly.

Here are some details about how I implemented the second solution in the 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 do -> used to create a function and call it immediately, which has a beneficial effect on keeping keysDown in closure for handlers and isPressed , while avoiding contamination of the main area onload Callback.

Then, at the beginning of my tick function (which runs once for each frame and processes the game logic, draws a 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 directionToKey map will be just the opposite of your keysToDirections .

Note that this means that the keys listed first in allowedDirections will take precedence, i.e. if you go right and press both up and down in the same frame, up will happen no matter what the first press.

An added benefit to this second method: you don’t need to change the key handler callbacks when switching between, say, a menu screen and a game. You have another tick function that checks that it is pressed.

+6
source

You must keep the last direction that the snake actually moved, and based on this, determine the permitted direction. Pressing a key represents the intention to move in that direction, but in fact it does not move when the key is pressed, but based on the speed of the game, I think.

+1
source

You have a snake that shows a problem with your detection detection code and hit detection code. If you hit the snake, the game should end. A snake is not an apple! Nevermind, apparently I missed the part where the game ends for you.

If you decide to enable pixel granularity (I don’t see your demo version, my work network is half down), you cannot force U to become β€œsafe”, as you could using the hex-granular approach. Do not say that your choice is bad, just tell you that you choose your battles, some of you simply can not win.

0
source

I had the same problem, but I solved it by completing the queue; each keystroke sets the snake direction and the direction (new) is sent to the array. Then, before I actually move the snake into the game loop, I will check to see if there are any items in my queue. If so, I change it once through Array.shift. The return value is the first element of the queue and a new direction for my snake. Array.shift also removes this element from the queue, which is what we want. If two keys are pressed almost simultaneously, the first and second direction changes are saved in our queue, and our above procedure will take care of the rest by first applying the first direction change in the next available tick, and then applying the second direction change to the next tick after that.

Hope this makes sense? :-)

0
source

All Articles