Click here to Skip to main content
11,711,750 members (79,870 online)
Click here to Skip to main content

PacSnake

, 17 Jun 2005 CPOL 84K 3.5K 75
Rate this:
Please Sign up or sign in to vote.
PacSnake is a mix of the classic PacMan game and the classic Snake game.

Sample Image - PacSnake.gif

Introduction


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.

The Classes

I'll explain each class (object) that the game uses here in detail:

Snake

The Snake object is responsible for moving the snake, checking for directions and collisions and for getting the snake images.

The 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:

/// <summary>
/// Check if the head of the snake crashed into a body part
/// </summary>
/// <returns>True if snake bumps into itself, otherwise false</returns>
public bool CheckCollision()
{
    for (int i = 1; i < length; i++)
        if (body[i].IntersectsWith(body[0]))
        {
            isDead = true;
            return true;
        }

    return false;
}

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!

GameData

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:

/// <summary>
/// Reads the game variables from the savegame. If there's no savegame
/// we use default values.
/// </summary>
public void ReadSavegame()
{
    string filename = path + "\\savegame.xml";
    XmlDocument doc = new XmlDocument();

    if (File.Exists(filename))
    {
        doc.Load(filename);
        skinFilename = doc.SelectSingleNode("//Skin").InnerText;
    }
    else
    {
        skinFilename = path + "\\skins\\ice.xml";

        // Create new file savegame.xml
        XmlTextWriter writer = new XmlTextWriter(filename, null);
        writer.Formatting = Formatting.Indented;
        writer.Indentation = 2;
        writer.WriteProcessingInstruction("xml",
            "version='1.0' encoding='ISO-8859-1'");
        writer.WriteStartElement("savegame");
        writer.Close();

        // Load XML document and create all elements
        doc.Load(filename);
        XmlNode root = doc.DocumentElement;
        XmlElement skin = doc.CreateElement("Skin");
        skin.InnerText = path + "\\skins\\ice.xml";

        // Add all elements to the root element
        root.AppendChild(skin);

        // Save file
        doc.Save(filename);
    }
}

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.

Level

An important part of the Level class is drawing the level. Here's the piece of code responsible for that:

/// <summary>
/// This method draws the level. It takes each item in the level and
/// draws the correspoding image.
/// </summary>
public Bitmap Draw(int levelWidth, int levelHeight, Skin skin)
{
    img = new Bitmap(levelWidth, levelHeight);
    g = Graphics.FromImage(img);

    g.DrawImage(skin.Background, 0, 0);

    // Draw the level
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            Image image = GetLevelImage(itemsInLevel[i, j], skin);

            // No free space
            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);
        }
    }

    return img;
}

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

The 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

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.

LevelSetInfoCollection

This class simply holds a collection of LevelSetInfo objects.

Skin

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.

SkinCollection

Holds a collection of skins that are found in the skins directory.

WinMM

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

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:

/// <summary>
/// Handles the key presses. When the game is running it controls the
/// snake. When the game is not running it controls the other keys to
/// start the game, etc...
/// </summary>
private void AKeyDown(object sender, KeyEventArgs e)
{
    switch (gameData.GameStatus)
    {
        // If the game is in progress, we control the snake
        case GameStatus.InProgress:
            switch (e.KeyData.ToString())
            {
                case "Left":
                    snake.DesiredDirection = MoveDirection.Left;
                    break;
                case "Right":
                    snake.DesiredDirection = MoveDirection.Right;
                    break;
                case "Up":
                    snake.DesiredDirection = MoveDirection.Up;
                    break;
                case "Down":
                    snake.DesiredDirection = MoveDirection.Down;
                    break;
                default:
                    // Don't respond on other key presses
                    break;
            }
            break;
        // If the snake has died we press a random key to restart
        case GameStatus.SnakeDied:
            InitializeGame();
            break;
        // If the level is finished we press a key to start next
        case GameStatus.LevelFinished:
            // If this wasn't the last level, start next level
            if (gameData.CurrentLevel < levelSet.NrOfLevelsInSet)
            {
                gameData.CurrentLevel++;
                level = levelSet[gameData.CurrentLevel - 1];
                InitializeGame();
            }
            else
                gameData.GameStatus = GameStatus.WaitingForKey;

            break;
        // Level is ready to start, let's wait for a key
        case GameStatus.WaitingForKey:
            gameData.GameStatus = GameStatus.InProgress;
            gameTicker.Interval = level.Speed;
            gameTicker.Enabled = true;
            mnuSkin.Enabled = false;
            break;
    }
}

FormLevelSet

Before we start the game, we have to choose which level set we want to play. This is done by this form.

FormSkins

A simple form where we can choose which skin we want to use in the game.

Enjoy!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

JasperB
Web Developer
Netherlands Netherlands
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionJust Say Pin
Adhi Ahmad M9-May-14 9:55
memberAdhi Ahmad M9-May-14 9:55 
QuestionHello Pin
zahra199122-Jan-12 3:33
memberzahra199122-Jan-12 3:33 
GeneralIt isn't new... Pin
Igen111-Oct-08 9:43
memberIgen111-Oct-08 9:43 
GeneralNice job...and addictive Pin
Bert delaVega15-Apr-08 7:27
memberBert delaVega15-Apr-08 7:27 
GeneralQuestions regarding pac snake game Pin
admirable28-Aug-06 21:41
memberadmirable28-Aug-06 21:41 
GeneralRe: Questions regarding pac snake game Pin
JasperB29-Aug-06 22:49
memberJasperB29-Aug-06 22:49 
GeneralHelp!!! Pin
Deadshadow5-Jul-06 20:11
memberDeadshadow5-Jul-06 20:11 
GeneralWebsite menu item causes exception Pin
Ashley van Gerven18-Jun-05 15:41
memberAshley van Gerven18-Jun-05 15:41 
GeneralRe: Website menu item causes exception Pin
JasperB18-Jun-05 22:43
memberJasperB18-Jun-05 22:43 
GeneralRe: Website menu item causes exception Pin
Ahmed.mb19-Aug-06 0:05
memberAhmed.mb19-Aug-06 0:05 
GeneralVery interesting concept Pin
Mike Ellison18-Jun-05 6:35
memberMike Ellison18-Jun-05 6:35 
GeneralRe: Very interesting concept Pin
JasperB18-Jun-05 22:47
memberJasperB18-Jun-05 22:47 
GeneralGreat concept! : ) Pin
Jakob Lund Krarup18-Jun-05 4:08
memberJakob Lund Krarup18-Jun-05 4:08 
GeneralSystem.IO.IOException Pin
reinux17-Jun-05 22:37
memberreinux17-Jun-05 22:37 
GeneralRe: System.IO.IOException Pin
JasperB18-Jun-05 1:35
memberJasperB18-Jun-05 1:35 
GeneralRe: System.IO.IOException Pin
reinux18-Jun-05 1:44
memberreinux18-Jun-05 1:44 
GeneralRe: System.IO.IOException Pin
JasperB18-Jun-05 2:45
memberJasperB18-Jun-05 2:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150819.1 | Last Updated 17 Jun 2005
Article Copyright 2005 by JasperB
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid