Build a Classic Snake Game in AS3

In this tutorial I would like to show you how easy it is to create a classic "Snake" game in Flash. I will try to explain everything easily, step by step, so that you can develop the game further to your needs! The Game will be developed in AS3 and I will use the FlashDevelop IDE.

Introduction

The game won't be complex. Whenever we hit a wall, it will restart the game. After eating an apple the snake will grow, and a 'new' Apple will appear. (Actually, it will be the same apple, but I'll explain this later.)

One of the most important aspects of the game is the code's reaction to KEY_DOWN events. The snake will only then change its direction after a tick has passed, not immediately after a keypress. This means that, if the snake is going right, and you press down and left very fast, the snake will go down, not down AND left. Without this 'feature' the snake would allow us to go left while we are going right, which would mean it hit itself.

Let's Look at the Game Already!

Let's take a look at the final result we will be working towards:

Step 1: Creating the Project

In FlashDevelop, create a new Project, and inside the 'src' folder create a 'com' folder. In the 'com' folder create a new class, and call it 'Element.as'.

Set the dimensions of the project to 600x600px.

Step 2: Wait... What's an Element?

The snake is make up of blue squares, which I call elements. We will create an Element Class, which draws the element. The red apple is going to be an element too, so we will extend the code with a few more lines.

Therefore we won't create a new class for the apple. (But if you really want to, you can.)

Step 3: Writing the Element Class

The Element class creates a square. It doesn't draw it on the stage, it just creates it. The registration point of the element - the position referred to by its x- and y-coordinates - is in the top-left.

First we need this to extend the Shape class, so we can use the graphics object to draw the square. After this, create two variables: one for the direction (if it's part of the snake), and one for the score value (if it's an apple), and then change the parameters of the constructor function:

Now, whenever we create an element, it will draw a rectangle and set the score value of the element to 0 by default. (It won't put the rectangle on stage, it just draws it within itself. Notice that we have not called the addChild() function.)

Let's finish this class and then we can finally test how much we have done already:

First we create the testElement variable which holds our element. We create a new Element and assign that to our testElement variable. Note the arguments we passed: first we give it a color, then the alpha, width and height. If you look in the Element class's Element function, you can see how it uses this data to draw the rectangle.

After creating the Element, we position it and put it on the stage!

Step 5: Setting Up the Variables

Look at the following code. I wrote the functions of the variables next to them (notice that we imported the necessary classes too):

The most important variable is the snake_vector. We will put every Element of the snake in this Vector.

Then there is the markers_vector. We will use markers to set the direction of the snake's parts. Each object in this Vector will have a position and a type. The type will tell us whether the snake should go right, left, up, or down after 'hitting' the object. (They won't collide, only the position of the markers and the snake's parts will be checked.)

As an example, if we press DOWN, an object will be created. The x and y of this object will be the snake's head's x and y coordinates, and the type will be "Down". Whenever the position of one of the snake's Elements is the same as this object's, the snakes elements direction will be set to "Down".

Please read the comments next to the variables to understand what the other variables do!

Step 6: Writing the attachElement() Function

The attachElement() function will take four parameters: the new snake element, the x and y coordinates, and the direction of the last part of the snake.

We create the first 10 Elements, and set the direction of them to 'R' (right). If it is the first element, we call attachElement() and we change its alpha a bit (so the "head" is a slightly lighter color).

If you wish to set the position somewhere else, then please keep the following in mind: the snake has to be placed on a grid, otherwise it would look bad and would not work. If you wish to change the x and y position you can do it the following way:

Setting the x position:(snake_vector[0].width+space_value)*[UINT], where you should replace [UINT] with a positive integer.

Setting the y position:(snake_vector[0].height+space_value)*[UINT], where you should replace [UINT] with a positive integer.

Step 7: Writing the placeApple() Function

This function does the following:

It checks whether the apple was caught. For this we will use the caught parameter, and set its default value to true, in case we don't pass any value as parameters in the future. If it was caught, it adds 10 to the apple's score value (so the next apple is worth more).

After this the apple has to be repositioned (we don't create new apples) at a random grid position.

There will be some math here, but if you think it through you should understand why it is so. Just draw it out on some paper if necessary.

boundsX will hold how many elements could be drawn in one row.

randomX takes this boundsX, multiplies it with a Number between zero and one, and floors it. If boundsX is 12 and the random Number is 0.356, then floor(12*0.356) is 4, so the apple will be placed on the 4th spot on the x-grid.

boundsY will hold how many elements can be drawn in one column.

randomY takes this boundsY, multiplies it with a Number between zero and one, and floors it.

Then we set the x and y position to these numbers.

In the for loop, we check whether the apple's new x and y positions are identical to any of the snake_vectors elements. If so, we call the placeApple() function again (recursive function), and set the parameter of it to false. (Meaning that the apple was not caught, we just need to reposition it)

(apple.stage) returns true if the apple is on the stage. we use the '!' operator to invert that value, so if it is NOT on the stage, we place it there.

The last thing we need to do is call the placeApple() function at the end of the init() function.

Notice that we pass false as the parameter. It's logical, because we didn't catch the apple in the init() function yet. We will only catch it in the moveIt() function.

Now there are only three more functions to write: the directionChanged(), moveIt() and the gameOver() functions.

Step 8: Starting the moveIt() Function

The moveIt() function is responsible for all of the movement. This function will check the boundaries and check whether there is an object at the x and y position of the snake's head. It will also look for the apple at this position.

For all of this, we will use our timer variable.

Add two more lines in the end of the init() function:

timer.addEventListener(TimerEvent.TIMER,moveIt);
timer.start();

Look at the comments in the sourcecode, to see which block of code does what.

private function moveIt(e:TimerEvent):void
{
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y)
{
//This code runs if the snakes heads position and the apples position are the same
}
if (snake_vector[0].x > stage.stageWidth-snake_vector[0].width || snake_vector[0].x < 0 || snake_vector[0].y > stage.stageHeight-snake_vector[0].height || snake_vector[0].y < 0)
{
//This block runs if the snakes head is out of the stage (hitting the walls)
}
for (var i:int = 0; i < snake_vector.length; i++)
{
/*
START OF FOR BLOCK
This whole 'for' block will run as many times, as many elements the snake has.
If there are four snake parts, this whole for cycle will run four times.
*/
if (snake_vector[i] != snake_vector[0] && (snake_vector[0].x == snake_vector[i].x && snake_vector[0].y == snake_vector[i].y))
{
//If the snakes heads position is the same as any of the snake parts, this block will run (Checking the collision with itself).
}
if (markers_vector.length > 0)
{
//if there are direction markers, this code runs
}
var DIRECTION:String = snake_vector[i].direction; //getting the direction of the current snake element.
switch (DIRECTION)
{
//Sets the new position of the snakes part
}
/*
END OF FOR BLOCK
*/
}
}

Now we need to code the movement. For this we jump into the switch block, which will run on every snake part, because of the for loop.

First we need to check the direction of the current element.

switch (DIRECTION)
{
case "R" :
//Here we need to set the new x position for the current part
break;
case "L" :
//Here we need to set the new x position for the current part
break;
case "D" :
//Here we need to set the new y position for the current part
break;
case "U" :
//Here we need to set the new y position for the current part
break;
}

When the direction of the part is set to "R", for instance, we need to add something to its current X position (the space_value plus the width of the snake part).

Step 11: The directionChanged() Function

We want to listen to the keyboard, so we can actually control the snake. For this we need to put some code into the init() function and the gameOver() function.

Put this at the end of the init() function (setting up the listener function):

stage.addEventListener(KeyboardEvent.KEY_DOWN,directionChanged);

And this at the end of the gameOver() function:

stage.removeEventListener(KeyboardEvent.KEY_DOWN,directionChanged);

Now create a new function:

private function directionChanged(e:KeyboardEvent):void
{
var m:Object = new Object(); //MARKER OBJECT
//this will be added to the markers_vector, and have the properties x,y, and type
//the type property will show us the direction. if it is set to right, whenever a snake's part hits it,
//the direction of that snake's part will be set to right also
if (e.keyCode == Keyboard.LEFT && last_button_down != e.keyCode && last_button_down != Keyboard.RIGHT)
{
//If we pressed the LEFT arrow,
//and it was not the last key we pressed,
//and the last key pressed was not the RIGHT arrow either...
//Then this block of code will run
}
markers_vector.push(m); //we push the object into a vector, so we can acces to it later (in the moveIt() function)
}

Here we need another for loop, to check every snake's part against every marker on the stage, and check whether they collide. If they do, we need to set the snake's part's direction to the marker's type. If it's the last snake part which collides with the marker, we need to remove the marker from the markers_vector, too, so the snake parts don't collide with it any more.

Now if you play with it it looks okay, but there is a bug in there. Remember what i said at the beginning of the tutorial?

For instance, if the snake is going to the right and you press the down-left combo very fast, it will hit itself and restart the game.

How do we correct this? Well it's easy. We have our flag variable, and we will use that for this. We will only be able to change the directions of the snake when this is set to true (Default is false, check the init() function for that).

So we need to change the directionChanged() function a little. The if blocks' heads should be changed: add a && flag clause at the end of every 'if'.

Step 13: Summing It All Up

Congratulations! You have just created a nice game. Now you can develop it further, and create a super apple or something. For that I recommend using another function called placeSuperApple() and a new class named SuperApple. Whenever you catch a super apple, the snakes parts could lengthen by three elements, perhaps. This could be set with setters/getters in the SuperApple class.

If you wish to do this, and you get stuck somewhere, just leave me a comment here.