Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Pocket 1945 - A C# .NET CF Shooter

0.00/5 (No votes)
2 Jun 2004 1  
An article on Pocket PC game development.

Introduction

Pocket 1945 is a classic shooter inspired by the classic 1942 game. The game is written in C# targeting the .NET Compact Framework. This article is my first submission to Code Project and is my contribution to ongoing .NET CF competition.

As well as being my first article this is also my first game ever. My every day work consists of building data centric business applications, so game writing is something completely different. So, go easy on me.

One of my goals when starting this project was to make a game that other developers could use as a starting point when getting into C# game development. I focused on keeping the code as clear and simple as possible. I also wanted to build this game without introducing any third party components such as the Game Application Interface (GAPI). The reason I did this was that I wanted to see what I could do with the core framework. Another goal was to take this game/example a step further than most tic-tac-toe examples and actually build a game that’s fun, challenging and looks good.

One of the things I realized when working on this project is that games take time, no matter how simple they are. The game is still not at version 1.0, but it is playable in it's current state. I’ve put the game up as a GotDotNet workspace and I encourage everyone that finds the game fun to join the workspace and help me build a fun shooter for the Pocket PC platform.

How to install/play Pocket 1945

In order to play you need a Pocket PC enabled device with the .NET Compact Framework 1.1 installed. To install simply copy the Pocket1945.exe file and the level XML files to a new folder on your device. No installation is required.

To play the game, you use the direction keys on your device. To exit, click the calendar button (first hardware button). To fire, click the second hardware button. Since I don’t own a real device I’m not sure what the “name” of these buttons are. But, just give it a go!

The current game is far from “finished”, but it is safe to run the code and it is playable. The game consists of 4 levels. To add levels of your own, simply make new level XML files and copy them to the game folder on your device. Since I don’t have a level editor yet I would suggest that you build your new level based on the existing one. If you make any fun levels, place share them with us.

Game design

The game consists of one Visual Studio .NET solution called Pocket1945. The project contains 13 classes, 3 interfaces, 1 structure and 7 enumerators. I’ve supplied a screenshot of the class view in VS.NET to illustrate the class design of the game.

The GameForm class is the main class of the application. This class takes care of drawing and running the actual game. The Level class loads a level XML file and parses the XML out to objects. The Background class draws the map and background elements to the screen. The Player class defines the player character in the game. The Enemy class defines all enemy planes used in the game. The Bonus class is used for bonus elements such as an extra life or shield upgrade. The LevelGui class is used to draw a simple in-game user interface. This class is used to display information about health, progress, score and such to the player.

The Input and StopWatch class are taken from one of the MSND articles and gives you access to detected hardware buttons and a high performance counter. These classes are exactly the same as in the MSDN examples.

The IArmed interface is implemented on game objects that can fire bullets and get hit by other bullets. The ICollidable interface is implemented by items that can collide, such as bonus items. The IDrawable interface is implemented by all objects that can be drawn to the screen during the game.

The different enums are used for properties such as type of bonus, weapon, enemy, movement and so on.

Level design

Each level is a XML file containing an ASCII table with the level map and XML nodes for background elements (such as islands), enemies and bonus elements. The Level class takes a path in the constructor and loads the XML file passed into the constructor and builds objects based on the nodes.

The ASCII table contains a table with 8 columns and an unknown number of rows. Each character in the table represents a 32x32 pixel background tile. So if the ASCII table is 56 rows high and 8 columns wide the background size will be 1792x256 pixels. The level file also has a setting called speed which sets the speed in pixels per second. An average map is 1800 pixels high and scroll at 15 pixels per second giving you approximately 2 minutes of game play. An example of the ASCII map:

<Map>
 <![CDATA[AAAAAAAA
 BBBBBBBB
 AAAAAAAA
 CCCCCCCC
 CCCCCCCC
 AAAAAAAA
 BBBBBBBB
 BBBCCCBB
 ABCDABCD]]>
</Map>

The enemy nodes contains all the settings for each enemy. The Y attribute tells the game engine when the enemy comes into play. So, for instance an enemy with Y=1500 starts to move when the player have scrolled to point 1500 on the map. Both enemies and bonus elements as positioned this way. An example enemy node looks something like this:

<Enemy X="140" Y="1700" Speed="80" MovePattern="1" 
  EnemyType="1" BulletType="1" BulletPower="5" BulletSpeed="150" 
  BulletReloadTime="1000" Power="10" Score="100" />
  • X = the horizontal start position for this enemy.
  • Y = the vertical start position for this enemy (when it gets focus).
  • Speed = the speed the enemy moves at (pixels pr second).
  • MovePattern = the way the enemy moves. At the moment straight ahead is the only pattern supported. I’ll add patterns like zigzag, swipe, kamikaze and simple AI. The MovePattern enum defines the different move patterns.
  • EnemyType = the type of enemy. There are currently 6 supported enemies. The EnemyType enum defines the different types of enemies.
  • BulletType = the type of bulled fired by this enemy. The BulletType enum defines the different types of bullets.
  • BulletPower = the power of the bullets fired by this enemy. This indicates how damaged the player gets by a hit.
  • BulletSpeed = the speed the bullet is traveling at (pixels per second).
  • BulletReloadTime = how long it takes for the enemy to reload (milliseconds).
  • Power = how thick the enemy shield is (how hard it is to kill).
  • Score = the score you collect by killing this enemy.

As you probably can guess I’m planning on writing a XML based level designer to build new levels for the game. I’m also thinking about making a XML Web Service based game server where you can upload and download new level sets. The XML format also needs to be formalized by making schemas. This is all on the TODO list.

Bonus elements are implemented almost the same way as enemies so I won’t go into details on the bonus nodes. An example level file can be downloaded here: Level1.xml(zipped)

Points of interests – Sprite list

One of the things that might be useful to look at is how I’ve implemented sprites. All the game graphics are embedded bitmap resources. At first I only used one single bitmap with all sprites, but I soon realized this would make the file hard to maintain and adding new sprites would cause problems with sprite indexes. I spited the image into logic sections like bullets, enemies, player, tiles, and bonuses.

All sprites are managed by the SpriteList class. The class implements the singleton pattern to ensure that there is only one instance of this class trough out the game. The class consists of one public method called LoadSprites() and several public Bitmap arrays holding each sprite. The LoadSprites() method reads the embedded resources and call a private method called ParseSpriteStrip() that reads a sprite strip (one large bmp with several sprite on it) and splits it into a Bitmap array. Each game object (like a bonus item, a bullet or an enemy) draws it self by reading a bitmap from one of the public Bitmap arrays.

By handling sprites this way you have a consistent way to access your graphical resources. By making the class a singleton you can be sure there is only one instance of the class trough out the application. All loading is done on game initialization making this a fast way to read sprites.

The following code shows the LoadSprites() method and the ParseSpriteStrip() method.

/// <summary>
/// Metod loading the sprites from the assembly resource files
/// into the public bitmap array. To be sure the sprites are only loaded
/// once a private bool is set to true/false indicating if the sprites
/// have been loaded or not.
/// </summary>
public void LoadSprites()
{
 if(!doneLoading)
 {    
  //Accessing the executing assembly to read embeded resources.
  Assembly asm = Assembly.GetExecutingAssembly();
  
  //Reads the sprite strip containing the sprites you want to "parse".
  Bitmap tiles = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.Tiles.bmp"));
  Bitmap bonuses = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.Bonuses.bmp"));
  Bitmap bullets = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.Bullets.bmp"));
  Bitmap smallPlanes = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.SmallPlanes.bmp"));
  Bitmap smallExplotion = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.SmallExplotion.bmp"));
  Bitmap bigBackgroundElements = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.BigBackgroundElements.bmp"));
  Bitmap bigExplotion = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.BigExplotion.bmp"));
  Bitmap bigPlanes = new Bitmap(asm.GetManifestResourceStream(
   "Pocket1945.Data.Sprites.BigPlanes.bmp"));

  //Parse the sprite strips into bitmap arrays.
  Tiles = ParseSpriteStrip(tiles);
  Bullets = ParseSpriteStrip(bullets);
  Bonuses = ParseSpriteStrip(bonuses);
  SmallPlanes = ParseSpriteStrip(smallPlanes);
  SmallExplotion = ParseSpriteStrip(smallExplotion);
  BigBackgroundElements = ParseSpriteStrip(bigBackgroundElements);
  BigExplotion = ParseSpriteStrip(bigExplotion);
  BigPlanes = ParseSpriteStrip(bigPlanes);

  //Clean up.
  tiles.Dispose();
  bullets.Dispose();
  bonuses.Dispose();
  smallPlanes.Dispose();
  smallExplotion.Dispose();
  bigBackgroundElements.Dispose();
  bigExplotion.Dispose();
  bigPlanes.Dispose();

  doneLoading = true;
 }
}

/// <summary>
/// Method parsing a sprite strip into a bitmap array.
/// </summary>
/// <param name="destinationArray">
/// The destination array for the sprites.</param>
/// <param name="spriteStrip">The sprite strip to 
/// read the sprites from.</param>
private Bitmap[] ParseSpriteStrip(Bitmap spriteStrip)
{       
 Rectangle spriteRectangle = new Rectangle(1, 1, 
   spriteStrip.Height - 2, spriteStrip.Height - 2);
 Bitmap[] destinationArray = new Bitmap[(spriteStrip.Width - 1) 
   / (spriteStrip.Height - 1)];

 //Loop drawing the sprites into the bitmap array.    
 for(int i = 0; i < destinationArray.Length; ++i)
 {
  destinationArray[i] = new Bitmap(spriteRectangle.Width, spriteRectangle.Height);
  Graphics g = Graphics.FromImage(destinationArray[i]);
  spriteRectangle.X = i * (spriteRectangle.Width + 2) - (i - 1);    
  g.DrawImage(spriteStrip, 0, 0, spriteRectangle, GraphicsUnit.Pixel);    
  g.Dispose();
 }

 return destinationArray;
}

Points of interests – Double buffering

Another thing worth mentioning is how I draw each game frame. I’m using a common technique called double buffering. Basically what this mean is that I draw the entire frame in memory before moving it onto the screen. By doing this I avoid unwanted flickering. I don’t own a real pocket pc, but I’ve been told that the game performs really well on them. I’m hoping to win a Pocket PC so that I can test this for my self.

The GameForm class (the main class of the game) has three private fields used for drawing:

private Bitmap offScreenBitmap;
private Graphics offScreenGraphics; 
private Graphcis onScreenGraphics;

The offScreenBitmap is the bitmap used to hold the in-memory version of each game frame. The offScreenGraphics is a Graphics object used to draw to the in-memory bitmap. onScreenGraphics is a Graphics object used to draw the in-memory bitmap onto the screen at the end of each game loop. All game elements that can be drawn implements the IDrawable interface which has one method called Draw(Graphics g), which is used to draw itself onto the game form. In the game loop you call player.Draw(offScreenGraphic) to make the player draw itself onto the off screen bitmap. Here is an example of the level loop showing how you pass the offScreenGraphcis object to game object and move the offScreenBitmap onto the screen at the end of the loop:

private void DoLevel(string filename)
{ 
 CurrentLevel = new Level(GetFullPath(filename));
 StopWatch sw = new StopWatch();
 bool levelCompleted = false;
 bool displayMenu = false;

 while((playing) && (!levelCompleted))
 {
  // Store the tick at which this frame started
  Int64 startTick = sw.CurrentTick();    
  input.Update();  

  //Update the rownumber.
  TickCount++;    
     
  //Draw the background map.     
  CurrentLevel.BackgroundMap.Draw(offScreenGraphics);    

  //Update bullets, enemies and bonuses.
  HandleBonuses();
  HandleBullets();    
  HandleEnemies();    

  //Update and draw the player.
  Player.Update(input); 
  Player.Draw(offScreenGraphics);
  playing = (Player.Status != PlayerStatus.Dead);

  //Draw in-game user interface
  levelGui.Draw(offScreenGraphics);
     
  //Move the offScreenBitmap buffer to the screen.
  onScreenGraphics.DrawImage(offScreenBitmap, 0, 0, 
   this.ClientRectangle, GraphicsUnit.Pixel);    
  
  //Process all events in the event que.
  Application.DoEvents();    
 }   
}

TODO

There are tons of things that need to be done before this can be considered a “real” fun and exiting game. But, we’re getting there. I won’t go into details of everything that needs to be done, but I’ll add some important points:

  • A good level editor.
  • A set of XML Web Services to upload and downloads levels and post scores.
  • New move patterns (how the enemies move).
  • A game GUI (main menu, title screen, high score list etc).
  • XML Schemas defining the rules for the level files.
  • Better designed levels that are well balanced and challenging.
  • Bosses. We need big bad bosses.
  • Much more.

Any suggestions are greatly appreciated, either here on Code Project or on the workspace site.

Resources

I’ve used several online resources when building this game. First of all I have to credit Ari Feldman for the great graphics I’ve used in the game. Ari has published several sprite sets on his website under the SpriteLib GPL foil. The sprites can be found on http://www.arifeldman.com/games/spritelib.html.

I would also like to mention everyone on #ms.net on EFNet. Special thanks to ^CareBear for instant feedback on how the game is performing on a real device.

Other resources used are series of game articles published on MSDN:

Closing comment

There are still several things I’d like to mention, but in order to get this article/game submitted in time to be a part of the competition I really need to finish it up now.

Part II of this article will be available in X days/months/years/or maybe never. The game is far from finished, but is playable in it's current state. I hope you download it and give it a go. If you find the project fun and promising I would encourage you to join the workspace up on http://workspaces.gotdotnet.com/pocket1945 and take part of the on-going development of this game. The workspace will also be the place to get your hands on the latest releases of the game.

All comments on this article and the game in general are greatly appreciated.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here