Lincoln-B

Software developer and writer

Making a Snake game with Phaser

September 18th, 2017

Don’t want to read? Play the game online or view the source on GitHub.


I remember a lot of lunch periods in junior high playing games on MiniClip and Kongregate with my friends. Browser games were the best thing ever back then — I wasn’t allowed more than twenty minutes of computer time at home during the school year, so it was a nice escape from reality without having to download anything.

Sadly, Flash’s death has been announced and with it, I’m sure, a whole category of browser games. But it’s certainly not the end of browser gaming — not even close.

There have been some really cool games I’ve seen in Unity Web Player (which is probably the best choice for anything 3D) and its honestly a great tool. Unity development is a good skill to have anyway, especially now that its complicated enough to be an employable skill. You can make native desktop applications, iOS/Android games, and even VR stuff for the forward-thinkers.

But anyway, UWP aside, I’m convinced that if you want to make 2D arcade-style games, either professionally or for your own projects, JavaScript is the best choice. It’s familiar to most developers, easy to learn for those who don’t already use it, and doesn’t involve downloading any browser plugins.

So let’s dive into what it actually looks like to make one!


The Framework Situation

I’ve flirted briefly with making JS games from scratch (see this short-lived experiment) and it’s really not too bad. I cut my teeth writing games for the TI-84+ — no game framework, no object oriented anything. So I won’t go so far as to say you need a framework. In fact, it may be a good exercise for you as a programmer to make something without any dependencies. It’s a liberating feeling.

For the interested, all it takes to get started in plain JS is a basic concept of how to draw something on the canvas, how to get player input, and maybe how to stabilize the frame rate with a window.setInterval() function.

But having a good game development framework makes a world of difference, especially if you care about backwards compatibility, extensibility with other game development plugins, constant frame rate, and other important but more complicated stuff. I haven’t looked at the different options — if you know of any that are good, feel free to give a shout-out in the comments.

Phaser was the first one I came across and it seems to be the most widely used, so that’s good enough for me.

I’d recommend going through the Phaser tutorials if you’re really interested in learning how to use Phaser. This post will just be a quick overview of making a simple game, plus some of my commentary.


The Hardest Part

By far the hardest part of game development, at least for someone like me, is getting good graphics to use in my games. I don’t need professional quality sprites or anything — this is just a hobby project — I just need something to cover the fact that I’m an absolutely horrible artist.

This is where amazing sites like Open Game Art come in. I’ve heard that /r/gameassets is a good one too. I don’t know who these people are, or why they’re willing to give away their hard work for free, but I definitely appreciate it.

So for my snake game, I started out with this animal pack. I wasn’t sure if it would look weird to use an elephant’s head as every segment of a snake, but I figured at least it would be an interesting and unique take on a classic game.

I suppose the second hardest part is actually deciding what game to make, but in my case, I just wanted something simple. Snake it is.


First Steps

My first task was to get something on the screen moving around. In Phaser, that looks something like this:


window.onload = function() {

    var canvasWidth = 832, canvasHeight = 640;

    var game = new Phaser.Game(canvasWidth, canvasHeight, Phaser.AUTO, '', { preload: preload, create: create, update: update });

    function preload() {}

    function create() {}

    function update() {}

};

This is a pretty straightforward Singleton pattern. Phaser gives us a game object to which we add images/controls/physics/etc. and it worries about rendering and collisions. Game logic goes in the corresponding functions — preload() handles loading assets, create() handles world initialization, and update() runs every frame, handling game logic. I know there’s a more complicated way of doing this involving states, which would be much more clean and organized, I think. This project ended up being almost 200 lines of JavaScript, which is probably about as much as you would comfortably want in a preload/create/update application.

So if you put the above in a game.js file, link it in your index.html file, and start your web server, it will append a canvas element to the body tag. So far it doesn’t show anything but a black screen.

Putting something on the screen is equally straightforward. Put this in the preload function:


game.load.image('elephant', 'elephant.png');
game.load.image('apple', 'apple.png');

And this in the create function:


game.add.image(0, 0, 'elephant');

And voila, you have an elephant in the top left corner!

Phaser makes this really easy, and I like that.


Following Along

If you’re following along, this is the JavaScript file you need to copy.

You can see in that file, I use this object literal:


var directions = Object.freeze({up: 0, down: 1, right: 2, left: 3});

to represent the different directions the snake can go. The numbers are arbitrary; I was just trying to get the functionality of an enum.

All the var declarations are at the top of the file because of JavaScript’s variable hoisting (plus I think it’s a little bit easier to understand).

The next interesting thing is the gameText declaration. I set the anchor to (1, 0) to make the position variables correspond to the top-right corner of the text. I’m not exactly sure why (1, 0) does that; I just experimented with a couple different values and it works the way I like.

The cursors var (line 33) is also a singleton; I use it later to check which of the arrow keys is down.

Now skip to line 141, for the movement functions. There’s a playerDirection variable that gets updated every frame based on which arrow key the player is pressing. Then I use that variable in the movePlayer function to update the player’s position. When the snake travels off the screen, instead of dying, the player wraps to the opposite side.

Notice the code that actually makes the snake move:


newHead(x, y);


Making the Snake

To represent the snake, I chose to use a linked list to avoid copying coordinates in a fixed array. A linked list is essentially a list of nodes that point to each other with a “next” property. Here’s some more information on linked lists. (I use a singly linked list.)

Start on line 68 at the initSnake() function. I use the global variables tail and head to keep track of the first and last segments of the snake, respectively. The initSnake() function references the newHead() function on line 96.

This code is worth explaining:

function newHead(x, y) {
    var newHead = new Object();
    newHead.image = game.add.image(x, y, 'elephant');
    newHead.next = null;
    head.next = newHead;
    head = newHead;
}

A new object is created to represent the newest “head” of the list (think the “head” of the snake, or the last element of the list). newHead.next points to null because it’s the end of the list. The previous head now points to this new head, and the global var is updated.

This all works because of the way JavaScript handles object assignment — by reference, like Java, rather than by making a copy, like C++.

The corresponding function, removeTail(), destroys the image and updates the global tail variable. I trust the JavaScript garbage collector to clean up those loose tail objects in a timely fashion.

deleteSnake() on line 77 simply iterates through the list and destroys the images as it goes, then resets both global variables to a null reference.


Collisions, Apples, Scoring, Speed-up, Etc.

As for collisions, there is probably an easier way to do it within Phaser’s system with groups or something, but I couldn’t figure out how.

My method was simply to iterate through the list and check 1) if the head of the snake is at the same position as any of the other segments, and 2) if the head of the snake is at the same position as an apple. Those functions are on lines 127 and 111, respectively.

When placing an apple, on line 85, I repeatedly assign the apple a random position on the board until it is not colliding with the snake.

And that covers all the functions!

Now, what brings it all together, the update function, is a bit of a mess. I use a frameCounter variable to control the speed, starting out by only moving the snake one square every 20 frames. Then, each time the snake eats an apple, that number goes down by one. Lines 42-52 in the update function deal with resetting the game, which turned out to be not as easy as I thought. It’d be nice if Phaser provided some kind of restart() function on the game object.

Growing the snake by one every apple is really easy — just don’t call the removeTail() function on the frames where the snake collides with an apple.


Conclusion

There are some obvious limitations/areas for improvement with how I chose to do this:

  1. Choppy snake movement
  2. Using the same sprite for the head and the snake segments
  3. Using an elephant head, of all things, for each segment of the snake
  4. There’s a weird bug at the start of the game where if you’re going right, and you press down and right really quickly, the snake thinks it ran into itself. I didn’t see an easy way to do this without complicating the code.

So if you take this game as a starting point, there’s some things you can do to make it better.

The game is also missing a menu and a long-term high score, maybe using localStorage, but I don’t think that would have been possible without using Phaser’s states system.

Overall, I enjoyed using Phaser quite a bit! I plan to make more games in the future — maybe I’ll do the helicopter game next.

css.php