First of all, PacSnake is now available as an Android game in the Google Play Store. It uses the same techniques as described in this article, but has many more features, like smooth snake movement, many levels, etc. I will soon write a new article describing the snake movement and more Android specific issues that I've come across. Click here.
PacSnake is a game that is inspired by PacMan and the Snake game. The object of the game is to move the snake through the maze and eat all the food. When the snake hits his own body, it dies.
The game was very interesting to write because it uses a lot of different techniques (which are explained in more detail later).
- XML: The game uses XML to read the different levels. It also uses XML file for skins and for storing/reading user preferences.
- Graphics, obviously. It uses a backbuffer to write graphics to screen, so the screen doesn't flicker each time it's repainted.
- Object oriented: I tried to use an object oriented approach while developing the game.
- Sounds: The game uses sounds.
The game uses level sets and skins:
- Level sets: A level set consists of one or more levels. You can create you own levels, put them in a level set file and that you can play the new levels.
- Skins: You can also create your own skins and put them in the game. A skin is a collection of graphics that decide how the game looks. You can for example make a 'fire' skin or a 'metal' skin.
I'll explain each class (object) that the game uses here in detail:
Snake object is responsible for moving the snake, checking for directions and collisions and for getting the snake images.
Snake consists of an array of rectangles. Each rectangle represents a body part of the snake (head, body or tail). Each rectangle has a position and a direction. This way, it's possible for each body part to move in a different direction (when the snake turns in corners). Using rectangles, it's easy to detect is the snake bumps into itself and dies. Here's the piece of code that checks for collisions:
public bool CheckCollision()
for (int i = 1; i < length; i++)
isDead = true;
The code takes the rectangle that represents the snake's head (which is the first in the array) and checks if it overlaps with any of the other body parts. If so, game over!
This is a fairly simple class which keeps track of the current level, remembers the user's preferences like playing sounds or not, and updating/reading the setting to an XML file on disk. The last point is worth showing here:
public void ReadSavegame()
string filename = path + "\\savegame.xml";
XmlDocument doc = new XmlDocument();
skinFilename = doc.SelectSingleNode("//Skin").InnerText;
skinFilename = path + "\\skins\\ice.xml";
XmlTextWriter writer = new XmlTextWriter(filename, null);
writer.Formatting = Formatting.Indented;
writer.Indentation = 2;
XmlNode root = doc.DocumentElement;
XmlElement skin = doc.CreateElement("Skin");
skin.InnerText = path + "\\skins\\ice.xml";
First we check if a savegame exists. A savegame doesn't exist when the game is started for the first time. In this case, we create an XML file and store the users preferences. In this case, we only have one setting, but there may come more settings in the future as the game gets bigger. It uses the XML namespace, and when you examine the code you can see it's pretty straightforward.
An important part of the
Level class is drawing the level. Here's the piece of code responsible for that:
public Bitmap Draw(int levelWidth, int levelHeight, Skin skin)
img = new Bitmap(levelWidth, levelHeight);
g = Graphics.FromImage(img);
g.DrawImage(skin.Background, 0, 0);
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
Image image = GetLevelImage(itemsInLevel[i, j], skin);
if (image != null)
g.DrawImage(image, mazeOffsetX + i * 20,
mazeOffsetY + j * 20, 20, 20);
Item item = itemsInLevel[i, j];
if (item.IsCandy && !item.IsCandyEaten)
g.DrawImage(skin.Food, item.Rect.X, item.Rect.Y);
First, we draw a background, which is stored in the
Skin class. The level is stored in a two-dimensional array. This array consists of
Item objects. An
item object tells us which item of the level it holds (like a different wall or a piece of food). The array is read row by row, column by column, and for every
item a corresponding image is drawn. The maze offsets you see in the code are for drawing the level in the center of the screen in case the level is smaller than the screen size.
LevelSetInfo class holds all the information about a certain level set. A level set consists of different levels that are stored in an XML file. This way you can create your own levels by putting them in an XML file and simply copying this file in the levels directory.
LevelSet inherits from
LevelSetInfo and in this class we also add the levels. While we use the
LevelSetInfo class for displaying the level set information on screen (we don't need to know how the levels look), we use the
LevelSet for reading the levels when we actually want to play the game.
This class simply holds a collection of
The game uses skins for graphics. This means that you can create your own graphics if you wish. The
Skin class loads these graphics from the skin directory. The location of these graphic files are stored in an XML file. The class reads the location from the XML files and retrieves the graphics from disc.
Holds a collection of skins that are found in the skins directory.
This is a helper class for playing sounds. It is a class I found on the web and for more details about playing the sounds, check the website mentioned in the source of this class.
Board is the main form of the game. It draws the level, snake, etc.. It also uses a timer to move the snake and it responds to key presses. This is showed in the following method:
private void AKeyDown(object sender, KeyEventArgs e)
snake.DesiredDirection = MoveDirection.Left;
snake.DesiredDirection = MoveDirection.Right;
snake.DesiredDirection = MoveDirection.Up;
snake.DesiredDirection = MoveDirection.Down;
if (gameData.CurrentLevel < levelSet.NrOfLevelsInSet)
level = levelSet[gameData.CurrentLevel - 1];
gameData.GameStatus = GameStatus.WaitingForKey;
gameData.GameStatus = GameStatus.InProgress;
gameTicker.Interval = level.Speed;
gameTicker.Enabled = true;
mnuSkin.Enabled = false;
Before we start the game, we have to choose which level set we want to play. This is done by this form.
A simple form where we can choose which skin we want to use in the game.