|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionPocket 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 1945In 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 designThe 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 The Input and The The different enums are used for properties such as type of bonus, weapon, enemy, movement and so on. Level designEach 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" />
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 listOne 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 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 /// <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 bufferingAnother 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 private Bitmap offScreenBitmap;
private Graphics offScreenGraphics;
private Graphcis onScreenGraphics;
The 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();
}
}
TODOThere 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:
Any suggestions are greatly appreciated, either here on Code Project or on the workspace site. ResourcesI’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 commentThere 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.
|
||||||||||||||||||||||