Making Snake

I’ve been working on some fun little p5.js sketches recently and wanted somewhere to host them. And so this: InsomniacPhysicist’s code-y compSci-y sister site. Everything’s still under construction, and I’m mostly just using this post to figure out how it’s all going to work. If you want to entertain yourself in the meantime, you can play some snake or minesweeper.

I want my sketch.js to look something like this

We’re going to need a snake class to achieve this

Nailed it. Snake is played on a grid (which I’d like to be visible), so a reasonable place to start is to define the grid size and draw the background. We add this.gridSize = 20; to the constructor, and call this.drawBoard(); in show()

Next, our snake is going to need some food. I’m going to use a P5 vector to store its position, write a function to randomize its position on the board (for when it gets eaten), and add a couple of lines to draw() to place it as a white rectangle. Click the bar to see the full code.

So now we’re here.

This looks good, but we still don’t have a snake! We need a vector for the head, an array for the tail, and an integer length. We also need a vector for its current velocity. Then, we need a function to give all that good stuff some reasonable initial values:

For movement this was the simplest solution I could come up with for having the tail “follow” the head:

  1. Destroy last tail segment
  2. Add the head to the front of the tail
  3. Move the head one square in the direction of its velocity

Next we need a way to control the snake. I decided to use a switch, like this:

And then in my sketch.js file I simply need to call this whenever a key is pushed

However, this implementation has an issue: if we’re going left and hit RIGHT_ARROW, our snake is going to turn 180 degrees and die. Adding an if statement to check for this partially solves the problem, but if the user inputs two commands during a single frame, they can still turn the snake 180 degrees. The way I chose to fix this was an “acceleration” vector; essentially storing the user’s command and then using it to update the velocity once per frame (an equivalent method would have been some sort of “allowInput” flag, that gets set to false when the velocity is changed).
So, the keyPressed switch now looks like this

And the update function pushes the “acceleration” to the snake’s velocity once per frame

Since the snake’s velocity is tied to how often we update, I set the frame rate to 15 in sketch.js’s setup() function to get a reasonable speed.

Woohoo! Moving, controllable snake. Now we need collision.

There’s two types of collision event we need to handle – the snake’s death (by hitting the wall or itself), and the snake eating the food and growing longer. We’ll start with the latter, since it’s considerably more straightforward.

Here, we just check if the head’s position vector is the same as the food’s position vector, and if it is, we increment the length, add a tail segment (by duplicating one of the existing ones; the update function will set its position the next frame anyway), and randomize the food’s position. Easy peasy. To kill the snake, we need to reset its length and randomize its position (I’ve changed the starting length to 8 in order to make it easier to test collision; hitting yourself with a length 5 snake is extremely difficult).

Now we need to check if the snake has it itself or a wall, and call reset if it has.

Now all that’s left to do is call eat() and collision() in the update function and we’re done!

There are a few things I still want to add to this: first, considering the size of the play area the game is much too easy – I think it’d be fun to add a set of increasingly difficult obstacles like the original snake campaign mode. Secondly, particularly if I’m making the game harder, I’d like to make the controls more responsive. Currently running at 15 fps is necessary so the snake moves at a reasonable speed, but I’d like to decouple its speed from the framerate so I can run the game at 30 or 60fps instead. This would allow the snake’s speed to increase with increasing length, and also make the controls feel much more responsive. I’ll tackle these in a future post.
Thanks for reading!


Leave a Reply

Your email address will not be published. Required fields are marked *