This chapter is is a very intense continuation of our Direct Draw coverage. I will go fast so take it at your pace.
One of the most important aspects of game programming is the sprite. No, a sprite isn't a mythical creature, it's a set of pictures that make up the animation of an object in your game. Consider my second favorite game, Warcraft II. In the game there is a a two headed ogre. The ogre can look in 8 different directions. The ogre has an axe that it uses to attack in 8 different directions. When the ogre dies it has 4 different looks, each one being more decayed. The 6 states and 8 directions of the ogre allow the ogre the ability to look 1 out of 20 ways at any point in the game. Let's look at another example. Pac-Man looks in four directions. He can either have his mouth open or closed. That allows 8 different looks for Pac-Man, not including his death sequence. The developers of Pac-Man made a picture of all of Pac-Man's possible looks that probably looked similar to:
The first
two lines show all the possible positions for Pac-Man when he is
alive. If you remember the game, every other step he took
he would have his mouth open. If Pac-Man was going right,
we would draw the top left Pac-Man and then the middle left
Pac-Man and then the top left Pac-Man again. If he turned
and started going up we would use the top right Pac-Man
followed by the middle right Pac-Man and back to the top right
Pac-Man. If Pac-Man died we would display the bottom row
from left to right. All of these pictures are in the same
bitmap so that they are all loaded into memory at the same
time. A doubly linked list, which is basically a
queue that has links in both directions, would hold the
coordinates for the top and middle row. Each node would
have the coordinates for the open and closed mouth like this:
struct SpriteNode
{
ClosedTopX; // The top left X
coordinate of Pac-Man with his mouth closed
ClosedTopY; // The top left Y
coordinate of Pac-Man with his mouth closed
ClosedBottomX; // The bottom
right X coordinate of Pac-Man with his mouth closed
ClosedBottomY; // The bottom
left Y coordinate of Pac-Man with his mouth closed
OpenTopX; // The top left X
coordinate of Pac-Man with his mouth open
OpenTopY; // The top left Y
coordinate of Pac-Man with his mouth open
OpenBottomX; // The bottom
right X coordinate of Pac-Man with his mouth open
OpenBottomY; // The bottom
left Y coordinate of Pac-Man with his mouth open
};
We would also have a class to hold the death sequence.
Current video cards are
being made with overlay capabilities. Overlays are
used to keep the primary surface the same while drawing over
it. The video cards that do allow overlays currently only
support one overlay for the entire system. If your
application is in a window and another window is using an overlay
you will not be allowed to use an overlay. The solution to
this is to ask for exclusive mode. Exclusive mode
lets you ask for and keep exclusive rights to a piece of
hardware. This isn't a friendly technique though. A
better idea is to be in full screen mode. The user may not
want your application to be in full screen mode so you should
allow the user to decide, unless your application simply can't
afford to be windowed. A game like StarCraft or Quake would
not run well in a window because the other applications would be
sharing the resources and the palette. Some video
cards that allow overlays have alignment and scaling
restrictions. In our example program we will not use an
overlay, if there are any alignment or scaling
restrictions. Here are the functions for overlays:
//This will show, hide, or move your overlay
UpdateOverlay(LPRECT
lpSrcRect, //Pointer to the
source that can be NULL to use the
//entire surface or to hide the overlay
LPDIRECTDRAWSURFACES
lpDDDestSurface, //Pointer to the destination
LPRECT lpDestRect,
//Pointer to the destination rectangle, NULL if you are hiding
//the overlay
DWORD dwFlags, //Control
flags
LPDDOVERLAYFX
lpDDOverlayFx); //Address for overlay effects
//Change the
position according to where your window was moved
SetOverlayPosition(LONG lX, //The new X coordinate
LONG lY); //The new Y
coordinate
The control flag should be set to DDOVER_SHOW if you want the overlay to be visible. To hide the overlay use DDOVER_HIDE . To use the source key, use the DDOVER_KEYSRC flag. To use the destination key, use the DDOVER_KEYDEST flag. Finally to use a DDOVERLAYFX structure for special effects use DDOVER_DDFX .
Palettes are color
schemes. If your in a window you must share the palette
with all the other programs. Most programs will use only a
few colors so this may not always be a problem. All
surfaces can have palettes. If you don't create a palette
your hardware will pick one for you. A palette can have 2,
4, 16 or 256 colors. Here is what a palette entry looks
like:
struct PALETTEENTRY
{
BYTE peRed; //amount of red
color
BYTE peGreen; //amount of
green color
BYTE peBlue; //amount of blue
color
BYTE peFlags; //Control flags
};
This should bring back
memories of high school art classes. Every color is made up
of red, green and blue. The amount of each color component
can be a value from 0 to 255. 0 would be none and 255 would
be the most. Black would be defined as follows:
PALETTEENTRY Black;
Black.peRed = 255;
Black.peGreen=255;
Black.peBlue=255;
White would be the exact opposite with all 0 values. For Red peRed would equal 255 an the other two colors would = 0.
Here are some useful
palette functions defined in ddraw.h :
//Create a palette
CreatePallette(DWORD dwFlags, //the
capability flags
LPPALETTEENTRY
lpColorTable, //An array of color values
LPDIRECTDRAWPALETTE FAR *
lplpDDPalette, //A pointer to the new palette
object
IUnknown FAR *
pUnkOuter); //not yet used in DirectX so pass
NULL here
//Associate a
palette with one of your surfaces
SetPallette(LPDIRECTDRAWPALETTE
lpDDPalette); //Address of the palette you want
to attach
//Returns the
palette attached to a surface
GetPallette(LPDIRECTDRAWPALETTE FAR *
lplpDDpalette); //Pointer to the palette object
//The capabilities
of the palette on the user's PC
GetCaps(LPDWORD lpdwCaps); //address of
capabilty flags
//Returns entries
from the color table to an array
GetEntries(DWORD dwFlags, //Currently
unused by DirectX so you must use 0
DWORD dwStartingEntry,
//Subscript of the first palette entry to return
DWORD dwNumEntries,
//Number of palette entries to retrieve
LPPALETTEENTRY
lpEntries); //Address of an array to be filled
with the entries
//Change an entry in
a color table to that of an array you have created
SetEntries(DWORD dwFlags, //Currently
unused by DirectX so you must use 0
DWORD dwStartingEntry,
//Index of the first palette entry to change
DWORD dwNumEntries,
//Number of palette entries to change
LPPALETTEENTRY
lpEntries); //Address of an array that has the
new values
Each pixel is created from the palette entries. In windows palettes can become sloppy. The 0 palette and 255 palette are used by the operating system and should never be changed. If you do change them the colors of your borders and other application colors could be distorted. Your application could use several palette entries. If you are in a windowed application and other applications are using palette entries you have to compete with them. The foremost application gets it's choice of palette entries. Have you ever seen colors on your screen change when you switch applications? That is caused by the applications running into each others palettes. Whenever your application is restored you must restore your palette.
With palettes you can create palette animation. If you want to change the screen colors without changing the pixels one by one you can change the palette your surface is attached to. Palette animation can be useful if you just want to change one color. Say your displaying your credit screen. If you want the names to slowly fade you could change the palette entry so that it slowly is changed to that of the background color. This can cause tearing so be watchful if what happens.
In our game we need to detect when our ship is hit. To do this we will divide the playing field into squares. The sprites will have it's position written in it's node. Every time the shots or ship moves we will check to see if their X and Y values are equal.
Our PC
can be going at varying speeds so we must use time values to
decide when and where to draw our ship and shots. The
formula for where to draw it should look something like the
following:
Direction
+ time since last update * speed
Clipping
is the way to remove unwanted sections from the surface. In
windows when the windows are overlapping each other we have to
clip anything that is usually drawn where a foreground
application has been placed. DirectX makes this very simple
because you can associate a clipper to a surface. The
clipper will do all the work for us, hurray. You will want
to use the following functions to make use of the clipper:
//Create
the clipper object
CreateClipper(DWORD
dwFlags, //Currently unused by DirectX so use 0
LPDIRECTDRAWCLIPPER FAR * lplpDDClipper,
//pointer that will point to the new clipper
IUnknown FAR * pUnkOuter); //Currently unused
by DirectX so use NULL
//Attach
a clipper to a window
SetHWnd(DWORD
dwFlags, //Currently unused by DirectX so use 0
HWND hWnd); //The window where the clipping
information is
//Attach
a clipper to a surface
SetClipper(LPDIRECTDRAWCLIPPER
lpDDClipper); //The clipper for overlays and
blitting
That's
all you need to know for this chapter's example but first I would
like to go back to resources stored in .rc files. Here is
what is actually in a resource file:
//Microsoft
Developer Studio generated resource script.
//
#include
"resource.h"
#define
APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
//
Generated from the TEXTINCLUDE 2 resource.
//
#include
"afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef
APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
English (U.S.) resources
#if
!defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef
_WIN32
LANGUAGE
LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma
code_page(1252)
#endif
//_WIN32
/////////////////////////////////////////////////////////////////////////////
//
//
Bitmap
//
SHIPS
BITMAP DISCARDABLE
"ships.bmp"
SHOTS
BITMAP DISCARDABLE
"shots.bmp"
GHOST
BITMAP DISCARDABLE
"ghost.bmp"
#ifdef
APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
//
TEXTINCLUDE
//
1
TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2
TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3
TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif
// APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
//
Icon
//
//
Icon with lowest ID value placed first to ensure application icon
//
remains consistent on all systems.
IDI_ICON1
ICON DISCARDABLE
"Space Adventure1.ico"
#endif
// English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef
APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
//
Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif
// not APSTUDIO_INVOKED
The compiler creates this to store information about your resources. It is written in a C script and can be used by a non Microsoft compiler. Avoid editing it because any edits can prevent the compiler from reading and editing it correctly.
Now here
is our hottest game: SPACE
ADVENTURE 
In the next chapter I'm planning on getting into sounds but who knows, maybe I won't!
PREVIOUS CHAPTER HOME NEXT CHAPTER