Upgrade Your Game: Crusader (C#)
- Posted: Nov 09, 2006 at 6:31AM
- 11 comments
Loading user information from Channel 9
Something went wrong getting user information from Channel 9
Loading user information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
|Welcome to the Upgrade Your Game series of tutorials. In this article, we will develop a role-playing game Crusader!!!|
Time Required: 1-3 hours
Visual C# Upgrade Your Game Series
Welcome to the Upgrade Your Game series of tutorials. There are four tutorials, each covering how to write a simple computer game using Visual Studio Express products. Though the games are simple, each one teaches some game development techniques and builds on the last to improve your skills. The skills can be applied to more complex games including 3D games using Microsoft DirectX.
Not all games are real-time action games. Role-playing games (RPGs) are generally slower paced and involve more thinking. Usually there's no penalty if you turn your attention to something else for a few moments. Many, many, many years ago, sometime in the 1970s, RPGs were played using text or graphics made up from text. Since we're no longer in the 1970s, we can do a little better than that using the sprites from the previous games.
For the fourth and final game, I will be creating a tile-based role-playing game. These days, role-playing games have as good graphics and special effects as any first person shooter. But the origins of graphical role-playing games are tile-based games such as the early Final Fantasy and Ultima games. Because this is a beginner series with limited code, I cannot create even games as complex as those early works; however, I can give you most of the techniques you will need to put together similar-looking games. After that, it's really just up to your imagination.
Note: This documentation assumes that you have a basic knowledge of programming concepts and the Visual C# environment. You can learn more about these topics in the product documentation by clicking Help on the menu bar, and then clicking Contents, Index, or Search. You can also access Help by positioning the mouse cursor on language keywords or user interface elements such as windows or dialog boxes, and pressing F1.
Once your project is loaded into the Visual C# environment, you can compile and run the program in one step.
To build and run Crusader:
The game launches and you can use the arrow keys to control the hero and press P to use magic potions. Find the keys to open the doors and kill the wizard who has captured the princess.
|Area.cs||Represents an 8 x 8 area of the world. Each area stores a set of tiles and current state of any dynamic objects. In addition, it stores connectivity information about which areas surround it.|
|BitmapCache.cs||A static class that stores bitmaps in a cache. This is used by the Sprite class to save on memory and loading time by reusing bitmaps.|
|Crusader.cs||The main display form. Other than UI initialization and keyboard events, little gameplay code is performed in here. Most code calls into GameState.|
|GameObject.cs||Abstract base class that represents an object that requires updating and drawing once per frame.|
|GameState.cs||Stores the current state of the game and controls all gameplay.|
|MapTile.cs||Stores an individual tile. Each tile has a background sprite and an optional transparent sprite in the foreground. In addition, for dynamic sprites such as monsters, we store the current state of that monster, for example, Health.|
|Sounds.cs||A static class that plays any of the game sounds.|
|Tile.cs||Represents a tile from the data file.|
|World.cs||The world is a collection of Areas. This class handles reading of the tile and map files and any gameplay related to the world.|
If you are new to 2D game development, check out the 2D Game Primer (C#).
Tile-based games get their name from the way the graphics are built up. Instead of having a single graphic for each area in the game, the area is built up from several small tiles. So there is one tile for grass, another with a tree, and another with a brick wall. By combining these squares like a patchwork quilt, you can make a huge number of different-looking levels from a small number of graphics. Take a look at the tiles.bmp in the GameData folder to get an idea of what the tiles look like for this game.
I asked the artist to cover three themes:
I asked him to create all the sections needed to make paths that connect in any direction, so there are 11 different combinations for each one. In addition, he created three doors of differing colors with both open and closed positions. The doors are themed to fit into the castle wall.
You will also see that this file includes tiles for all of the in-game objects. These are not drawn as part of the tile-based background but instead are layered over the top of the tiles depending on the location of the item in the game.
It's far more efficient to load all of the graphics in one go rather then having many different files as I did in Space Blitz. As you have already seen, the graphics for this game are all in one file. Well, actually they are in two. I asked the artist to put the multi-frame animations in a second file, so that we could control the layout without having to fit them around the other tiles. Take a look at animations.bmp.
The Sprite class currently loads its own bitmap with a file name, which is not really very efficient either; in SpaceBlitz you loaded the same bitmap multiple times each time it was used. To improve this, I added a constructor that takes an already loaded bitmap and just stores the reference to it. Now multiple sprites can share the same bitmap. This doesn't make sense for sprites that you want to change (like the bases in SpaceBlitz), but those are the exception to the rule.
So how does the sprite know which bit of the large bitmap it should draw? In the same constructor, you pass a rectangle structure that describes the top, left, height, and width of the region you wish to extract. When the sprite is drawn it uses this to draw only the relevant part. The function also takes the number of frames of animation within that rectangle and automatically subdivides the rectangles to extract the correct frame into the _frames collection.
public Sprite(GameState gameState, float x, float y, Bitmap bitmap, Rectangle rectangle, int numberAnimationFrames)
for (int i = 0; i < numberAnimationFrames; i++)
//Set the location and use the height and width from the 1st frame
initialize(gameState, x, y, rectangle.Width /
_rectangle.Add(new Rectangle(rectangle.X + i * rectangle.Width /
rectangle.Width / numberAnimationFrames, rectangle.Height));
public override void Draw(Graphics graphics)
If the game objects were drawn over the top of the tiles in the same way that all of the other sprites are drawn, they would look like this:
In SpaceBlitz, all of the aliens were actually being drawn inside a black box (try changing the form background color and running the game). Since the game background was black, you could not see this. That was fine for SpaceBlitz but obviously isn't suitable for Crusader. The technique used is called Color Key and it's the same one used in movies to put actors in front of a green screen into a sci-fi setting. It's more often called chroma key. The general idea is that you do not draw a pixel where it is a certain color. I requested the artist choose an RGB color that he was not using in the artwork and set the backgrounds to that color. He chose (75, 75, 75), which is the dark grey you see in the image above. When you ask GDI to draw the shape, you tell it which color to remove and all the magic is done for you giving this corrected image:
The code is just a few lines of modification to the sprite drawing routine: colorKey is Color.ToArgb(75, 75, 75).
//Set the color key for this sprite;
_attributes= new ImageAttributes();
Describing what goes where in a game like this makes much more sense in a data file than inside the code. This means that some aspects of the game are very easy for you to change without even recompiling.
There are two data files that drive most of the look and feel of the game. Things like how the doors open, how the monsters fight, and what happens when you pick things up are, for this sample, hard-coded in the code. But there's no reason why you couldn't change the data files to make them even more descriptive and then drive the game from the new files. You could debate file formats and how they are laid out forever, so find something that works for your game and stick to it. To keep down the amount of code, the error checking is minimal. For a game that is designed to be modded by the end users, this is something that would have to be improved
This file describes all of the tiles and sprites in the game. It's a comma delimited format that contains the following information:
This file describes the set of 8 x 8 areas that make up the world. There is a single blank line between each area.
One thing that distinguishes RPGs from other kinds of games is that usually the story is very relevant to the plot rather than being a poor excuse for why the character is about to do something very unlikely. In addition, you usually take the character from a low start point to some high point in the game by changing or improving the way that character works. You have probably heard of games with levels, hit points, strength, agility, and more, though different games refer to these differently. The more advanced the game gets, the more of these there are to track and the more effect they have in different parts of the game.
Given the limited space for these games, I chose to have a very simple but often heard story. The princess has been captured by the forces of evil (in this case a wizard since that's what the artist drew) and it is your job to rescue her. The story plays out in three settings: an outdoor area, a dungeon, and a castle. Each has a distinct look and type of monster to fight.
The characters' attributes are:
All of these attributes and the code to manipulate them can be found in the GameState or the World classes. There is too much to list here but spend some time looking through to see how the world works. Searching for "Health," for example, will show you all the places that the health points are increased or decreased.