Adding sprites
At this stage we have all the basic requirements for our game to run. The next step is to cast some sprites onto the surface. We will start with a character image and then we’ll add a background so that it can move around in inside the 2D game space. For the purposes of DirectDraw, each sprite requires a separate surface object, so we will begin by creating a new surface object for the class, followed by some initialisation stuff in the initialise() routine.
//add sprite in constructor method frmGame()
public Surface ddSprite1;
...
//add sprite in initialize()
ddSDesc.Clear();
ddSDesc.SurfaceCaps.OffScreenPlain = true;
ddSprite1 = new Surface(“sprite1.bmp”, ddSDesc, ddDevice);
Then in the RenderSurface() function we alter the try() code to display our sprite in the top left hand corner of the screen. This works by first rendering everything onto the back buffer (ddSurface2) and then calling the flip method of the primary surface.
//add sprite in try() block of the RenderSurface() method
ddSurface2.ColorFill(Color.Black);
ddSurface2.ForeColor = Color.Black;
ddSurface2.DrawFast(0, 0, ddBgSurface, DrawFastFlags.DoNotWait);
ddSurface1.Flip(ddSurface2, FlipFlags.Wait);
Now that we have a sprite image displaying in our game window, we can start adding some interactive game elements to animate it.
Animating sprites
There are two aspects of animation that may apply to our sprite object. Firstly, we will want to be able to move it around the screen using, say, the arrow keys. Secondly, we may want to animate the sprite as it moves around the screen. To make this happen, we use a single bitmap with each frame of the animation shown side-by-side. Then, as the sprite moves around the screen, we change the section of the bitmap that is rendered to the sprite’s surface. To do this, we use an array of Rectangle objects which stores the viewports for each frame of the animation. An integer is then used as a counter to keep track of which frame we need to show for each step of the animation.
Our animated sprite bitmap consists of four frames stored in the one image file.
For instance, the image above shows the four frames of animation for our sprite. We create two new variables for our game class as follows:
Rectangle[] rectFrames = new Rectangle[4];
public int intFrame = 0;
Then, to initialise the rectangle object, a new function called buildAnimation is called from the Initialise() method as shown here:
public void buildAnimation()
{
int x;
for(x=0;x<4;x++)
{
rectFrames[x] = new Rectangle(105 * x, 0, 105, 105);
}
}
The four rectangles that are created in this loop are identical except for the first parameter. This parameter specifies the X co-ordinate for each member, followed by the Y, width and height values. As our sprite measures exactly 105 pixels square, the four rectangles stored in rectFrames correlate exactly to the location of our four sprite images in the loaded bitmap. Next, to have each frame correctly displayed, we need to update the RenderSurface() method to use the rectangle object as a viewport for the surface.
ddSurface2.DrawFast(0, 0, ddSprite1, rectFrames[intFrame], DrawFastFlags.DoNotWait);
To complete the animation, we need to have the value of intFrame updated whenever the sprite is instructed to move. This can be done by adding a new test in the key press handler for the right arrow key as shown here.
if(e.KeyCode==Keys.Right)
{
intFrame = intFrame + 1;
if (intFrame > 3)
{
intFrame = 0;
}
RenderSurface();
}
Compiling and executing the game now lets us animate the sprite with the right arrow key, but it appears to be “running on the spot”. Now we need to address the other type of animation, and move the sprite across the screen as well.