Вы находитесь на странице: 1из 120

Game Programming Genesis

Part I : Beginning Windows


Programming
by Joseph "Ironblayde" Farrell

Introduction
The purpose of this article is to introduce the very basics of Windows
programming. By its end, you should be able to put together a working, albeit
simple Windows program. All that is required is a basic knowledge of C. I rarely
use C++ extensions in my code. However, since Windows itself is object-oriented,
a little knowledge about classes never hurt anyone. If you're not familiar with it,
don't worry, you won't need anything complicated, so you should be able to pick
up what you need as you go. All of the code examples included have been tested
using the Microsoft Visual C++ 6.0 compiler. If you don't have a Win32 C/C++
compiler, this is the one to get. That said, let's get started!

Setting Up
The two header files that contain most of the Windows functions you'll need are
windows.h and windowsx.h. Make sure you include both in your programs. Aside
from that, you'll just be using the standard C headers, like stdio.h, conio.h,
and so on. Aside from that, there's one line of code you'll see at the beginning of
many Windows programs:
#define WIN32_LEAN_AND_MEAN
Besides having a cool sound to it, this line excludes some MFC stuff from the
Windows header files, to speed up your build time a little bit. Since you're not
likely to be using MFC for games programming, it's probably a good idea to use
this most of the time. If you've never seen this type of statement before -- a
#define directive followed by only a name -- it has to do with something called
conditional compilation. Take a look at this example:
#ifdef DEBUG_MODE
printf("Debug mode is active!");
#endif
If the program containing this code has a line in the beginning that reads #define
DEBUG_MODE, then the printf() statement will be compiled. Otherwise, it will be
left out. This is a useful way to enable or disable code within your program that
helps you track down any logic errors you might have. In the case of
WIN32_LEAN_AND_MEAN, its definition is used to remove rarely-used components of
the Windows header files. Got it? Good. On to the code...

The WinMain() function


Just as all DOS-based C programs begin execution with the main() function,
Windows programs begin with the WinMain() function. A basic, empty WinMain()
function looks something like this:

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow)
{
return(0);
}
For a function that does nothing but return a value, it's sure got a lot of unfamiliar
stuff in it! First things first; what's the deal with the WINAPI declarator? This is an
example of what's known as a calling convention. It affects such things as the
way parameters are passed to the function, which function performs stack
cleanup, and a few other things that you never really see. A function with the
WINAPI calling convention passes parameters left-to-right, as opposed to the
default right-to-left order. Unless you're going to be using assembly language with
your programs, you don't need to know all the details of how calling conventions
work, only that WinMain() must have the WINAPI convention specified.
Next, let's take a look at the four parameters that the function receives:
HINSTANCE hinstance: This is a handle to the instance of your application.
Basically, these are like pointers that are used to keep track of all the applications
that are running at any given time. Many Windows functions take the instance of
your application as a parameter, so it knows which application to apply the action
to.
HINSTANCE hPrevInstance: You don't need to worry about this parameter, as it's
now obsolete. In older versions of Windows, this would be a handle to the
instance of the application that called your application. The only reason it's
included anymore is for backwards compatibility. You'll see a few more things like
that as you go on with Windows programming.
LPSTR lpCmdLine: This is a pointer to a string containing the command-line
parameters used when the program was invoked. Note that there is no parameter
specifying the number of command-line parameters, so you'll need to determine
that yourself.
int nCmdShow: This integer indicates how the main window should be opened.
You don't need to do anything with this if you don't want to. It takes values given
by constants beginning with SW_. Some examples are SW_SHOWNORMAL for the
default method, SW_MAXIMIZE or SW_MINIMIZE for maximizing or minimizing
windows, etc.
That's about it for WinMain()'s parameters. Often, the only one that will be of any
consequence is hinstance. Before we go on to actually displaying a window,
something needs to be said about the way Microsoft names variables.

Hungarian Notation
Microsoft uses a standardized way of naming variables, functions, constants, and
classes that is known as Hungarian notation. You've already seen an example of
this in the WinMain() function. The Hungarian notation for variable names
consists of several prefixes which reveal the variable's data type. These are the
prefixes used:
b

BOOL (int)

by

BYTE or UCHAR (unsigned char)

char

cx, cy short (usually lengths; c stands for "count")


dw

DWORD (unsigned long)

fn

Function pointer

Handle (like a pointer, used for Windows objects)

int

LONG (long int)

lp

Long pointer (32-bit)

msg

Message (we'll cover this later)

number (short or int)

String

sz, str Null-terminated ("ASCIIZ") string


w

WORD or UINT (unsigned int)

x, y

short (usually coordinates)

In addition, variable names consisting of more than one word have each word
capitalized, with no underscores. For instance, a pointer to an area of memory
used for player data might be called lpPlayerData. This standard notation is
often helpful in understanding code. For instance, in the WinMain() function
discussed above, without seeing the function header, Hungarian notation tells you
that hinstance and hPrevInstance are handles, lpCmdLine is a 32-bit pointer,
and nCmdShow is an integer.
Function naming under Hungarian notation follows the same rules as variables,
minus the prefixes. In other words, the first letter is capitalized, and the first
letter of subsequent words in the function name are capitalized as well. An
example might be ShowCharacterStats().
The rule for naming constants is that all capital letters are used, and underscores
often separate words within a constant's name, or separate a prefix from a
constant's name. The constant WIN32_LEAN_AND_MEAN is an example. One thing
you'll see often in Windows is that constants are often prefixed by an abbreviation
of the function with which they are meant to be used. For example, the constants
SW_SHOWNORMAL, SW_MAXIMIZE, and SW_MINIMIZE, which I mentioned briefly
earlier, have the SW_ prefix because they are meant to be used as arguments to a
function called ShowWindow().
Finally, classes are named in the same manner as functions, except with a capital
C preceding the name, so an example class for a vehicle in a racing game might
be CVehicle.
You don't have to use this naming convention in your programs, but you should
be familiar with it, because all Microsoft products follow these guidelines. It is
rather convenient if you can convince yourself to start using it. I'm still working
on that. Anyway, moving on...

Messages
When you were programming in DOS, you didn't need to worry about other
programs that might be running, because DOS is not a multitasking OS. When
programming in Windows, however, you must take this into consideration. For
this and other reasons, Windows uses what are called messages to communicate
with applications and tell them what's going on. Messages serve a variety of
purposes. They tell an application when its window is being resized, moved, or
closed. They signal to a program that it is about to be closed. They inform a
program when part of its window must be refreshed. They can be used to track
mouse movement and button presses. The list goes on. In any case, your
Windows program must be able to handle these messages.

The way this is done is through the use of a special type of function called a
callback function. Callback functions are functions which you don't actually call
from your code; rather, certain events cause them to be called. You create such a
function by using the CALLBACK calling convention, much like the WINAPI
convention is used for WinMain(). I'm going to leave this topic for a minute,
though, because before you can process messages for your window, you have to
be able to create the window in the first place.

Window Classes
Here's where it helps to know a little about C++, because the first thing you must
do to create a window is to create a window class. The class contains all the
information about the window such as what icon it uses, the menu attached to it
(if any), etc. In just about every Windows program you create, you'll need to
create a window class to meet your needs. In order to do this, you need to fill out
a WNDCLASSEX structure. The "EX" part of the name stands for "extended," as
there is an older version of this structure called WNDCLASS. We'll be using the
extended version. Here's what it looks like:
typedef struct _WNDCLASSEX {
UINT
cbSize;
UINT
style;
WNDPROC lpfnWndProc;
int
cbClsExtra;
int
cbWndExtra;
HANDLE hInstance;
HICON
hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON
hIconSm;
} WNDCLASSEX;
This structure has quite a few members to it, and you must set them all in order
to create your window class. It's not so bad, though. Let's run through a brief
description of all the fields.
UINT cbSize: This is the size of the structure, in bytes. You'll see this a lot,
especially if you get into DirectX. It's included so that if a structure of this type
(or rather, a pointer to that structure) is passed as a parameter to a function, the
structure size can simply be looked up rather than having to be computed. Always
set it to sizeof(WNDCLASSEX).
UINT style: This is the window style, which takes constants prefixed by CS_.
Furthermore, you can combine several of these constants by using the | (bitwise
OR) operator. Most times there are only four you'll use. For the sake of keeping
the length of this article down, I'll show you those four. You can always look up
the rest on your MSDN Help. You did remember to get Visual C++, didn't you?
CS_HREDRAW Specifies that the window should be redrawn if it is horizontally
resized.
CS_VREDRAW Specifies that the window should be redrawn if it is vertically resized.
CS_OWNDC

Allows each window to have a unique device context or DC (not


covered in this article).

CS_DBLCLKS Discerns between single- and double-clicks while the mouse is in this
window.

WNDPROC lpfnWndProc: A pointer to the callback function that handles messages


sent to this window. If you've never used function pointers, the address of a
function is simply the function's name, without the parenthesis afterwards.
int cbClsExtra: This is reserved for extra class info, which most programs don't
need. Certainly you won't find many uses for this when writing games, so simply
set it to 0.
int cbWndExtra: Basically the same as cbClsExtra, except for extra window
information. You'll almost always be setting this one to 0 as well.
HANDLE hInstance: This is the instance of the application using the window class,
which is one of the parameters passed to WinMain(). This should be set to
hinstance.
HICON hIcon: This is a handle to the icon that represents the program, and will
usually be set using the LoadIcon() function. Until you learn how to use
resources in your programs, you can set this to a generic system icon by using
LoadIcon(NULL, IDI_WINLOGO). There are other IDI_ constants representing
Windows icons; you can find the list in the Help files for your compiler.
HCURSOR hCursor: This is a handle to the cursor used for the mouse while it is in
your window. This is usually set using the LoadCursor() function. Again, you can
use resources to load your own custom cursors, but until you learn that, or if you
just want the standard Windows cursor, use LoadCursor(NULL, IDC_ARROW).
HBRUSH hbrBackground: When your window receives a message that it needs to
be refreshed (or "repainted"), the least that will happen is that Windows will
repaint the area with a solid color or "brush." That brush is defined by this
parameter. There are several kinds of stock brushes you can load using the
GetStockObject() function. Some of these are BLACK_BRUSH, WHITE_BRUSH,
GRAY_BRUSH, etc. For now, you're safe using GetStockObject(BLACK_BRUSH).
Sorry I'm touching on all of these functions so briefly, but I'm trying to keep the
length down. I'll revisit them in future articles, I promise!
LPCTSTR lpszMenuName: If you want to create a window with pull-down menus,
this parameter gives the name of the menu to load and attach to the window.
Since you don't know how to create menus yet, you can specify no menu by
setting this to NULL.
LPCSTR lpszClassName: This is simply a name by which you refer to the class.
You can call it anything you want, so use a descriptive name. You might call it
"Game_Class" or something like that.
HICON hIconSm: This is a handle to the small icon used on the window's title bar
and on the Start Menu bar. You set this the same way you set hIcon -- by using
the LoadIcon() function. For now, we'll use LoadIcon(NULL, IDI_WINLOGO) for
the standard Windows logo icon.
That's it! Now that you're familiar with all the fields of the WNDCLASSEX structure,
you can fill it out and you're ready to create a window. A sample class might look
like this:
WNDCLASSEX sampleClass;
structure variable
sampleClass.cbSize =
use this!
sampleClass.style =
settings
sampleClass.lpfnWndProc =
to write this!
sampleClass.cbClsExtra =
class info, not used

// declare
sizeof(WNDCLASSEX);

// always

CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;

// standard

MsgHandler;

// we need

0;

// extra

sampleClass.cbWndExtra =
0;
// extra
window info, not used
sampleClass.hInstance =
hinstance;
//
parameter passed to WinMain()
sampleClass.hIcon =
LoadIcon(NULL, IDI_WINLOGO); // Windows
logo
sampleClass.hCursor =
LoadCursor(NULL, IDC_ARROW); // standard
cursor
sampleClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //
a simple black brush
sampleClass.lpszMenuName = NULL;
// no menu
sampleClass.lpszClassName = "Sample Class"
// class
name
sampleClass.hIconSm =
LoadIcon(NULL, IDI_WINLOGO); // Windows
logo again
And you're all set. There is one thing I should mention, though. Notice the
typecast to HBRUSH on the result of the GetStockObject() function. This is
because GetStockObject() can be used to load many objects, not just brushes,
and so it returns a variable of type HGDIOBJ, which is a bit more general. In older
versions of Visual C++, you wouldn't need the typecast, but VC++ 6.0 is more
picky about typecasting, so you'll get an error if you try to compile without it.
The last thing you need to do is register the new class with Windows so you can
use it to create new windows. This is accomplished with a simple function call to
RegisterClassEx(). It only takes one parameter: the address of your structure.
So in the example listed above, you would register the class like this:
RegisterClassEx(&sampleClass);
Now that Windows is familiar with the new class you've created, you can use it to
create a window. It's about time, hey?

Creating Windows
The good news is that all you need to create a window is a call to
CreateWindowEx(). The bad news is that this function takes a lot of parameters.
You're probably getting sick of these long lists by now, but this one isn't too bad.
Here's the function prototype:
HWND CreateWindowEx(
DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
identifier
HINSTANCE hInstance,
LPVOID lpParam
);

//
//
//
//
//
//
//
//
//
//

extended window style


pointer to registered class name
pointer to window name
window style
horizontal position of window
vertical position of window
window width
window height
handle to parent or owner window
handle to menu, or child-window

// handle to application instance


// pointer to window-creation data

First things first: the return value. By now, all these crazy data types that
Windows uses are probably starting to look familiar. If not, don't worry, you'll get
used to it sooner than you think. The return type is HWND, which is a handle to a

window. You'll want to store the value returned by CreateWindowEx(), as you'll


need it as a parameter for several Windows functions. Now, let's tackle that
parameter list. Many are self-explanatory.
DWORD dwExStyle: Extended window style is something you'll rarely use, so you
can set this to NULL most of the time. If you're interested, the Help files for your
compiler list a ton of constants beginning with WS_EX_ that can be used here.
LPCTSTR lpClassName: Remember when you named the class you created? Just
pass that name here.
LPCTSTR lpWindowName: This is simply the text that will appear on the window's
title bar.
DWORD dwStyle: The window style parameter allows you to specify what type of
window you want to create. There are a lot of constants that can be used here,
beginning with WS_, and they can be combined with the | operator. I'll list just a
few of the common ones:
WS_POPUP

A window that has no controls built into it.

WS_OVERLAPPED

A window with simply a title bar and a border.

WS_OVERLAPPEDWINDOW A window with a title bar including all standard controls.


WS_VISIBLE

Specifies that the window is initially visible.

The WS_OVERLAPPEDWINDOW constant is actually a combination of several other


constants in order to create a standard window. Basically, you can follow these
guidelines. If you want a window that can be maximized, minimized, resized, etc.,
use WS_OVERLAPPEDWINDOW. If you want a window with a title bar but which has a
fixed size, use WS_OVERLAPPED. If you want a window that has no controls on it
whatsoever, use WS_POPUP. Such a window will just appear as a black rectangle
originally. This is what you'll probably use for writing fullscreen games. Also,
always specify the WS_VISIBLE flag, unless for some reason you don't want
anyone to see your window, or if you want to take care of some other things first,
and display the window later.
int x, y: These are the coordinates on the screen at which the upper-left corner
of the newly created window will appear.
int nWidth, nHeight: These are, you guessed it, the width and height of the
window, in pixels.
HWND hWndParent: This is a handle to the parent window of the window you're
creating. This is mostly used with controls such as checkboxes and pushbuttons.
For creating a main window, set this to NULL, which represents the Windows
desktop.
HMENU hMenu: This is a handle to the menu which should be attached to the
window. If you're loading a resource menu -- after you learn how to do that -you would use the LoadMenu() function. For a window with no menu attached,
simply set this to NULL.
HINSTANCE hInstance: This is the instance of the application; again, pass the
parameter that was passed to WinMain().
LPVOID lpParam: This is something you're not likely to use, especially for games,
where only simple windows are needed. It's used for creating things like multiple
document interfaces. Just set it to NULL.
At last, we have everything we need to create a window. Here's a sample call that
would get the job done:
HWND hwnd;
if (!(hwnd = CreateWindowEx(NULL,
style, not needed
"Sample Class",
identifier

// extended
// class

position, size
parent (the desktop)
(none)
instance handle
return(0);

"Sample Window",
WS_POPUP | WS_VISIBLE,
0, 0, 320, 240,

// window title
// parameters
// initial

NULL,

// handle to

NULL,

// handle to menu

hinstance,

// application

NULL)))

// who needs it?

This might be something you'd use for a game, because it's a popup window.
Notice that I've enclosed the CreateWindowEx() call inside an if statement. This
is because if CreateWindowEx() fails, it returns NULL. The way this statement is
set up, if the window can't be created for some reason, WinMain() simply returns
and the program ends.
Now you've almost got enough to make a Windows program that creates a
functional window. Almost. Remember when we created "Sample Class," and had
to provide a pointer to a message handler function? We need to write that
function before Windows will let us create anything.

Handling Messages
I've already explained some of the things messages are used for in Windows.
Now I'll go over how to make use of them. The prototype for a message handling
function looks like this:
LRESULT CALLBACK MsgHandler(
HWND hwnd,
// window handle
UINT msg,
// the message identifier
WPARAM wparam, // message parameters
LPARAM lparam // more message parameters
};
The LRESULT type of the return value is used specfically for message processing
functions like the one we're going to write. I talked about the CALLBACK
convention a little bit earlier. The parameters are very simple:
HWND hwnd: This is the handle of the window that sent the message currently
being processed.
UINT msg: This is a message identifier. The values for this parameter are
constants beginning with WM_ (for "Windows message"). The number of different
messages that can be sent is ridiculously high, but here are some important
ones:
WM_ACTIVATE

A new window is receiving the focus.

WM_CLOSE

A window is being closed.

WM_COMMAND

A menu option has been selected.

WM_CREATE

A window has been created.

WM_LBUTTONDBLCLK Left mouse button has been double-clicked.


WM_LBUTTONDOWN

Left mouse button has been pressed.

WM_MOUSEMOVE

The mouse has been moved.

WM_MOVE

A window has been moved.

WM_PAINT

Part of a window needs to be repainted.

WM_RBUTTONDBLCLK Right mouse button has been double-clicked.


WM_RBUTTONDOWN

Right mouse button has been pressed.

WM_SIZE

A window has been resized.

WM_USER

Use this for whatever you want.

WPARAM wparam, LPARAM lparam: The exact use of these parameters depends on
which message is being sent, but they are used to further specify the meaning of
the message.
If you had to write code to handle every message that your window might
receive, you'd probably go insane. I know I would! Thankfully, Windows provides
a default message handler. If you don't have any special instructions for handling
certain messages, you can always call DefWindowProc(). With that in mind, here
is the simplest, fully functional message handler that you could possibly write:
LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam,
LPARAM lparam)
{
return(DefWindowProc(hwnd, msg, wparam, lparam));
}
Simple, hey? Usually you'll want to handle some of these messages yourself. In
that case, you can write your own code, and return 0 to tell the program that
you've dealt with the message. Here's an example of a message handler that calls
an initialization function when the window is created, and calls the default handler
for anything else.
LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam,
LPARAM lparam)
{
if (msg == WM_CREATE)
{
Initialize_Game();
return(0);
}
return(DefWindowProc(hwnd, msg, wparam, lparam));
}
Your message handler will probably end up being a big switch statement to
accommodate the messages you want to handle manually, followed by a call to
the default handler for everything else. Now there's just one more thing I need to
show you before everything is working smoothly, and that's how to make sure
your message handler is getting called when it has work to do.

Reading the Message Queue


Near the beginning of your program's main loop, you need to see if the message
queue -- where all pending messages are stored -- has anything waiting for you.
If so, there are a few things you need to do in order for your handler to do its job
correctly. The function you need here is PeekMessage(). Here is its prototype:
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,

// pointer to structure for message


// handle to window
// first message

UINT wMsgFilterMax,
UINT wRemoveMsg

// last message
// removal flags

);
The return type, BOOL, is really just an int, but it takes only two values: TRUE or
FALSE. If a message is waiting on the queue, the function returns TRUE.
Otherwise, it returns FALSE. The parameters are pretty straightforward:
LPMSG lpMsg: This is a pointer to a variable of type MSG. If a message is waiting,
this variable will be filled with the message information.
HWND hWnd: The handle of the window whose queue you want to check.
UINT wMsgFilterMin, wMsgFilterMax: The indices of the first and last messages
in the queue to check. Most of the time, you'll only be interested in the first
message on the queue, so you would set both of these parameters to 0.
UINT wRemoveMsg: Generally this takes only two values, PM_REMOVE or
PM_NOREMOVE. Use the former if you want to remove the message from the queue
after reading it, and the latter if you want to leave the message on the queue.
Usually, if a message is waiting, you'll prepare it to be handled right away, in
which case you should use PM_REMOVE.
If a message is waiting, you need to do a few things to get your handler to kick
in. Don't worry, it's only two simple calls: one to TranslateMessage() and one to
DispatchMessage(). Their prototypes are very similar:
BOOL TranslateMessage(CONST MSG *lpmsg);
LONG DispatchMessage(CONST MSG *lpmsg);
The first call performs a bit of translation on the message, as you may have
guessed, and the second call invokes your message handler and sends it the
appropriate information from the MSG structure. That's all you need to know! With
every iteration of your main loop, if a message is waiting, you call these two
functions and your MsgHandler() function takes care of the rest. Here's a code
example:
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
No problem! Now you can write a Windows program that creates and registers a
window class, and creates a window with a valid message handler. That wasn't so
bad, was it? There are just a few more things I'd like to mention before I wrap
this article up. While we're on the topic of messages, there will come a time when
you want to send messages manually. Here's how.

Sending Messages
There are actually two ways to go about doing this. You can either call
PostMessage() or SendMessage(). Their prototypes are very similar:
BOOL PostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
LRESULT SendMessage(

//
//
//
//

handle of destination window


message to post
first message parameter
second message parameter

HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam

//
//
//
//

handle of destination window


message to post
first message parameter
second message parameter

);
The parameters are the same as those taken by the MsgHandler() function we
wrote, so I won't go over them again. The only thing you need to know is the
difference between the two functions, so I'll go over each one briefly.
PostMessage() is used when you simply want to add a message to the queue and
let your program logic take care of it. The function returns a nonzero value (TRUE)
if it succeeds, or zero (FALSE) if it fails. It simply adds the message you specify to
the queue, and returns immediately. In most cases, a call to PostMessage() will
get the job done.
SendMessage() is a bit different. Notice that it returns an LRESULT, which is used
for message processing functions. That's because SendMessage() doesn't post a
message to the queue -- it translates the message and invokes the message
handler immediately, and doesn't return until the message handler has finished
processing the message. SendMessage() is used rather than PostMessage() for
events of higher priority that need to occur quickly. Use it when you want
something done immediately.
Now that you know that, the topic of messages leads into the last topic I need to
mention for now, and that is a major difference between Windows programming
and DOS programming.

Program Flow
In DOS, you don't need to worry about any of this message stuff. You don't need
to concern yourself with multiple programs running simultaneously. But when
you're programming in Windows, these are very important matters. As a result,
your programs need to work a bit differently than they do in DOS. Consider this
bit of pseudo-code:
// main game loop
do
{
// handle messages here
// ...
// update screen if necessary
if (new_screen)
{
FadeOut();
LoadNewMap();
FadeIn();
}
// perform game logic
WaitForInput();
UpdateCharacters();
RenderMap();
} while (game_active);
Suppose FadeOut() works like this: when the function is called, it dims the image
on the screen over a period of about a second. When the screen is totally black,

the function returns. FadeIn() works in a similar fashion. WaitForInput() simply


waits until a key is pressed. Perhaps it stores the input in a global variable
somewhere. Now, in a DOS-based game, this is a perfectly acceptable way to do
things. In a Windows game, it is certainly not!
Why not? Well, what happens when new_screen becomes true? It fades the
screen out, loads a map, and fades back in. Altogether this takes about two
seconds. That's two seconds during which no messages are being processed, so
the user could do something such as minimize the window, but the program will
keep running like it hasn't happened yet. This sort of thing can cause erroneous
output, general protection faults, etc. Needless to say, this is unacceptable. The
WaitForInput() function is even worse, because it suspends the flow of the
program until a key is pressed during every frame. Whereas the previous
example has the potential to cause trouble, this one is a near-certainty.
The bottom line is that if your game runs at 30 FPS, you need to make sure the
entire main loop executes 30 times a second. Each iteration of the main loop
should show only one frame, not many frames as in the theoretical FadeOut()
function in the example. When first learning Windows programming, this can be a
bit of an obstacle, because it's a different way of thinking. However, once you
figure out how to set up your program to run this way, I think you'll find it makes
for a much more organized and flexible program.

Closing
That's about it for basic Windows programming. While the example we developed
over the course of the article doesn't do much except display a window, it
contains the entire framework for a functional Windows application. Next time I'll
get into handling resources, which allows you to incorporate custom icons,
cursors, sounds, menus, and more -- right into your .EXE!
If you have any questions or comments about this article or anything else, feel
free to contact me via E-mail at ironblayde@aeon-software.com, or via ICQ. My
UIN is 53210499. Until next time, farewell!

Game Programming Genesis


Part II : Using Resources in Win32
Programs
by Joseph "Ironblayde" Farrell

Introduction
Welcome back! As you may have guessed by the title, in this article I'm going to
show you how to use resources in your Windows programs. Simply put, resources
are binary data that's appended to your .EXE file after the actual program code.
Using resources is easy to learn and has a lot of advantages. It allows the
developer to consolidate a lot of data into one file, include custom icons and such
things with their programs, and prevent users from altering that data. Windows
supports a large number of resource types, so I'm just going to cover the ones I
think are most convenient and easiest to learn: bitmaps, cursors, icons, menus,
and string tables. After that, I'll show you how to create a custom resource type,
so you can include anything you want.
Again, all you need to understand this article is a basic understanding of the C
language. C++ always helps since Windows itself is object-oriented, but most of
my code is straight C. Also, I will assume that you have read my previous article,

"Beginning Windows Programming," or have the equivalent knowledge. I use and


recommend the Microsoft Visual C++ compiler, but if you're using a different one,
it's not a big deal. Ready? Here we go!

Resource Scripts
Before we get into any of the specific resource types, we need to go over the
method used to tell the compiler what resources to include, and how. This method
is to use a special file called a resource script, which is simply a text file either
written by the developer or automatically generated by Visual C++, or whatever
IDE you happen to be using. Your script file should have the file extension .rc.
Most of a script file is taken up by lines which define or specify the resources to
include. The simplest of these lines is used by several resource types, and looks
like this:
[identifier] [resource type] [filename]
The identifier can be one of two things: a string representing the resource, or a
numeric constant that's #defined in a header file meant to accompany the
resource script file. If you use numeric constants, which is usually a good idea,
you can use the #include directive in your script file to include the header that
corresponds to it. You can also use C-style comments to make things a little
easier to understand. That said, here's what a very simple resource script file
might look like:
#include "resource.h"
// icons
ICON_MAIN

ICON

myicon.ico

// bitmaps
IMG_TILESET1
IMG_TILESET2

BITMAP
BITMAP

tileset.bmp
tileset2.bmp

That's not too bad, right? There's one thing that can be confusing, though. Just
by looking at my brief example, you can't tell if ICON_MAIN and IMG_TILESET are
meant to be strings or numeric constants. The file would appear the same no
matter which case were true. At compile time, your compiler will look at the
identifiers you're using and search through your header files looking for their
definitions. If no matching #define statements are found, it's assumed that
you're using string identifiers.
Don't worry about the actual lines themselves just yet; I'll explain each type of
entry when I get to that particular resource. If you don't want to bother with
resource scripting at all, you can just insert the resources from your IDE (in
Visual C++, go to "Resource..." under the Insert menu) and a resource script will
be generated automatically. I prefer to do it myself with good old Notepad, but
don't ask me why because I can't think of a good reason. :) Now that you know
the basics of creating a resource script, let's get started on the specific resource
types.

Icons and Cursors


Most of the Windows programs you use every day have their own icons built in,
and now you know how it works: they're simply resources included in the EXE
file. Custom cursors that are used by those programs are also included as
resources. You've already seen an example of the script line that includes an icon
resource, and the line for cursors is very similar. Here they are:

[identifier]
[identifier]

CURSOR
ICON

[filename]
[filename]

After adding a line such as this to your script file -- make sure to include the
script file in your project -- the icon or cursor specified by [filename] will be
included as a resource in your EXE file. That's all there is to it! You can use any
icon/cursor editor to generate the files you want to include. I use the one that's
included in Visual C++.
Including the resources doesn't do a whole lot for your program, though, because
you don't know how to use them yet! To get an idea for how icon and cursor
resources are utilized in a program, let's revisit the window class we developed in
the last article:
WNDCLASSEX sampleClass;
// declare structure variable
sampleClass.cbSize =
// always use this!
sampleClass.style =

sizeof(WNDCLASSEX);
CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;

// standard settings
sampleClass.lpfnWndProc =
MsgHandler;
// message handler function
sampleClass.cbClsExtra =
0;
// extra class info, not used
sampleClass.cbWndExtra =
0;
// extra window info, not used
sampleClass.hInstance =
hinstance;
// parameter passed to WinMain()
sampleClass.hIcon =
LoadIcon(NULL, IDI_WINLOGO);
// Windows logo
sampleClass.hCursor =
LoadCursor(NULL, IDC_ARROW);
// standard cursor
sampleClass.hbrBackground =
(HBRUSH)GetStockObject(BLACK_BRUSH); // a simple black
brush
sampleClass.lpszMenuName = NULL;
// no menu
sampleClass.lpszClassName = "Sample Class"
// class name
sampleClass.hIconSm =
LoadIcon(NULL, IDI_WINLOGO);
// Windows logo again
You remember this, don't you? The hIcon field specifies the icon to be used to
represent the program, and the hIconSm field is the icon used on the Start Menu
and the window's title bar. The hCursor field sets the cursor to be used when the
mouse is within the boundaries of the window you create. I promised you we'd
take a look at the functions used to fill these fields a little more closely, so here
are their prototypes:
HICON LoadIcon(
HINSTANCE hInstance,
instance
LPCTSTR lpIconName
resource identifier
);
HCURSOR LoadCursor(

// handle to application
// icon-name string or icon

HINSTANCE hInstance,
instance
LPCTSTR lpCursorName
resource identifier
);

// handle to application
// name string or cursor

The return type is a handle to the cursor you're loading. The parameters are very
straightforward:
HINSTANCE hInstance: This is a handle to the instance of your application. To
load resources from your program, just pass the HINSTANCE that is passed to your
WinMain() function when the program is executed. To use standard Windows
resources like we did in the window class above, set this to NULL.
LPCTSTR lpIconName, lpCursorName: This is a string identifier that identifies the
resource you want to load. If your script file refers to resources by string, simply
pass the string. But if you're using numeric constants, the Windows header files
include a macro that changes an integer to a form compatible with this parameter
called MAKEINTRESOURCE().
As an example, let's look at the line that sets the icon to represent the program.
Suppose your resource script file looks like this:
#include "resource.h"
ICON_MAIN
CURSOR_ARROW

ICON
CURSOR

myicon.ico
arrow.cur

If the identifiers ICON_MAIN and CURSOR_ARROW do not have matching #define


statements somewhere in resource.h, then you would pass the corresponding
string to the appropriate resource-loading function, like this:
sampleClass.hIcon = LoadIcon(hinstance, "ICON_MAIN");
Now let's say that resource.h contains a few #define directives:
#define ICON_MAIN
#define CURSOR_ARROW

1000
2000

Now you have to use the MAKEINTRESOURCE() macro to turn the numerical
identifier into something of type LPCTSTR. This gives you a little more ease of
flexibility in loading resources. Any of the following calls would be correct:
sampleClass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(ICON_MAIN));
or...
sampleClass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(1000));
or...
int ident = 1000;
sampleClass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(ident));
That's about all you need to know about including icons and cursors in your
programs, but I'll mention one more thing while we're on the topic. If you want to
set a cursor sometime other than at the beginning of the program, there's a
simple Windows function you can use to accomplish this:
HCURSOR SetCursor(HCURSOR hCursor);
The one parameter is the handle you get by calling LoadCursor(), and the handle
that is returned is a handle to the previous cursor. If no previous cursor was set,
the return value is NULL. Relatively painless, wouldn't you say? Let's move on to
something a bit more interesting.

Bitmaps
Including bitmap resources is probably the easiest way to add images to your
program. Bitmaps are native to Windows and so there are functions included to
deal with loading and manipulating them, but remember, if you include too many,
you'll end up with an enormous .EXE file. In any case, you include bitmaps in
your resource script file in basically the same way you handle icons and cursors:
[identifier]

BITMAP

[filename]

There is a function called LoadBitmap() that is analagous to LoadCursor() and


LoadIcon(); it is used to retrieve a handle to a bitmap, but since I haven't talked
about graphics yet, I won't describe this function here. You can probably guess
exactly how it works, but once you have a handle to a bitmap, what would you do
with it? More to come on that in the future, don't worry! For now, I just wanted to
show you how to include a bitmap resource. Now let's look at something you can
use right away.

String Tables
The string table is one of my favorite resource types. It's exactly what you're
thinking: a giant table full of strings. There are any number of purposes for using
a string table. You can use it to store data filenames, character dialogue for a
game, message-box text, text for menus that are generated by the program,
anything you want. Creating a string table in your script file is easy. Here's what it
looks like:
STRINGTABLE
{
// entries go here
}
An entry in a string table consists of a number to identify the string, followed by a
comma, then the string itself, enclosed in double quotation marks. The strings in
a string table can include escape sequences like \n or \t. Note that the string
table itself does not have an identifier, so each program you write can include
only one string table. A simple string table might look something like this:
// program information
STRINGTABLE
{
1, "3D Space Game v1.0"
2, "Written by The Masked Coder"
3, "(C) 2000 WienerDog Software"
}
To load a string from your program's string table, you use the -- you guessed it -LoadString() function. Here is its prototype:
int LoadString(
HINSTANCE hInstance,
string resource
UINT uID,
LPTSTR lpBuffer,
resource
int nBufferMax
);

// handle to module containing


// resource identifier
// pointer to buffer for
// size of buffer

The integer returned by the function is the number of characters, excluding the
terminating null character, that were successfully copied into the buffer. This
corresponds to the length of the string. If you load a blank string, or if the
function fails, the return value is 0. Take a look at the parameters:
HINSTANCE hInstance: Once again, this is the instance of your application.
UINT uID: This is the number that identifies the particular string you want to
load.
LPTSTR lpBuffer: This is a pointer to the location you want the string copied to.
int nBufferMax: This is the size of the buffer in bytes. If the string to be loaded
is longer than the buffer can hold, the string is truncated and null-terminated.
For example, to load WienerDog Software's copyright message, the following code
would be used:
char buffer[80];
LoadString(hinstance, 3, buffer, sizeof(buffer));
Even though the declaration of a string table in your script file has to use
numbers and not identifiers, I usually #define a number of string table constants
in one of my header files when using a string table. For instance, to accompany
the string table above, I might have a line like:
#define ST_WIENERDOGCOPYRIGHT

Your code will be much easier to read if you have LoadString() calls that use
readable constants for the uID parameter, rather than just having the index
numbers. This doesn't mean you should have a constant for every string table
entry; that would take ages if you have a large string table. Usually I like to use
one #define per "section" of the string table. For instance, ST_FILENAMES for the
first index where filenames are stored, ST_DIALOGUE for the first index of the
character dialog strings, etc.

Menus
This is the last type of Windows resource I'll go over, and it's also one of the most
useful. Menu resources are used to define the menu bar that would appear
underneath the title bar of your application, and are loaded during the definition
of the window class. Looking back, in the window class we developed during the
last article, there was a line that looked like this:
sampleClass.lpszMenuName = NULL;
If you're creating a windowed application, chances are that you'll want to have a
menu bar of some sort. This is done using the menu resource. The script file
entry for this one can get a little complicated, but here is its most basic form:
[identifier] MENU
{
POPUP [menu name]
{
MENUITEM [item name], [identifier]
}
}
The identifier is what you're used to: either a string or a numeric constant that is
used to refer to the menu. Within the MENU brackets, there can be one or more
POPUP menus, each of which represent a pull-down menu, whose name is given

by [menu name]. Within the POPUP brackets, there can be one or more
MENUITEMs, each of which represents a final menu selection, with a name given
by [item name] and an identifier that must be a numeric constant. Within the
menu and item names, if you want that option to be accessible by a keyboard
shortcut, you precede the letter of the shortcut with the ampersand (&). For
instance, if you want to create a File menu accessible by pressing Alt+F, the menu
name should be &File. Menu and item names should be enclosed in double
quotation marks. With that, here is an example of a simple menu resource:
MAIN_MENU MENU
{
POPUP "&File"
{
MENUITEM "&New",
MENUITEM "&Open...",
MENUITEM "&Save",
MENUITEM "Save &As...",
MENUITEM "E&xit",
}
POPUP "&Help"
{
MENUITEM "&Contents",
MENUITEM "&Index...",
MENUITEM "&About",
}
}

MENUID_NEW
MENUID_OPEN
MENUID_SAVE
MENUID_SAVEAS
MENUID_EXIT

MENUID_CONTENTS
MENUID_INDEX
MENUID_ABOUT

You can also create submenus by including one POPUP inside of another, specify
menu items as being initially grayed or checked, or do several other more
advanced things, but I'm not going to go into that here. To obtain a handle to a
menu resource, use the LoadMenu() function whose prototype is shown here:
HMENU LoadMenu(
HINSTANCE hInstance,
instance
LPCTSTR lpMenuName
resource identifier
);

// handle to application
// menu name string or menu-

You should be used to these parameters by now. The first one is the instance of
your application, and the second is the identifier you assigned to the menu.
Remember to use MAKEINTRESOURCE() if you used a numerical constant. Now, to
attach a menu to a window, you have two options. The first is to set the menu as
the default for your window class, like this:
sampleClass.lpszMenuName = LoadMenu(hinstance,
MAKEINTRESOURCE(MAIN_MENU));
The second option is to leave lpszMenuName equal to NULL, and attach a menu
yourself later. This can be useful if you want to create two windows with different
menus, but don't want to define two separate window classes. To attach a menu,
use the SetMenu() function:
BOOL SetMenu(
HWND hWnd,
// handle to window
HMENU hMenu, // handle to menu
);

The return value is TRUE if the function succeeds, or FALSE if it fails. The
parameters are pretty easy to figure out:
HWND hWnd: This is the handle to the window to which you want to attach the
menu. Pass the handle that was returned when you called CreateWindowEx().
HMENU hMenu: To identify the menu, pass the handle returned by LoadMenu(). If
you pass NULL, the specified window's menu is removed.
This resource is particularly nice because all the functionality of the menu is
defined by that simple scripting. But what happens when the user selects a menu
option? The answer is that Windows sends a WM_COMMAND message informing the
program that it must take action. Let's pay a visit to our message-handling
function and see if we can't figure out how to handle this.

Handling Menu Events


As you probably remember, Windows messages are handled by a special callback
function usually called WindowProc() or something similar. The simple one we
wrote last time was called MsgHandler(), and its prototype looked like this:
LRESULT CALLBACK MsgHandler(
HWND hwnd,
// window handle
UINT msg,
// the message identifier
WPARAM wparam, // message parameters
LPARAM lparam, // more message parameters
};
When a menu message is sent, msg will be WM_COMMAND, and the menu item that
was selected will be contained in wparam. This is why menu item identifiers can't
be strings; they need to fit into the wparam parameter. More specifically, the menu
item identifier is the low word of wparam. To extract the low or high word of a 32bit variable type like WPARAM, LPARAM, int, etc. Windows provides macros called
LOWORD() and HIWORD() that do the job. They are shown here:
#define LOWORD(l)
((WORD) (l)) #define HIWORD(l)
((WORD) (((DWORD) (l) >> 16) & 0xFFFF))
In the case of LOWORD(), the typecast to WORD simply truncates the value to the
lower 16 bits. HIWORD() shifts the upper 16 bits to the right, then performs a
logical AND with 0xFFFF just to be sure any bits above the lower 16 are all set to
zero. If you're not familiar with the >> and << operators, they are bit shifts. The
<< operator shifts all the bits of a variable a number of positions to the left, and
the >> operator shifts to the right. For example, suppose you had a 16-bit
variable x whose value was 244. In binary this is 0000 0000 1111 0100. The
following example shows a bit shift, and the effect on x:
short int x = 244, y;
y = x << 4;
Contents of x: 0000 0000 1111 0100
Contents of y: 0000 1111 0100 0000
Anyway, use LOWORD() to extract the low word of wparam, and you have the ID of
the menu item that was selected. So, somewhere in your MsgHandler() function,
you should have something like this:
// handle menu selections
if (msg == WM_COMMAND)

switch (LOWORD(wparam))
{
case MENUID_NEW:
// code to handle File->New goes here
break;
case MENUID_OPEN:
// code to handle File->Open goes here
break;
// the rest of the option handlers go here
}
// tell Windows you took care of it
return(0);

}
Make sense? Good. That about wraps it up for the specific resource types I'm
going to cover. There are others, such as accelerator tables (tables full of
keyboard shortcuts), HTML pages, WAV files etc. but I think these are the most
useful. Before I wrap this up, though, there's one more very powerful feature of
Windows programs I'm going to show you, and that's defining a custom resource
type.

Custom Resources
The standard Windows resources are those which have special functions for
loading and handling them, but they are not the only types you can use.
Resources can be any data you want them to be! Working with custom resources
requires a little more work since you must locate and read the resource data
manually, but it's not too bad. The script file entry for a custom type follows the
basic format you're already used to:
[identifier]

[resource type name]

[filename]

The resource type name is a string that defines your custom resource, and can be
whatever you want. For the purposes of this example, let's say you want to
include a data file called p1config.dat that contains information necessary to
initialize a character in a game program. We'll call the custom resource type
CHARCONFIG. With that in mind, here's an example of what the script file entry
might look like for your data file:
DATA_PLAYERINIT

CHARCONFIG

p1config.dat

Pretty simple, hey? Now that you've included your file, there are three steps you
must take in order to retrieve a pointer to the resource data. Each involves calling
a function we haven't talked about yet, so let's go through them one at a time.
The first thing you must do is to find the resource with a call to FindResource().
Here's the prototype:
HRSRC FindResource(
HMODULE hModule, // module handle
LPCTSTR lpName, // pointer to resource name
LPCTSTR lpType
// pointer to resource type
);

The return value is a handle to the resource's information block, or NULL if the
function fails. The parameters are as follows:
HMODULE hModule: The HMODULE data type is simply an HINSTANCE. Don't ask me
why they felt they needed another name for it, but you should simply pass the
instance of your application. You don't even need a typecast because the data
types are exactly the same.
LPCTSTR lpName: This is the resource identifier. Remember to use
MAKEINTRESOURCE() on this one if you're using numeric constants to define your
resources.
LPCTSTR lpType: This is the resource type, so pass the string you used to define
your resource type. In our case, this is CHARCONFIG.
A sample function call looks like this:
HRSRC hRsrc = FindResource(hinstance,
MAKEINTRESOURCE(DATA_PLAYERINIT), "CHARCONFIG");
This is a handle to the info block the resource resides in. The next step to getting
a pointer to the data is to take this handle and pass it to LoadResource() to
actually load the data. This yields a handle to the resource itself. Here is the
function prototype:
HGLOBAL LoadResource(
HMODULE hModule, // resource-module handle
HRSRC hResInfo
// resource handle
);
The return type, HGLOBAL, is a pretty general handle type, as opposed to the
other load functions we've covered, which returned specific handle types like
HBITMAP or HICON. If the function fails, this value will be NULL. The parameters
are straightforward:
HMODULE hModule: Again, simply the application instance.
HRSRC hResInfo: Pass the handle that was returned by FindResource().
Now that you have a handle to the resource, you can finally get a pointer to the
data that was in the resource file you included. This is achieved with a call to
LockResource(), shown here:
LPVOID LockResource(HGLOBAL hResData);
Simply pass the handle that was returned by LoadResource(). If the return value
is NULL, the function call failed. If not, you've got your pointer! Now you're free to
do whatever you like with the data. Note that the return type is LPVOID
(Windows-speak for void*), so if you want to use array notation on the pointer,
you need to cast it to something like a BYTE*. Now that we've gone through all
the steps, I'll show you an example of a function you might write to return a
pointer to a specified resource:
UCHAR* LoadCustomResource(int resID)
{
HRSRC hResInfo;
HGLOBAL hResource;
// first find the resource info block
if ((hResInfo = FindResource(hinstance,
MAKEINTRESOURCE(resID), "CUSTOMRESOURCETYPE")) == NULL)
return(NULL);
// now get a handle to the resource

if ((hResource = LoadResource(hinstance, hResInfo))


== NULL)
return(NULL);
// finally get and return a pointer to the resource
return ((UCHAR*)LockResource(hResource));
}

Closing
Well, that about does it for resources! See, programming for Windows is fun. :)
Even with all this knowledge of resources, you're still pretty limited in what you
can actually get your programs to do, so next time I'll be going over some basic
Windows GDI (Graphics Device Interface) functions, so you can start using all this
stuff to put some demo programs together. As always, send me your comments,
your ideas, your death threats:
E-mail: ironblayde@aeon-software.com
ICQ: UIN #53210499
Farewell everyone, until we meet again...

Game Programming Genesis


Part III : Tracking Your Window and
Using GDI
by Joseph "Ironblayde" Farrell

Introduction
If you've been with me for the last two articles, you've probably been asking
yourself when I'm going to show you something useful. Well, the wait is over!
Today I'll be showing you the basics of Windows GDI (Graphical Device Interface),
and a few other things along the way, like responding to user input and dealing
with some more of the messages that Windows generates. As far as actually
displaying graphics, I'm going to go over three basic topics: showing text, plotting
pixels, and displaying bitmaps. Before getting into too much of that though, I'm
going to cover several more Windows messages in detail so you will be sure to
know what's going on when the user starts messing with things. They always
do. :)
As always, you need only a basic knowledge of the C language, and the
information that was covered in previous articles of this series. Since this article
will enable you to make some working graphical demos, there is a sample
program available along with the article. The code used for this program was
written and compiled in Visual C++, but it is simple enough that you shouldn't
have to change it to get it working with other compilers. All right, enough with
the disclaimers, and on to the fun stuff!

Device Contexts
In the first article in this series, we defined and registered a window class. One of
the lines in that definition, giving the window's capabilities, was this:
sampleClass.style = CS_DBLCLKS | CS_OWNDC |

CS_HREDRAW | CS_VREDRAW;

standard settings

//

Three of those attributes are fairly self-explanatory, but the other -- CS_OWNDC -requires some explanation. If you recall, I told you that this attribute allowed for
the window to have its own unique device context, and that device contexts
would not be covered just yet. Well, grasshopper, the time has come.
A device context is a structure that represents a group of graphic objects and
their attributes, as well as some output device and its attributes and settings.
Using device contexts allows you to manipulate graphics in a very straightforward
manner, without having to worry about a lot of low-level details. Windows GDI is
a graphics-rendering system which takes Windows graphics calls and passes the
information to the appropriate device driver. To make use of GDI graphics, you
must use device contexts. Thankfully, it's very easy to do. You can get a device
context for a window using a simple function call:
HDC GetDC( HWND hWnd

// handle to a window );

That looks pretty harmless, doesn't it? All you do is pass a handle to the window
for which you want a device context (or DC), and the return value is a handle to
that device context. If you pass NULL, the handle returned is for a DC to the
entire screen. If the function call fails, the return value is NULL.
Now is a good place to mention that device contexts are a little more general than
dealing with graphics calls only. The type of DC we'll be talking about is called a
display device context, because it deals with displaying graphics. In addition,
there are printer device contexts, which use a printer as the output device;
memory device contexts, which allow for manipulation of bitmap data; and
information device contexts, for retrieving data for a specified device. Don't worry
if this all sounds complicated. It's Windows -- its primary function is to confuse
people. :) Once we get into some code, I think you'll find that it's actually not
that difficult.
When you're finished with a device context, you have to release it. This frees up
any memory that was being used by the object -- you'll come across the concept
of releasing objects a lot more in the future. Once again, this is done by using a
simple function call:
int ReleaseDC(
HWND hWnd,
HDC hDC

// handle to window
// handle to device context );

The return value is 1 if the DC was successfully released, or 0 if something went


wrong. The parameters are self-explanatory, but I'll list them here anyway.
HWND hWnd: This is the handle to the window which is referred to by the DC you're
trying to release. If you have a DC for the whole desktop, pass NULL.
HDC hDC: The handle to the device context you want to release.
Before we get into doing some graphics displays with device contexts and GDI, I
want to talk about some of the important messages you'll encounter when
creating a windowed application. The four messages I want to cover briefly are
WM_MOVE, WM_SIZE, WM_ACTIVATE, and WM_PAINT.

Tracking the Status of Your Window


The first two are relatively simple. WM_MOVE is called whenever the window is
moved by the user. The new window coordinates are stored in lparam.
(Remember, messages are further specified by the contents of lparam and
wparam, which are parameters received by your message-handling function.) The

low word of lparam is the x-coordinate of the upper-left corner of the window's
client area. The high word of lparam is the y-coordinate.
The WM_SIZE message is sent when the window is resized. Like the WM_MOVE
message, its parameterization is held in lparam. The low word is the client area's
width, and the high word is its height. But unlike WM_MOVE, the wparam parameter
also holds some significant. It can take any of the following values:
SIZE_MAXHIDE

Some other window has been maximized.

SIZE_MAXIMIZED Window has been maximized.


SIZE_MAXSHOW

Some other window has been restored.

SIZE_MINIMIZED Window has been minimized.


SIZE_RESTORED

Window has been resized, but neither maximized nor minimized.

When I'm writing windowed applications, I usually like to keep a few global
variables that give the window's current position and size. If these variables were
called xPos, yPos, xSize, and ySize, you'd handle the WM_SIZE and WM_MOVE
messages something like this:
if (msg == WM_SIZE)
{
xSize = LOWORD(lparam);
ySize = HIWORD(lparam);
}
if (msg == WM_MOVE)
{
xPos = LOWORD(lparam);
yPos = HIWORD(lparam);
}
Next up is the WM_ACTIVATE message, which tells you when a new window
becomes the active window. This can be useful because you may not want to be
processing all of your program's logic if some other application has the focus.
Sometimes, such as in writing fullscreen DirectX programs, ignoring the
WM_ACTIVATE message can cause your program to experience a fatal error by
doing something it's not supposed to be doing. In any case, it's good to watch the
WM_ACTIVATE messages and take action accordingly.
The WM_ACTIVATE message is sent to both the window being activated, and the
window being deactivated. You can determine which is the case by looking at the
low word of wparam. It will be set to one of three possible values:
WA_CLICKACTIVE Window was activated by a mouse click.
WA_ACTIVE

Window was activated by some other means (keyboard, function


call, etc.)

WA_INACTIVE

Window was deactivated.

For dealing with this message, I'll keep another global variable called bFocus, and
change its value when a WM_ACTIVATE message is received. The code would look
something like this:
if (msg == WM_ACTIVATE)
{
if (LOWORD(wparam) == WA_INACTIVE)
focus = FALSE;
else
focus = TRUE;

// tell Windows we handled it


return(0);

There are two related messages called WM_KILLFOCUS and WM_SETFOCUS, which a
window receives immediately before it loses or gains the keyboard focus,
respectively. Since it's possible for no window to have the keyboard focus, I
suggest using the WM_ACTIVATE message to track your window's status. Now, on
to the biggie.

The WM_PAINT Message


A window receives this important message when part of its client area has
become invalidated. Suppose your program doesn't have the focus, and the active
window is on top of your window. If the user moves that active window, it's going
to reveal a part of your window. Since that part of the window needs to be
refreshed, it is said to be invalidated. To handle this, there are a couple of things
you can do. The first involves a pair of functions designed exclusively for use with
the WM_PAINT message. The first is BeginPaint(). Here's the prototype:
HDC BeginPaint(
HWND hwnd,
LPPAINTSTRUCT lpPaint
paint information );

// handle to window
// pointer to structure for

Before I tell you exactly what the return value is, let's look at the parameters:
HWND hwnd: This is a handle to the window which needs repainting. You should be
used to seeing this parameter by now, right?
LPPAINTSTRUCT lpPaint: Here's the important one. This is a pointer to a
PAINTSTRUCT structure, which contains all sorts of information about the area to
be painted.
And before we go on, I should show you exactly what a PAINTSTRUCT looks like...
typedef struct tagPAINTSTRUCT { // ps
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT;
And the members of the structure are as follows:
HDC hdc: Aha! I knew there was some reason we went over device contexts,
even if it took awile to get here. This is a DC that represents the invalidated area
-- the area that needs to be painted.
BOOL fErase: This specifies whether or not the application should erase the
background. If set to FALSE, the system has already deleted the background.
Remember in our window class when we defined a black brush as the
background? This will cause the system to automatically erase the invalidated
area with that black brush.
RECT rcPaint: This is the most important member, as it tells you the rectangle
that needs to be repainted in order to cover the whole invalidated area. I'll show
you the RECT structure in just a bit.

BOOL fRestore, BOOL fIncUpdate, BYTE rgbReserved[32]: Good news! These


are reserved and are used by Windows, so you and I don't have to worry about
them. :)
Now that I've showed this to you, I can tell you just what BeginPaint() is
accomplishing. It actually does three things. First, it validates the window again,
so that another WM_PAINT message will not be generated unless the window
becomes invalidated again. Second, if your window class has a background brush
defined, like ours does, it paints the affected area with that brush. Third, it
returns a handle to a device context which represents the area needing to be
painted. That area, as we saw, is defined by the important RECT structure:
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;
You've already figured out that this structure represents a rectangle, but there is
one thing that needs to be said about it. RECTs are upper-left inclusive, but lowerright exclusive. What does that mean? Well, let's say you define a RECT like this:
RECT myRect = {0, 0, 5, 5};
This RECT includes the pixel at (0, 0), but it stops short of (5, 5), so that the
lower-right corner of the area described by this rectangle is actually at (4, 4). It
doesn't seem to make much sense at first, but you'll get used to the idea.
Now, remember what I said about using device contexts? Once you're done using
one, you have to release it. In this case, you use the EndPaint() function. Each
call to BeginPaint(), which should only be made in response to a WM_PAINT
message, must have a matching EndPaint() function to release the DC. Here's
the function:
BOOL EndPaint(
HWND hWnd, // handle to window
CONST PAINTSTRUCT *lpPaint // pointer to structure
for paint data );
The function returns TRUE or FALSE indicating its success or failure, respectively,
and takes two simple parameters:
HWND hWnd: Just the handle to the window. Again.
CONST PAINTSTRUCT *lpPaint: A pointer to the PAINTSTRUCT containing the
information about the area in question. Don't let the CONST confuse you. It's just
there to denote and ensure that the function does not alter the contents of the
structure.
For the record, the other way you can validate a window is with a call to
ValidateRect(). If you want to do everything manually instead of letting
BeginPaint() handle it, that's fine. There may be some cases where this is
necessary. So here's the prototype:
BOOL ValidateRect(
HWND hWnd, // handle of window
CONST RECT *lpRect // address of validation
rectangle coordinates );
The return value is TRUE or FALSE for success or failure, and the parameters are
easy to figure out:

HWND hWnd: Are you getting tired of seeing this yet? :)


CONST RECT *lpRect: This is a pointer to the RECT to validate. Again, you don't
need to declare it as a constant; the CONST is just to make sure the function
doesn't go changing things on you. If you pass NULL, the entire client area is
validated.
Now, to wrap up our discussion of this message, I'll show you the framework for
handling a WM_PAINT message. This would be somewhere in your message
handler, as usual. I'm assuming here that we have a global variable called
hMainWindow that is the handle to our window.
if (msg == WM_PAINT) {
PAINTSTRUCT ps; // declare a PAINTSTRUCT for use with
this message
HDC hdc;
// display device context for
graphics calls
hdc = BeginPaint(hMainWindow, &ps); // validate the
window
// your painting goes here!
EndPaint(hMainWindow, &ps);

// release the DC

// tell Windows we took care of it


return(0);
}
The only part of that code that probably doesn't make sense is the place where I
have commented, "Your painting goes here!" Well, if you want your window to be
refreshed with something other than your window class's default brush, you have
to do it yourself, and that involves some graphics work that you haven't seen yet.
Never fear, we'll get there in just a minute! While we're on the topic of messages,
though, there's something I need to explain.

Closing Your Application


There are three messages that seem to be pratically identical, and all deal with
closing things out. They are WM_DESTROY, WM_CLOSE, and WM_QUIT. They're similar,
but you need to know the difference! WM_CLOSE is sent when a window or
application should be closing. When you receive a WM_CLOSE message, it's a good
place to ask the user if they're sure they want to quit, if you want to do it. You
know those little message boxes that are always popping up on your screen when
errors or notifications occur? Well, they're easy to create. In addition to serving
many functions in the final program, they're also handy for reporting debug
information. The call to create your very own message box is pretty simple:
int MessageBox(
HWND hWnd,
LPCTSTR lpText,
box
LPCTSTR lpCaption,
box
UINT uType

// handle of owner window


// address of text in message
// address of title of message
// style of message box );

The parameters, especially the last one, require some explanation:


HWND hWnd: Sooner or later we'll get to a function that doesn't have this, I
promise!

LPCTSTR lpText: This is the text that will appear in the message box. As always,
you can use escape sequences like \n to format the output a little if you want.
LPCTSTR lpCaption: This is the text appearing in the message box's caption bar.
UINT uType: You can combine several different flags in order to create this
parameter, which defines what kind of message box it will be. There are a lot of
MB_ constants you can use, and you can combine any number of them with the |
operator. Here's a list of the useful ones:
Button Definition Flags
MB_ABORTRETRYIGNORE Creates a box with "Abort," "Retry," and "Ignore" buttons.
MB_OK

Creates a box with an "OK" button.

MB_OKCANCEL

Creates a box with "OK" and "Cancel" buttons.

MB_RETRYCANCEL

Creates a box with "Retry" and "Cancel" buttons.

MB_YESNO

Creates a box with "Yes" and "No" buttons.

MB_YESNOCANCEL

Creates a box with "Yes," "No," and "Cancel" buttons.

Icon Definition Flags


MB_ICONEXCLAMATION

Adds an exclamation point icon to the box.

MB_ICONINFORMATION

Adds an information icon to the box.

MB_ICONQUESTIION

Adds a question mark icon to the box.

MB_ICONSTOP

Adds a stop sign icon to the box.

Default Button Flags


MB_DEFBUTTON1

Defines the first button as the default.

MB_DEFBUTTON2

Defines the second button as the default.

MB_DEFBUTTON3

Defines the third button as the default.

MB_DEFBUTTON4

Defines the fourth button as the default.

Other Flags
MB_HELP

Adds a help button to the box. A WM_HELP message is


generated if the user chooses it or presses F1.

MB_RIGHT

Message box text is right-justified.

MB_TOPMOST

Sets the message box to always be the topmost window.

I don't know about you, but I'm starting to think that Microsoft has a programmer
who does nothing but write #define statements all day! Now, the return value is
0 if the box could not be created. Otherwise, the result is one of the following:
IDABORT

"Abort" button was selected.

IDCANCEL "Cancel" button was selected.


IDIGNORE "Ignore" button was selected.
IDNO

"No" button was selected.

IDOK

"OK" button was selected.

IDRETRY

"Retry" button was selected.

IDYES

"Yes" button was selected.

Those lists were so long I almost forgot what we were originally talking about.
Anyway, when you receive a WM_CLOSE message, you can do two things. First, you
can allow the default handler to return a value. If you do this, the application or
window will close as planned. However, if you return 0, the message will have no
effect. This is the basis of the following bit of code:
if (msg == WM_CLOSE) {
if (MessageBox(hMainWindow,
"Are you sure want to quit?",
"Notice",
MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
return(0);
// otherwise, let the default handler take care of it
}
Now, WM_DESTROY is a bit different. It is sent when a window is being closed. By
the time you get a WM_DESTROY message, the window it applies to has already
been deleted from view. If the main window closes, that does not necessarilly end
the application. It will keep running, but without a window. However, when a user
closes the main window, they almost always mean to close the application, so you
have to post a WM_QUIT message when you receive WM_DESTROY if you want the
application to end. You could use PostMessage(), but since this is a special case,
there's a special function for it:
VOID PostQuitMessage(int nExitCode);
The parameter is an exit code that your application returns to Windows.
Remember, WinMain() returns an int, not a void. The nExitCode parameter also
becomes the wparam member of the WM_QUIT message that results. WM_QUIT
represents a request to close the application, so when you get one, you should
end your main loop and return wparam to Windows. Here's an example of what a
simplified WinMain() function might look like with this in place:
int WinMain(HINSTANCE hinstance,
HINSTANCE hPrevInstance,
LPSTR
lpCmdLine,
int
nCmdShow)
{
// initialization stuff goes here
// main loop - infinite!
while (TRUE)
{
// check the message queue
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) // exit main loop on
WM_QUIT
break;

TranslateMessage(&msg);
DispatchMessage(&msg);

// main program logic goes here


}

// perform any shutdown functions here - releasing


objects and such
return(msg.wparam);

// return exit code to Windows

}
Sorry about all that stuff, but it's necessary to make sure your program behaves
correctly instead of causing errors for Windows -- like that ever happens! Now
instead of making you any more impatient with me than you probably already
are, let's look at some basic GDI graphics.

Plotting Pixels
At last! Plotting pixels with GDI is a cinch as long as you've got a display device
context to work with. Remember, calling GetDC() does this for you. To plot a
pixel, not surprisingly, you call SetPixel():
COLORREF SetPixel(
HDC hdc,
int X,
int Y,
COLORREF crColor

//
//
//
//

handle to device context


x-coordinate of pixel
y-coordinate of pixel
pixel color );

The return type is something we haven't encountered yet, a COLORREF. This is not
a structure, but a 32-bit value in the form 0x00bbggrr, where bb is an 8-bit value
for the blue component, gg is green, and rr is red. The high byte is unused and is
always set to zero. Let's take a look at the parameters for SetPixel():
HDC hdc: This is a device context for your window that you should obtain with a
call to GetDC(). You only need to call GetDC() once, and then you can use it for
any number of these functions. Don't get a new DC every time you want to plot a
pixel!
int X, Y: The x- and y-coordinates of the pixel. These are in client coordinates,
meaning that (0, 0) represents the upper-left corner of your window's client area,
not the upper-left corner of the screen.
COLORREF crColor: This is the color you want to set the pixel to. To do this, it's
easiest to use the RGB() macro, which takes values for red, green, and blue -- in
that order -- between 0 and 255. SetPixel() will choose the closest available
color to the one you have specified.
If the function succeeds, the return value is the color that the pixel was set to.
This may not always be exactly the COLORREF you pass if you're working in less
than 24-bit color; Windows will choose the closest match. If the function fails, it
returns -1. As an example, if you want to set the upper-left corner of your client
area to white, you'd use the following call:
SetPixel(hdc, 0, 0, RGB(255, 255, 255));
This call assumes you've gotten a display device context named hdc. Pretty easy,
hey? There's one other way to do it that's just a tad faster. Here's the function:
BOOL SetPixelV(
HDC hdc,
int X,
int Y,
COLORREF crColor

//
//
//
//

handle to device context


x-coordinate of pixel
y-coordinate of pixel
new pixel color );

The parameters are all the same. The return value is simply TRUE or FALSE for
success or failure. SetPixelV() is slightly faster since it doesn't need to return

the actual color that was used to plot. You'll probably never even notice the
difference, unless you're using it thousands of times per frame, but if you don't
need the extra information SetPixel() provides, there's no reason not to take
the slightly increased performance, right?
The only other thing you need to know about plotting pixels is how to read the
value of a pixel that's already been plotted. It's no problem; a quick call to
GetPixel() does the job for you:
COLORREF
HDC
int
int

GetPixel(
hdc,
// handle to device context
XPos, // x-coordinate of pixel
nYPos // y-coordinate of pixel );

The return value is obviously the color of the pixel at the given coordinates. If the
coordinates specified are outside the clipping region (the area represented by the
device context), the return value is CLR_INVALID. The parameters are the same
as for SetPixel(): a device context to use, and the coordinates to operate on.
That's it for plotting pixels. Now let's have a look at GDI's text-rendering
functions.

GDI Text Functions


There are two functions for actually plotting text that you need to be concerned
with. The simpler of the two is TextOut(), as shown here:
BOOL TextOut(
HDC hdc,
int nXStart,
position
int nYStart,
position
LPCTSTR lpString,
int cbString
);

// handle to device context


// x-coordinate of starting
// y-coordinate of starting
// pointer to string
// number of characters in string

By now we've seen enough BOOL-returning functions to know what that means:
TRUE for success, FALSE for failure. The parameters are:
HDC hdc: The device context to use.
int nXStart, nYStart: These are the coordinates of the starting point for the
text, called the reference point. By default, this is the upper-left corner of the
rectangular area occupied by the string. You can change this setting, as we'll see
in just a bit.
LPCTSTR lpString: The text to print out. Since the number of characters is given
in the final parameter, this string does not need to be null-terminated.
int cbString: This is the length of the string, in characters.
TextOut() uses the current settings for text color, background color, and
background type. Before looking at the other, more complicated text-rendering
function, let's take a look at the functions you can use to control the colors being
used.
COLORREF SetTextColor(
HDC hdc,
COLORREF crColor

// handle to device context


// text color );

COLORREF SetBkColor(
HDC hdc,
COLORREF crColor

// handle of device context


// background color value );

SetTextColor() sets the active text color, and SetBkColor() sets the active
background color. The parameters are obviously the device context to apply the
settings to, and the colors to use. Since these are COLORREFs, remember that you
can use the RGB() macro for specifying your colors. Each function returns the
previous value of the attribute it deals with. For instance, if you call
SetTextColor(hdc, RGB(255, 0, 0)), the return value will be the active color
that was being used before you turned it red. Finally, to set the background type,
use SetBkType() as shown:
int SetBkMode(
HDC hdc,
// handle of device context
int iBkMode
// flag specifying background mode );
The device context parameter we've seen before, but the other, iBkMode, can
take one of two values: TRANSPARENT or OPAQUE. If set to TRANSPARENT, any text
you plot will not disturb the background around the text itself. If set to OPAQUE,
plotting text will cause the rectangular region surrounding that text to be filled
with the active background color. The return value of SetBkMode() is simply the
previous background mode.
One more thing about TextOut(). I said you could change the way the reference
point is interpreted, and the way to do it is by using SetTextAlign(), whose
prototype is shown below.
UINT SetTextAlign(
HDC hdc,
// handle to device context
UINT fMode
// text-alignment flag );
The parameters are:
HDC hdc: The device context again. No surprises here.
UINT fMode: A flag or set of flags (logically combined with |) that determine the
meaning of the reference point in a call to TextOut(). Only one flag can be
selected from those affecting horizontal and vertical alignment, and only one of
the two flags affecting use of the current position can be used. The flags are:
TA_BASELINE

The reference point will be on the baseline of the text.

TA_BOTTOM

The reference point will be on the bottom edge of the bounding


rectangle.

TA_TOP

The reference point will be on the top edge of the bounding


rectangle.

TA_CENTER

The reference point will be aligned horizontally with the center of


the bounding rectangle.

TA_LEFT

The reference point will be on the left edge of the bounding


rectangle.

TA_RIGHT

The reference point will be on the right edge of the bounding


rectangle.

TA_NOUPDATECP The current position is not updated by a call to a text output


function. The reference point is passed with each call.
TA_UPDATECP

The current position is updated by each call to a text output


function, and is used as the reference point.

The default setting is TA_LEFT | TA_TOP | TA_NOUPDATECP. If you set


TA_UPDATECP, subsequent calls to TextOut() will ignore the nXStart and nYStart
parameters, and render the text where the last call left off. Now that that's out of

the way, let's look at the bells-and-whistles version of TextOut(), called


DrawText():
int DrawText(
HDC hDC,
LPCTSTR lpString,
int nCount,
LPRECT lpRect,
formatting dimensions
UINT uFormat

//
//
//
//

handle to device context


pointer to string to draw
string length, in characters
pointer to struct with

// text-drawing flags );

This one gets a bit complicated. Since DrawText() formats text, possibly to
multiple lines, the return value is the height of the text in pixels, or 0 if the
function fails. Let's take a look at the parameters, shall we?
HDC hDC: Nothing new here; it's just our good buddy the DC.
LPCTSTR lpString: This is the string to print.
int nCount: This is the length of the string in characters.
LPRECT lpRect: Here's where things start to get a bit different. DrawText() does
several different methods of formatting, including word wrapping, so you must
specify a RECT within which to format the text, rather than simply passing
coordinates.
UINT uFormat: For this, you can use one or more (logically combined with |) of a
long list of flags that represent different methods of formatting. I'll show you a
few of them.
DT_BOTTOM

Justifies text to the bottom of the RECT. This must be combined


with DT_SINGLELINE.

DT_CALCRECT

Calculates the RECT needed to hold the text. If the text is on


multiple lines, DrawText() uses your RECT's width and alters the
height. If the text is on a single line, DrawText() alters your
RECT's width. In both cases, DrawText() adjusts the RECT but
does not actually draw the text.

DT_CENTER

Centers text within the RECT you specify.

DT_EXPANDTABS

If the string contains any tabs (\t), this attribute causes


DrawText() to expand them. The default is eight spaces per tab.

DT_LEFT

Left-justifies the text.

DT_NOCLIP

Draws without clipping. This speeds up DrawText() a bit.

DT_RIGHT

Right-justifies the text.

DT_SINGLELINE Displays text on a single line only. Carriage returns and line feeds
do not overrule this attribute.
DT_TABSTOP

Alters the number of spaces per tab. The number of spaces per
tab must be specified in bits 15-8 (the high byte of the low word)
of uFormat. Again, the default setting is eight.

DT_TOP

Justifies text to the top of the RECT. This must be combined with
DT_SINGLELINE.

DT_VCENTER

Centers the text vertically within the RECT. This must be


combined with DT_SINGLELINE.

There are more of these flags, but you get the idea. All in all, this constitutes a
pretty powerful text rendering system, but remember, all those cool features are
going to slow the function down. You can usually get by just fine by using

TextOut(). That takes care of the text rendering system, so let's do something a
little more exciting.

Displaying Bitmaps With GDI


Remember when I told you that bitmaps are easy to work with, because they're
native to Windows? Well now we're going to find out just how easy it is. :) There
are four basic steps to displaying a bitmap with GDI:
1. Get a device context to your window.
2. Obtain a handle to the bitmap.
3. Create a device context for the bitmap.
4. Copy the image from one device context to the other.
You already know how to do the first step. I alluded to the second one last time,
but didn't go over it. I said that there was a function called LoadBitmap() that
retrieves a handle to a bitmap resource. However, this function is obsolete now; it
has been superseded by LoadImage(), which is much more flexible. So that's
what we'll be using. Here she is:
HANDLE LoadImage(
HINSTANCE hinst,
containing the image
LPCTSTR lpszName,
UINT uType,
int cxDesired,
int cyDesired,
UINT fuLoad

// handle of the instance


//
//
//
//
//

name or identifier of image


type of image
desired width
desired height
load flags );

The function returns NULL if it fails. Otherwise, you get a handle to the bitmap,
which can either be loaded from a resource or from an external file. Notice that
since this function can be used for bitmaps, cursors, or icons, the return type is
simply HANDLE. In Visual C++ 6.0, you'll need to include a typecast to HBITMAP or
the compiler will become angry with you. Here are the parameters for the
function:
HINSTANCE hinst: This should be the instance of your application if you're
loading a resource, or NULL if you want to load from an external file.
LPCTSTR lpszName: This is either the resource identifier -- remember to use
MAKEINTRESOURCE() if you're using numerical constants -- or the full filename of
the image you want to load.
UINT uType: Depending on what you want to load, this should be set to either
IMAGE_BITMAP, IMAGE_CURSOR, or IMAGE_ICON.
int cxDesired, cyDesired: These are the desired dimensions for the image to
be loaded. If you set them to zero, the image's actual dimensions will be used.
UINT fuLoad: Like everything else we've done today, this is one or more of a
series of flags which can be logically combined with the | operator. Here are the
useful flags:
If uType is IMAGE_BITMAP, this causes the function to
return a DIB section rather than a compatible bitmap.
LR_CREATEDIBSECTION (DIB stands for device-independent bitmap.) This basically
means to use all of the bitmap's own properties rather
than making it conform to the properties of the display
device.
LR_DEFAULTSIZE

For icons and cursors, if cxDesired and cyDesired are set


to 0, this flag causes the system metric values for icons

and cursors to be used, rather than the actual dimensions


of the image.
LR_LOADFROMFILE

You must specify this flag if you want to load from a file
rather than a resource.

For loading bitmaps you should use LR_CREATEDIBSECTION, and LR_LOADFROMFILE


if it is appropriate. Now that you have obtained a handle to your image, you must
create a device context and load the bitmap into it. The first step is taken by
calling CreateCompatibleDC(), as follows:
HDC CreateCompatibleDC(HDC hdc);
The parameter is a DC with which to make the new DC compatible. If you pass
NULL, the DC will be made compatiable with the display screen, which is what we
want. The return value is a handle to a memory device context -- not a display
device context! This means that the contents of this DC won't be visible. If the
function fails, the return value is NULL. Now, to get the bitmap into the memory
device context, we use this:
HGDIOBJ SelectObject(
HDC hdc,
HGDIOBJ hgdiobj

// handle to device context


// handle to object );

The type HGDIOBJ is more general than our HBITMAP, so never fear, they're
compatible without any tricks on our part. Here are the parameters:
HDC hdc: This is a handle to the device context which we want to fill with an
object. For loading bitmaps, this must be a memory device context.
HGDIOBJ hgdiobj: And this is a handle to that object. This function is used with
bitmaps, brushes, fonts, pens, and regions; but the only one that concerns us is
bitmaps.
The return value is a handle to the object that is being replaced in the DC, or
NULL if an error occurs. The return values are different for regions, but like I said,
we don't care about regions. :)
Now you've got a bitmap loaded into a DC, and you need only take the last step:
copying the contents of the memory device context to our display device context.
However, it's necessary to obtain some information about the bitmap, such as its
dimensions, which must be used in the function call that will display the image.
For that, we need the GetObject() function, which is used for obtaining
information about graphical objects such as bitmaps.
int GetObject(
HGDIOBJ hgdiobj,
interest
int cbBuffer,
information
LPVOID lpvObject
information );

// handle to graphics object of


// size of buffer for object
// pointer to buffer for object

The return value is the number of bytes successfully obtained, or 0 for function
failure. The parameters for the function are the following:
HGDIOBJ hgdiobj: The handle to the graphics object we want information on. In
this case, pass the handle to the bitmap we loaded.
int cbBuffer: This is the size of the structure receiving the information. In the
case of loading bitmaps, the receiving structure is of type BITMAP, so set this to
sizeof(BITMAP).
LPVOID lpvObject: Pass the address of the structure receiving the information.

You need to define a variable of type BITMAP, and with a quick call to the
GetObject() function, you'll have the information you need. Since the BITMAP
structure is new to us, I'll show you what it looks like:
typedef struct tagBITMAP {
LONG
bmType;
LONG
bmWidth;
LONG
bmHeight;
LONG
bmWidthBytes;
WORD
bmPlanes;
WORD
bmBitsPixel;
LPVOID bmBits;
} BITMAP;

// bm

There aren't too many members to this thing, and we're really only interested in
two of them, but I'll list them all here anyway.
LONG bmType: This is the bitmap type and must be set to zero. Useful, isn't it?
LONG bmWidth, bmHeight: These are the two we're after -- the width and height
of the bitmap, in pixels.
LONG bmWidthBytes: Specifies the number of bytes in each line of the bitmap.
Note that the number of bytes per pixel can be obtained by dividing this value by
bmWidth.
LONG bmPlanes: This is the number of color planes.
LONG bmBitsPixel: This is the number of bits required to represent one pixel. It
would appear that the note I left about figuring this out is useless. :)
LPVOID bmBits: If you want to access the actual image data, this is a pointer to
the bit values for the bitmap.
All right, almost done! Now we have the bitmap in a memory device context, and
we know its dimensions. All we have to do is copy it from one DC to the other,
and that only takes a single function call. See, I told you bitmaps were easy to
deal with. There are actually two options you can use here. I'll show you both of
them.
BOOL BitBlt(
HDC hdcDest, // handle to destination device context
int nXDest, // x-coordinate of destination
rectangle's upper-left corner
int nYDest, // y-coordinate of destination
rectangle's upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
HDC hdcSrc, // handle to source device context
int nXSrc,
// x-coordinate of source rectangle's
upper-left corner
int nYSrc,
// y-coordinate of source rectangle's
upper-left corner
DWORD dwRop // raster operation code );
The return value is TRUE or FALSE based on whether the function succeeds. You've
seen that plenty of times before. There are a lot of parameters, but most of them
are pretty easy to figure out.
HDC hdcDest: The destination device context handle. In our case, this will be the
display device context for our window.
int nXDest, nYDest: The coordinates of the upper-left hand corner of the region
where the bitmap will end up. Remember that for our DC, these coordinates are
client coordinates -- relative to the client area of our window.

int nWidth, nHeight: These are the width and height of the destination and
source rectangles, since this function doesn't perform scaling. Pass the
dimensions of the bitmap.
HDC hdcSrc: This is the source device context handle. In our case, this is the
memory device context that our bitmap is currently residing in.
int nXSrc, nYSrc: The x- and y- coordinates of the source rectangle's upper-left
corner. In this case you would use (0, 0), but this may not always be the case,
depending on what you're using the source DC for, or if you only want to copy a
part of the image.
DWORD dwRop: There are a lot of operation codes you can use here, most of them
dealing with Boolean operations on the data in the two device contexts. The only
one we're interested in is SRCCOPY, which copies the contents of the source DC
directly to the destination DC.
That's about all there is to it! The other option you have is to use StretchBlt(),
which requires you to specify the width and height for both the source and
destination rectangles. StretchBlt() then scales the image in the source DC to
fit in the rectangle specified on the destination DC. This can be useful for scaling
images, but as always, since BitBlt() has fewer capabilities to worry about, it's
faster. Here is the prototype for StretchBlt():
BOOL StretchBlt(
HDC hdcDest,
// handle to destination device
context
int nXOriginDest, // x-coordinate of upper-left
corner of dest. rectangle
int nYOriginDest, // y-coordinate of upper-left
corner of dest. rectangle
int nWidthDest,
// width of destination rectangle
int nHeightDest, // height of destination rectangle
HDC hdcSrc,
// handle to source device context
int nXOriginSrc, // x-coordinate of upper-left
corner of source rectangle
int nYOriginSrc, // y-coordinate of upper-left
corner of source rectangle
int nWidthSrc,
// width of source rectangle
int nHeightSrc,
// height of source rectangle
DWORD dwRop
// raster operation code );
The parameters are basically the same as those in BitBlt(), so I shouldn't have
to go through them again. The raster operation codes for StretchBlt() are the
same as those for BitBlt(); just set it to SRCCOPY. Now, the last thing you need
to know is a little bit of cleanup. Creating a DC like we did for this example is not
quite like getting a DC for the display device. Instead of calling ReleaseDC(), you
must call DeleteDC(). It looks basically the same:
BOOL DeleteDC(HDC hdc);
The parameter is simply the device context to delete, and the return value is a
BOOL again, and we all know what that means, right? All right, are you feeling
pretty good about all this stuff? Just for the sake of tying all these steps together,
I'll show you a function you can use for loading and displaying a bitmap resource
using all GDI functions. For this example, I'm assuming you have saved the
handle to the instance of the application in a global called hinstance.
int ShowBitmapResource(HDC hDestDC, int xDest, int yDest,
int nResID)
{

HDC hSrcDC;
context
HBITMAP hbitmap;
BITMAP bmp;
int nHeight, nWidth;

// source DC - memory device


// handle to the bitmap resource
// structure for bitmap info
// bitmap dimensions

// first load the bitmap resource


if ((hbitmap = (HBITMAP)LoadImage(hinstance,
MAKEINTRESOURCE(nResID),
IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION))
== NULL)
return(FALSE);
// create a DC for the bitmap to use
if ((hSrcDC = CreateCompatibleDC(NULL)) == NULL)
return(FALSE);
// select the bitmap into the DC
if (SelectObject(hSrcDC, hbitmap) == NULL)
return(FALSE);
// get image dimensions
if (GetObject(hbitmap, sizeof(BITMAP), &bmp) == 0)
return(FALSE);
nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;
// copy image from one DC to the other
if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight,
hSrcDC, 0, 0,
SRCCOPY) == NULL)
return(FALSE);
// kill the memory DC
DeleteDC(hSrcDC);
// return success!
return(TRUE);
}

One Last Thing


You may not realize it, but you now have enough knowledge of Windows
programming to create a working game based on Windows GDI! You can create
the window, and you can show the graphics. The game logic is just the same old
C you've come to know and love. You can even use the mouse by processing the
messages it generates. There's just one thing you're missing, and even though
it's off-topic for this article, I can't leave without mentioning it, and that's
keyboard support. Windows has a great function for determining the state of the
keys on the keyboard: GetAsyncKeyState(). It returns a 16-bit value, the high
bit of which indicates whether the key is currently depressed or not. Here's the
prototype:
SHORT GetAsyncKeyState(int vKey);

The parameter is an identifier for a key on the keyboard, and takes constants
beginning with VK_ for "virtual key." Some of the most common ones are
VK_RETURN, VK_ESCAPE, VK_UP, VK_LEFT, VK_RIGHT, and VK_DOWN. You can even
use VK_LBUTTON and VK_RBUTTON for the mouse buttons! How convenient. Just
mask out the high bit, and if it's 1, the key is being pressed. A useful macro for
doing this that I always use is:
#define KEYSTATE(vknum) ((GetAsyncKeyState(vknum) &
0x8000) ? TRUE : FALSE)
If you're scratching your head over that, you probably haven't seen the
conditional operator (?) before. This operator -- the only ternary operator in the C
language -- evaluates the expression on its left. If the expression is true, the
expression evaluates to the value on the left side of the colon. If the expression is
false, the expression evaluates to the value on the right side of the colon. Got it?
Cool, hey?
A few notes: first, the method I have just shown you only tells whether the key is
up or down. It doesn't tell you when the key was pressed. So if you want to test
for discrete keypresses rather than continuous ones, you'll have to make up some
logic to do this. GetAsyncKeyState() also uses the low bit of its return value to
tell whether the key has been pressed since the last call to the function, but that
may not be good enough. One solution is to create a table of the 256 possible
values the function can take, then call each one every frame, and compare the
new values to the old ones.

Closing
Now you've got all it takes for a GDI-based game, and I've taken this article a lot
longer than I wanted it to go. Congratulations to both of us. :) Since I covered a
lot today, I have developed a sample program for you to look at. It recreates the
starfield screensaver that comes with Windows, only in an .EXE format and in a
window instead of fullscreen. The program illustrates the creation of a window,
the processing of several key messages, and using a device context for pixelplotting. The download includes the source and the compiled program, and is
available here. Any questions? I'm always happy to help out. You can reach me by
E-mail at ironblayde@aeon-software.com, or on ICQ at UIN #53210499.
Now that we've covered all this Windows stuff, next time I'll be taking you on a
magical journey into the wonderful world of DirectX. See you then!

Game Programming Genesis


Part IV : Introduction to DirectX
by Joseph "Ironblayde" Farrell

Introduction
Ahh, it's a good day. Know why? Because today we'll be taking a first look at the
awesome creation that is DirectX. It's several times faster than Windows GDI,
usable from different languages and different platforms, and supports everything
from plotting pixels to advanced 3D effects, from playing simple sound effects to
streaming digital music, from handling the keyboard to using Force Feedback
controllers. It even gives you a hand with networking, and installing the run-time
libraries on the user's machine. Of course, it's a huge subject and it would take

several books to cover all of it, not to mention about a hundred times more
knowledge than I have to give you, but I can get you started. :)
For this article you need only the knowledge of Windows programming covered in
my first few articles, and a working knowledge of C. Since I'll be talking a bit
about the Component Object Model on which DirectX is based, some knowledge
of C++ will definitely help out this time around. If you don't have it, don't worry
about a thing. I'll give you a brief explanation of the C++ elements of the article
as we get to them. Rest assured that you don't need to know too much about C+
+ to use DirectX. All right? Let's do it.

What is DirectX?
DirectX is a game developer's API, or Application Development Interface. It is a
group of software libraries that lets you make non-hardware-specific function
calls, and handles all the details of the machine it happens to be running on. If
the hardware supports the functions you're using, then hardware acceleration
does the job, which means it's FAST. If not, DirectX has software emulation that
takes over. It will be slower -- much slower in some cases -- but the point is that
you don't have to worry about the low-level details. You can just focus on the
game. The layer of DirectX that takes care of hardware-supported features is
called the hardware acceleration layer (HAL), and the software emulation layer
that kicks in if the hardware doesn't support a feature is called the hardware
emulation layer (HEL).
DirectX has several components, each one with a specific purpose. DirectDraw is
probably the most important one, beacuse in the end, all graphics end up going
through it. Used by itself, it's basically a bitmap engine used for 2D graphics, but
Direct3D -- another component of DirectX -- also works through DirectDraw when
it comes time to display each frame. DirectDraw is what we'll be concentrating
our attention on today. Other components of DirectX include DirectInput, which
handles every input device you can think of, and probably a lot of devices neither
of us has ever heard of; DirectSound, which is used for digital sound and music;
DirectMusic, which is a more recent addition to the DirectX package and is used
for MIDI music; and DirectPlay, for networking.
DirectX is based on a standard called the Component Object Model, which can be
a bit confusing. Creating your own COM objects can be a tricky subject -- don't
ask me how to do it -- but understanding the way it works isn't too bad. I'm only
going to cover the properties of COM, not the details about how you would create
a COM object yourself. So if this next section confuses you a little, don't worry.
You don't have to recreate the functionality I'm describing. Using COM objects is a
lot easier than creating them, as we'll see a bit later.

The Component Object Model (COM)


COM is a specification that defines rules by which reusable software components,
called COM objects, are created. Each object is a collection of interfaces, which
themselves are collections of functions -- basically, C++ classes. Using Microsoft's
notation, the name of an interface always begins with an "I," much like the way
regular class names begin with a "C."
Every COM interface is derived from a special base class called IUnknown, which
has only three methods: QueryInterface(), AddRef(), and Release(). For you
C programmers, a method is simply a member function of a class, and a class is
nothing more than a struct whose member functions or variables may not be
directly accessible to the user. I know that's not a great definition, but I don't
want to get into a lengthy discussion of OOP here! Anyway, back to the three
methods of IUnknown:

QueryInterface(): This is probably the most important function of a COM


object. It is used to retrieve a pointer to the interface you want to work with. In
order to get such a pointer, you must know the GUID of the interface. GUID
stands for "globally unique identifier" and is a 128-bit value used to identify the
interface. GUIDs used in this way are often called interface IDs, or IIDs. When
creating a COM object, you can't just make up a GUID. You must use a special
program to create one; most Win32 compilers come with such a tool. These
programs apply an algorithm that guarantees that no GUID will ever be generated
twice.
It sounds impossible, but it's not. There are enough combinations that these
programs can contain rules by which they generate GUIDs such that no
combination will ever be repeated. Let me put it this way. There are six billion
people living on this planet. If every one of those people had five million kids, and
every one of those kids had five million dogs, and men, women, children and
dogs alike sat around all day, every day, generating one GUID per second, it
would take them about seventy million years to use up all the combinations.
That's probably not going to happen. :) I know of only one dog that sits around
generating GUIDs, and it takes him at least ten seconds for each one.
AddRef(): COM objects use a variable called a reference count to track their
usage. This function simply adds one to the reference count for that particular
COM object, and must be called when retrieving a pointer to one of the interfaces
contained within that COM object. You almost never do this manually. There are
several ways you can obtain an interface pointer, and the ones you'll be using
automatically call AddRef().
Release(): When you're finished using an interface pointer, you must release it.
This decrements the reference count for the COM object. So when you're writing
a program that will be using one or more COM objects, the reference count for
each COM object should be at zero when the program ends. Sometimes, if you
create an object, and then use that object to create a child object, releasing the
parent will also release the child. Since this is not guaranteed, it's usually a good
idea to release objects in the reverse order that you created them in.
One of the nice things about COM is that its objects can be used with any
language, and on any platform. How is this possible? One of the guidelines
defining the COM specification states that every COM object must match the
binary image that would be generated by a Microsoft Visual C++ compiler. It
seems a bit strange to think of it that way, but consider this: once you compile
your code, it just becomes binary data. You can't look at a string of 1s and 0s and
divine whether it was C++ or Visual Basic before it was compiled, can you?
Neither can I.
Another feature of COM is that you can recreate or update COM objects without
changing or recompiling the programs that use them. The reason this is possible
is that COM objects are usually dynamic-link libraries (.DLL files). They aren't
compiled into programs that use them; they are linked at run-time. Thus, if a
program you write ships with a few COM objects you created, and later on you
want to add or change something in one of those COM objects, you simply need
to supply users with the new .DLL file. No recompiling the main program is
necessary.
The new version of the COM object, however, must contain all of the old
interfaces and functions. This is to preserve backwards-compatibility. It
guarantees that any program created to run with old versions of COM objects will
function exactly the same if it's using new versions of those objects. Since DirectX
follows the COM specification, the same rules apply. A program developed with
DirectX 5.0 will work just fine if the user has DirectX 7.0 installed on his
computer. Cool, hey?

Setting Up

There are three main things you need to develop programs with DirectX. First are
the COM objects themselves, which are inside .DLL files. These .DLLs need to be
registered with Windows, which is what happens when you install DirectX. Inside
these objects are interfaces like IDirectDraw that we'll be using to create DirectX
applications. But this isn't enough, because handling DirectX on the COM level
would be tedious and frustrating. There are times when you'll make a few direct
COM calls, but for the most part, we want to use something easier.
That's where the static libaries (.LIB files) come into play. These are the main
part of the DirectX SDK, which is available free from Microsoft. They contain
"wrapper" functions to make using DirectX a whole lot easier. To use the various
components of DirectX, you need to link the appropriate libraries to your project.
For DirectDraw, this would be ddraw.lib.
Finally, you need the DirectX header files (.H files) that contain the function
prototypes, macros, constants, and variable types that are used for each
component of DirectX. For DirectDraw, this would be ddraw.h.
To make sure you're using the correct versions of these files, you must set your
compiler to include the directories where you have installed the SDK. There are
several lists of directories to search for library and header files, and the SDK
directories must be first on the list. This is important, because most Win32
compilers ship with some version of these files already included. If the SDK
directories are not first on the list, the older versions that came with your
compiler will be included. The two directories you need to set are those for library
and header files. For example, if you installed the DirectX SDK in your c:\mssdk
directory, the first item on the include file directory list should be
c:\mssdk\include and the first item on the library directory list should be
c:\mssdk\lib. In Visual C++, you can find these settings by choosing
"Options..." from the Tools menu.
For this and any remaining articles dealing with DirectX, we will be using DirectX
7.0 (or higher). If you don't have the DirectX SDK, you'll need to get it from
Microsoft. You don't need the whole thing, just the essentials. The entire SDK has
tons of examples packed in with it, which makes for a whopping 128MB
download. If you're like me and you're not lucky enough to have high-speed
Internet access, you probably don't want to wait 12 hours for that sucker to
download. Scroll down on the SDK download page, and you'll find the links for
only the libaries, headers, and docs. It's around 10MB that way, which is much
more reasonable.

DirectX Version Numbers


You wouldn't think something as simple as a version number would require an
explanation, but remember who we're dealing with here. :) Microsoft may have
created an incredible technology in DirectX, but they're still out to confuse
everyone. With each release of DirectX, not all of the interfaces are updated.
Therefore, even though there have been seven versions of DirectX, there have
not been seven versions of DirectDraw. When DirectX 6 was the most recent
version, the most recent interface for DirectDraw was IDirectDraw4, not
IDirectDraw6. The most recent release which updated DirectDraw was DirectX 7,
and so we'll be dealing with IDirectDraw7. Strange, hey? I thought I'd bring that
up now, so you know what's going on when you see it later.
One last thing. When I wrote this article, DirectX 7 was the most recent API
available, but you've probably heard that now we have DirectX 8. You have
probably also heard that as of now, DirectDraw is no longer being updated.
Instead, there's DirectX Graphics, which is one big catch-all graphics API. Now,
just because DirectDraw isn't being updated doesn't mean we can't still use it.
This is COM, after all. If you want to write 2D games with DirectX 8 interfaces,

you need to employ 3D methods to create a 2D view. It sounds cool, and it is,
since using 3D gives you access to more hardware-supported features like alphablending, but it has a problem. It's going to be slow if the user's machine doesn't
have hardware acceleration.
DirectDraw is easier to learn, and its software layer is nice and fast since the
added processor burden of using 3D is not present. So I will be using DirectDraw
in this series. This also has to do with the fact that this and the next couple of
articles in the series were written before the release of DirectX 8, and also
because I still know very little about how to use the DirectX 8 interfaces, so I can
hardly go telling you how to do it, can I? :) In any case, DirectDraw still makes a
great introduction to the world of DirectX.

Overview of DirectDraw
To set up DirectDraw for use in your program, you need to do a minimum of four
things in the initialization section of your program. They are:
1. Create a DirectDraw object.
2. Set the cooperation level.
3. Set the screen resolution and color depth (fullscreen applications).
4. Create at least one DirectDraw surface.
Before looking at how these steps are accomplished, let's go over a brief
explanation of what each one means. First, you need to create a DirectDraw
object, meaning we want to retrieve a pointer to the IDirectDraw7 interface.
That's pretty simple, right? There are actually three ways to do this. You can
make a direct COM call, or use one of two DirectDraw wrapper function. Each has
its merits, so we'll be looking at all three of them in a bit.
Second, you need to set the cooperation level. This is probably new to you.
Cooperation is a concept that's introduced because Windows is a multitasking
operating system. The idea is that all programs running at any given time have to
inform Windows what resources they're going to be using, and in what way. This
is to make sure Windows doesn't try to take resources essential to your program
and allocate them to something else, and so it has the details of how your
program will be operating. Don't worry, it's just a simple function call.
The third item on the list is familiar, right? If you're writing a fullscreen
application, like a game will usually be, you need to set the screen resolution and
color depth to where you want them. Doing this in a windowed application is
usually not a good idea, because it can cause problems with other programs that
are running at the same time. You'd also have to make certain to restore it when
you are finished. In fullscreen mode, setting the resolution is just a single call to
DirectDraw, and when your program ends, the Windows desktop settings are
restored.
Lastly, and most importantly, is the concept of a DirectDraw surface. Manipulating
surfaces is what DirectDraw is all about. Basically, a surface is an area in memory
reserved for graphics and graphical operations. The size of a DirectDraw surface
is defined by its width and height, in pixels, so you can think of it as a rectangular
area for drawing graphics. It has its own interface, called IDirectDrawSurface7.
There are three main kinds of surfaces, each of which we'll be using between this
article and the next.
Primary surfaces: Every DirectDraw application must have a primary surface in
order to accomplish anything. The primary surface is the surface that represents
the user's display. Its contents are always visible. As such, a primary surface is
automatically set to the width and height of the screen mode.
Back buffers: Back buffers are surfaces that are attached to the primary
surface, but not visible. This is how animation is created without flicker. Normally,
you draw each frame on a back buffer, and then copy the contents of the back

buffer to the primary urface, causing it to appear instantaneously. Since they are
attached to the primary surface, they are also the same size as the display.
Offscreen buffers: These are very much like back buffers, only they are not
attached to the primary surface. They are most often used for storing bitmaps,
although you can do anything you want with them. Offscreen buffers can be any
size you want. The only limitation is the amount of memory the system has.
DirectDraw surfaces can either be created in system memory, or directly on the
video card in VRAM. If you have all of your surfaces in video memory, the speed
is going to be fantastic. System memory is slower. Also, if you have one surface
stored in video memory, and one stored in system memory, performance will
suffer quite a bit, especially if the video card in question has lousy memory
bandwidth. Anyway, the moral is that if you can get away with creating all of your
surfaces in video memory, it's probably worth doing so.
All right, now that we have some idea for what we have to do, let's see how we
go about doing it. Here's the plan. We're going to create a fullscreen DirectX
application that runs in 640x480x16bpp. I'll go through all the DirectX stuff you
need to do that, but before you can go setting up DirectX, you need to have a
window to operate on. That much is up to you! We went through it in my first
article, so you should be pretty familiar with creating windows by now. Since this
is going to be a fullscreen application, you're going to want a window that doesn't
have any Windows controls on it, so for the window style, use WS_POUP |
WS_VISIBLE. Got it? All right, here we go.

Creating a DirectDraw Object


As I said before, there are three ways to do this. We could either use one of two
DirectDraw wrapper functions, or make a call directly to the COM object. Let's
look at all of them, just to get ourselves used to this stuff. The last method I'll
show you is by far the easiest, so you'll probably want to use that. As for the
other two, they give you a first look at a few different things you'll come across
again. First, have a look at DirectDrawCreate():
HRESULT WINAPI DirectDrawCreate(
GUID FAR *lpGUID,
LPDIRECTDRAW FAR *lplpDD,
IUnknown FAR *pUnkOuter
);
Looks a bit strange, doesn't it? The HRESULT return type is standard for
DirectDraw functions. If successful, the return value is DD_OK. Each function has
several constants it can return describing various errors, but in the interest of
keeping the length down a bit, I won't list the values for every function. You can
always look them up. One thing I will mention is that there are two useful macros
you can use to determine the result of a DirectDraw function call: SUCCEEDED()
and FAILED(). Pretty self-explanatory, wouldn't you say? Just put the function
call inside the macro to see what's happening. Anyway, the parameters for the
function are:
GUID FAR *lpGUID: This is the address of the GUID identifying the driver to use.
For the default, simply pass NULL. Otherwise, there are two values you can use
here for debugging purposes:
Creating a DirectDraw object with this flag means that
DDCREATE_EMULATIONONLY no hardware support will be utilized, even if it is
available. All operations are handled in the HEL.
DDCREATE_HARDWAREONLY

Creating a DirectDraw object with this flag means that

only the HAL will be used; if the hardware doesn't


support an operation you call, the function calling that
operation will return an error rather than invoking the
HEL.
LPDIRECTDRAW FAR *lplpDD: This is the address of a pointer to a DirectDraw
object; it will be initialized as such if the function succeeds. You must declare a
variable of type LPDIRECTDRAW that will hold your interface pointer, then pass the
address to that pointer.
IUnknown FAR *pUnkOuter: This parameter is reserved for advanced COM
features that are not yet implemented. Always pass NULL.
That wasn't so bad, but there is a problem. This function gives you a pointer to an
IDirectDraw interface, but we want a pointer to the IDirectDraw7 interface!
What do we do? The answer is to call the QueryInterface() method of the
DirectDraw object we have obtained, and request the IDirectDraw7 interface.
Here's what the call looks like:
HRESULT QueryInterface(
REFIID iid,
// Identifier of the requested
interface
void **ppvObject
// Address of output variable that
receives the
);
// interface pointer requested in
iid
The parameters are pretty straightforward:
REFIID iid: Remember when I said GUIDs are sometimes called IIDs for
interfaces? This is what I was talking about. The IID for IDirectDraw7 is
IID_IDirectDraw7. To use this constant, you must link the dxguid.lib library to
your project.
void **ppvObject: Similar to what we did with DirectDrawCreate(), except
here you should declare a pointer of type LPDIRECTDRAW7, and pass its address. If
you're using Visual C++ 6.0, you'll probably need a typecast here.
Now we have two interface pointers: one to an IDirectDraw interface, and one to
IDirectDraw7. The latter is what we want; the former is useless. Remember that
when you're done using an interface, you must call its Release() method to
decrement the reference count for its COM object. The prototype for Release() is
simple:
ULONG Release(void);
The return value is the resulting reference count, which you would only need to
worry about for testing or debugging purposes. Also, it's recommended that for
safety, you should set a pointer to a released interface to NULL. It's also usually a
good idea to set such pointers to NULL when you declare them in the first place.
Are you following me? It can be a bit much to remember in the beginning, but it'll
become second nature to you in no time. Let's bring it all together and look at an
example of getting an IDirectDraw7 interface pointer:
LPDIRECTDRAW lpdd = NULL;
// pointer to IDirectDraw
(temporary)
LPDIRECTDRAW7 lpdd7 = NULL; // pointer to IDirectDraw7
(what we want)
// get the IDirectDraw interface pointer
if (FAILED(DirectDrawCreate(NULL, &lpdd, NULL)))
{

// error-handling code here

// query for IDirectDraw7 pointer


if (FAILED(lpdd->QueryInterface(IID_IDirectDraw7,
(void**)&lpdd7)))
{
// error-handling code here
}
else
{
// success! release IDirectDraw since we don't need it
anymore
lpdd->Release();
lpdd = NULL;
}
Now, if you're a C programmer, you may be a little confused by the way I called
QueryInterface() and Release(). You've probably seen the -> operator before.
It's used to dereference members of a struct when using a pointer to the struct
rather than the variable itself. It's the same thing with objects, except that in this
case, the member you're making reference to is a function instead of a variable.
While we're on the topic, I'll introduce another bit of C++ notation as well, the
scope resolution operator (::). It's used to show the class (or interface, in our
case) of which a certain function or variable is a member. In this case,
QueryInterface() is a method of the base interface IUnknown, so we would refer
to it as IUnknown::QueryInterface(). I'll be using this often in the future, so
remember it!
To be honest, that's only useful for demonstrating how to use the
QueryInterface() method, which is a part of all DirectX interfaces, so let's move
on. Next, just to show you what it looks like, let's use the COM method. The nice
thing about doing it this way is that you can get an IDirectDraw7 interface
pointer immediately, instead of getting the older pointer and querying for the new
one. First, you have to initialize COM, like this:
HRESULT CoInitialize(LPVOID pvReserved);
It doesn't get much easier. The parameter is reserved as must be set to NULL.
When you're finished with COM calls, you need to uninitialize it, with an even
easier call:
void CoUninitialize(void);
I usually call CoInitialize() in the very beginning of my DirectX programs, and
call CoUninitialize() at the very end, after I've released all my DirectX objects.
Once COM is initialized, you can get the pointer you're after by calling
CoCreateInstance(), which can get a little ugly:
STDAPI CoCreateInstance(
REFCLSID rclsid,
// Class identifier (CLSID) of
the object
LPUNKNOWN pUnkOuter, // Pointer to whether object is
or isn't part
// of an aggregate
DWORD dwClsContext, // Context for running
executable code
REFIID riid,
// Reference to the identifier
of the interface

LPVOID *ppv
that receives
);
requested in riid

// Address of output variable


//

the interface pointer

If the call succeeds, the return value is S_OK. The parameters require a bit of
explanation, so here's the list:
REFCLSID rclsid: This is a class identifier (not to be confused with the IID),
which also has constants defined for it. For IDirectDraw7, use
CLSID_DirectDraw. Notice the version number is not specified because this is a
class identifier, not an interface identifier.
LPUNKNOWN pUnkOuter: This is the same thing we saw in DirectDrawCreate().
Set it to NULL.
DWORD dwClsContext: The value required here is called an execution context, and
it defines the way that the code that manages the newly created object will run.
The values it can take are defined in an enumeration called CLSCTX. In this case,
use CLSCTX_ALL, which includes all the possible values.
REFIID riid: We saw this in our call to QueryInterface(). The IID is
IID_DirectDraw7.
LPVOID *ppv: This was also in DirectDrawCreate(), and is the address of the
pointer to our interface.
That one call takes the place of our calls to DirectDrawCreate(),
QueryInterface(), and Release() in the previous example, so it's a bit less
code, but it really doesn't matter which way you decide to use. The COM call is a
little less hassle than the first method we saw, since it doesn't require getting that
extra interface pointer. Once you create an object using CoCreateInstance(),
though, you have to initialize it by calling the Initialize() method of the
object. In C++ this would be written as IDirectDraw7::Initialize(). Here's
the prototype:
HRESULT Initialize(GUID FAR *lpGUID);
Use the same GUID you would have used in the call to DirectDrawCreate(). In
other words, NULL. So before moving on, let me show you the example of using
COM to create a DirectDraw object:
LPDIRECTDRAW7 lpdd7; // interface pointer
// initialize COM
CoInitialize(NULL);
// create the object
CoCreateInstance(CLSID_DirectDraw, NULL, CLSCTX_ALL,
IID_IDirectDraw7, (void**)&lpdd7);
// initialize the object
lpdd7->Initialize(NULL);
It's good to see an example of calling COM directly, since there may come a time
when you want to -- or have to -- do this instead of calling one of the high-level
wrapper functions. Finally, now that you've seen the hard ways to do this, let's
use the easy way. There is another wrapper function that takes care of everything
we want to do, in a single call. No querying for higher interfaces, no setting up
COM, no nothing. Here's the function:
DirectDrawCreateEx(
GUID FAR *lpGuid,

);

LPVOID *lplpDD,
REFIID iid,
IUnknown FAR *pUnkOuter

All these parameters should look familiar, because we've just seen them. The
first, second, and fourth parameters are the same ones we passed to
DirectDrawCreate(), only in this case we need to cast the address of our
interface pointer to void** -- don't ask me why; it wasn't my idea. The third
parameter, riid, is the interface ID that we passed to CoCreateInstance, so just
use IID_IDirectDraw7 and we're good to go. That's it! Now that we've got our
DirectDraw object, we can start doing things with it. The first two things we want
to do are setting the cooperation level and screen resolution, so let's take a look.

Setting Cooperation and Resolution


We don't need anything fancy here; setting the cooperation level with Windows is
as easy as calling IDirectDraw7::SetCooperativeLevel(), and setting the
resolution is a simple call to IDirectDraw7::SetDisplayMode(). Is that cool or
what? First up is the cooperation level. Here's the function you use:
HRESULT SetCooperativeLevel(
HWND hWnd,
DWORD dwFlags
);
The return type is the HRESULT you should already be getting used to seeing from
these DirectX functions. On all such calls, you should be using the SUCCEEDED()
or FAILED() macros to test the result of the call. Here are the parameters:
HWND hWnd: Something familiar! Pass the handle to your main window, so
Windows knows who's going to be hording all of its resources. :)
DWORD dwFlags: This should look familiar too. Every time we've seen a dwFlags
parameter, it almost always results in a big list of constants that can be combined
with the | operator. I'd hate to disappoint you!
DDSCL_ALLOWMODEX

Allows the use of Mode X display modes (like 320x200,


320x240, or 320x400). This must be combined with
DDSCL_EXCLUSIVE and DDSCL_FULLSCREEN.

DDSCL_ALLOWREBOOT

Allows the use of the Ctrl+Alt+Del Windows shortcut


while running.

DDSCL_EXCLUSIVE

Indicates the application will have exclusive access to


the display screen; must be combined with
DDSCL_FULLSCREEN.

DDSCL_FULLSCREEN

Microsoft's effort to create redundancy; it must be


combined with DDSCL_EXCLUSIVE. :)

DDSCL_NORMAL

Indicates a normal Windows application. Use this for


windowed DirectX programs. Cannot be combined with
DDSCL_ALLOWMODEX, DDSCL_EXCLUSIVE, or
DDSCL_FULLSCREEN.

DDSCL_NOWINDOWCHANGES Indicates that DirectDraw may not minimze or restore


the application window on activation.
There are also a few flags specific to Windows 98 and NT 5.0 that deal with
device windows, but I've left them off the list because we'll only be using these

simple flags. Since we want to create a fullscreen application in 640x480x16


resolution, we would use the following call:
lpdd7->SetCooperativeLevel(hwnd, DDSCL_ALLOWREBOOT |
DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
So now that the cooperation level is all taken care of, let's look at the function
used to change the screen resolution:
HRESULT SetDisplayMode(
DWORD dwWidth,
DWORD dwHeight,
DWORD dwBPP,
DWORD dwRefreshRate,
DWORD dwFlags
);
Remember to test for success or failure! Most of the parameters are just what
you'd expect:
DWORD dwWidth, dwHeight: The dimensions of the new display mode, in pixels.
DWORD dwBPP: The color depth of the new display mode, in bits per pixel. This can
be set to 8, 16, 24, or 32. be warned, most video cards do not support 24-bit
color.
DWORD dwRefreshRate: This is used to set the refresh rate for the screen, but you
should probably just set it to 0, which indicates that the default for your display
mode should be used.
DWORD dwFlags: Sorry, I haven't got a big list for you this time. The only valid
flag is DDSDM_STANDARDVGAMODE, which sets screen mode 0x13 (the DOS coder's
best friend!) instead of Mode X 320x200x8 mode. If you're using something other
than 320x200x8, which you almost certainly will be, set this to 0.
That's all it takes to set the resolution to what you want it to be. Keep in mind
that the video card may not support every resolution you want to create.
640x480, 800x600, 1024x876, etc. are pretty standard, but if you ask for
something like 542x336, you'll almost certainly get an error. Some devices will
build some odd display modes, but the idea is to be compatible with as many
machines as possible, right? Let's move on.

Creating Surfaces
It's time for something that requires a little more than just a function call!
Creating surfaces isn't too tough. Actually, it is accomplished with a single
function call, but first you need to fill out a structure that describes the surface
you want to create. Before I show you this, I just want to say that you don't have
to fill out the whole thing, so never fear. :) Here it is, the DDSURFACEDESC2:
typedef struct _DDSURFACEDESC2 {
DWORD
dwSize;
DWORD
dwFlags;
DWORD
dwHeight;
DWORD
dwWidth;
union
{
LONG
lPitch;
DWORD
dwLinearSize;
} DUMMYUNIONNAMEN(1);
DWORD
dwBackBufferCount;
union

DWORD
dwMipMapCount;
DWORD
dwRefreshRate;
} DUMMYUNIONNAMEN(2);
DWORD
dwAlphaBitDepth;
DWORD
dwReserved;
LPVOID
lpSurface;
DDCOLORKEY
ddckCKDestOverlay;
DDCOLORKEY
ddckCKDestBlt;
DDCOLORKEY
ddckCKSrcOverlay;
DDCOLORKEY
ddckCKSrcBlt;
DDPIXELFORMAT ddpfPixelFormat;
DDSCAPS2
ddsCaps;
DWORD
dwTextureStage;
} DDSURFACEDESC2, FAR *LPDDSURFACEDESC2;
DirectX has lots of big structures. This one is pretty big, and what's worse, it's got
all sorts of structures nested inside it! Anyway, let's take a look at this monster
and see what it's got to offer. I'm only going to cover the important ones, so I
don't grow old writing this.
DWORD dwSize: Any DirectX structure has a dwSize member, which must be set to
the size of the structure. It's there so that functions receiving a pointer to one of
these structures can determine its size.
DWORD dwFlags: Great, more flags. :) These flags are used to tell a receiving
function which fields are important. Any field containing valid information must
have its corresponding flag specified in dwFlags, and they can all be combined
with | as usual. Here's the list:
DDSD_ALL

Indicates that all input fields are valid.

DDSD_ALPHABITDEPTH

Indicates that the dwAlphaBitDepth member is valid.

DDSD_BACKBUFFERCOUNT Indicates that the dwBackBufferCount member is valid.


DDSD_CAPS

Indicates that the ddsCaps member is valid.

DDSD_CKDESTBLT

Indicates that the ddckDestBlt member is valid.

DDSD_CKDESTOVERLAY

Indicates that the ddckDestOverlay member is valid.

DDSD_CKSRCBLT

Indicates that the ddckSrcBlt member is valid.

DDSD_CKSRCOVERLAY

Indicates that the ddckSrcOverlay member is valid.

DDSD_HEIGHT

Indicates that the dwHeight member is valid.

DDSD_LINEARSIZE

Indicates that the dwLinearSize member is valid.

DDSD_LPSURFACE

Indicates that the lpSurface member is valid.

DDSD_MIPMAPCOUNT

Indicates that the dwMipMapCount member is valid.

DDSD_PITCH

Indicates that the lPitch member is valid.

DDSD_PIXELFORMAT

Indicates that the ddpfPixelFormat member is valid.

DDSD_REFRESHRATE

Indicates that the dwRefreshRate member is valid.

DDSD_TEXTURESTAGE

Indicates that the dwTextureStage member is valid.

DDSD_WIDTH

Indicates that the dwWidth member is valid.

DWORD dwHeight, dwWidth: These are the dimensions of the surface in pixels.

LONG lPitch: This one needs to be explained a bit. The lPitch member
represents the number of bytes in each display line. You'd think this would be
obvious. For example, in 640x480x16, there are 640 pixels in each line, and each
one requires 2 bytes for color information, so the pitch (also called the "stride")
should be 1280 bytes, right? Well, on some video cards, it will be greater than
1280. The extra memory on each line doesn't hold any graphical data, but
sometimes it's there because the video card can't create a perfectly linear
memory mode for the display mode. This will happen on a very small percentage
of video cards, but you need to take it into account.
LPVOID lpSurface: This is a pointer to the memory represented by the surface.
No matter what display mode you're using, DirectDraw creates a linear
addressing mode you can use to manipulate the pixels of the surface. In order to
do this, you must lock the surface... but we're getting ahead of ourselves!
DWORD dwBackBufferCount: This is the number of back buffers that will be
chained to the primary surface, either for double- or triple-buffering, or for a page
flipping chain. Again, more on this later.
DWORD ddckDestBlt, ddckSrcBlt: These are color keys, which control which
color pixels can be written to, or written, respectively. This is used to set
transparent colors in bitmaps, as we'll see in a future article.
DDPIXELFORMAT ddpfPixelFormat: This structure contains a number of
descriptors for the pixel format of the display mode. We'll be covering this next
time as well, so I won't show you this giant structure right now.
DDSCAPS2 ddsCaps: This is the last important one, and it's another structure full
of control flags. Thankfully, the structure a small one, and only one of its
members is important right now. Take a look:
typedef struct _DDSCAPS2{
DWORD dwCaps;
DWORD dwCaps2;
DWORD dwCaps3;
DWORD dwCaps4;
} DDSCAPS2, FAR* LPDDSCAPS2;
The only important one is dwCaps. The third and fourth members aren't even used
at all; they're just there for future expansion. Anyway, dwCaps can take the
following values, logically combined with the | operator. This is only a partial list,
leaving out some that are advanced or not currently implemented.
DDSCAPS_BACKBUFFER

Indicates that this surface is a back buffer on a


surface flipping structure.

DDSCAPS_COMPLEX

Indicates a complex surface, which consists of a


primary surfaces and one or more attached surfaces,
usually for page flipping.

DDSCAPS_FLIP

Indicates that this surface is part of a surface flipping


structure. The result is that a front buffer along with
one or more back buffers are created.

DDSCAPS_FRONTBUFFER

Indicates that this surface is the front buffer on a


surface flipping structure.

DDSCAPS_LOCALVIDMEM

Indicates that this surface exists in true, local video


memory rather than non-local video memory. If this
flag is specified then DDSCAPS_VIDEOMEMORY must be
specified as well. This cannot be used with the
DDSCAPS_NONLOCALVIDMEM flag.

DDSCAPS_MODEX

Indicates that this surface is a 320200 or 320240

Mode X surface.

DDSCAPS_NONLOCALVIDMEM

Indicates that this surface exists in nonlocal video


memory rather than true, local video memory. If this
flag is specified, then DDSCAPS_VIDEOMEMORY flag
must be specified as well. This cannot be used with
the DDSCAPS_LOCALVIDMEM flag.

DDSCAPS_OFFSCREENPLAIN

Indicates that this is any plain offscreen surface that


is not a texture, back buffer, etc.

DDSCAPS_OWNDC

Indicates that this surface will have a device context


association for a long period.

DDSCAPS_PRIMARYSURFACE

Indicates that this is the primary surface.

Indicates that this surface is a standard VGA surface


DDSCAPS_STANDARDVGAMODE rather than a Mode X surface. Cannot be used with
the DDSCAPS_MODEX flag.
DDSCAPS_SYSTEMMEMORY

Indicates that this surface exists in system memory.

DDSCAPS_TEXTURE

Indicates that this surface can be used as a 3D


texture, though it is not necessary to use it that way.

DDSCAPS_VIDEOMEMORY

Indicates that this surface exists in display memory.

All right, we're finally done looking at structures. Now we're just about ready to
create a surface. The first step is to fill out a DDSURFACEDESC2 structure. Microsoft
recommends that before using a structure which you won't be filling out
completely, you should zero it out to initialize it. To this end, I usually use a
macro something like this:
#define INIT_DXSTRUCT(dxs) { ZeroMemory(&dxs,
sizeof(dxs)); dds.dwSize = sizeof(dxs); }
This can be used for any DirectX structure, since they all have the dwSize
member, so it's pretty convenient. If you've never seen ZeroMemory() before, it's
just a macro that expands into a memset() call. It's #defined in the Windows
headers, so you don't need to #include anything new to use it.
After initializing the structure, the minimum you'll need to fill out depends on the
surface. For primary surfaces, you'll just need ddsCaps and dwBackBufferCount.
For offscreen buffers, you'll also need dwHeight and dwWidth, but not
dwBackBufferCount. For some surfaces you may also want to use the color keys,
but we're not that far yet. After you've filled out the structure, you make a call to
IDirectDraw7::CreateSurface(), which looks like this:
HRESULT CreateSurface(
LPDDSURFACEDESC2 lpDDSurfaceDesc,
LPDIRECTDRAWSURFACE7 FAR *lplpDDSurface,
IUnknown FAR *pUnkOuter
);
The parameters, you can probably figure out. But here they are, since we're just
getting used to all this crazy DirectX stuff:
LPDDSURFACEDESC2 lpDDSurfaceDesc: This is a pointer to the DDSURFACEDESC2
you'll need to fill out to describe the surface.
LPDIRECTDRAWSURFACE7 FAR *lplpDDSurface: Since a surface is defined by an
interface pointer, you need to create a variable of type LPDIRECTDRAWSURFACE7 to
hold the pointer, and then pass its address here.

IUnknown FAR *pUnkOuter: Starting to see a pattern? Whenever you see


something called pUnkOuter, it's for advanced COM stuff we don't want to mess
around with. :) Set it to NULL.
Let's run through an example. Suppose that for our program, we want a primary
surface with one back buffer attached, and one offscreen buffer to use for bitmap
storage. Assuming we already have our IDirectDraw7 interface pointer, the
following code would create the primary surface:
DDSURFACEDESC2 ddsd; // surface description structure
LPDIRECTDRAWSURFACE7 lpddsPrimary = NULL; // primary
surface
// set up primary drawing surface
INIT_DXSTRUCT(ddsd);
initialize ddsd
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
valid flags
ddsd.dwBackBufferCount = 1;
back buffer
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
primary surface
DDSCAPS_COMPLEX |
buffer is chained
DDSCAPS_FLIP |
allow page flipping
DDSCAPS_VIDEOMEMORY;
create in video memory

//
//
// one
//
// back
//
//

// create primary surface


if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsPrimary,
NULL)))
{
// error-handling code here
}
Now, when you specify the DDSCAPS_COMPLEX flag, the call to CreateSurface()
actually creates the front buffer along with any back buffers it may have. Since
we specified that there was to be one back buffer, all we need to do is get the
pointer to it. This is done by calling
IDirectDrawSurface7::GetAttachedSurface():
HRESULT GetAttachedSurface(
LPDDSCAPS2 lpDDSCaps,
LPDIRECTDRAWSURFACE7 FAR *lplpDDAttachedSurface
);
The parameters are pretty easy to deal with:
LPDDSCAPS2 lpDDSCaps: A pointer to a DDSCAPS2 structure which describes the
attached surface. You can use the corresponding member of our DDSURFACEDESC2
structure.
LPDIRECTDRAWSURFACE7 FAR *lplpDDAttachedSurface: This is the address of
the interface pointer that will represent the attached surface. Simply declare a
pointer and pass its address.
With that relatively painless function, we can get the back buffer which was
created along with the primary surface. The following code does the job:
LPDIRECTDRAWSURFACE7 lpddsBack = NULL;
// get the attached surface

// back buffer

ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
if (FAILED(lpddsPrimary>GetAttachedSurface(&ddsd.ddsCaps, &lpddsBack)))
{

// error-handling code here

}
Are you starting to get the hang of this stuff? If you're still having trouble
remembering all these steps, just give it time. And nobody remembers every
member of every structure there is, so don't worry about those huge lists of
members and flags. That's why you have your MSDN Library CD. :) Anyway, the
last step is to create the offscreen buffer. Suppose we wanted it to be 400 pixels
wide and 300 pixels high. We would go about creating it like this:
LPDIRECTDRAWSURFACE7 lpddsOffscreen = NULL;
buffer

// offscreen

// set up offscreen surface


INIT_DXSTRUCT(ddsd);
initialize ddsd
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
valid flags
ddsd.dwWidth = 400;
set width
ddsd.dwHeight = 300;
set height
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN |
offscreen buffer
DDSCAPS_VIDEOMEMORY;
video memory

//
//
//
//
//
//

// create offscreen buffer


if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsOffscreen,
NULL)))
{
// error-handling code here
}
And we're all set! Now we've got all of our surfaces created and we're ready to do
graphics. Of course, the only problem is that this article has gone pretty long
already, so we're going to have to stop here. You can now create a working
DirectX program, even though it won't do anything except set up your surfaces.
Remember that when you're done with the DirectDraw interface, and with all your
surfaces, you must release them all, and it's best to release in the opposite order
of creation.

Closing
Sorry to cut this off before we got to any graphics, but if I were to start talking
about graphics, the plain text version of this article would go over 100KB, to say
nothing of the HTML-ified version. :) There's so much to explain when it comes to
working with graphics, so I think I'm going to split it up over the next two
articles. The next installment will cover pixel-based graphics and setting up
palettized modes, and the article after that will discuss bitmaps and clipping. Until
then, if you have any questions, send me an E-mail, or catch me on ICQ at UIN
#53210499.
See you next time!

Game Programming Genesis


Part V : Palettes and Pixels in
DirectDraw
by Joseph "Ironblayde" Farrell

Introduction
Today we're going to go over basic DirectDraw graphics using both palettized and
RGB modes. What's the difference? Well, a palettized screen mode is probably
what you're used to if you've been programming in DOS. To plot pixels, you write
a single byte to the video memory. That byte is an index into a lookup table full of
colors, which is called a palette. RGB modes are different, because there's no
lookup table. To plot a pixel in an RGB display mode, you write the values for red,
green, and blue directly into the video memory. Any color depth higher than 8-bit
is RGB instead of palettized. But now we're getting ahead of ourselves!
In writing this article, I assume you have either read my previous entries in the
series, or are otherwise familiar with setting up DirectDraw and creating surfaces.
We'll be using version 7 of DirectX, which contains the most recent DirectDraw
interfaces. Actually, on a side note, the DirectDraw interfaces in DirectX 7 will be
the last ones released! Don't worry, future versions of DirectX will still be
backwards-compatible, but future implementations will have DirectDraw and
Direct3D integrated into one component. But let's not worry about that now. :)
One last thing I will mention before we get started: the information on palettes is
not necessary for anything I'll be covering later in the series, so if you're not
interested in palettized modes, feel free to skip over the first part of this article
and pick up when we get to the Pixel Formats section. Now that my disclaimers
are at an end, let's get going!

Creating a DirectDraw Palette


Before working with graphics in a color depth of 8-bit or less, you must create a
palette, which is simply a table full of colors. More specifically, in DirectX, a
palette is a table full of PALETTEENTRY structures. To create one, there are three
steps we need to take:
1. Create the color lookup table.
2. Get a pointer to an IDirectDrawPalette interface.
3. Attach the palette to a DirectDraw surface.
I'll assume that we're using 8-bit color for the rest of this section; if you wanted
to write a game with 16 colors (or less), you wouldn't be reading up on all this
crazy Windows stuff, right? Anyway, in 8-bit color depth, you have a palette with
256 entries. So to create our lookup table, we'll need 256 of these:
typedef struct tagPALETTEENTRY { // pe
BYTE peRed;
BYTE peGreen;
BYTE peBlue;
BYTE peFlags;
} PALETTEENTRY;
The first three members are fairly obvious; they are the intensities of red, green,
and blue in the given color. Each one can range from 0 to 255, as BYTE is an
unsigned data type. The last member of the structure is a control flag and should
be set to PC_NOCOLLAPSE. Basically what this does is guarantees that your colors

will be preserved as they are, rather than being matched to an existing system
palette. We've all seen what MS Paint does when you try to save a picture as an
8-bit image. You want that happening to your game? I didn't think so. :)
Now, as I said, we're going to need a bunch of these structures, so the logical
thing to do is to declare an array that we'll use for the lookup table. It would look
like this:
PALETTEENTRY palette[256];
So now that we've got our array, you can load in your colors. When I'm working
in a palettized mode, I'll usually have my colors saved in an external file, and use
something like this to load the colors in:
FILE* file_ptr;
int x;
if ((file_ptr = fopen("palette.dat", "rb")) != NULL)
{
fread(palette, sizeof(PALETTEENTRY), 256, file_ptr);
fclose(file_ptr);
}
All right, that does it for step one. Now we need to get the palette interface. This
is accomplished by a call to IDirectDraw7::CreatePalette(), which looks like
this:
HRESULT CreatePalette(
DWORD dwFlags,
LPPALETTEENTRY lpColorTable,
LPDIRECTDRAWPALETTE FAR *lplpDDPalette,
IUnknown FAR *pUnkOuter
);
The return type is the standard HRESULT we're used to, so you can use the
FAILED() and SUCCEEDED() macros to test whether or not the call succeeds. The
parameters are as follows:
DWORD dwFlags: Any number of a series of flags describing the palette object. As
usual, you can combine the values with the | operator. The valid flags are:
DDPCAPS_1BIT

1-bit color; this corresponds to a 2-color palette.

DDPCAPS_2BIT

2-bit color; this corresponds to a 4-color palette.

DDPCAPS_4BIT

4-bit color; this corresponds to a 16-color palette.

DDPCAPS_8BIT

8-bit color; this corresponds to a 256-color palette.

DDPCAPS_8BITENTRIES

Indicates that the index refers to an 8-bit color index.


That is, each color entry is itself an index to a
destination surface's 8-bit palette. It must be
combined with DDPCAPS_1BIT, DDPCAPS_2BIT, or
DDPCAPS_4BIT. This is called an indexed palette.
Weird, hey?

DDPCAPS_ALPHA

Indicates that the peFlags member of each


PALETTEENTRY should be interpreted as an alpha value.
Palettes created with this flag can only be attached to
a Direct3D texture surface, since DirectDraw itself
doesn't support alpha-blending.

DDPCAPS_ALLOW256

Allows all 256 entries of an 8-bit palette to be used.


Normally, index 0 is reserved for black, and index 255
is reserved for white.

DDPCAPS_INITIALIZE

Indicates that the palette should be initialized with the


values of an array of PALETTEENTRYs that is passed in

another parameter of CreatePalette().


Indicates that the palette will be attached to the
DDPCAPS_PRIMARYSURFACE primary surface, so that altering it immediately
changes the colors of the display.

DDPCAPS_VSYNC

Forces palette changes to take place only during the


vertical blank, minimizing the odd effects normally
associated with palette-changing during a drawing
cycle. This is not fully supported.

For the most part, you'll want to use DDPCAPS_8BIT | DDPCAPS_INITIALIZE,


though you can take out the latter if you just want to create an empty palette and
set it up later. Or you may want to add DDPCAPS_ALLOW256 if you really want to
change the two normally reserved entries.
LPPALETTEENTRY lpColorTable: This is a pointer to the lookup table we created,
so just pass the name of the array.
LPDIRECTDRAWPALETTE FAR *lplpDDPalette: This is the address of a pointer to
an IDirectDrawPalette interface, which will be initialized if the call succeeds.
IUnkown FAR *pUnkOuter: As always, this is for advanced COM stuff and should
be set to NULL.
That's not too bad, so now we can go ahead and create our palette object. The
last step is to attach that palette to a surface, which requires only a simple
function call to IDirectDrawSurface7::SetPalette(). The function is shown
below:
HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette);
Pretty straightforward, isn't it? All you do is pass the interface pointer we got in
the last step. So, to put a quick example together, let's assume we already have a
lookup table created in an array called palette, like we did above. To create a
DirectDraw palette to hold the colors, and attach it to our primary surface (which
has also been created already), we do the following:
LPDIRECTDRAWPALETTE lpddpal;
// create the palette object
if (FAILED(lpdd7->CreatePalette(DDPCAPS_8BIT |
DDPCAPS_INITIALIZE,
palette, &lpddpal,
NULL)))
{
// error-handling code here
}
// attach to primary surface
if (FAILED(lpddsPrimary->SetPalette(lpddpal)))
{
// error-handling code here
}
And that's all there is to it. Once your palette is set up, plotting pixels is not too
different from the way it's done in RGB modes. From this point on, I'll be covering
both RGB and palettized modes. Before we get into actually displaying graphics,
though, I need to show you what RGB pixel formats are like.

Pixel Formats
As I said earlier, hen you're writing a pixel into memory in a palettized mode, you
write one byte at a time, and each byte represents an index into the color lookup
table. In RGB modes, however, you write the actual color descriptors right into

memory, and you need more than one byte for each color. The size of the
memory write is equivalent to the color depth; that is, for 16-bit color, you write
two bytes (16 bits) for each pixel, and so on. Let's start out at the top, because
it's easiest to understand. 32-bit color uses a pixel format like this, where each
letter is one bit:
AAAA AAAA RRRR RRRR GGGG GGGG BBBB BBBB
The As are for "alpha," which is a value representing transparency. Those are for
Direct3D though; like I said, DirectDraw doesn't support alpha blending. So when
you're creating a 32-bit color for DirectDraw, just set the high byte to 0. The next
eight bits are the intensity of red, the eight following that are for green, and the
low byte is for blue.
A pixel in 32-bit color needs to be 32 bits in size, and so the variable type we use
to hold one is a UINT, which is an unsigned integer. Usually I use macros to
convert RGB data into the correct pixel format, so let me show you one here.
Hopefully if you're a little confused at this point, this will clear things up a bit:
#define RGB_32BIT(r, g, b) ((r << 16) | (g << 8) | (b))
As you can see, this macro creates a pixel value by shifting the bytes
representing red, green, and blue to their appropriate positions. Is it starting to
make sense? To create a 32-bit pixel, you can just call this macro. Since red,
green, and blue have eight bits each, they can range from 0 to 255. To create a
white pixel, you would do this:
UINT white_pixel = RGB_32BIT(255, 255, 255);
24-bit color is just about the same. As a matter of fact, it is the same, except
without the alpha information. The pixel format looks like this:
RRRR RRRR GGGG GGGG BBBB BBBB
So red, green, and blue still have eight bits each. This means that 24-bit color
and 32-bit color actually have the same number of colors available to them, but
32-bit color just has some added information for transparency. So if you don't
need the extra info, 24-bit is better than 32-bit, right? Well, not exactly. It's
actually kind of a pain to deal with, because there's no data type that's 24 bits.
So to write a pixel, instead of just writing in one value, you have to write red,
green, and blue each individually. Working in 32-bit color is probably faster on
most machines, even though it requires more memory. In fact, many video cards
don't support 24-bit color at all, because having each pixel take up three bytes is
just too inconvenient.
Now, 16-bit color is a bit tricky, because not every video card uses the same pixel
format! There are two formats supported. One of them, which is by far more
common, has five bits for red, six bits for green, and five bits for blue. The other
format has five bits for each, and the high bit is unused. This is used mostly on
older video cards. So the two formats look like this:
565 format: RRRR RGGG GGGB BBBB
555 format: 0RRR RRGG GGGB BBBB
When you're working in a 16-bit color depth, you'll need to determine whether
the video card uses 565 or 555 format, and then apply the appropriate technique.
It's kind of a pain, but there's no way around it if you're going to use 16-bit color.
Since there are two different formats, you'd write two separate macros:
#define RGB_16BIT565(r, g, b) ((r << 11) | (g << 5) |
(b))
#define RGB_16BIT555(r, g, b) ((r << 10) | (g << 5) |
(b))
In the case of 565 format, red and blue can each range from 0 to 31, and green
ranges from 0 to 63. In 555 format, all three components range from 0 to 31. So
setting a pixel to white in each mode would look like this:
USHORT white_pixel_565 = RGB_16BIT565(31, 63, 31);
USHORT white_pixel_555 = RGB_15BIT555(31, 31, 31);
The USHORT data type is an unsigned short integer, which is 16 bits long. This
whole business of having two pixel formats makes things a bit confusing, but

when we actually get into putting a game together, you'll see that it's not as bad
as it seems at first. By the way, sometimes 555 format is referred to as 15-bit
color. So if I call it that sometime later on, you'll know what I'm talking about
instead of thinking I made a typo. :)
Here is probably a good place to show you just how exactly how determine
whether a machine is using the 555 or 565 format when you're running in 16-bit
color. The easiest way to do it is to call the GetPixelFormat() method of the
IDirectDrawSurface7 interface. Its prototype looks like this:
HRESULT GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat);
The parameter is a pointer to a DDPIXELFORMAT structure. Just declare one,
initialize it, and pass its address. The structure itself is huge, so I'm not going to
list it here. Instead, I'll just tell you about three of its fields. The members in
question are all of type DWORD, and they are dwRBitMask, dwGBitMask, and
dwBBitMask. They are bit masks that you logically AND with a pixel value in order
to extract the bits for red, green, or blue, respectively. You can also use them to
determine what pixel format you are dealing with. If the video card uses 565,
dwGBitMask will be 0x07E0. If it uses 555 format, dwGBitMask will be 0x03E0.
Now that we've seen all the pixel formats you can encounter, we can get into
actually showing graphics in DirectX. About time, wouldn't you say? Before we
can manipulate the actual pixels on a surface, though, we need to lock the
surface, or at least a part of it. Locking the surface will return a pointer to the
memory the surface represents, so then we can do whatever we want with it.

Locking Surfaces
Not surprisingly, the function we'll use to do this is
IDirectDrawSurface7::Lock(). Let's take a look at it.
HRESULT Lock(
LPRECT lpDestRect,
LPDDSURFACEDESC lpDDSurfaceDesc,
DWORD dwFlags,
HANDLE hEvent
);
Remember to check the function call for success or failure, or it could lead to
problems. If the lock fails, then the pointer to the surface won't be correct, and
who knows what part of memory you'd be messing with then? The parameters for
this function are:
LPRECT lpDestRect: This is a RECT that represents the area on the surface we
want to lock. If you want to lock the entire surface, simply pass NULL.
LPDDSURFACEDESC2 lpDDSurfaceDesc: This is a pointer to a DDSURFACEDESC2
structure, which is the big baddie we covered last time. All you need to do is
initialize the structure, then pass the pointer. If Lock() succeeds, it fills in some
of the members of the structure for you to use.
DWORD dwFlags: What kind of DirectX function would this be if it didn't have a list
of flags do go along with it? Here are the most useful ones, which you can
combine in the usual way:
DDLOCK_READONLY

Indicates that the surface being locked will only be


read from, not written to.

Indicates that a valid memory pointer to the upperleft corner of the specified RECT should be returned in
DDLOCK_SURFACEMEMORYPTR the DDSURFACEDESC2 structure. Again, if no RECT is
specified, the pointer will be to the upper-left corner
of the surface.

DDLOCK_WAIT

If a lock cannot be obtained because a blit is in


progress, this flag indicates to keep retrying until a
lock is obtained, or a different error occurs.

DDLOCK_WRITEONLY

Indicates that the surface being locked will only be


written to, not read from.

Since we'll be using the lock to manipulate pixels, you'll always want to use
DDLOCK_SURFACEMEMORYPTR. And even though we haven't gotten to using the
blitter yet, it's usually a good idea to include DDLOCK_WAIT as well.
HANDLE hEvent: This parameter is not used and should be set to NULL.
Once we lock the surface, we need to take a look at the DDSURFACEDESC2
structure to get some information about the surface. We went over all of the
members of this structure last time, but there are only two that we need to
concern ourselves with at this point. Since both are very important, I'll list those
two here again.
LONG lPitch: The lPitch member represents the number of bytes in each
display line. You'd think this would be obvious. For example, in 640x480x16,
there are 640 pixels in each line, and each one requires 2 bytes for color
information, so the pitch (also called the "stride") should be 1280 bytes, right?
Well, on some video cards, it will be greater than 1280. The extra memory on
each line doesn't hold any graphical data, but sometimes it's there because the
video card can't create a perfectly linear memory mode for the display mode. This
will happen on a very small percentage of video cards, but you need to take it
into account.
LPVOID lpSurface: This is a pointer to the memory represented by the surface.
No matter what display mode you're using, DirectDraw creates a linear
addressing mode you can use to manipulate the pixels of the surface.
The lpSurface pointer is pretty easy to understand, but are you following me on
this whole pitch thing? It's an important value to remember, because you'll have
to use it to calculate the offset to a particular pixel. We'll get back to it in just a
minute. There's one thing I want to get out of the way first. When you're done
plotting pixels, you need to unlock the surface that you locked. The prototype for
IDirectDrawSurface7::Unlock() is this:
HRESULT Unlock(LPRECT lpRect);
The parameter is the same RECT you passed to Lock(). All right, let's plot some
pixels.

Plotting Pixels
The first thing is to typecast the pointer we got from the Lock() function.
Logically, we're going to want a pointer that's the same size as the pixels we're
writing. So we want a UCHAR* for 8-bit color depth, a USHORT* for 16-bit, and a
UINT* for 32-bit. But what about 24-bit? Since there's no 24-bit data type, we'll
need to use a UCHAR* for this also, and do things a little differently.
We should also convert the lPitch member so it's in the same units as our
pointer. Remember, when we first retrieve lPitch from the DDSURFACEDESC2
structure, it's in bytes. For 16-bit mode, we should divide it by 2 to get the pitch
in terms of USHORTs, and for 32-bit mode, we divide by 4 to get it in terms of
UINTs.
Let's stop for a second and look at some example code. Suppose we are in 32-bit
mode and want to lock the primary surface for plotting pixels. Here's what we
would do:
// declare and initialize structure
DDSURFACEDESC2 ddsd;
INIT_DXSTRUCT(ddsd);

// lock the surface


lpddsPrimary->Lock(NULL, &ddsd, DDLOCK_WAIT |
DDLOCK_SURFACEMEMORYPTR, NULL);
// now convert the pointer and the pitch
UINT* buffer = (UINT*)ddsd.lpSurface;
UINT nPitch = ddsd.lPitch >> 2;
Now let me go ahead and show you a pixel-plotting function, and then I'll explain
it in detail:
inline void PlotPixel32(int x, int y, UINT color, UINT
*buffer, int nPitch)
{
buffer[y*nPitch + x] = color;
}
All right, let's take it apart. First of all, notice that I have declared it as an inline
function. This is to eliminate the overhead of passing all the parameters every
time we want to do something as simple as plotting one pixel. Now, the only line
in the function locates the pixel of interest and sets it to the color we want. Note
that the color is just one value, not one each for red, green, and blue, so we'll
need to use our RGB_32BIT() macro to create the color for this function.
The formula used to locate the pixel at location (x, y) is y*nPitch + x. This
makes sense because nPitch is the number of UINTs in a line. Multiplying that by
the y value yields the correct row, and then we just need to add x to locate the
correct column. That's all you need to know! Pretty simple, hey? Let me show you
a couple of functions for plotting pixels in 16-bit and 8-bit modes, because they're
very similar:
inline void PlotPixel8(int x, int y, UCHAR color, UCHAR*
buffer, int byPitch)
{
buffer[y*byPitch + x] = color;
}
inline void PlotPixel16(int x, int y, USHORT color,
USHORT* buffer, int nPitch)
{
buffer[y*nPitch + x] = color;
}
The only things different about these functions are the data types used for the
parameters. Also remember that for the 8-bit version, the pitch is in bytes, and
for the 16-bit version, the pitch is in USHORTs. That just leaves one mode left, and
that's 24-bit. Since there's no data type that's 24 bits, we need to pass and write
values for red, green, and blue individually. The function might look like this:
inline void PlotPixel24(int x, int y, UCHAR r, UCHAR g,
UCHAR b, UCHAR* buffer, int byPitch)
{
int index = y*byPitch + x*3;
buffer[index] = r;
buffer[index+1] = g;
buffer[index+2] = b;

}
As you can see, this is going to be a bit slower because we have an extra
multiply, and three memory writes. You can speed it up a little bit by replacing
x*3 with (x+x+x) or (x<<1)+x, but that probably won't do a lot for you. Still, it
can't hurt to throw it in for good measure. Now you can see why working with 24bit color is a bit of a pain.

Notes on Speed
There are a few things you should do to make sure this all goes as quickly as
possible. First of all, locking a surface is not the fastest thing in the world, so you
should try to keep the number of times you lock a surface per frame to a
minimum. For many things, including a simple pixel-plotting demo, one lock per
frame is all you'll need.
Second, let's say you're working in 640x480x16. The pitch will almost always be
1,280 bytes. You still need to take into consideration that it might not always be
that way, but you may want to add some optimized code to take advantage of the
fact that the pitch will almost always be 1,280. More specifically, you can use a
pixel-plotting function that uses bit shifts instead of multiplication to locate the
correct pixel. The function we were using earlier used this line of code:
buffer[y*nPitch + x] = color;
We can speed that up if we know nPitch is going to be 640 (since nPitch is in
USHORTs, not bytes). 640 isn't a power of two, but 512 is 2^9, and 128 is 2^7.
And guess what? 512 + 128 = 640. :) Convenient, hey? The line above will
execute just a tad faster if we use this instead:
buffer[(y<<9) + (y<<7) + x] = color;
Most resolutions you'll use have widths that are either powers of two, or a sum of
two powers of two. No such luck if you're in 800x600 (512 + 256 + 32 = 800),
but it's a good trick to keep in mind. Bit shifts are some of the fastest operations
you could ask for.
Finally, if you are going to be using two functions -- one using multiplication, and
the other using bit-shifts -- keep the comparison outside of the plotting loop. That
is, don't do this:
for (x=0; x<1000; x++)
{
if (nPitch == 640)
PlotPixelFast16();
else
PlotPixel16();
}
Putting the decision statement inside the loop is just detracting from the
advantage you gain with the fast function. Do it this way instead:
if (nPitch == 640)
{
for (x=0; x<1000; x++)
PlotPixelFast16( parameters );
}
else
{
for (x=0; x<1000; x++)
PlotPixel16( parameters );
}
Make sense? Whenever you've got a big for loop, keep as much as you can on the
outside of it. There's no sense in making a calculation a thousand times that's
going to come up the same way every time. :) Also, if you're going to be plotting
pixels in such a way that the locations are in a pattern, like a horizontal or vertical
line (or even a diagonal one), there's no need to re-calculate the location every
time. Look at this example, for drawing a line of randomly colored pixels:
for (x=0; x<640; x++)
PlotPixel16(x, 50, color, buffer, pitch);
The function is re-calculating the correct line every time, when all you need to do
is advance the pointer by one each time. This is a faster way of doing it:
// get the address of the line
USHORT* temp = &buffer[50*pitch];

// plot the pixels


for (x=0; x<640; x++)
{
*temp = color;
temp++;
}
None of these things are going to make a huge difference in your program's
speed unless you're plotting thousands and thousands of pixels each frame -which you might be doing -- but even if not, it's always good policy to make any
little optimization you can find, no matter how insignificant it seems.
Considering how long the previous articles in this series have been, stopping this
one here would seem like kind of a rip-off, wouldn't it? Now that we know how to
plot pixels, let's take a look at some of the things you can use it for.

Fading Out
One of the most commonly used screen transitions in games is the fade to black,
or the fade in from black. Each one is achieved in the same manner. You simply
draw your frame, then apply some sort of transformation that alters the
brightness of the image. To fade out, you decrease the brightness from 100% to
0%, and to fade in, you increase it from 0% to 100%. If you're working in a
palettized mode, you've got it easy! You just change the colors in your palette,
and the result shows up onscreen. If you're working in RGB mode, you have to
consider other methods.
Now, I'll say right here that manually fading the screen has some better
alternatives. You can use Direct3D, which supports alpha-blending, to set each
frame as a texture, then set the transparency level. Or, even easier, you can use
the DirectDraw color/gamma controls. But if you only want to fade part of the
screen, or fade to a color besides black, and you're not a Direct3D expert -- I'm
certainly not! -- then knowing how to do the manual transform might come in
handy.
Basically all you do is to read each pixel and break it up into red, green, and blue.
Then you multiply each of the three values by the level of fading to apply,
recombine the RGB values, and write the new color back out to the buffer. Sound
complicated? It's not too bad. Take a look at this (highly unoptimized) sample
code. It applies a fade to a viewport of size 200x200 in the upper-left corner of
the display, in a 16-bit color depth using the 565 pixel format.
void ApplyFade16_565(float pct, USHORT* buffer, int
pitch)
{
int x, y;
UCHAR r, g, b;
USHORT color;
for (y=0; y<200; y++)
{
for (x=0; x<200; x++)
{
// first, get the pixel
color = buffer[y*pitch + x];
// now extract red, green, and blue
r = (color & 0xF800) >> 11;
g = (color & 0x0730) >> 5;
b = (color & 0x001F);
// apply the fade
r = (UCHAR)((float)r * pct);

g = (UCHAR)((float)g * pct);
b = (UCHAR)((float)b * pct);

// write the new color back to the buffer


buffer[y*pitch + x] = RGB_16BIT565(r, g, b);

}
Now, there are a number of things wrong with this function. First, not only is the
calculation of the pixel location inside the inner loop, it's in there twice! You can
get away with calculating it once in the entire function, but in this example it's
calculated 80,000 times. :) Here's what you should do. At the very top of the
function, you should declare another USHORT*, and set it equal to buffer. Inside
the inner loop, increment the pointer by one to get to the next pixel. And inside
the outer loop, increment it by whatever it takes to get down to the next line.
Here's the considerably better example:
void ApplyFade16_565(float pct, USHORT* buffer, int
pitch)
{
int x, y;
UCHAR r, g, b;
USHORT color;
USHORT* temp = buffer;
int jump = pitch - 200;
for (y=0; y<200; y++)
{
for (x=0; x<200; x++, temp++) // move pointer to next
pixel each time
{
// first, get the pixel
color = *temp;
// now extract red, green, and blue
r = (color & 0xF800) >> 11;
g = (color & 0x0730) >> 5;
b = (color & 0x001F);
// apply the fade
r = (UCHAR)((float)r * pct);
g = (UCHAR)((float)g * pct);
b = (UCHAR)((float)b * pct);

// write the new color back to the buffer


*temp = RGB_16BIT565(r, g, b);

// move pointer to beginning of next line


temp+=jump;

}
}
This is better. The value of jump is the number of USHORTs from the end of a line
on the 200-pixel-wide viewport to the beginning of the next line. Still, there's not
much you can do to speed up those floating-point multiplies and all the
extracting/recombining of colors. Or is there? Take a look at this:
USHORT clut[65536][20];
If you asked a DOS programmer to put an array that size into one of his
programs, he'd probably cry out in pain. He might even drop dead on the spot, or
spontaneously combust. But in Windows, it's not really a problem if you need to

do it, because you have all the system's free RAM available to you. Wouldn't it be
nice if you could replace the entire inner loop with this one line?
*temp = clut[*temp][index];
That would speed things up a bit, right? :) Instead of passing a float to the
function, you could pass an integer that's between 0 and 100. If the value is 100,
no fade is needed, so return without doing anything. If the value is 0, just use a
ZeroMemory() call to do all the work. Otherwise, divide the value by 5, and use it
for the second subscript into the giant lookup table.
If you're wondering where I got the dimensions for the lookup table, 65536 is
2^16, so that's how many colors there are in a 16-bit color depth. Since our color
values are unsigned values, they range from 0 to 65535. The 20 is just the
number of increments you'd be using for a fade. It seemed a decent choice to
me, considering the memory involved.
For 24-bit and 32-bit color, you obviously can't create a lookup table to span
every color combination, so what you could do is use three smaller tables:
UCHAR red[256];
UCHAR green[256];
UCHAR blue[256];
Then, when you read each pixel, split it up into its color components, look each
component up in its table, and recombine them into the resulting color. There are
all sorts of ways to do things like this. The best way to determine what works well
for your purposes is just to throw some code together, and then play around with
it for awhile. With that in mind, I will briefly go over one other transformation you
might find useful.

Basic Transparency
Overlaying a transparent image on an opaque one is not something you can use a
lookup table for, because it would have to have 65,536 entries in both
dimensions. It's going to be awhile before the average computer has the 8.6 GB
of RAM it takes to hold that monster. :) So you'll have to do all the calculations for
each pixel. I'll give you the basic idea. Suppose you want to place image A on top
of image B, and image A is to have a transparency percentage of pct, which is a
floating-point number between 0 and 1, where 0 is fully transparent (invisible)
and 1 is fully opaque. Then, let's call a pixel in image A pixelA, and its
counterpart in image B we'll call pixelB. You would apply the following equation:
color = (pixelA * pct) + (pixelB * (1-pct));
Basically, this is just a weighted average of the two pixel colors. By multiplying
the colors, I mean to multiply the intensities for red, green, and blue. So you're
actually looking at six floating-point multiplications per pixel. You can use some
small lookup tables to lighten the workload a little. Experiment with it!
The other thing you might want to do is to create a window of a solid color that's
partially transparent. If you've seen a demo or screenshots of my upcoming RPG,
Terran, you know what I mean. An effect like that can be done entirely with a
lookup table, because in Terran's case, I just needed to provide a color of blue for
every possible color on the screen. In fact, a lookup table is exactly how I do the
effect. I'll show you exactly what I mean.
void Init_CLUT(void)
{
int x, y, bright;
UCHAR r, g, b;
// calculate textbox transparency CLUT
for (x=0; x<65536; x++)
{
// transform RGB data
if (color_depth == 15)

r = (UCHAR)((x & 0x7C00) >> 10);


g = (UCHAR)((x & 0x03E0) >> 5);
b = (UCHAR)(x & 0x001F);

}
else // color_depth must be 16
{
r = (UCHAR)((x & 0xF800) >> 11);
g = (UCHAR)((x & 0x07E0) >> 6);
bits instead of 5 to put green
b = (UCHAR)(x & 0x001F);
scale instead of 0-63
}

// shifting 6
// on a 0-31

// find brightness as a weighted average


y = (int)r + (int)g + (int)b;
bright = (int)((float)r * ((float)r/(float)y) +
(float)g * ((float)g/(float)y) +
(float)b * ((float)b/(float)y) + .5f);
// write CLUT entry as 1 + one half of brightness
clut[x] = (USHORT)(1 + (bright>>1));

}
}
This is the code from Terran that creates the lookup table with which the text
boxes are generated. There are typecasts everywhere just for safety, and it could
be sped up quite a bit, but I didn't bother because it's only called once, in the
very beginning of the game. First, the values for red, green, and blue are
extracted. Since this is in 16-bit color, notice that I have a variable called
color_depth that takes into account whether the pixel format is 565 or 555.
Then, the brightness of the pixel is calculated using this formula:
y = r + g + b;
brightness = r*(r/y) + g*(g/y) + b*(b/y);
It's just another weighted average. I'm not sure if that's really how the brightness
of a color is defined, but it seems logical and it produces a nice effect. Anyway, at
the end of the equation I've added .5, because when you cast a float to an int,
the decimal is truncated. Adding .5 turns that truncation into rounding. Finally, I
divide the brightness in half and add one. The division is so the text boxes don't
get too bright, and the one is so the box is never totally black. Since the low bits
of a 16-bit color descriptor are for blue, I can just set the color by setting the
value of blue, instead of having to use a macro. Make sense? Finally, before I
wrap this up, I'll also show you how I create a text box:
int Text_Box(USHORT *ptr, int pitch, LPRECT box)
{
int x, y, jump;
RECT ibox;
// leave room for the border
SetRect(&ibox, box->left+3, box->top+3, box->right-3,
box->bottom-3);
// update surface pointer and jump distance
ptr += (ibox.top * pitch + ibox.left);
jump = pitch - (ibox.right - ibox.left);
// use CLUT to apply transparency
for (y=ibox.top; y<ibox.bottom; y++)
{
for (x=ibox.left; x<ibox.right; x++, ptr++)

*ptr = clut[*ptr];
ptr += jump;
}
return(TRUE);
}
Now that it's just a lookup table, this looks a lot like the code for fading. The only
difference is that the lookup table holds different values. And it's only one column
instead of 20. :) The declaration for the lookup table, by the way, looks like this:
USHORT clut[65536];
With that, you should be able to produce some rather interesting effects. To get
you started, check out the sample code that comes with this article. It's available
here. You might try modifying it so that it fills the screen with pixels, then plots a
transparent box over the top of them.

Closing
That does it for pixel-based graphics. Next time around, we'll be working with
bitmaps! Believe it or not, working with bitmaps is easier than all this pixel stuff.
Seems a little backwards, doesn't it? You'll find out. In the meantime, send me
any questions you might have and I'll be happy to help you out. My E-mail
address is ironblayde@aeon-software.com, and my ICQ UIN is 53210499. Oh,
one other thing... the next article will be the last one covering general DirectX
techniques. After that, we'll get into specific applications that you can use for
developing an RPG. More details to follow. :) Later!

Game Programming Genesis


Part VI : Bitmapped Graphics in
DirectDraw
by Joseph "Ironblayde" Farrell

Introduction
At last, the good stuff! You already know enough to make a fully functioning
Windows game, but it would have to use GDI. Today we're going to go over the
DirectX implementations of everything you know how to do using GDI, and quite
a bit more. I'm going to cover loading bitmaps, using the blitter to fill a surface
with a color, and copying bitmaps like lightning, with clipping, color keys, and a
host of other effects.
None of the material we'll be covering today will draw on the most recent article,
so it's not necessary that you've read it. However, the section on pixel formats
was very important, and I'll allude to it from time to time, so at least read that
part. Aside from that, I assume you've read the first four articles in the series,
and have the DirectX 7 SDK. All right? Fire up your compilers, ladies and
gentlemen. :)

Loading Bitmaps
Believe it or not, you already know almost everything you need to load a bitmap
onto a DirectDraw surface. How can that be? Well, the method you used under
Windows GDI will work in DirectDraw as well, with only one little change. To

refresh your memory a little bit, what we did was to retrieve a handle to the
bitmap using LoadImage(), select the bitmap into a memory device context, and
then use BitBlt() to copy the image from the memory DC to another DC, which
was a display device context we had gotten with a call to GetDC(). If that
destination DC was a device context to a DirectDraw surface, we'd have our
DirectX bitmap loader all done! Thankfully, the IDirectDrawSurface7 interface
provides a simple function for retrieving a device context:
HRESULT GetDC(HDC FAR *lphDC);
The return type is the same as for any DirectDraw function, and the parameter is
just a pointer to an HDC which will be initialized with the device context handle if
the function succeeds. Is that cool or what? This article just started and we can
already load a bitmap onto one of our surfaces! Just remember that when you're
all done with the surface device context, you need to release it. As you've
probably guessed, this is achieved with the ReleaseDC() method of the surface
interface:
HRESULT ReleaseDC(HDC hDC);
Just so you don't have to go rooting through the old articles, looking for the GDI
bitmap loader, I'll show the modified version to you here. The only difference is
that instead of taking a device context as a parameter, it takes a pointer to a
DirectDraw surface. Then the function gets the device context from the surface,
uses it to copy the image, and releases the device context.
int LoadBitmapResource(LPDIRECTDRAWSURFACE7 lpdds, int
xDest, int yDest, int nResID)
{
HDC hSrcDC;
// source DC - memory device
context
HDC hDestDC;
// destination DC - surface
device context
HBITMAP hbitmap;
// handle to the bitmap resource
BITMAP bmp;
// structure for bitmap info
int nHeight, nWidth; // bitmap dimensions
// first load the bitmap resource
if ((hbitmap = (HBITMAP)LoadImage(hinstance,
MAKEINTRESOURCE(nResID),
IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION))
== NULL)
return(FALSE);
// create a DC for the bitmap to use
if ((hSrcDC = CreateCompatibleDC(NULL)) == NULL)
return(FALSE);
// select the bitmap into the DC
if (SelectObject(hSrcDC, hbitmap) == NULL)
{
DeleteDC(hSrcDC);
return(FALSE);
}
// get image dimensions
if (GetObject(hbitmap, sizeof(BITMAP), &bmp) == 0)
{
DeleteDC(hSrcDC);
return(FALSE);
}
nWidth = bmp.bmWidth;

nHeight = bmp.bmHeight;
// retrieve surface DC
if (FAILED(lpdds->GetDC(&hDestDC)))
{
DeleteDC(hSrcDC);
return(FALSE);
}
// copy image from one DC to the other
if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight,
hSrcDC, 0, 0,
SRCCOPY) == NULL)
{
lpdds->ReleaseDC(hDestDC);
DeleteDC(hSrcDC);
return(FALSE);
}
// kill the device contexts
lpdds->ReleaseDC(hDestDC);
DeleteDC(hSrcDC);
// return success
return(TRUE);

}
This function is set to load from a resource, but you can easily modify it to load
from an external file. Or better still, you could have it try to load a resource, and
if the call fails, it retries as a file. Just remember to include the LR_LOADFROMFILE
flag in the call to LoadImage(). The nicest thing about this function is that
BitBlt() performs all the conversions on the pixel formats. That is, you can load
a 24-bit bitmap into the memory device context, and blit it to a 16-bit surface,
and all the colors will show up correctly, regardless of whether the pixel format is
565 or 555. Convenient, hey?
If you want to manipulate the actual bitmap data manually instead of simply
using a function to copy, you have two options. First, you can use a modified
version of the function above, and use the bmBits member of the BITMAP
structure, which is an LPVOID pointing to the bits making up the image. Second, if
you really want to have control of how the load is performed, you can write a
function that opens the file and reads it in manually, using standard file I/O
functions. To do that, you need to know the structure of a bitmap file. I'm not
going to go through developing the whole function, since we already have the
functionality we need, but I'll show you everything you need to do so.

The Bitmap File Format


The nice thing about writing a bitmap loader is that there are Win32 structures
designed to hold the bitmap headers, so loading all the header info as simple as
making a few calls to fread(). First up in a bitmap file is the bitmap file header,
which contains general information about the bitmap. Not surprisingly, the
structure that holds this header is called BITMAPFILEHEADER. Here's what it looks
like:
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD
bfType;
// file type - must be "BM" for
bitmap
DWORD
bfSize;
// size in bytes of the bitmap
file
WORD
bfReserved1; // must be zero

WORD
bfReserved2;
DWORD
bfOffBits;
BITMAPFILEHEADER

// must be zero
// offset in bytes from the

// structure to the bitmap bits


} BITMAPFILEHEADER;
I'm not going to detail all the members; I've commented the structure to give you
an idea of what everything is. Just use a call to fread() to read this in, and check
the bfType member to make sure it is equal to the characters "BM" to ensure
you're dealing with a valid bitmap. After that, there's another header file to read,
called the info header. It contains image data like the dimensions, compression
type, etc. Here's the structure:
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
// number of bytes required by
the structure
LONG
biWidth;
// width of the image in
pixels
LONG
biHeight;
// height of the image in
pixels
WORD
biPlanes;
// number of planes for target
device - must be 1
WORD
biBitCount;
// bits per pixel - 1, 4, 8,
16, 24, or 32
DWORD biCompression;
// type of compression BI_RGB for uncompressed
DWORD biSizeImage;
// size in bytes of the image
LONG
biXPelsPerMeter; // horizontal resolution in
pixels per meter
LONG
biYPelsPerMeter; // vertical resolution in
pixels per meter
DWORD biClrUsed;
// number of colors used
DWORD biClrImportant;
// number of colors that are
important
} BITMAPINFOHEADER;
A few of the fields need some explanation. First, a note on compression. Most
bitmaps that you'll be dealing with will be uncompressed. The most common type
of compression for bitmaps is run-length encoding (RLE), but this is only used for
4-bit or 8-bit images, in which case the biCompression member will be BI_RLE4
or BI_RLE8, respectively. I'm not going to go into run-length encoding, but it's a
fairly straightforward method of compression, so it's not too hard to deal with if
you come across it.
Second, biClrUsed and biClrImportant will usually be set to zero in high-color
bitmaps, so don't worry about them too much. The biSizeImage field may also
be set to zero in some BI_RGB uncompressed bitmaps. Finally, the resolution
fields are also unimportant for our purposes. Mainly the only things you're
interested in from this structure are the width, height, and color depth of the
image.
After you've read in the info header, if the bitmap has eight bits per pixel or less,
it is palettized, and the palette information immediately follows the info header.
The palette information, however, is not stored in PALETTEENTRY structures, but
rather in RGBQUAD structures. An RGBQUAD looks like this:
typedef struct tagRGBQUAD { // rgbq
BYTE
rgbBlue;
BYTE
rgbGreen;
BYTE
rgbRed;
BYTE
rgbReserved;
} RGBQUAD;
Don't ask me why the red, green, and blue intensities are stored in reverse order,
but they are. Just read in the RGBQUADs, and transfer the data into an array full of

PALETTEENTRYs to create a DirectDraw palette with. Remember to set the


peFlags member of each PALETTEENTRY to PC_NOCOLLAPSE as you do so.
After the palette, or immediately following the info header if there is no palette,
you'll find the actual image bits. You'll probably want to simply create a pointer
and allocate enough memory to it to hold the image data, and then read it in.
Just to be clear, I'll show you the code for doing this, assuming the info header is
stored in a BITMAPINFOHEADER structure called info, and your file pointer is called
fptr:
UCHAR* buffer = (UCHAR*)malloc(info.biSizeImage);
fread(buffer, sizeof(UCHAR), info.biSizeImage, fptr);
Just remember that Microsoft warns that biSizeImage may be set to zero in
some cases, so check it before running code like the above. If it is set to zero,
you'll have to calculate the size of the image by figuring out how many pixels
comprise the image, and how many bytes are required for each pixel.
Writing your own bitmap loader isn't too bad, but if you want to avoid it, you can
always use the code we developed back in the GDI article and modified at the
beginning of this one. Now that that's over with, let's get into what DirectDraw's
all about: using the blitter!

Using the Blitter


The blitter is a part of the video card hardware that is used for manipulating
bitmap data. You can also use it to do color fills, as we'll see in a minute, and any
number of other cool tricks if the hardware supports them. Having easy access to
hardware acceleration is one of the best parts about DirectX. Also, remember that
most of the things we'll be doing will be taken care of in the HEL if there's no
hardware support. There are some things that don't have counterparts in the HEL
though, which is why you have to be careful to always check whether your calls
succeed or not.
There are two main functions for accessing the blitter in DirectDraw: Blt() and
BltFast(). Both are methods of the IDirectDrawSurface7 interface. The
difference is that BltFast() doesn't do clipping, scaling, or any of the other
interesting things that Blt() does. The advantage is about a 10% speed increase
over Blt() if the HEL is being used. If hardware acceleration is supported, though
-- and it almost always is for blitting operations -- there is no speed difference for
average blits, so I use Blt() for just about everything. Let's take a look at it:
HRESULT Blt(
LPRECT lpDestRect,
LPDIRECTDRAWSURFACE7 lpDDSrcSurface,
LPRECT lpSrcRect,
DWORD dwFlags,
LPDDBLTFX lpDDBltFx
);
Because Blt() can do all sorts of special effects, as evidenced by that last
parameter, it's got a few long lists of flags associated with it. I'll show you what
I've found to be the most useful ones. Also, note that when you're blitting from
one surface to another, you call the Blt() method of the destination surface, not
the source surface. All right? Here are the function's parameters:
LPRECT lpDestRect: This is the destination RECT to blit to. If it differs in size
from the source RECT, Blt() will automatically scale the image in the source RECT
to fit the destination RECT! If the destination is the entire surface, you can set this
to NULL.
LPDIRECTDRAWSURFACE7 lpDDSrcSurface: This is the source surface of the blit. If
you're using the blitter to do a color fill on the destination surface, you can set
this parameter to NULL.

LPRECT lpSrcRect: This is the source RECT to blit from. If you mean to blit the
entire contents of the surface, set this to NULL.
DWORD dwFlags: There's a huge list of flags for this parameter, which can be
logically combined with the | operator. Quite a few of them have to do with
Direct3D stuff (like alpha information), so I'll show you a partial list here.
DDBLT_ASYNC

Performs the blit asynchronously through the FIFO (first


in, first out) in the order received. If no room is
available in the FIFO hardware, the call fails, so be
careful using this one.

DDBLT_COLORFILL

Fills the destination rectangle with the color denoted in


the dwFillColor member of the DDBLTFX structure.

DDBLT_DDFX

Uses the dwDDFX member of the DDBLTFX structure to


specify effects used for the blit.

DDBLT_DDROPS

Uses the dwDDROPS member of the DDBLTFX structure to


specify raster operations (ROPs) that are not part of the
Win32 API.

DDBLT_KEYDEST

Uses the color key associated with the destination


surface.

DDCLT_KEYDESTOVERRIDE

Uses the dckDestColorKey member of the DDBLTFX


structure as the color key for the destination surface.

DDBLT_KEYSRC

Uses the color key associated with the source surface.

DDCLT_KEYSRCOVERRIDE

Uses the dckDestColorKey member of the DDBLTFX


structure as the color key for the source surface.

DDBLT_ROP

Uses the dwROP member of the DDBLTFX structure for


the ROP for the blit. These ROPs are those defined in
the Win32 API.

DDBLT_ROTATIONANGLE

Uses the dwRotationAngle member of the DDBLTFX


structure as the rotation angle (specified in hundreths of
a degree) for the surface.

DDBLT_WAIT

Waits for the blitter to be available instead of returning


an error, if the blitter was drawing at the time this blit
was called. The function returns when the blit is
complete, or another error occurs.

I almost always use DDBLT_WAIT. The color key flags are also important; we'll get
to them in just a bit. Now, though, here's the last parameter to Blt():
LPDDBLTFX lpDDBltFx: A pointer to a DDBLTFX structure, which can contain all
sorts of special effects information. If no effects are specified using this structure,
you can pass NULL. Let's take a look at the structure. I'm warning you, it's
massive!
typedef struct _DDBLTFX{
DWORD dwSize;
DWORD dwDDFX;
DWORD dwROP;
DWORD dwDDROP;
DWORD dwRotationAngle;
DWORD dwZBufferOpCode;
DWORD dwZBufferLow;
DWORD dwZBufferHigh;
DWORD dwZBufferBaseDest;
DWORD dwZDestConstBitDepth;

union {
DWORD
dwZDestConst;
LPDIRECTDRAWSURFACE lpDDSZBufferDest;
};
DWORD dwZSrcConstBitDepth;
union {
DWORD
dwZSrcConst;
LPDIRECTDRAWSURFACE lpDDSZBufferSrc;
};
DWORD dwAlphaEdgeBlendBitDepth;
DWORD dwAlphaEdgeBlend;
DWORD dwReserved;
DWORD dwAlphaDestConstBitDepth;
union {
DWORD
dwAlphaDestConst;
LPDIRECTDRAWSURFACE lpDDSAlphaDest;
};
DWORD dwAlphaSrcConstBitDepth;
union {
DWORD
dwAlphaSrcConst;
LPDIRECTDRAWSURFACE lpDDSAlphaSrc;
};
union {
DWORD
dwFillColor;
DWORD
dwFillDepth;
DWORD
dwFillPixel;
LPDIRECTDRAWSURFACE lpDDSPattern;
};
DDCOLORKEY ddckDestColorkey;
DDCOLORKEY ddckSrcColorkey;
} DDBLTFX, FAR* LPDDBLTFX;
If I went through this whole structure, we'd all be standing in line for our AARP
cards by the time I got finished. So I'll just go over the important ones.
Thankfully, most of this structure is for z-buffers and alpha information, which
don't concern us. It makes my job a little easier. :)
DWORD dwSize: Like all DirectX structures, this member has to be set to the size
of the structure when you initialize it.
DWORD dwDDFX: These are kinds of effects that can be applied to the blit. The list
isn't too long, don't worry.
DDBLTFX_ARITHSTRETCHY

The blit uses arithmetic stretching along the y-axis.

DDBLTFX_MIRRORLEFTRIGHT The blit mirrors the surface from left to right.


DDBLTFX_MIRRORUPDOWN

The blit mirrors the surface from top to bottom.

DDBLTFX_NOTEARING

Schedules the blit to avoid tearing.

DDBLTFX_ROTATE180

Rotates the surface 180 degrees clockwise during the


blit.

DDBLTFX_ROTATE270

Rotates the surface 270 degrees clockwise during the


blit.

DDBLTFX_ROTATE90

Rotates the surface 90 degrees clockwise during the


blit.

The only one that might need some explanation is DDBLTFX_NOTEARING. Tearing is
what can happen when you blit a surface out of sync with the vertical blank. If
you blit to the entire primary surface while it's being drawn, you can see the top
half of the old frame along with the bottom half of your updated frame. In my

experience, this has almost never been a problem. On with the DDBLTFX
structure...
DWORD dwROP: You use this flag to specify Win32 raster operations, like those that
can be used with the GDI functions BitBlt() and StretchBlt(). Most of them
have to do with combining source and destination images using Boolean
operators. You can call IDirectDraw7::GetCaps() to retrieve a list of supported
raster ops, among other things.
DWORD dwRotationAngle: This is an angle by which to rotate the bitmap,
specified in hundredths of a degree. This is pretty cool, but unfortunately, rotation
is only supported in the HAL, which means that if the user's video card doesn't
support accelerated rotation, it won't work at all. Hardware support for this is not
too common, either, so the bottom line is that you shouldn't use this in a program
you're going to distribute. If you really need rotation, you'll have to write your
own code for it. Doing that is a topic for another entire tutorial, though, so we'll
just pass it by for now. But note that rotations by multiples of ninety degrees are
easy, so using DDBLTFX_ROTATE90, etc. instead of using dwRotationAngle will
work no matter how lousy the user's video card is.
DWORD dwFillColor: When you're using the blitter to perform a color fill, you
must set the color to be used in this parameter.
DDCOLORKEY ddckDestColorKey, ddckSrcColorKey: Specify these members
when you want to use a destination or source color key other than the one
specified for the surface involved. These guys are important, but we can't talk
about them just yet because we haven't covered color keys. It's coming soon
enough...
That about does it for the immediately useful members of the DDBLTFX structure,
which means you now have enough information to go ahead and start blitting
images! If it's all rather confusing at this point, don't worry, it will become second
nature once you use it a couple of times. Let's look at an example or two.
Suppose you have a back buffer called lpddsBack, and you want to blit its
contents to the primary surface. Here's the call:
lpddsPrimary->Blt(NULL, lpddsBack, NULL, DDBLT_WAIT,
NULL);
How easy is that? To refresh your memory a bit, the first and third parameters
are the destination and source RECTs for the blit, respectively. Since I have
specified NULL for each, the entire surface is copied. Now, let's say you have a tile
that's 16x16 in size in an offscreen surface called lpddsTileset, and you want to
blit it to the back buffer, scaling it up to 32x32. Here's what you might do:
RECT dest, src;
SetRect(&src, 0, 0, 16, 16);
// the coordinates of the
tile
SetRect(&dest, 0, 0, 32, 32); // where you want it to
end up on the back buffer
lpddsBack->Blt(&dest, lpddsTileset, &src, DDBLT_WAIT,
NULL);
The only difference between this example and the last one is that we've specified
the coordinates to be used in the blit. Since the two RECTs are of different sizes,
Blt() scales the image appropriately. Finally, to illustrate using the DDBLTFX
structure, let's do a color fill. Suppose you're in 16-bit color, with a 565 pixel
format, and you want to fill your back buffer with blue. This is all you need:
DDBLTFX fx;
INIT_DXSTRUCT(fx);
// zero out the
structure and set dwSize
fx.dwFillColor = RGB_16BIT565(0, 0, 31); // set fill
color to blue
lpddsBack->Blt(NULL, NULL, NULL, DDBLT_WAIT |
DDBLT_COLORFILL, &fx);

Note that since there is no source surface involved, the second parameter is set
to NULL. Got it? All right, let's take a look at the alternative blitting function,
BltFast(). It's basically a stripped-down version of Blt(), so we don't need to
spend nearly as much time on it.
HRESULT BltFast(
DWORD dwX,
DWORD dwY,
LPDIRECTDRAWSURFACE7 lpDDSrcSurface,
LPRECT lpSrcRect,
DWORD dwTrans
);
As you can see, it's very similar to Blt(). This is also a member function of the
IDirectDrawSurface7 interface, and should be called as a method of the
destination surface. Let's have a look at the parameters:
DWORD dwX, dwY: Here's a difference between Blt() and BltFast(). BltFast()
uses these x- and y-coordinates to specify the destination of the blit, rather than
a RECT. This is because you can't do scaling with BltFast().
LPDIRECTDRAWSURFACE7 lpDDSrcSurface: This is the source surface, just like
before.
LPRECT lpSrcRect: This is also what we used in Blt(), the source RECT for the
blit.
DWORD dwTrans: This parameter takes one or more of the flags listed below, and
specifies the type of transfer to perform. The list is very simple; there are only
four possible values you can use.
DDBLTFAST_DESTCOLORKEY Specifies a transparent blit that uses the destination's
color key.
DDBLTFAST_NOCOLORKEY

Specifies a normal copy blit with no transparency.

DDBLTFAST_SRCCOLORKEY

Specifies a transparent blit that uses the source's color


key.

DDBLTFAST_WAIT

Waits for the blitter to be available instead of returning


an error, if the blitter was drawing at the time this blit
was called. The function returns when the blit is
complete, or another error occurs.

That's it! BltFast() supports color keys, and that's about it. Just as a quick
demonstration, let's look at how you would do the first example I gave above with
BltFast(). Here's how you would copy your entire back buffer onto the primary
surface:
lpddsPrimary->BltFast(0, 0, lpddsBack, NULL,
DDBLTFAST_WAIT);
Now that you're an expert on using the blitter, there are a few things we can
cover that are very useful in any DirectDraw program: color keys and clipping.
Since you've seen a million different flags concerning color keys in some way and
are probably wondering how to make use of them, let's cover that first.

Color Keys
A color key is a method of blitting one image onto another whereby not all of the
pixels are copied. For instance, let's say you have a character sprite you want to
blit onto a background image. Chances are that your hero isn't shaped precisely
like a rectangle (unless your game is a celebration of "modern art"), so copying
the RECT that encloses him is going to produce some unwanted effects. Take a
look at this example picture from Terran to see what I mean:

Now this isn't really the way the game is drawn. In reality, the characters are
blitted to the display before the background has finished being drawn, so that
things like the tops of trees can obscure the player if he's standing behind them.
But that's unimportant for our purposes here; we'll get to it in the next article.
The important point is that without color keying, all of your non-rectangular
images like this character would have boxes around them.
To solve this problem, we use a source color key. The source color key tells the
blitter which colors not to copy. A color key consists of two values: a lower color
value, and an upper color value. When the color key is applied to a blit, any colors
lying between the two values, including the values themselves, are not copied.
There's a structure in DirectX that goes along with this, called DDCOLORKEY. Take a
look:
typedef struct _DDCOLORKEY{
DWORD dwColorSpaceLowValue;
DWORD dwColorSpaceHighValue;
} DDCOLORKEY, FAR* LPDDCOLORKEY;
I just told you what the members are so there's no need to go over them further.
Let me show you what the above blit would look like with a color key activated. In
Terran, I use color keys with the low and high values both set to black. Thus,
black is the only color that doesn't get copied. When this color key is applied to
the blit, this is the result:

Much better, hey? That's the look we're going for! Now before I show you how to
create and use a color key, I want to briefly mention destination color keys,
although they're not often used. Whereas source color keys define which colors
are not copied, destination color keys define which colors can't be written to.
Sound weird? It is. :) Destination color keying would be used if you wanted to blit
one image to a position where it looks like it's underneath another. Suppose that
for some odd reason, you wanted to draw text boxes on your blank back buffer,
and then copy the background image afterwards, but you still wanted the text
boxes to appear on top of the map. What you could do is set a destination color
key to include every color except black. Thus, the only areas on the back buffer
that can be written to are those pixels which are currently black. Take a look at
this illustration to see what I mean:

Like I said, I'm not sure why you'd want to do that, but there it is. Maybe you can
think up a good use for it. If you do, be sure to let me know. :) Now that you
know what color keys are, let's see how you use them.

Setting Color Keys


There are two ways to use a color key in DirectDraw. First, you can attach one (or
two, if you're using source and destination) to a surface, and then specify the
DDBLT_KEYSRC, DDBLT_KEYDEST, DDBLTFAST_SRCCOLORKEY, or
DDBLTFAST_DESTCOLORKEY flag when blitting, depending on which blit function
and what type of color key you're using. Second, you can create a color key and
pass it to the blit operation through the DDBLTFX structure. The first method is
recommended if you're going to be using a color key over and over, whereas the
second method is ideal if you only want to use a certain color key once.
You can either attach a color key to a surface that has been created, or you can
create the color key at the same time you create the surface. I'll show you how to
do both. Let's assume you're working in a 16-bit display mode, with a 565 pixel
format, and you want a source color key on your back buffer that includes black
only. If your back buffer has already been created, you simply create a
DDCOLORKEY structure like we saw before, and pass it to
IDirectDrawSurface7::SetColorKey(), shown below:
HRESULT SetColorKey(
DWORD dwFlags,
LPDDCOLORKEY lpDDColorKey
);
Remember to test the return value of this function with the FAILED() macro when
you call it, to make sure everything happens the way you want it to. The
parameters are straightforward:
DWORD dwFlags: One or more of a series of simple flags describing the color key.
There are only three you'll use:
DDCKEY_COLORSPACE Specifies that the color key describes a range of colors, not
just one.
DDCKEY_DESTBLT

Specifies that the color key should be used as a destination


color key for blit operations.

DDCKEY_SRCBLT

Specifies that the color key should be used as a source color


key for blit operations.

LPDDCOLORKEY lpDDColorKey: This is a pointer to the DDCOLORKEY structure you


filled out.
And that's all there is to it. From that point on, you just specify the appropriate
flag on blits which you want to use the color key. Note that just because a surface
has a color key attached to it, doesn't mean you have to use it every time. If you
specify blits with only the DDBLT_WAIT or DDBLTFAST_WAIT flag and nothing else,
any color keys will be ignored. So here's how you'd set up the color key we
described earlier:
DDCOLORKEY ckey;
ckey.dwColorSpaceLowValue = RGB_16BIT565(0, 0, 0); // or
we could just say '0'
ckey.dwColorSpaceHighValue = RGB_16BIT565(0, 0, 0);
if (FAILED(lpddsBack->SetColorKey(DDCKEY_SRCBLT, &ckey)))
{
// error-handling code here
}
If you want to create a surface with the color key already specified, there are just
a few things you have to do. First, when you're specifying the valid fields of the

DDSURFACEDESC2 structure, you need to include DDSD_CKSRCBLT or


DDSD_CKDESTBLT in the dwFlags member, depending on which type of color key
you want to use. Looking back at the DDSURFACEDESC2 structure, it contains two
DDCOLORKEY structures. One is called ddckCKSrcBlt, and the other is called
ddckCKDestBlt. Fill out the appropriate structure, create the surface, and you're
all set! Here's the example code for an offscreen surface that's 640x480 in size.
// set up surface description structure
INIT_DXSTRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT |
DDSD_CKSRCBLT;
ddsd.dwWidth = 640;
// width of the surface
ddsd.dwHeight = 480;
// and its height
ddsd.ddckCKSrcBlt.dwColorSpaceLowValue =
RGB_16BIT(0,0,0); // color key low value
ddsd.ddckCKSrcBlt.dwColorSpaceHighValue =
RGB_16BIT(0,0,0); // color key high value
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
// type of surface
// now create the surface
if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsBack,
NULL)))
{
// error-handling code here
}
That wraps up color keys, so now we can cover the final topic of this article, which
is clipping.

The IDirectDrawClipper Interface


Suppose you have a graphic that you want to appear such that only half of it is on
the screen. How would you do it? If you've programmed games in DOS, you've
probably done clipping the hard way. Well, in DirectX, it's trivial! First of all, it's
pretty easy to do manually, since DirectX uses RECTs for blitting, and changing
the coordinates of a RECT is much easier than figuring out which parts of the
graphic's memory should be copied, like you would have to do in DOS. But
second, DirectDraw provides an entire interface to take care of this for you, called
IDirectDrawClipper.
The clipping capabilities included in DirectDraw are about as flexible as you could
ask for. Not only can you clip to any RECT area on any surface, but you can clip to
multiple areas! That is, if you wanted a main viewing window, a status bar on the
side of your screen, and a text area on the lower part of the screen, with a black
divider partitioning the screen into those three areas, you could set up a
DirectDraw clipper that would clip to all three regions. How cool is that?
There are several steps involved in creating a clipper to do this kind of work for
you. The first is to actually retrieve a pointer to the IDirectDrawClipper
interface. Not surprisingly, this is accomplished with a call to
IDirectDraw7::CreateClipper, as shown here:
HRESULT CreateClipper(
DWORD dwFlags,
LPDIRECTDRAWCLIPPER FAR *lplpDDClipper,
IUnknown FAR *pUnkOuter
);

Before you make this call, you'll need to declare a pointer of type
LPDIRECTDRAWCLIPPER, so you can pass its address to the function. Remember to
test for failure, as always. Here are the parameters:
DWORD dwFlags: Nice and easy -- this isn't used yet and should be set to 0.
LPDIRECTDRAWCLIPPER FAR *lplpDDClipper: Pass the address of your
LPDIRECTDRAWCLIPPER pointer.
IUnknown FAR *pUnkOuter: You know what to do. Just say no... er, NULL. :)
Once that's out of the way and you have your interface pointer, the next thing to
do is to create a clip list. A clip list is basically a list of the RECTs you want to clip
to. The structure that's used is called an RGNDATA, and it contains enough
information to define an arbitrary region, which can be made up of multiple
components. Let's look at the structure.
typedef struct _RGNDATA {
RGNDATAHEADER rdh;
char
Buffer[1];
} RGNDATA;
It's not at all clear what those parameters are for, so I'll go over them in detail.
RGNDATAHEADER rdh: This is a structure nested within the RGNDATA that contains
information about the second parameter, Buffer. Its fields include things like how
many areas comprise the region, what shape those areas are, etc. We'll cover
that structure in just a second.
char Buffer[1]: This isn't actually meant to be a single-valued array; it's going
to be an area in memory of arbitrary size that holds the data for the actual
clipping areas. As such, instead of declaring an RGNDATA structure, what we do is
to declare a pointer to the structure, and then use malloc() to set enough
memory aside for the RGNDATAHEADER, and the clip list itself. One thing I'll
mention now: RECTs in the clip list should be ordered from top to bottom, then
from left to right, and must not overlap.
I realize that this is all a little hazy right now, but all will be made clear. To that
end, here's the RGNDATAHEADER structure, which is relatively easy to understand.
typedef struct _RGNDATAHEADER {
DWORD dwSize;
DWORD iType;
DWORD nCount;
DWORD nRgnSize;
RECT rcBound;
} RGNDATAHEADER;
DWORD dwSize: This is the size of the structure in bytes. Simply set it to
sizeof(RGNDATAHEADER).
DWORD iType: This represents the shape of each of the areas which make up the
region. It is included so that it may be expanded upon later. Right now, the only
valid setting for this member is RDH_RECTANGLES, which is what we want anyway.
DWORD nCount: This is the number of rectangles that make up the region. In
other words, it's the number of RECTs you plan to use in your clip list.
DWORD nRgnSize: Set this to the size of the buffer that will be receiving the
region data itself. Since we're using RECTs, this size will be sizeof(RECT) *
nCount.
RECT rcBound: This is a RECT that bounds all the rectangles in your clip list.
Usually you'll set this to the dimensions of the surface on which the clipping will
occur.
Now that we've seen all of the structures involved, we can generate a clip list.
First we declare an LPRGNDATA pointer and allocate enough memory to it to hold
our clip list, then simply fill out the fields of each structure according to their
descriptions above. Let's look at the simplest case, which you'll probably use
often, which is that of only a single clipping area. Furthermore, let's make it size

of the whole screen, in a 640x480 display mode. Here's the code that will get the
job done.
// first set up the pointer -- we allocate enough memory
for the RGNDATAHEADER
// along with one RECT. If we were using multiple
clipping area, we would have
// to allocate more memory.
LPRGNDATA lpClipList =
(LPRGNDATA)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT));
// this is the RECT we want to clip to: the whole display
area
RECT rcClipRect = {0, 0, 640, 480};
// now fill out all the structure fields
memcpy(lpClipList->Buffer, &rcClipRect,
sizeof(RECT)); // copy the actual clip region
lpClipList->rdh.dwSize =
sizeof(RGNDATAHEADER);
// size of header
structure
lpClipList->rdh.iType =
RDH_RECTANGLES;
// type of clip region
lpClipList->rdh.nCount =
1;
// number of clip regions
lpClipList->rdh.nRgnSize =
sizeof(RECT);
// size of lpClipList>Buffer
lpClipList->rdh.rcBound =
rcClipRect;
// the bounding RECT
Once you have a clip list, you need to set it as such with your clipper. The call you
want is SetClipList(), which is of course a method of the IDirectDrawClipper
interface. Here's what it looks like:
HRESULT SetClipList(
LPRGNDATA lpClipList,
DWORD dwFlags
);
All you need to do is pass a pointer to the RGNDATA structure you just filled out.
The dwFlags parameter is not used, so just set it to 0. Now that the clip list is
set, there's just one more step, and that's attaching the clipper to the surface you
want to do your clipping on. This requires a call to SetClipper(), which is a
method of the surface you want to attach the clipper to, not the clipper itself.
HRESULT SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper);
And you know what to do with that: just pass your interface pointer and you're all
set. Anytime you try to blit to a surface that has a clipper attached to it, the
clipper does all the work. So if you want to show a tile that's half-on, half-off the
screen, go ahead and blit to a RECT like {-10, -10, 6, 6}, or whatever it happens
to be. Pretty sweet, hey?
The last thing I'll say about clippers is that you should remember to free() the
memory that you allocated with malloc(), no matter what happens with your
clipper. That is, if the call to SetClipList() or SetClipper() fail for some
reason, make sure you're still freeing the memory before you return an error
code or whatever you do to handle errors. You won't be needing the LPRGNDATA
pointer anymore after you use it to set the clip list, so its memory should be
deallocated immediately.

Closing

That just about wraps up the series on general DirectDraw stuff! Can you believe
how much we've covered in just six articles? Congratulate yourself if you're still
reading these; you've come a long way. :) To better illustrate some of the things
we've covered today, I've thrown a little demo program together for you. It
demonstrates loading a bitmap resource; using the blitter for image copying,
color filling, and scaling; and using a clipper to make it all easy. The program is
available here.
There are still some things we haven't covered that didn't quite fit into the articles
like I wanted them to, like page flipping as an alternative to double-buffering, and
using DirectDraw in a windowed app, but that's all right, because we'll just pick
them up as we go on.
Now that the initiation is over, I'm going to be shifting the focus of these articles
off of general Windows programming, and onto developing a tile-based RPG.
Future articles will include such things as developing a good input mechanism
with DirectInput, writing a basic scripting engine, playing background sound and
music, developing utilities to help you with game design, etc. Next time, we'll
take a look at developing a simple scrolling engine for a tile-based game. It's
easier than you might think!
As always, until then, feel free to send me your questions at ironblayde@aeonsoftware.com, or reach me on ICQ at UIN #53210499. Practice up on the
techniques we've covered so far, because you're going to need them all. :) Later,
everyone!

Game Programming Genesis


Part VII : Developing the Game
Structure
by Joseph "Ironblayde" Farrell

Introduction
I know, I know, this was supposed to be about writing a tile engine, but after I
finished that last article and started thinking about how best to approach the
series from here on, I decided that wasn't a good idea. Let me tell you why.
Several times I have either alluded to or actually talked about the fact that you
can't structure your game code the same way in Windows as you would in DOS.
Everything has to be more organized because the entire main loop must execute
at least once per frame, so you don't lose track of the messages that Windows is
sending to your application.
Now, you've seen a little bit of this, in the short demo programs I've provided
with the previous articles. But you probably haven't already tried to extend this
method of programming to something large, unless you've already been writing
Windows games based on the previous articles in the series. So, I came to the
conclusion that before starting to introduce you to the workings of all the parts of
a game, it would be best to devote an article to examining the structure of a
game, to see how all those components will eventually fit together.
This article is a bit different from the others in that we won't be looking at much
code, if any. So if you haven't read up on DirectDraw, or if some things are a bit
unclear, don't worry, because we're taking a break from it. :) All you need is a
basic knowledge of how a Windows program works, so the first article or two will
suffice.
One other thing that I'll mention here: for this article, and most of the
subsequent articles, I'm going to be referring to Terran a lot as an example, so
you may want to download the demo so you can see the working implementation

of what I'm describing. As of the time of this writing, the demo may not run with
all video cards, but once Demo 2 comes out, this will hopefully be resolved. All
right then. Let's get to it!

Overview
Above all, before you start programming, you should have a detailed design
planned out of exactly how the logic in your program is going to operate. There is
no bigger mistake you can make than to just sit down and start coding right
away. It will all seem clear and organized at first, but soon you'll start to realize
how many things you didn't think of, and as you start adding them to your code
in whatever place you can find for them, your program will quickly become
disorganized and inefficient. Trust me; that's why I started over on Terran. :)
You should start with the WinMain() function, as that's where the program
begins. This may seem obvious to you, but a lot of people start off by writing
fading routines, or collision detection functions, and all the rest of the specific,
detailed stuff, before writing the main functions that bring them all together. This
is a little backwards. In an ideal top-down design, you should write the main
function, followed by every function called by that main function, and so on. This
is a good way to do things for at least two reasons.
First, everything is functional right away. If you start your game project by writing
all kinds of technical functions, you can't test and debug them without creating
some temporary code that sets up a scenario in which they would be used. On
the other hand, if you design your game from the top down, the idea is that you
should be able to run it at any time, which is essential because you'll probably be
wanting to test your game constantly as you go. At each stage of development,
more and more of the details are added, and the game starts to take shape.
Second, the overall structure of the game is apparent from the beginning. When
you write the details first, you may find later on that you're making your more
general functions messy in order to call the details in the correct way. In essence,
the details would be dictating the structure of your program, whereas the
structure of your program should be defining how the details should be
implemented. So the better approach is to go from general to specific. If you
have your general program structure up and running by the time you start
developing the details, you have several advantages. You know exactly how that
last level of functions needs to be written in order to work with your design, and
you don't have to write test cases for your functions, because they're already in
place!
What I have done with Terran is to divide the main program into five sections,
and then proceeded with a top-down design on each section. Four of the sections
are the actual game content: the scrolling engine, the scripting engine, the battle
engine, and the menu system. The fifth component encompasses all of the things
that need to be done with Windows and DirectX to make things run. This includes
initialization, shutdown, and things like changing from fullscreen to windowed
mode. Each of the four game components has multiple substates which further
break down the roles of each function. What I'm going to do with this article is to
cover them all in detail, so you can see how everything is organized. Hopefully it
will give you an idea as to what you'd like to do with your game, and make it a bit
easier to understand how to implement specific game components when I start
going through them in the next article.

Example Implementation: Terran


Before anything else, let me show you a little map of the program flow in Terran,
to illustrate what I'm talking about:

This diagram by no means represents all of the functions in the game, but it
shows the basic flow of things. Let's work through the whole thing, one step at a
time.
Execution starts at the Initialization element of the map, which is basically
the first call made from WinMain(). It's broken down into many more stages than
are shown, but if I showed every function in Terran on this map, it would be
impossible to read. :) You should be pretty familiar with the Windows and DirectX
components of this function -- they create a window, and all the required DirectX
interfaces. There are a lot of interfaces we haven't covered yet, but that's not
important for this article. We'll get to them as we go. The Load Game Data
element loads all sorts of information from external files. This includes data on
the items available in the game, data the scripting engine needs to operate,
magic system information, etc.
The last thing done in initialization is to load a script which tells the program
where to go from there. Almost every aspect of Terran is driven by the scripting
engine, as I'll talk about in detail later. In fact, there will be an article in the
future devoted to creating a very basic, albeit very useful scripting engine for
your own games.
After initialization is complete, the program enters the main loop, where it stays
until the user's had enough and shuts the game down. Each iteration of the main
loop takes four basic steps that are shown on the diagram.
First, the input devices are updated. There are two input devices in Terran, the
keyboard and the joystick. This function takes the current state from each device
and logically ORs them together into one master input table which has two
columns. Column 1 tells whether or not the button or key is currently up or down,
and column 2 tells whether or not the button has been pressed just that instant.
Column 1 is used for continuous input, like walking around on the map, whereas
column 2 is used for discrete input, like making menu selections.
Step two of the main loop is to update the music state table. Basically all this
does is checks through all the game songs to see if any of them are flagged as
playing. If they are, the game checks to make sure they're really still going, or if
they've stopped since last time the main loop ran. If they've stopped, the function
sets their flags to reflect this. Don't worry about the details of this right now -implementing music is a topic for much later.
The third step is the most important one: the main loop calls one of the game's
five states. These are World Map, Menu System, Battle System, Scripts Only,
and Shutdown. You get the basic idea of what each one does. Note from the

diagram that Shutdown is a single-use function; once it gets called, that's it.
Everything closes down. The other four require a bit of explanation.
Each of the game's states has a number of substates, which are shown in light
blue on the diagram. Each state function (for example, WorldMapMain())
performs some tasks that are common to all the instances of that state, and then
branches into actions specific to each substate. In some cases this is a simple if
or switch statement, and in some cases it is a whole set of functions.
World Map: The world map state is very simple. First it runs any scripts that are
currently loaded. Second, based on the substate, it either interprets the current
user input or ignores it. Finally, all onscreen characters are updated, the map is
drawn, and any effects currently loaded are performed. (More on implementing
effects later.) That's it!
Menu System: The menu system is a bit more complex, because it has to handle
everything from loading games to generating characters, from purchasing items
to setting equipment. The main function sets the colors for the menu based on
which options are active, then branches to the appropriate menu subfunction.
This is done using an array of function pointers. The game substate is an integer
that serves as an index into this array. The line looks like this:
(*lpfnMenuSubfunctions[substate.current])();
If that looks a little weird to you, you might want to read up a little on function
pointers, as they can be rather useful. Each substate handles the menu-specific
details like determining which options are valid and taking the final action if the
user makes a choice in the final level of a menu. It also renders the current
frame, since the method used to do this may vary depending on the menu. After
that's taken care of, the main menu function runs the current scripts, applies any
active effects, and plots any auxiliary text boxes, like one showing the player's
current gold, or a character's stats.
Battle System: Battle is a strange one because it's closely linked to the menu
system; the battle system is semi-active, a la Final Fantasy, so whenever a menu
pops up to control a character, the battle system transfers control over to the
menu system. Thus, since both the menu system and the battle system must be
able to handle the battle logic, nearly all of the logic is placed in another function,
which can be called from either game state. The main battle function, then, is
largely a placeholder. It does, however, handle the turn queue, which I'll get to in
a second. The function that handles all the logic works something like this:
First, all active characters and enemies are checked to see if their turn has come
up. If an enemy's turn comes up, the enemy AI takes over and makes a choice
for that enemy. If a character's turn comes up, and the menu system is not
currently active, then the menu system is activated and the player receives
control of that character. If the menu system is already handling another
character, then the character whose turn has come up is placed in a data
structure called the turn queue. Once the main battle function is active rather
than the menu system, it will check the turn queue. If it's not empty, the function
dequeues the character who's been in there the longest and sends control back to
the menu system.
In either case, once a character or enemy's action has been decided, the details
of that action are placed into a second queue, called the action queue. Each time
the battle logic comes up (from either the battle system or the menu system),
one of two things happens. If there's an action currently underway, the script
controlling that action is run. If there is no action currently happening, the game
checks the action queue and sets up the next action, if there is one.
After all that is done, the background is copied, the characters and enemies are
drawn, and any active effects are applied, at which point the battle logic is
finished.
The difference between the substates for regular and boss fights is simply that
you can't escape the fight -- you must finish it. This corresponds to a simple if
statement. The third substate, Victory, is a bit different. It reports the gains of

the battle. There's no additional logic needed for it, though, because it's all
handled by a script, as usual.
Scripts Only: This last of the four main game states is dead simple: it runs any
scripts that are loaded. That's it! This state is usually not encountered during play
except duing initialization, but it can come up whenever the game isn't displaying
anything and is doing some behind-the-scenes work.
Shutdown: This does exactly what you'd expect: it exits the main loop, releases
all the DirectX objects that were created, deallocates any memory that's still
being used, and shuts the program down.
The final step of the main loop is to display the current frame, which is simply
copying the back buffer to the screen, using double-buffering if the game is in
windowed mode, or page-flipping if the game is in fullscreen mode. I don't think
we've covered page flipping yet, but it'll come up sooner or later. :)

A Word on Game States


The approach that I have just described, dividing your program up into several
states, is a good one because what it does is to allow you to consider your
program as a number of smaller programs. Instead of trying to develop the whole
thing at once, you can just do one state at a time, which is simpler to write, and
simpler to debug. All you have to do is change the game's initial script to set the
active game state to whatever it is you're currently working on, and you can test
it independently of the other aspects of the game!
My approach to Terran was obviously to write the scripting engine first, since it
requires a script to initialize. Mainly I wrote up all the basic logical functions like
decision statements, loops, and variables. After the scripting engine was on its
feet, I went to the world map state. Once I had that all set up, and had tested it
independently of the scripting engine, I wrote the menu system. After it was
done, then I tested the game with all three elements integrated. This was not
hard to do -- in fact, it required no C code on my part, because the scripting
engine makes all the transfers of control. So basically all I did was write a simple
script, and then sit back and watch it all happen. :)
Now I've come to the battle system. I actually haven't started coding it yet, but
you wouldn't know that from the level of detail I already know the
implementation in. I have all the logic diagrams and such drawn out, so I know
exactly how everything will work. The only thing that remains is to actually write
it, which takes awhile even if you know exactly what you're going for. But when I
start doing this, I'll take out the game's current initialization script that sets up
the whole game, and insert one that simply sets up a battle. In this way, I can
develop the battle system as though the rest of the game wasn't even there, so I
don't have to worry about it. Once the battle system is done, I'll just put the old
initialization script back into place, and just like that, the new component is
integrated.
My recommendation would definitely be to try setting up something like this for
your game when you start it. As you can see, it makes the development process a
lot nicer and results in fewer shouting matches between the programmer and
Visual C++. Those are tiring, becausethe compiler always wins. It does against
me, anyway. :)

Extended Events
By extended events, I mean anything that takes place over the span of multiple
iterations of the main loop. This is a main difference between DOS and Windows
programming. In DOS games, you probably wrote your fade so that the whole
thing took place in one iteration of the main loop. That is, you probably called a

function called FadeOut() or something similar, which displayed 30 or so frames


before returning. You know by now that Windows doesn't like you doing things
like that, so you need to write effects that know how to update themselves each
frame. Figuring out exactly how to do this can be a point of some confusion to
new Windows programmers, so once again, I'm going to explain how I've done it
in my game, to use it as an example. Hopefully it will give you some ideas as to
how to pull off the same kinds of things.
First let's figure out exactly what's going to need to run over a number of frames.
The first and most obvious category is graphical effects. This includes fades,
animations, particle system displays, etc. Up next is text effects, which can
include fading text onto or off of the screen, or simply displaying text over a
number of frames. Third, we have things like waiting for a key before going any
further. It's so simple you may not have thought of it, but you can't just sit in an
empty loop until the user presses a key! You have to keep going! Finally, perhaps
most importantly, are scripts. Most scripts will not execute in a single frame. You
don't want them to, because they may be controlling things that show up on the
screen. Terran's script for the opening scene, for instance, takes about six and a
half minutes to run. At thirty frames per second, that's about 11,700 frames.

Scripts
All right, then... so how do we get a script to run over multiple frames? It's
actually quite simple. When I wrote up the set of commands supported by my
scripting engine, I separated them into two groups: terminating and nonterminating. The former contains commands which, once executed, cause the
script to stop running until the next frame. The latter contains commands which
can be executed all in the same frame; that is, they don't terminate the script for
that frame. Thus, in any given iteration of the main loop, a script may execute
any number of non-terminating commands, but only one terminating command.
Before going any further, let me show you the simple data structures I use to hold
my scripts:
int nScripts[MAX_LINES][MAX_PARAMETERS + 1][MAX_SCRIPTS];
int nNextLine[MAX_SCRIPTS];
There are several constants used here that are #defined elsewhere in the
program. MAX_LINES is the maximum number of lines that a script may contain.
MAX_PARAMETERS is the maximum number of parameters that a script command
can take. I've added one to it in the array declaration because there needs to be
a column to hold the command itself, in addition to its parameters. MAX_SCRIPTS
is the maximum number of scripts that can be executing simultaneously. There
are implementations that use less memory than this, such as allocating memory
as more scripts are needed at once, and using a variable-size memory location for
each command, but it's really not necessary since this represents only a small
chunk of memory, unless you want to run fifty scripts at a time or something
insane like that. In truth, you'll probably never need more than three. I have
MAX_SCRIPTS set to five just in case.
The nNextLine array holds the index of the next line to be executed for each
script. When a script is loaded, its corresponding entry in nNextLine is set to 0 to
indicate the first line should be executed. If an entry in nNextLine is -1, this
means no script is loaded in that slot. The entries in nNextLine are used to easily
implement if statements, loops, and all sorts of other goodies.
Whenever the scripting engine is called to run anything that's currently loaded, it
checks the script slots one at a time. If a script is loaded in that slot, and no
effects are linked to it (more on this in a sec), then it executes commands from
that script until a terminating command is encountered. That command is

executed, and then the engine leaves the script and goes on to the next one.
Pretty simple, hey?
So now you're probably wondering: if Terran's opening scene script takes 11,700
frames to execute, does that mean that there are 11,700 terminating commands
in it!? Of course not! If you write a script that ridiculously long, I expect no less
than a cure for cancer to come out of it. The idea behind the long execution time
is that the script can be suspended. For example, take a look at this pseudoscript-code:
DoSomeAnimation
ShowText 12, 133
Suppose this corresponds to "Do some random animation, then display twelve
lines of text, starting with line #133." Well, the animation is going to take time to
finish! If you want to display the text after the animation is complete, you can't
just execute the animation in one frame, and display the text in the next. This is
where the idea of effects comes into play.

Effects
An effect as I define it for my game is anything you can see happening on the
screen. Fades, particle systems, displaying text, etc. all fall into this category. I
store an effect in a structure that looks like this:
typedef struct GAMEFX_type
{
DWORD dwID;
// effect ID number
DWORD dwFlags;
// control flags
int nCurrent;
// for FOR loops
int nTarget;
// ''
int nStep;
// ''
int nLinkedScript; // ID of script this is linked to
(or -1 for none)
int x, y;
// a screen location
void *buffer;
// optional data table
} GAMEFX, FAR* LPGAMEFX;
Let me run through the parameters and explain what I'm using all this stuff for.
DWORD dwID: This is the effect identifier. It takes a value from one of a number of
FX_ constants I have defined, such as FX_FADE, FX_TEXTIN, or
FX_PARTICLESYSTEM.
DWORD dwFlags: This can be used to store miscellaneous flags for each effect,
and so its use varies from one effect to another.
int nCurrent, nTarget, nStep: These are used to define for-style loops, or
incremental effects. For example, I have a script command called FadeDown. An
example call would be:
FadeDown 34, 11
This means, "Fade down to 34% by increments of 11%." The way the GAMEFX
structure is set up for this, nCurrent is set to the current fade percentage,
whatever that happens to be, nTarget is set to 34, and nStep is set to -11. In
each iteration of the main loop, nCurrent is decreased by 11, and that
percentage of fade is applied to the frame. Once nCurrent is less than or equal to
nTarget, the effect terminates.
int nLinkedScript: This is the important one. If this is -1, the effect executes
side-by-side with any scripts that are running. But if this value is nonnegative,
then the script with that index cannot continue execution until the effect finishes.
This is important! Let's go back to our earlier example.
DoSomeAnimation
ShowText 12, 133

Suppose these commands were found in the script in slot #1. Then when the
effect generated by DoSomeAnimation was set up, it would be linked to script #1,
and thus the script would halt execution until the effect was done, at which point
the text would be displayed, like we wanted! This adds one more step to our
script handler: if the script is linked to any effects, don't execute it. Also note that
any script command which will link the script to an effect must be terminal, so
script execution stops right away.
int x, y: Many effects, like displaying text, require a location on the screen, so I
provide one here. If you need multiple locations for an effect, well, that's what
the final parameter is for...
void *buffer: Many effects can easily be specified by using only the above
parameters. But what if you want to create a blizzard by sweeping 800 particles
across the screen on increasing-amplitude sine waves? You don't want to be
calculating that stuff as you go! The solution is to take this pointer, malloc()
yourself some memory, and you've got a lookup table. My philosophy is to
precalculate everything that can be precalculated. Just remember to free() the
memory when the effect is done.
So is this all starting to fall into place? With the above specifications laid out, you
can easily make your effects such that they can be executed one frame at a time.
The only question that remains is how to store the effects in memory. Well,
effects aren't something I like to have a limit on, and since you're going to be
accessing them in sequential order all the time, this is a great candidate for a
linked list. When you want a new effect, just stick a structure on the end of the
list. When an effect is finished, just cut the links. It's as simple as that! If you're
not familiar with linked lists, find a tutorial, because I use them a lot. :)
One other thing I'll do with effects is to give them an extra parameter in their
corresponding script commands that flags whether or not they should link the
script that calls them. For instance, if I wanted to fade the screen down, and only
then display some text, I could put this in my script:
FadeDown 50, 5, TRUE
ShowText 4, 230
This says that the call to FadeDown should link the script, and thus the text
should be shown only after the fade is complete. But change the TRUE to FALSE,
and the text will show while the screen is fading. This is a very simple, yet very
effective way to either run several effects one after the other or simultaneously,
with very little effort on your part.

Closing
All right, that about wraps up this long-overdue article. We covered splitting a
program into multiple components which can be developed independently of one
another, and I think you've got all the basics now for designing a scheme that will
let you run scripts and graphical effects that make Windows happy. In other
words, they don't hog the processor for too long at once. You should be able to
implement all these things and still maintain a good frame rate.
One thing to notice from this article is just how much of the game can be
controlled with scripts. I use them for everything -- initialization, image loads,
map loads, NPC setup, battle effects, fading, enemy AI, menu setup, etc. And as
we'll see in a future article, probably #9, this can all be achieved with a scripting
language that is very straightforward and easy to implement, with no need for
any black magic on the programmer's part.
Now that I've gone over the basics of how you structure a Windows game
program, the next article will finally be the long-awaited tile engine. I promise
this time. :) I'm not sure how long it will be until I get around to it, but it's
coming. In the meantime, if you've got any questions, go ahead and hit me with
them:

E-mail: ironblayde@aeon-software.com
ICQ: UIN #53210499
Happy coding, until we meet again...

Game Programming Genesis


Part VIII : Basic Tile Engines
by Joseph "Ironblayde" Farrell

Introduction
Whew... it's taken seven articles to get this far, but by now, we've all got a good
idea of how to fight the beast known as Windows, and how DirectDraw works.
We're ready to start putting a game together! In this article I'll show you how to
construct a basic tile engine with DirectDraw. We'll go over the different ways to
design such an engine, and then take a look at how to implement scrolling and
tile animations. I'll be focusing on RPGs as we go along in the series, but you can
certainly use a tile engine for other things as well, like strategy or puzzle games.
If you're reading this, I assume that you have a working knowledge of basic
Windows programming and DirectDraw, using the C or C++ programming
language. Just about everything I went through in the previous seven articles will
be coming together today, so make sure you're up to speed, fire up Visual C++,
and let's get going!

The Design
Before you jump in and start coding, you have to have some idea of what it is
you're trying to create. There are any number of variables you have to consider
when designing a tile engine, like tile size and shape, tile animations, number of
layers, map sizes, and so on. As I pointed out more than once last time, you don't
want to just start writing code without having all this stuff planned out. So before
we get into the actual development of the engine, let's briefly consider a few of
these variables.
Tile size and shape: Here you have a few options. There are three common
choices for tiles: squares, hexagons, and diamonds. Square tiles are good for
RPGs since the arrow keys or directional buttons on a gamepad normally provide
four major directions of movement. Conversely, hexagons are generally not good
for the type of game where the player will be actively controlling the character's
movement... unless you've got some kind of strange keyboard with six arrow
keys on it. Hexagons are usually a good choice for games where most of the
movement will be done with some sort of pathfinding, as in many realtime
strategy games. The diamond-shaped tiles make for a game that looks like it's
using square tiles, but with the viewpoint rotated 45 degrees. It all depends on
what kind of game you're going for. Normally when someone brings up tile-based
games, I tend to think of the good old days of the Genesis and Super Nintendo, of
games like Final Fantasy VI. So for the example in this article, we'll go that route
and use square tiles.
Tile size is something you want to choose based on how much of the world
around the character you want to be able to see, and what resolution you'll be
using. For a resolution of 640x480, I've found that making square tiles at 32x32
works well. So that's what we'll do.
Tile animations: The next thing to consider is if you want to support animated
tiles, and if so, what kind of restrictions you want to place on the animations so
that they look nice, but don't become too tough for you to handle. The answer to

the first question is, of course we want animations! Who wants to have water
that doesn't flow, fire that doesn't burn, or fountains that have little water
droplets somehow magically suspended in the air above them?
Don't get worried about this; animating tiles is actually very easy to do, and I'll
take you through it step by step. What we'll do is to create a system that
substitutes one tile for another at given intervals of time. Since the screen will be
getting redrawn during every frame, swapping the tiles around like this will give
the effect of animation. It will be flexible enough to support animations with any
number of frames, moving at varying speeds, but simple enough that all you
need to know is how to use a linked list. Incidentally, if you don't know how to
use a linked list, I'm sure GameDev.Net has an article or two that will show you
how.
There's one more thing to consider for tile animations, and that's realtime effects.
Imagine this: what if instead of making a torch burn by drawing the frames of
animation, you actually applied an algorithm to the fire during each frame to
make it look like it's burning? Realtime effects can be very cool, but they're just a
little too advanced for what I'll be showing you here. Perhaps I'll cover how to do
it in a later article, or maybe you can figure it out on your own!
Layers: First let me clear up the question a little bit: by layers I mean layers of
graphics on the screen. The simplest maps have only one layer: the whole map is
drawn in one pass, with one tile in each map position. Complicated maps might
have separate layers for backgrounds, background objects, items that can be
picked up, foreground objects, foreground effects... I've seen some engines
where I could just swear the person who wrote it was just trying to come up with
as many layers as possible. :)
I recommend using at least two layers: one for background, and one for
foreground. This will let the character walk "behind" certain objects, and add
some depth to your maps. In Terran, I use three layers. The first is for
background, the second is for objects that can appear on multiple backgrounds
(like grass, dirt, etc.), and the third is foreground. Let me show you an example
of this to make it a little clearer. This is a small section of a map from the game,
drawn one layer at a time so you can see exactly what's going on.
Layer 1: Here you see only the tiles from layer 1, which constitute the
background for the map. None of these tiles will ever have anything behind them,
which is why they are in layer 1.
Layer 2: Now layer 2 is added. Color keys are used for layers 2 and up, so that
the tiles don't have to be square objects. Notice how the flower can appear on
different backgrounds because it is above layer 1. The second row of the tree is
also in layer 2, so that trees can grow near different backgrounds such as grass,
coastlines, or other trees.
Characters: Characters are drawn next, so that they appear in front of the first
two layers, but behind the third. An important thing to remember here is that
characters must be drawn in order from top to bottom so that they occlude each
other properly. Hence before drawing characters, Terran sorts all the visible
characters by their y-values.
Layer 3: Drawing this layer completes the image. Since the characters have
already been drawn, notice how the top of the tree occludes the character to give
a sense of depth. Other things that can go in layer 3 are the tops of mountains or
houses, the arch above a doorway, etc. So depth is actually relatively easy to
implement!
Map size: Finally, you should consider what sizes you will be using for your
maps. It goes without saying that not all of your maps will be of the same size, so
the better question would be to consider what the maximum map size will be. My

thoughts are, why limit the map size at all? If you want to make a ridiculously
huge map that takes an hour to cross, you should be able to! The only thing to
keep in mind is how much memory this will take. If you have fewer than 256 tiles
in a given tileset, then you can represent each tile by a single byte. If you use
three layers, and set the maximum map size to 300x300, you get 300*300*3 =
270K bytes. That's certainly not an unreasonable amount of memory to use for
maps. And if you want to create larger maps, you can always implement a system
that works by loading the map one 300x300 section at a time, for whichever part
of the map the player happens to be at. For this example, though, we'll say
300x300 is the maximum size.
We're now starting to get an idea for what sorts of things we will include in our
tile engine. Before moving on, we need to take a closer look at maps for a
moment, to figure out how we will store them.

Map Files
Before you decide on a file format, you need to decide exactly what game data
will be stored in the map files. There are all kinds of things you could stuff in
there if you wanted to. For instance:
The tiles themselves
Items that can be picked up
Other characters (NPCs)
Enemies
Scripts
The tiles themselves make up the map, so obviously those must be included. The
others are all optional. Items can be included if you want to have items lying
around the map for the player to pick up. Characters can be included if you will
always (or usually) have the same NPCs on the map every time you load it.
Enemies are also an option, in a few different ways. For a game in which enemies
will be encountered on the actual map screen, you can include actual enemy data
about where enemies are located. For a game in which encounters are random,
you can include the types of enemies which can be encountered in given regions.
Finally, scripts are always a good choice, because this allows you to link scripts to
a map. For instance, suppose the player is approaching the throne room of a
castle, and you've got a great story scene all lined up. If you include a script
index associated with the tile that the player will step on as he enters the throne
room, you can write your game such that the script will automatically load and
run at that time, launching the game into your story scene. I know that sounds a
bit vague since we haven't covered scripting, so just keep in the back of your
mind for now. :)
Once you've answered the previous question about what to include in your map
files, it's pretty easy to come up with a file format you can use for them. An
example might be something like the basic format below. My format for Terran is
very similar, only with the sections for items and NPCs removed.
--HEADER-- File identifier (flags this file as a game map file)
- Dimensions of map in tiles
- Number of layers in map
- Number of scripts linked to map
- Number of enemy regions on map
- Number of items on map
- Number of NPCs on map
--DATA-- Tile data

- Script data
- Region data
- Item data
- NPC data
All there is to it is to write the relevant data to a file in the correct order. If you're
not totally comfortable with file I/O and that sounds a little sketchy, never fear...
writing and reading map files will be demonstrated in the sample code that comes
with this article. When you want to actually create large map files, you'll need to
write yourself a map editor. That might sound daunting... but if you can write a
tile engine, you can write a map editor. They're actually quite similar. Getting into
that would make this article way too long, so again, perhaps I will cover this in
the future. Try a few things and see what you can come up with!

Data Structures
All right, now it's time to figure out how we will represent our world. In our case,
this will mean creating two data structures: one to represent a map, and one to
represent an individual tile. Let's do the map structure first. You are going to want
to keep track of all the sorts of data that is stored in a map file, plus one more
very important thing: the camera. The camera keeps track of where on the map
the player currently is. We'll see exactly how it works in just a minute. For now,
let's have a look at a sample map structure:
typedef struct MAPDATA_type
{
int xMax, yMax;
// map size in tiles
int xCamera, yCamera;
// camera location in
pixels
int xMaxCamera, yMaxCamera; // max camera coordinates
int nNPCCount;
// number of NPCs
int nScriptCount;
// number of linked
scripts
LPNPC lpnpc;
// linked NPCs
LPSCRIPT lpscr;
// linked scripts
} MAPDATA, FAR* LPMAPDATA;
And now we declare a variable of this type, along with an array of BYTEs to hold
the actual tile indices:
BYTE byMap[300][300][2];
// the actual tiles
MAPDATA mapdata;
// the other map data
Depending on what kinds of things you'll be adding to the map, you might also
want to include entries for items, enemies, or enemy areas. For the actual
example code that I'll include with this article, I won't even be including the
scripts and NPC members, since I don't want to introduce too much at once. I've
included it here so you can see an example of how it might be included once you
expand your engine into something you can build a game on. Now, let's take a
closer look at the members of this structure.
int xMax, yMax: These are the actual map dimensions. They are stored in the
map file.
int xCamera, yCamera: These are the current coordinates of the "camera," and
so they are changing constantly. The initial values are set by whatever script
loaded the map. Each frame, the map is drawn with the upper-left hand corner of
the screen at the camera coordinates. Thus, the minimum camera coordinates are
(0, 0).
int xMaxCamera, yMaxCamera: These are the maximum camera coordinates, and
are calculated based on the size of the map. Each tile in Terran is 32x32, so the
width of the map in pixels is 32 * xMax, and the height is 32 * yMax. But the
camera is located at the upper-left corner of the screen, so we have to subtract

the screen dimensions. The maximum camera coordinates are thus calculated in
this way:
mapdata.xMaxCamera = (mapdata.xMax - 19) * 32;
if (mapdata.xMaxCamera < 0)
mapdata.xMaxCamera = 0;
mapdata.yMaxCamera = (mapdata.yMax - 14) * 32;
if (mapdata.yMaxCamera < 0)
mapdata.yMaxCamera = 0;
The 19 and 14 are subtracted from the map dimensions because the screen
resolution is 640x480, and thus a full screen is 20 tiles across by 15 tiles down.
This accounts for the fact that the camera is positioned in the upper-left corner of
the screen. If a map is ever encountered that is smaller than the screen, the
maximum camera coordinates are set to (0, 0) so we don't get negative values.
int nNPCCount, nScriptCount: These are the number of NPCs and scripts
currently on the map, respectively. The latter is included in the map file; the
former is not. The number of NPCs will be set by the same script that is loading
the map.
LPNPC lpnpc: This pointer will be an array of NPC structures, each of which
describes the location and behavior of a single character. I won't get into the NPC
structure here.
LPSCRIPT lpscr: This will be an array of SCRIPT structures, which simply hold
the index of each script (used for locating the correct script file), and the tile it is
linked to.
So that's not so bad. Now let's consider how to hold information about our tiles.
What do we need to know about tiles? For starters, we need to know where on
the DirectDraw surface each tile is located. There will probably be a pattern here
that you can use just as easily, but I prefer to actually include a RECT for each
tile, because then if you ever want to use tiles of variable sizes, the ability to do
so is there. You also need to know whether or not that tile can be walked on. This
will define where a player can go on the map. That's the bare minimum you need.
So let's take a look at what Terran is using:
typedef struct TILE_type
{
RECT rcLocation;
// location on DirectDraw surface
int bWalkOK;
// can the tile be walked on?
int nAnimSpeed;
// animation speed in frames
DWORD dwFlags;
// approach flags
TILE_type *lpNext; // next tile in animation
} TILE, FAR* LPTILE;
Some of this stuff is pretty simple to figure out, but more of it needs explanation,
so here's the member list:
RECT rcLocation: This is the location on the surface, which we already talked
about.
int bWalkOK: This simply tells whether or not the tile can be walked on. My
variable name suggests a Boolean value, but you can do other things with this as
well. For instance, if you were creating a real-time strategy game, you might
want to use this field to not only say whether or not a unit can move on this tile,
but how quickly or efficiently as well.
int nAnimSpeed: This is used for tile animations. If the tile is not animated, this
member is 0. If the tile is animated, such as water or fire, then this member is
the number of frames to delay before displaying the next tile in the animation.
DWORD dwFlags: You can stuff just about anything you want in a parameter like
this, but I'm using it for approach information. That is, when the character is
walking on this tile, how does his location change? For instance, if you're walking
left onto a tile depicting a staircase, you don't just walk straight onto it... you

walk up the stairs! The dwFlags member is used to specify if and how a
character's location changes when traversing this tile.
TILE_type *lpNext: If the tile is animated, this is a pointer to the TILE structure
representing the next tile in the animation. So basically, in addition to having a
TILE structure for each tile, you can also string the structures together in linked
lists to account for animation.
To keep track of my tilesets, each of which have a maximum of 256 tiles, I have
an array of 256 TILEs to keep track of the tile data itself, and an array of 256
LPTILEs that point to the corresponding structures. Then, when an animation
needs to be advanced to the next frame, I can simply do this:
lptile[x] = lptile[x]->lpNext;
And the animation advances by one frame for every instance of that tile on the
map, without even having to touch the map data itself! Is that easy or what? :)
Finally, we need to have information about the player. For the purposes of
showing a map on the screen and being able to wander around on it, we really
don't need anything except the player's location. My player structure has all sorts
of information about stats, spellbooks, inventory, etc. that is irrelevant as far as
the tile engine is concerned, and actually, I think this will end up being long
enough without getting into animating the player on the map, so let's not worry
about that right now. Our goal is simply to get an animated map on the screen,
and to be able to scroll it around with the arrow keys. For this demo, then, all we
need to keep track of location are the camera coordinates, and those are already
included in the map data structure. All right, ready to code this thing? Me too. :)

Writing the Code


First things first: let's see what this is going to look like. The structure of this
demo will be very similar to the other Windows programs we've written so far in
this series, only it will be a lot more exciting, because you'll be able to interact
with it and expand upon it later. Take a look:
Create a window.
Initialize DirectX:
o Set up DirectDraw.
o Create three surfaces: primary, back buffer, and tileset.
o Create a clipper and attach it to the back buffer.
Load tileset and map.
Main loop:
o Handle the Windows message queue.
o Exit loop if WM_QUIT message found.
o Check for user input.
o Render the map to the back buffer:
Find the horizontal and vertical ranges of tiles that need to
be drawn.
Update tile pointers to account for animations.
Blit the first layer without color keying, so it overwrites the
previous frame completely.
Blit the second layer with color keying.
o Flip the frame to the primary surface.
o Lock to a constant frame rate.
Shut down DirectX.
Return to Windows.
It's a long list of tasks compared to some of the things we've done in the past,
but you already know how to do most of this. You can do all the initialization and

shutdown functions, and you can handle the message queue and the user input.
When the user is pressing the arrow keys -- which you can check for using the
GetAsyncKeyState() function -- you simply update the camera coordinates
accordingly, and the part of the code that draws the map will handle the rest. The
only thing that remains to be demystified is that map-rendering process, so that's
what we'll do next.
One last thing before we get into the actual map drawing. Notice that I said we
will "flip" the frame to the primary surface, instead of blitting it. That means that
we'll be using a technique called page-flipping, instead of the double buffering we
used in article #6. It's my fault for forgetting to cover it -- sorry! Anyway, it's not
hard. All you need to do is create your primary surface using DDSCAPS_FLIP and
DDSCAPS_COMPLEX, with one back buffer attached. Remember, we saw an example
of doing this near the end of article #4. At least, I think it was #4. :) Anyway,
once you've got a back buffer chained to the primary surface, you simply copy
the latter to the former by calling IDirectDrawSurface7::Flip(), which looks
like this:
HRESULT Flip(
LPDIRECTDRAWSURFACE3 lpDDSurfaceTargetOverride,
DWORD dwFlags
);
The first parameter is the surface in the flipping chain to flip the image to. If you
pass NULL, the image cycles through the buffers in the order in which they are
attached to one another, which is what we want. The second parameter should
just be set to DDFLIP_WAIT, to specify that the call should not return until the flip
is complete. The nice thing about page flipping is that the image isn't really
getting copied! What happens is that DirectDraw switches the pointers to the two
surfaces in memory, so that in essence, the back buffer becomes the primary
surface, and vice versa. Nice and fast, nice and easy. Anyway, an example of all
this is in the code, so let's move on.

Drawing the Map


Now we're getting somewhere! The first thing to do when we draw the map is to
determine the range of tiles which are currently in the player's view, so we don't
waste time trying to blit a whole bunch of tiles that are way off the screen
somewhere. I already said that our standard tile size is 32x32, and the screen
size is 640x480, so a full screen is 20 tiles across by 15 tiles down. However, this
assumes that the boundaries of the tiles line up perfectly with the boundaries of
the screen. For example, suppose the leftmost tile is only halfway on the screen.
Then there's going to be another tile halfway on the screen at the extreme right
side of the screen, making for 21 tiles altogether. Similarly, there may be 16 rows
of tiles vertically rather than 15, if the upper row is only halfway on the screen.
How do we account for this? It's simple. If the camera's x-coordinate is divisible
by 32, the tile boundaries line up with the screen boundaries horizontally, and
there will be 20 tiles across. Otherwise, there will be 21 tiles across. Similarly, if
the camera's y-coordinate is divisible by 32, there will be 15 tiles down;
otherwise, 16.
Now, how about finding the index of the tile to use to start with? Consider this: if
the camera's x-coordinate is anywhere between 0 and 31, then the first column of
tiles (column 0) is going to be at least partially visible. As soon as the camera's xcoordinate becomes 32, that first column of tiles is completely off the screen, and
we use column 1 instead. Look at that for awhile and you'll realize pretty quickly
that the first column of visible tiles is given by the camera's x-coordinate, divided
by 32, where we truncate the decimal rather than rounding. The same goes for
the rows of tiles: the first row that is visible will be given by the camera's ycoordinate, divided by 32.

The last thing we need to do, just in case we have a map smaller than the screen
size, is to make sure that the ending values for our tile ranges do not exceed the
maximums that are stored in our map. Have a look at the code that does all this:
// set original destination RECT for first tile -aligned with the
// upper-left corner of the screen
RECT rcDest = {0, 0, 32, 32};
// find default tile range -- divide camera coordinates
by 32 and
// use the default of 21 tiles across and 16 tiles down
int xStart = mapdata.xCamera >> 5;
int yStart = mapdata.yCamera >> 5;
int xEnd = xStart + 20;
int yEnd = yStart + 15;
int x, y;
// now check if the camera coordinates are divisible by
32
x = mapdata.xCamera & 0x0000001F;
y = mapdata.yCamera & 0x0000001F;
if (!x)
{
// if xCamera is divisible by 32, use only 20 tiles
across
xEnd--;
}
else
{
// otherwise move destination RECT to the left to clip
the first column of tiles
rcDest.left-=x;
rcDest.right-=x;
}
if (!y)
{
// if yCamera is divisible by 32, use only 15 tiles
down
yEnd--;
}
else
{
// otherwise move destination RECT up to clip the first
row of tiles
rcDest.top-=y;
rcDest.bottom-=y;
}
// finally make sure we're not exceeding map limits
if (xEnd > mapdata.xMax)
xEnd = mapdata.xMax;
if (yEnd > mapdata.yMax)
yEnd = mapdata.yMax;
All right, now we've got the starting and ending indices for the tiles we need to
blit, and the RECT for the first tile's destination on the screen. I already showed
you the one-line method for updating animations. All you need to add is
something to make sure it only happens according to the animation speed set by
the nAnimSpeed member of the TILE structure. There are two ways to do this.

You can keep a running frame count for each tile, and then when that frame
counter reaches the value of nAnimSpeed, then advance the animation and reset
the counter. The other way is to simply use an array of counters, say
nCounters[10], and update them each frame like this:
for (x=2; x<10; x++)
{
if (++nCounters[x] == x)
nCounters[x] = 0;
}
That way, the counter in position x in the array is equal to 0 once every x frames.
So when you're updating animations, just check to see if the appropriate counter
is equal to 0, and if it is, advance the animation. Remember that if a TILE's
nAnimSpeed is equal to 0, you don't need to do this because it's not an animated
tile. This method takes a little less memory, and possibly less time to update,
depending on how many animations you have. The downside is that you don't
want to create too many of these counters, or you'll be using too much time to
update them. For example, if you wanted to have an inn sign that blows in the
wind every once in awhile, you might only want to run the animation once every
100 frames, and so a counter for each tile would be appropriate.
All right, now we're ready to start drawing! All we need are two nested for loops,
one to draw the columns and one to draw the rows. In the inner loop, we update
the destination RECT, and blit the tile. That's it. The code is very straightforward:
BYTE byTile;
// store original rcDest RECT
RECT rcTemp;
rcTemp = rcDest;
// plot the first layer
for (x=xStart; x<=xEnd; x++)
{
for (y=yStart; y<=yEnd; y++)
{
// blit the tile
byTile = byMap[x][y][0];
lpddsBack->Blt(&rcDest, lpddsTileset,
&tile_ptrs[byTile]->rcLocation, DDBLT_WAIT, NULL);

// advance rcDest RECT


rcDest.bottom += 32;
rcDest.top += 32;

// reset rcDest RECT to top of next column


rcDest.left += 32;
rcDest.right += 32;
rcDest.bottom -= ((yEnd - yStart + 1) << 5);
rcDest.top -= ((yEnd - yStart + 1) << 5);
}
That's all you need. This code draws the entire first layer, advancing the
destination RECT as it goes without having to re-calculate the whole thing each
time. Notice that at the end of the outer loop, we can't just decrease the y-values
of the RECT by 480 because we might be dealing with a map that is smaller than
the screen size. I won't even bother showing the code for the second layer,
because it's nearly identical to this, and so it would just be redundant. These are
the only differences:
1. Before drawing the second layer, make sure you reset the destination
RECT by setting it equal to rcTemp.

2. Obviously, use byMap[x][y][1] for the second layer instead of byMap[x][y]


[0]. :)
3. Add the DDBLT_KEYSRC flag to the blitter call for the second layer.
4. For the second layer, since the previous frame has already been
overwritten, you only need to draw the tile if its index is greater than 0,
since I leave tile 0 blank in all my tilesets (usually a good idea).
With that, you can easily draw the whole map. And it may not seem like it, but
you now have all you need to know to create the tile-scrolling demo on your own!
It's going to be a simple demo, but it is our first program that allows considerable
customization and user interaction. There are all sorts of things you can add to
this demo, and so it would be a great exercise to try and implement a few things
on your own, like adding a third layer, or adding the ability to move from one map
to another. If you're really feeling ambitious, you could even take a shot at a map
editor!

Closing
That's it for today's installment... but we covered a lot! In one day, we've gone
from simple knowledge of DirectDraw to being able to implement a scrolling,
animated, multi-layer tile engine. Are you starting to see how powerful the
DirectX API is? We actually haven't covered a whole lot of interfaces and
functions, but we already have the tools to put a pretty good-looking game
together. For now we've just got a blank world, but next time around, I'll be
showing you how to add characters it, to make it come alive.
The sample code for this article has a lot of stuff in it that you should take note
of. For one thing, it draws on the material from just about every article in the
series up to this point, so if you understand what's going on here, you're doing
great! It demonstrates how to read and write a basic map file as well, which is
something I've seen people ask about quite a bit. There are two programs
included in the .ZIP file. One is a very simple example of writing a map. This is a
Win32 Console Application, so keep that in mind if you decide to compile it. The
other program is the actual tile engine. It has one source file, two headers file,
and one resource script -- be sure to include them all when you compile. Also,
remember to link ddraw.lib and dxguid.lib to the project. Finally, the bitmap
needs to be in the same directory as the source files, and you should be OK. I've
included the executable as well, just in case you have problems compiling the
code. I wanted to keep the tileset small, and I spent about two minutes on the
tiles that are in there, so forgive me for the abysmal quality of the graphics. :) As
always, I'm open to questions on any of this stuff:
E-mail: ironblayde@aeon-software.com
ICQ: UIN #53210499
Have fun experimenting with this stuff, and I'll see you in a few weeks.

Game Programming Genesis


Part IX : Adding Characters
by Joseph "Ironblayde" Farrell

Introduction
So you've got the beginnings of a tile engine. You've got your world scrolling
across the screen in 16-bit color, complete with animations and anything else you

decided to add on your own... but it's a pretty lonely place so far, isn't it? Today
we're going to fix that problem by going over an easy way to implement the
game's main character, along with some NPCs to keep him company. I'll show you
how to get your world scrolling around the character so he stays in the middle of
the screen, how to configure NPCs, and how to work them into the function you
already have for drawing the map. When all is said and done, we'll have a nice
start on an RPG (or other tile-based game) that is easily expanded upon. As
usual, I'll have a complete code example for you to play with.
Throughout this article I'll be referring back to the basic tile engine we developed
back in article #8, so if you haven't read it, and you haven't created a tile engine
of your own before, go back and look over article #8 so you can follow along.
Aside from that, all you need is a working knowledge of DirectDraw, the allpowerful Visual C++ (or one of those other ones), and some artwork to use.
Ready to breathe a little life into your game world? Let's go!

Keeping Track of Characters


As usual, before we can go about getting something to move around on screen
the way we want it to, we have to come up with a logical way to represent it in
memory. Naturally, player characters and NPCs (non-player characters) are going
to be represented somewhat differently. For instance, NPCs will need some flags
or even a script assigned to them that describes how they move and act, whereas
player characters do not, because they will be controlled by whoever's playing
your game. Similarly, unless you're making your NPCs very complicated, they
won't need a lot of the details that go into your player characters. For instance,
non-combative NPCs don't need a number of hit points, a list of available spells,
or a value for dexterity in a fight.
This leads to a little bit of a problem: what happens when we want to start
moving NPCs and player characters around? Are we going to have to write one
set of functions for player characters, and another set for NPCs? Obviously we
don't want to do that. You C++ people should be thinking about a Character base
class right now, and child classes called Player and NPC, or something similar. For
those of us using C, we can do much the same thing by nesting a structure. That
is, we'll create a struct for player characters and a separate one for NPCs, but
each one will include a common struct that contains movement data. This might
be a little unclear right now, so let me show you an example of such a structure,
and we'll take it from there.
typedef struct CHARMOVE_type
{
int xTile, yTile;
// character's current
location
int xOffset, yOffset;
// offset from tile location
in pixels
int xMove, yMove;
// number of tiles character
is currently moving
int nFace;
// direction character is
facing; takes FACE_ constants
} CHARMOVE, FAR* LPCHARMOVE;
#define FACE_SOUTH 0
#define FACE_NORTH 3
#define FACE_EAST 6
#define FACE_WEST 9
All right, so what is all this stuff?
int xTile, yTile: As you've probably guessed, this will be the character's current
location in tiles. These values will always be at least zero, and less than the size
of the map in tiles. Furthermore, when a character is in the process of moving

from one tile to another, these variables will be the coordinates of the tile the
character is moving towards, not the tile the character is coming from. The
reason for this will become clear later.
int xOffset, yOffset: Characters won't always be standing directly on top of a tile.
When they're in the middle of moving from one to another, we'll need these
offsets to track their exact location in pixels.
int xMove, yMove: These values are the number of tiles the character is set to
move. For player characters these will always be -1, 0, or 1 (depending on
direction); but for NPCs, whose every movement is not being controlled by an
input device, the program may want to make them move greater distances at
once. If xMove and yMove are both 0, the character is standing still. If xMove and
yMove are both nonzero, the character is moving diagonally somehow. This will
only happen if you allow eight-directional movement in your game. I didn't do
that in Terran simply because there's no way in hell I can draw a half-decent
character as viewed from a 45-degree angle. :)
int nFace: This simply keeps track of which direction the character is facing. Why,
you ask, did I use 0, 3, 6, and 9 for the direction values instead of 0, 1, 2, and 3?
Well, you'll understand when you see how I've set up the character image files
we'll be using.
From here we might want to get started on structures for the whole player
characters and NPCs, but I'm going to largely leave that out for now because all
we'll really be dealing with is movement. The player structure would normally
have all sorts of things in it like what level the character is on, what items he's
carrying, what spells he can cast, and all kinds of other things. The NPC structure
should have flags that determine how NPCs will be controlled. For now, I'm just
going to leave those out and we'll use some kind of default values. Once we learn
how to manipulate that, I'll show you what you can do to individualize your NPCs
a bit. But for now, we'll just use these rather pointless structures:
typedef struct PLAYER_type
{
CHARMOVE move;
} PLAYER, FAR* LPPLAYER;
typedef struct NPC_type
{
CHARMOVE move;
} NPC, FAR* LPNPC;
Now that we've got two structures serving no purpose other than to differentiate
between a player and non-player character, let's go on to seeing just how we get
them moving around in our game.

Character Image Files


To move characters around, I usually suggest a minimum of 12 frames. There are
four cardinal directions, and in each direction, there should be one frame of the
character standing still (facing that direction), one of him in mid-stride with left
foot forward, and one with the right foot forward. That's what I'm using in Terran,
and in each character image file, the frames are arranged in a vertical line like
you see below. Notice that the leftmost character (frame #0) is facing south,
frame #3 faces north, frame #6 faces east, and frame #9 faces west. Now do you
see why I assigned the FACE_ constants the values that I did? To find out which
frame to use for a character standing still, you just use the move.nFace variable.
To get the frame with his right foot forward, you use move.nFace +1. And to get
the frame with his left foot forward, you just use move.nFace +2. Convenient,
hey? :)

This character, by the way, is one of my current main character graphics from
Terran. It'll probably change before the final game comes out. But my point in
bringing this up is that all the graphics included for the demo that come with this
article are pulled directly from my game; they are there for you to experiment
with until you can come up with your own stuff, but when you do end up making
a game and distributing it, please don't use my art, since it may appear in Terran
as well. I know there's someone out there saying, "But Blayde, your art is so
terrible, why would anyone want to steal it?" Well... you're probably right, but
still, I thought I'd mention it.
Anyway, matters of copyright aside, this is a convenient way to set up your image
files and I recommend you use it, or something like it. Vertical lines work just as
well as horizontal ones do; it's just a matter of preference. For my game and for
our demo, each character fits inside a 32x64 box, so each character image should
be 384x64, and you can easily locate a RECT containing the correct frame by
doing this:
RECT rcChar = {0, nFrame << 5, 64, (nFrame << 5) + 32};
The nFrame variable, in this case, is simply the number of the frame you're
looking for. We'll do something like this when we get to actually blitting the
characters onto the screen. Notice, though, that I have the y-values permanently
set to 0 and 64. This only works if you have a separate surface for each character.
Generally I don't like to do that; I have one large surface with all my characters
on it. So then you'll need some way of computing the y-values. Usually I do this
by including a value in the NPC or player character structure that says which
"slot" on the surface that character's image is loaded into.

Handling the Camera


Remember back in article #8 when we set up the camera to define what part of
the map was being drawn in each frame? Well, now we have to change it just a
little. Last time, we set the camera so that it shifted in the appopriate direction
anytime the user pressed the arrow keys. But in an actual game, chances are that
you're not controlling the camera directly with the arrow keys; rather, you are
controlling the character, and the camera follows the character.
The simple method we used in that demo would be fine if the character was
always going to be centered on the screen, but generally that is not the case.
What happens when the character approaches the very edge of a map? If we
centered the camera on him, then there would be an area of the screen with
nothing displayed on it, and we don't want that. I'm sure you know what I mean;
you've probably played a game that has the character centered on the screen,
except when he gets near the edge of a map, at which point the camera remains
still and the character moves towards the edge of the screen as well. That way,
the whole screen is always occupied by the current map.
So how do we implement this? Actually, it's quite easy. Instead of changing the
camera every time the arrow keys are pressed, we'll calculate the camera
coordinates during every frame, based on where the character is positioned in the
world. Since our screen is 640 pixels wide and our character is 32 pixels wide, we
can center the camera horizontally by locating the camera (640 - 32) / 2 = 304
pixels to the left of the character. Similarly, the screen is 480 pixels tall and our
character is 64 pixels tall, so the camera should be placed (480 - 64) / 2 = 208
pixels above the character to center it vertically. Then to make sure the camera
stops when the character gets near the edge of the screen, all we have to do is
make sure the camera's coordinates don't drop below (0, 0), or go above the

maximum allowable camera coordinates as defined in the MAPDATA structure we


created in article #8. It's that easy! So we can get the camera to follow our
character with this simple code:
// center camera on main character
mapdata.xCamera = (player.move.xTile<<5) +
player.move.xOffset - 304;
mapdata.yCamera = (player.move.yTile<<5) +
player.move.yOffset - 240;
// clip camera to map boundaries
if (mapdata.xCamera < 0)
mapdata.xCamera = 0;
if (mapdata.yCamera < 0)
mapdata.yCamera = 0;
if (mapdata.xCamera > mapdata.xMaxCamera)
mapdata.xCamera = mapdata.xMaxCamera;
if (mapdata.yCamera > mapdata.yMaxCamera)
mapdata.yCamera = mapdata.yMaxCamera;
It's worth mentioning here that this code should probably be included somewhere
other than the map rendering function, because you may not always want to use
it. For instance, in game story scenes, you'll almost certainly want to control the
camera using a script instead of simply setting it to follow the character. If you
think back to the logic diagram I showed you for Terran in article #7, this cameracontrol code is only used while the World Map game state is active. Story scenes,
on the other hand, occur under the Scripts Only state, and so whatever scripts
happen to be running at the time can do what they like with the camera.

Checking For Movement


Now we have everything we need in terms of organization and setup, and we're
ready to create some sort of mechanism for controlling the character. Since we
haven't covered DirectInput and creating a universal input system from multiple
devices (such as keyboard and gamepad), we'll just use the Win32 API function
GetAsyncKeyState(), which we met back in article #3. As you may recall, we
wrapped the function in a macro which masks out the bit we're interested in.
Here's the macro again, to refresh your memory:
#define KEYSTATE(n) ((GetAsyncKeyState(n) & 0x8000) ?
TRUE: FALSE)
Now what we need is for the character to start moving in the specified direction
as soon as an arrow key is pressed. How do we do that? Well, thinking back to
the CHARMOVE structure we just set up, there were two member variables called
xMove and yMove. For now, all we'll do is set those to their correct values based
on the key being pressed, and we'll see how to handle the data when we write
the actual animation code in a bit. So, all we do is check each arrow key. If it's
being pressed, and the character is not already moving, then we set xMove or
yMove appropriately. The other thing we need to do is to adjust xTile and xOffset,
or yTile and yOffset, to reflect the character's new location. We don't want the
character's position to change suddenly, so when we increase a tile setting by 1,
we decrease the corresponding offset by 32 (our tile size). In this way, the
character's position remains constant, but represented in a different way, so that
the animation code knows to do something with it. Finally, we update nFace so
the character turns to face the right direction. In this code, and for the rest of the
article, I'll assume we have a PLAYER structure set up called player.
if (KEYSTATE(VK_UP) && (player.move.xMove == 0) &&
(player.move.yMove == 0))
{
player.move.yMove = -1;

player.move.yTile--;
player.move.yOffset += 32;
player.move.nFace = FACE_NORTH;

}
if (KEYSTATE(VK_DOWN) && (player.move.xMove == 0) &&
(player.move.yMove == 0))
{
player.move.yMove = 1;
player.move.yTile++;
player.move.yOffset -= 32;
player.move.nFace = FACE_SOUTH;
}
if (KEYSTATE(VK_LEFT) && (player.move.xMove == 0) &&
(player.move.yMove == 0))
{
player.move.xMove = -1;
player.move.xTile--;
player.move.xOffset += 32;
player.move.nFace = FACE_WEST;
}
if (KEYSTATE(VK_RIGHT) && (player.move.xMove == 0) &&
(player.move.yMove == 0))
{
player.move.xMove = 1;
player.move.xTile++;
player.move.xOffset -= 32;
player.move.nFace = FACE_EAST;
}
That's not so bad, right? Just a few simple lines. Consider this for a minute: what
would happen if we didn't check whether or not the player was already moving?
And what would happen if, when the player pressed up or down, we only checked
vertical movement, not horizontal, before assigning a value to yMove (and
similarly for when right or left was pressed)?
The answer to the first question is that keystrokes would build up in a sort of
buffered fashion... if your game runs at 30 FPS, and the player were to hold down
the up key for one second, the player would move 30 spaces to the north. That's
obviously not what we want!
The answer to the second question is something more useful: it would yield eightdirectional movement instead of our four-directional system. However, it would
allow the character to start moving vertically in the middle of moving horizontally,
which means the end destination would not be centered on a tile. This means
we'd have a pixel-by-pixel scrolling game instead of pixel-by-tile, which is a bit
harder to handle; I'll explain the difference once we come to animation.
Before we get any character animations onscreen, there's one more thing we
have to consider. Our movement code as it stands right now will work, but it will
let the character go anywhere, even off the edge of the map! And what's the
point of having a map if the character can just walk through everything? What we
need is a function that performs a series of checks on the intended destination,
and tell us whether or not it's allowable to move there.

Collision Detection
Collision detection in a tile engine like this one is so simple it hardly deserves
such an important-sounding name. All we have to do is look at the map at the
place to which we want to move. If there's an object there, movement is not
allowed. If the spot is open, movement is OK. How hard can that be? Basically
there are three things we must check:
1. Has the character reached the boundary of the map?

2. Is the character attempting to walk into a solid object on the map?


3. Is the character attempting to walk into another character?
The first one is a complete triviality. The second and third and pretty easy as well,
as long as we have a way to keep track of that information, which we do! For
detecting objects on the map, we have only to look at our tileset, which is
represented by the TILE structure we set up back in article #8. If you remember,
each TILE has a value called bWalkOK, which is TRUE if the tile can be walked on,
or FALSE for solid objects. And remember that we check layer 1 of the map, not
layer 2, because layer 2 is the foreground. If we were employing a three-layer
system like I use in Terran, then what I do is to check layer 2 first. If layer 2 has
some graphic in it, that tile determines whether or not the space can be walked
on. If layer 2 is empty there, I check layer 1.
For checking characters, we look at the main character and all the NPCs, to see if
any of them are standing on that tile. This is why, when a character begins
moving, we change their tile location immediately instead of waiting until
movement is done. This way, walking onto a tile that another character is moving
towards is not allowed, whereas walking onto a tile that another character is
currently walking away from is all right. Now all we need is an array of NPCs so
we can keep track of them:
LPNPC lpnpc[100];
I've allowed for a lot of NPCs here; change the array size to match what you're
going to need. Or you can always use a linked list. Remember that we've got a
data member in our map structure to keep track of NPCs and we could use that
too; I'm declaring it here separately to keep things a little clearer. Finally, you'll
notice that I've declared pointers to structures instead of structures themselves.
Why would I do that? Well, it's because we're going to have to sort NPCs later on,
and it's faster to swap pointers than to swap entire functions.
Anyway, now we can write a function that checks the validity of a space. We'll
pass it an x and y coordinate for the space to check, and have it return TRUE or
FALSE. Here she is:
int MoveOK(int x, int y)
{
int z;
// first check the map boundaries
if ((x < 0) || (y < 0) || (x > mapdata.xMax) || (y >
mapdata.yMax))
return(FALSE);
// now check if the main character is already there
if ((x == player.move.xTile) && (y ==
player.move.yTile))
return(FALSE);
// check all the NPCs
for (z=0; z<mapdata.nNPCCount; z++)
{
if ((x == lpnpc[z]->move.xTile) && (y == lpnpc[z]>move.yTile))
return(FALSE);
}
// the tile itself is now the deciding factor, so just
return that
return(tileData[byMap[x][y][0]].bWalkOK);
}

If you're wondering where mapdata and tileData came from, go back and look
over article #8. These variables were defined in the code example that came with
that article; we're just continuing the example this time around. Now, the only
thing you might be wondering about is, if we're using this function to see if the
player can move somewhere, why on earth would we check if the player is
already standing there? Well, we'll also be utilizing this function to determine
movement for NPCs, so it's necessary.
Now all we have to do is add a call to MoveOK() in our previous code that checks
for whether the player is moving the character around or not. I won't repeat the
whole code block here, but here's what the code for moving in the -y direction
would look like:
if (KEYSTATE(VK_UP) && (player.move.xMove == 0) &&
(player.move.yMove == 0) &&
MoveOK(player.move.xTile,
player.move.yTile - 1))
{
player.move.yMove = -1;
player.move.yTile--;
player.move.yOffset += 32;
}
And we're all set! Now the game responds to keypresses by first checking
whether or not a player is allowed to move in that particular direction, then
changes the player's location settings if appropriate. The only thing left is the
character's visual representation on the screen, and so we move to animation.

Animation
The first thing we should do is to think about what we want to happen when the
player presses an arrow key. You may be thinking, "Oh, that's simple: the
character should walk from one tile to the next," but that is not always the case.
In pixel by tile (PxT) scrolling, this is what happens. As any movement key is
pressed, the character walks from one tile to the next. An alternate method is
called pixel by pixel (PxP) scrolling, and allows you a bit more freedom, because
as soon as the player releases the arrow key, the character stops moving, even if
he's between two tiles. The good part about PxP scrolling is that it can take away
from the feeling that your character is locked into a grid, especially if you also
employ eight-directional movement. The bad part is that it makes collision
detection a bit tricky. I'm not going to cover PxP scrolling here, since it's a good
idea to get PxT up and running first so you can get a feel for creating a simple
game engine, but think about it... it does require a bit more thought, but once
you've come up with a good way to handle it, it's easily worth a little extra effort.
All right, so we're going to move one tile at a time. Our animation code checks if
animation is necessary by simply looking at the xMove or yMove member of a
character's CHARMOVE structure. If it is nonzero, the character is moving. So
what do we do from there? Well, remember that at the beginning of an animation,
the character is standing on one tile, but his location variables say he's on the
next. This is possible because one of the character's offsets is set to 32 or -32. So
for each frame of animation, we gradually move the offset back towards zero, and
as a result, the character will move towards his correct location. It's really that
simple.
The only thing that even deserves any thought is to consider which frames are
displayed when. We have three frames of animation for each direction: standing
still, right foot forward, and left foot forward. This animation should play in the
sequence: 0, 1, 0, 2, 0, 1, 0, 2, etc. So it doesn't look like he's taking ridiculously
small steps, we'll say that a movement from one tile to the next is done in four
steps: 0 to 1, 1 to 0, 0 to 2, 2 to 0. This will actually appear as two full steps on

the screen. To set a reasonable character speed, let's shift the character by four
pixels every frame. So a movement from one tile to the next will look like this:
Game Frame Offset (absolute value) Animation Frame
0

32

28

24

20

16

12

Hopefully this will clear things up a bit. The leftmost column is simply a
numbering system for game frames -- these are happening at about 30 per
second. The middle column represents player.move.xOffset for horizontal
movement, or player.move.yOffset for vertical. This is an absolute value because
it may in fact range from -32 to 0, for instance if the character is walking up
instead of down. The important thing to note is that this gradually approaches 0.
This represents the character's actual movement onscreen, since xOffset and
yOffset, as you should remember, are used in the calculation for where the player
appears on the map. When the offset reaches 0, the appropriate movement
variable -- that is, xMove or yMove, is reset to 0, and the character stops. The
final column represents the animation frame to display. Note that it goes in the
sequence we decided on earlier. We will add this number to player.move.nFace to
get the actual frame number within the image file. Remember, that's why we used
0, 3, 6, and 9 for the movement directions.
To further demystify things, let's get some code down that implements the table
above. What needs to happen? First, the code should check if movement in a
given direction is occurring. Second, the offset should be updated. Third, the code
should check to see if the character has finished moving from one tile to the next.
If so, reset the movement variable. We could write this in two cases, one for
horizontal movement and one for vertical, but just in the interest of keeping
things easy to understand, I'm going to split it into four instead, one for each
direction. I'm also going to replace player.move with simply move, because
eventually this will be a function that gets used for players and NPCs. Let's see
how it goes.
// first check for movement south
if (move.yMove > 0)
{
move.yOffset += 4;
if (move.yOffset == 0)
move.yMove = 0;
}
// now check north
if (move.yMove < 0)
{
move.yOffset -= 4;
if (move.yOffset == 0)
move.yMove = 0;
}

// check movement east


if (move.xMove > 0)
{
move.xOffset += 4;
if (move.xOffset == 0)
move.xMove = 0;
}
// check movement west
if (move.xMove < 0)
{
move.xOffset -= 4;
if (move.xOffset == 0)
move.xMove = 0;
}
This takes care of moving the player around from step to step, and so there's
only one more step remaining: we still have to draw the player on the screen. The
only thing that's not obvious about this is how to find the coordinates at which to
draw the player. After all, he won't always be at the center of the screen. The
answer is simply to calculate his coordinates in terms of world coordinates, and
then subtract the camera coordinates from them to get the final rendering
position. Then we just get the correct frame like we discussed earlier, and voila,
we've finally got what we want! Here's the code:
// set up a default source RECT
RECT rcSrc = {0, 0, 32, 64}, rcDest;
int nFrame;
// first figure out which frame to use
nFrame = move.nFace;
if (((move.xMove) && ((abs(move.xOffset)
(abs(move.xOffset) == 8))) ||
((move.yMove) && ((abs(move.yOffset)
(abs(move.yOffset) == 8))))
nFrame++;
if (((move.xMove) && ((abs(move.xOffset)
(abs(move.xOffset) == 24))) ||
((move.yMove) && ((abs(move.yOffset)
(abs(move.yOffset) == 24))))
nFrame += 2;

== 4) ||
== 4) ||
== 20) ||
== 20) ||

// update rcSrc to correct frame


rcSrc.left += (nFrame<<5);
rcSrc.right += (nFrame<<);
// set up rcDest
rcDest.left = (move.xTile<<5) + move.xOffset mapdata.xCamera;
rcDest.top = (move.yTile<<5) + move.yOffset mapdata.yCamera - 32;
rcDest.right = rcDest.left + 32;
rcDest.bottom = rcDest.top + 64;
// blit sprite
lpddsBack->Blt(&rcDest, lpddsCharacters, &rcSrc,
DDBLT_WAIT | DDBLT_KEYSRC, NULL);
The only part of this code that might strike you as odd is that I'm subtracting 32
from the y-value of the character. Remember, that's because we calculated the
screen location of the tile on which the character is standing... but the character

is two tiles tall, so we need to plot him one tile above his actual location to make
sure he's standing in the right place.
Now the character responds to arrow keypresses by walking in the appropriate
direction. The animation follows exactly what we set up, and the map scrolls to
center the character when he's not near the map's edge. About bloody time, hey?
:) He's probably going to get tired of wandering around all by himself though, so
let's throw some NPCs in there. We've actually done most of the work already...

NPCs and Random Movement


In a really well-done game, NPCs will probably be doing something suitable to
who they are. Old men and women won't move around much, kids will run
through the town playing, and thieves will quickly walk away whenever they
notice anyone paying too much attention. But to do all that, we either need to
hard-code a bunch of behavior for each NPC type (which is not a good idea), or
use a script (which we don't know how to do yet). So for now, let's start off our
NPCs by implementing the simplest form of behavior that's found in lots of old
RPGs -- random movement.
The nice part about implementing NPCs is that we can use a lot of the same code
that we used for setting up the player. Our function that checks whether or not a
character can move to a certain position can be used for NPCs, and the code we
just wrote for actually plotting characters can also be used for NPCs, with one
minor change. We'd just have to stick it in a function that takes one argument:
the y-value to use for the source RECT. Hence we will have multiple character
images loaded onto a single surface, and specify which character to use via this
parameter.
So what's left to do? First we'll need to set up some sort of system that
determines random movements for the NPC. The simplest way to do it is just to
choose a random number, and if it falls in a certain range, or is divisible by a
certain number, then the character should move, so we pick a random direction
and that's it! The code would look something like this:
// choose a random number
int nDir;
int nRand = rand();
// move if 128 divides nRand (bits 0-6 used) and NPC is
still
if (((nRand & 0x0000007F) == 0) && (move.xMove == 0) &&
(move.yMove == 0))
{
// use bit 7 to determine positive (south, east) or
negative (north, west) movement
nDir = ((nRand & 0x00000080) >> 6) - 1;
// use bit 8 to determine horizontal or vertical
movement
if ((nRand & 0x00000100) == 0)
{
// move vertically
if (MoveOK(move.xTile, move.yTile + nDir))
{
move.yMove = nDir;
move.yTile += nDir;
move.yOffset -= (nDir << 5); // remember, this is
multiplication by 32
move.nFace = ((nDir == 1) ? 0 : 3);
}
}

else
{
// move horizontally
if (MoveOK(move.xTile + nDir, move.yTile))
{
move.xMove = nDir;
move.xTile += nDir;
move.xOffset -= (nDir << 5);
move.nFace = ((nDir == 1) ? 6 : 9);
}
}

}
All right, I'm using some pretty odd-looking code in there to people who don't use
bitwise operations a lot, so I'll clear it up a little. The bitwise AND operator & can
be used to extract bits from a number. That's what I'm doing here -- essentially
extracting three random numbers from one. In the case of nDir, I mask out a
single bit: bit 7. So the result of the & operation is either 2^7 * 1 = 128 or 2^7 *
0 = 0. Shifting by six places is equivalent to dividing by 64, so the result is now
either 2 or 0. Then I subtract one to get either 1 or -1. Cool, hey? You can do it
more straightforwardly if you want; I use bitwise operators and shifts a lot so that
looks pretty natural to me, but do what works for you.
Anyway, this is all it takes to get NPCs moving around, and we already said that
we could use our player drawing function to draw NPCs as well. So we're finished,
right? Well, not quite.

Ordering Characters
As soon as we introduce a second character into the game, we create a new
problem for ourselves: the characters must be drawn in the correct order. When
two characters who are two tiles tall come within one tile of each other, the one
with the lesser y-value had better be drawn first, or the results will look a little
strange!

Right

Wrong

See what I mean? Ordering is the difference between having your characters
appearing as they would naturally, or having an old NPC standing on the
shoulders of a headless main character. :) What we need to do is make sure that
our NPCs are always sorted in order of increasing y-value, so that they get drawn
properly. Also, we need to be watching for when the main character should be
drawn, because he's part of the order too.
The first thought that comes to mind is a sorting function -- but do you really
want to run a full sort every time an NPC moves? If you've got a hundred NPCs,
that's going to be a large sort running way too often. What we do instead is to
sort the NPCs once, right when they're initialized. Then, anytime an NPC's yTile
member changes, we move that element only. Since yTile only changes by 1 at a

time, we won't need to run a sort on the whole NPC array. We'll just find the NPC
with the next smallest (or next largest, as the case may be) yTile, and swap
those two. Much faster. This is also why we created the NPCs as pointers instead
of structures. Swapping pointers is faster than swapping the contents of an entire
structure.
For that initial sort that runs right when the NPCs are initialized, you can use
whatever you like. If the number of NPCs is going to be small, then something
like a bubble sort or insertion sort will serve your purposes just fine. If your vision
is slightly larger and involves lots of characters all going at once, then you may
want to use something a little more high-powered, like QuickSort. The code to
keep the array sorted is no problem. We need two cases: one in which the NPC
has moved up, and one for if he's moved down. All we do is find the nearest NPC
whose y-value is lesser or greater, respectively, then swap the current NPC to be
next to that position. Here it is in action for when NPC number nIndex moves
upwards:
int nPointer = nIndex;
LPNPC lpnpcTemp;
// first check to see if this is equal to the highest NPC
if (lpnpc[nIndex]->move.yTile > lpnpc[0]->move.yTile)
{
// nIndex is not highest, so a reordering is required.
// step up the array until we find the next highest NPC
while (lpnpc[nPointer - 1]->move.yTile ==
lpnpc[nIndex]->move.yTile)
nPointer--;
// now swap NPCs at nIndex and nPointer
lpnpcTemp = lpnpc[nPointer];
lpnpc[nPointer] = lpnpc[nIndex];
lpnpc[nIndex] = lpnpcTemp;

}
else
{
// swap with highest NPC
lpnpcTemp = lpnpc[0];
lpnpc[0] = lpnpc[nIndex];
lpnpc[nIndex] = lpnpcTemp;
}
The code for moving down is analogous so I won't bother showing it here. Note
that this code might cause some ugly errors if you run it when the NPC array is
not sorted, so be careful to implement it correctly!
Well we're just about done here! Our NPCs now move randomly, and the array of
NPCs will always be ordered correctly, with minimal work required to keep it that
way. The only thing left to do is to render the characters onto the map. Here's
how it's done. Immediately after the map itself has been drawn, start searching
through the NPC array for the NPC with the smallest y-value who is in the
viewable vertical range. This can be done simply by checking the NPC's location
against the camera's y-coordinate. From that point, start going through the NPC
array one at a time, drawing each character if he is within the viewable horizontal
range. Remember that the player must also be drawn at the correct time, so
watch the NPC y-values and how they compare to the player character's y-value.
The source code uses some variables that are found in the function for rendering
maps, so I'll just let you look at the sample source file for this one. Look at how
it's set up in RenderMap(), and how it's executed in RenderCharacters().

Closing

This demo of ours is starting to look more and more like a working game, isn't it?
Go grab the source code for this article and play with it a little bit. See what you
can write on your own without having to look at the source. And then see what
you can add to it! Give your NPCs a little bit more freedom by letting them vary
from each other a bit more. By adding some data members to the NPC structure,
you can make different NPCs walk at different speeds, and more or less often
than others. You can allow NPCs to move more than one tile at a time. If you're
feeling really ambitious, try giving them some paths to follow. If you visit the
GameDev Game Design forum at all, you've probably heard the phrase "NPCs are
people too." So don't subject your characters to a lifetime of wandering randomly
if you can come up with some better ideas. :)
Well I'm sad to say it, but Game Programming Genesis is just about at an end. I
had planned on extending the series to 12 articles and adding a couple more
topics, but there are a few reasons for stopping at Part X. For one, my life has
gotten a lot crazier in the last week or two, so I've gotten very pressed for time,
and these articles take a lot of time to come up with, especially with the demos
getting larger all the time. I really wanted to get to scripting, but I realized that
it's such a huge topic, I'd have to devote an entire series to it. There's no way I
could cover everything I want to say about it in one article. So a new series
dedicated to building up this scripting engine I'm always talking about is a real
possibility for the not-too-distant future if I get the time to do it.
Next time, in the series' final entry, I'll show you all kinds of assorted tips and
tricks for you to implement as you start building our demo into a full game. It'll
be a little bit of information on a lot of topics, like generating log files of your
programs at runtime, protecting your game data from being changed by end
users, and whatever else I come up with in the next few weeks. As always, feel
free to E-mail me at ironblayde@aeon-software.com, or find me on ICQ at
#53210499, with any questions you have, and I'll see you next time!

Game Programming Genesis


Part X : Tips and Tricks
by Joseph "Ironblayde" Farrell

Introduction
Here we are, with nine articles behind us, and only one left to go. It's unfortunate
that I won't have time to take this any further, since there a lot more things I
could go over, but we've gotten off to a good start in Windows game
programming, and there are any number of places you can go from here: input
devices, sound effects and music, scripting, the DirectX Graphics API, etc. There
are lots of articles covering things like this to be found at GameDev.Net, or
there's always the DirectX documentation.
In any case, to close the series out, I'm going to show you a few little things you
can use while developing your games. A lot of these, like organizing large
programs logically into multiple source files, are going to be necessities when you
start building up a full game. For most of today's article you won't actually need
knowledge of DirectX to follow along, so don't worry if you've missed previous
articles in the series. All set? Let's start with the topic I just referred to:
organizing programs.

Organizing Projects

All of the demo programs I've shown you along the way in this series have been
relatively short. The source file for the most recent one, on adding characters to
your game, was about a thousand lines long, but that includes comments and
whitespace, and I use both quite a bit. Even so, when you start putting a full
game together, you'll quickly find that putting all your code in a single source file
just won't work. It's not very organized that way. Sure, you can use Visual C++
to search for functions pretty quickly, but it's still much better to have your
program logically broken up so you can find things when you need them. Also, it's
nice to be able to jump from one function to another just by switching
documents, or to compare code from two documents side by side.
Organizing a program in C++ is easy: if each class has its own source and header
files, you'll always be able to find what you're looking for. For C programs it's not
so well-defined, so you should just do your best to group functions which serve
similar purposes together. To give you an example, I'll show you what my source
files for Terran look like. There are quite a few, but it makes it easy for me to
know where everything is.
audio.cpp

This file contains all functions needed for loading and playing
sound effects and music.

battle.cpp

These functions handle the details of the battle system, including


the code that makes battle decisions for enemies.

chars.cpp

This contains everything pertaining to character handling, such as


movement and animation, keeping NPCs sorted, and locating
characters on the map.

directx.cpp

All the code that actually makes changes to DirectX interfaces is


here, mostly for initialization and shutdown, but also for things like
restoring and reloading lost surfaces.

game.cpp

The very general framework for each major game state is


contained here.

graphics.cpp Any function which renders graphics to a surface, be it fading or


fireballs, maps or text boxes, is found here.
input.cpp

This file detects and reads input devices, and combines the
relevant information into a small input structure used by the rest
of the game.

items.cpp

Item functions such as buying, selling, using, transferring, or


discarding items are here, along with anything else needed to
manage character inventories.

magic.cpp

Spells are managed from here, both the learning and the casting
of them. Graphical output is done in graphics.cpp, but this file is
responsible for setting up those effects.

maps.cpp

This wraps up anything map-related: loading maps and collision


data, performing collision detection, and launching scripts linked to
the map.

menus.cpp

This file allows the user to navigate through any menu in the
game, and sends commands to the rest of the game as necessary.

scripts.cpp

Here is where the scripting language extensions are set up, and
specialized script functions are located. The general script loader
and parser are in a library I wrote; I'll get to that later.

stats.cpp

These functions manage character statistics: computing those that


are constantly changing, updating main stats at level increases,

and so on.
stdafx.cpp

This is the precompiled header Visual C++ sets up for you when
you create a simple project.

terran.cpp

Probably the simplest one, this one just has variable declarations,
WinMain() and WindowProc(), plus some functions for loading
game data which are called only once, at startup.

terranrs.rc

Terran's resource script, it contains a few icons, several audio files,


and an enormous string table.

text.cpp

Anything text-related, such as generating lists or loading dialogue,


is done here.

So hopefully that will give you an idea how to keep source files organized. I also
have a large header file called stdafx.h, which contains declarations for a bunch
of structure types, function prototypes, extern statements for global variables,
and about five hundred million #define statements. If you haven't seen it before,
the extern keyword allows other source files to access a global variable that's
been declared in a different source file. It's just a qualifier that goes on the front
of a variable declaration, like this:
extern int bImageLock;
extern STATE sGameState;
By including these extern statements in a header file, you can just include the
header file at the top of every source file, and you'll have access to all your
globals. The other thing to make sure you do with header files is to avoid
including things like type declarations multiple times. For this, you use the #if
and #endif directives. The way it's usually done is to check if a certain constant
has been defined. If not, define it, then include all the stuff you need. That way,
the next time that block of code is encountered, the constant has already been
defined, so your declarations are only included once. For example:
#if !defined(_GAME_HEADER_001_INCLUDED)
#define _GAME_HEADER_001_INCLUDED
// ... constant declarations and such go here...
#endif
There are a lot of blocks like this inside the main Windows headers, which is why
using #define WIN32_LEAN_AND_MEAN can keep a lot of unnecessary code out of
your project builds. Most things you'll put in header files should only be included
once, and so should be inside a #if block. But including external variables with
extern should be included in every source file, so be sure to keep those
statements outside of your #if block.
One last thing on this: if you're using Visual C++, you can also use a single line of
code that reads #pragma once to accomplish the same thing. This line specifies
that the header in which it is located should only be included one time in a build.
Most #pragma directives are compiler-specific, though, so you'll need to use
#if..#endif if you're not using Visual C++, or if you may work with your code on
a different compiler one day.

Creating Libraries
When you've got a fairly lengthy bit of code that gets used pretty often, it's often
convenient to build a library out of it rather than having to cut and paste code
every time you want to use it. A good example of this is initialization code for
DirectX. Do you really want to go through all that clip list crap every time you

want to create a DirectDraw clipper? Me neither. So I wrote a large set of


initialization functions, and dropped them all into a library that gets used in just
about every DirectX program I write. Being able to set up a surface, a clipper, a
game controller, a DirectMusic interface, etc. in a single function call is pretty
convenient.
Building a static library is very easy. When you go to create a project in Visual C+
+, just select "Win32 Static Library." The only difference between creating a static
library and creating any other Windows program is that a library is just a
collection of functions. You don't execute it by itself, so it doesn't need a
WinMain() or anything like that. Just write as many functions as you want in as
many source files as you like, and they compile into a single .lib file you can
include in your projects. The other thing you'll need to write is a header file for
the library that contains things like constants or data types used in the library.
Terran uses two libraries of my own in addition to the standard DirectX libraries.
The first one, adxl.lib, is my general-purpose library. It contains all kinds of nice
functions for Win32 and DirectX, as well as loaders for different image and audio
file formats. The other, aeonscript.lib, is my general scripting engine. It
contains the code for loading and parsing scripts that contain general functionality
like variable assignments and equations, if statements, loops, image loads,
calling other scripts, and so on. Specialized functions for things like moving NPCs
can be easily added to the scripting engine by any program that uses this library.
Re-using code will save you a lot of time when you get into large projects, so you
might as well start now! Take a look at the programs you've been writing and see
what kind of things you're using over and over. Those functions might serve you
better in a library. Anyway, let's move away from organization now and take a
look at some things you can add to the program code itself.

Runtime Log Files


How many times have you fired up your game program, anxious to see how your
newest additions are working out, only to see a black screen or have the program
crash on you? If you said "never," you're either a god or a liar. And since gods
have no need to read these articles, that makes you a liar. :) Tracking down logic
errors is no fun, so you need anything you can get that will help you locate
problems. The Visual C++ debugger is a great tool for this, and log files are
another. A log file is simply a text file generated by a program while it's running
that tells the results of various function calls, the states of variables at certain
points, or anything else you care to throw in there. Here's an example, a little
snippet of Terran's log file:
DIRECTDRAW
DirectDraw interface created.
Fullscreen cooperation level set.
--Message Received: WM_DISPLAYCHANGE
Resolution 640x480x16 set.
Surfaces created successfully in system memory.
Pixel format is 5.6.5 (16-bit).
DirectDraw clipper created.
Clipper attached to back buffer.

This, obviously, is one of the blocks of text generated during initialization of the
game. Something like this can help you see exactly which function call is failing,
or what setting is coming out not as you'd expect. When I first released a demo
of Terran, some people said that the fading wasn't working properly. Thankfully
they send back this log file, and I was able to see immediately that it would
always fail when the pixel format was 5.5.5, so I knew right where to look for the
bug. Another thing you can use log files for is outputting information about the
user's computer. If things are running slowly for someone, it's possible that their

machine doesn't accelerate some feature of DirectDraw that your machine does.
Things like that are easy to spot when you can look at a log file for reference.
There are two basic ways to create a log file. One is to just open a file at the
beginning of the program, write all your information with sprintf() statements
during the game's run, and close the file at the end. The other way is to write a
function that does that every time you want to log something. I prefer the latter,
because if the program crashes, it's better not to have left the file open. That
approach also makes it possible to easily enable or disable logging while the
program is running, or add extra features instead of just a straight write. Here's
an example of the logging function that I use:
BOOL ADXL_LogText(char *lpszText, ...)
{
// only do this if logging is enabled
if (bLogEnabled)
{
va_list argList;
FILE *pFile;
// initialize variable argument list
va_start(argList, lpszText);
// open the log file for append
if ((pFile = fopen("log.txt", "a+")) == NULL)
return(FALSE);
// write the text and a newline
vfprintf(pFile, lpszText, argList);
putc('\n', pFile);
// close the file
fclose(pFile);
va_end(argList);
}
// return success
return(TRUE);
}
Chances are you haven't written a function with an arbitrary number of
parameters before, so I'll explain briefly. The elipsis (...) in the function header
says that after lpszText, any number of additional arguments may follow, of any
data type. To handle something like that, you need the va_list data type, and
the va_start and va_end macros. The "va" presumably stands for "variable
arguments." Anyway, the va_list type is just a pointer to a list of arguments.
You need to create one in any function that receives a variable number of
arguments. The va_start macro initializes a list of type va_list, and takes the
list, and the previous argument as parameters. The va_end macro simply resets
the argument list pointer to NULL.
Finally, the other bit of syntax to notice in the log function is the use of the
vfprintf() function, which is the version of fprintf() that is designed for use
with variable argument lists. There are also vsprintf() and vprintf() functions
that function similarly if you ever need them.
The variable bLogEnabled that you see near the top is set by the other logging
function I use, which simply enables the log file. It's a pretty straightforward one:
int ADXL_EnableLog()
{
FILE* pFile;
// enable log

bLogEnabled = TRUE;
// clear the file contents
if ((pFile = fopen("log.txt", "wb")) == NULL)
return(FALSE);
// close it up and return success
fclose(pFile);
return(TRUE);

}
The bLogEnabled variable is just a global variable within my ADXL library, where
these functions are coming from. The variable initially is set to FALSE, so that
nothing gets logged until after you call ADXL_EnableLog(). It's nice to have it set
up this way, because then if you want to distribute a version of your program that
doesn't generate a huge log file, you can just remove one line, instead of
searching through your code to remove all the calls to the logging function.

Protecting Image Data


Suppose you've got a great new game project going, but it needs a ton of images
-- say 15MB worth. How can you make it so that the end user can't change your
images to whatever they want? You could include all your images as resources...
but then your .EXE would be enormous, and wasting a lot of memory space. The
only other option is to include them as external files, but then people can just
open them up in any image editor and change them, right? Well, there are a few
things you can do with this.
The first one is obvious: make up an image file format. This can be a good bit of
work, though, since you have to come up with a fully descriptive header that
won't be so easy for people to figure out at a glance, then decide on some
method of filtering and compression to store the image data. There are plenty of
compression libraries out there that you can use, like zlib, but there's one other
way to go.
The other thing you can do is to either expand an existing file format, or assign
meaning to some bytes in an image header that are either reserved or not
currently used. As a couple examples, the .BMP file header has two reserved
fields that must be set to zero for a standard bitmap, and the .PNG file structure
is based on "chunks" and is expandable, so you can add your own fields. Now,
what good is this? The idea is to use these extra storage locations to hold some
sort of image "key" which is calculated in a manner known only to you, based on
the image data. Ideally, you should have it set up so that if the image data
changes, the key will also change. That way, when you load an image from your
game, you can look at the image data and calculate the key value. If the
calculated key is different from the one stored in the image file, you know the
user's been tampering with your image, and you can spit out an angry error
message.
The only question is, how do you decide on a method for generating a key? Let's
try a few things. How about using the width plus the height of the image? That's
no good, because if people were to change the image, chances are that they
would not alter the dimensions, only the contents. So, how about the image file
size in bytes? That's OK for some things, but for uncompressed file formats like
.BMP, the file size won't change unless the color depth or image dimensions
change, neither of which is likely. A better idea would be to do something like
this. While you're displaying the image, you have to go through every pixel
anyway... so while you're doing that, you might as well add up the total values for
red, green, and blue as you go. Or just one of the three would be OK. Let's say
you do it for green. At the end, you'll have a very large number which is the

combined intensity for green from every pixel in the image. Now just because you
can, raise that value to the 10/9 power, multiply by 3, and add 1331. Why?
Because nobody would guess to do that particular operation.
Key choices like that are much better because they will almost certainly be
affected no matter what the user does to the image. There are a few things to
watch out for, though. For instance, in my last example, a very large image might
give you an overflow when you calculated the key. You can combat that by
making sure the original key never gets too high. For example, as you're adding
up the green values, you can do the addition modulo 216 so that when you're
done, you have a lot of room to work with. If you're working with an integer key,
then you need to make sure you don't narrow the range too much. For example,
if your key involves something like taking a fifth root of your initial calculation,
then a very high key value can change quite a bit, and still have the fifth root
evaluate the same when you're working with integers. Floating-point keys don't
have that fault.
Other possible choices for keys would be things like how much each pixel differs
from the one immediately to the right of it, in terms of any one of the color
channels. Calculate all those values and add them up. For compressed image file
formats like .PNG, you have even more choices. You can look at how each row
compresses, and form a key based on the ratio of the compression of each row to
the row beneath it. In any case, you can simply store the key within the image
header, or make up a new section of the image file in which to store data. You can
even generate multiple keys to make it that much harder to figure out. Users will
probably be able to see your image files if you take this approach, since you're
still using a standard image file format, but it's not easy to change them without
the game program realizing it.
If you're interested in working with other image file formats, check out wotsit.org
for as much information as you could ever want on more file formats than you
ever knew existed. :) I'd highly recommend looking into using .PNG for games. It
supports high-color modes, unlike .GIF, and uses a lossless compression
algorithm, unlike .JPG. In fact, .PNG's compression is so good that for artistictype images (as opposed to photorealistic ones), .PNG will often compress as
good as, if not a good deal better than .JPG will. The .JPG format wins handsdown for photorealistic images, but that's generally not what you'll be using for
games, at least not at this point, so it's better to have something using lossless
compression.

Introduction to Scripting
I've talked about scripting engines so much in this series and yet never got
around to explaining how exactly it works. Plus I've gotten a lot of E-mails from
people wanting to know this stuff, so I thought I'd give you a bit of an overview
here. Obviously I can't come anywhere close to completely covering this since
this is only a small part of a single article, but hopefully it will give you an idea for
a framework you can build on.
To implement a scripting engine in your game, you'll need to do three things.
First, you have to design the language. Second, you have to write a "compiler"
that turns text scripts into some sort of code, so that users can't look at your
scripts and know how to change them to do what they want. Third, you have to
write the interpreter that loads and executes script files.
The first part isn't too difficult since you've already seen at least one
programming language -- probably several -- so you have a good idea as to what
you need in a scripting language. You need variables, decision statements, loops,
the ability to read mathematical expressions, and functions. There are two ways
to go as far as functions are concerned. You can build functions like MoveNPC into
the scripting engine itself, or you can allow the scripting language to directly call

functions which are in your game code. The first approach is easier, but not as
flexible since you're adding functions to a scripting language that really only apply
to one game. The latter approach is much better, because it allows you to use a
general scripting engine in a variety of different programs. In addition to those,
there are probably some general functions you want to add directly to the
scripting engine that can be used from any program. Image loaders are a good
example of that.
Once you've decided on what your language needs, and what the syntax is going
to be, you're ready to write the compiler, which is easily the most difficult part of
doing this. (The interpreter is actually quite straightforward.) What I do is to
break all my script code down into fixed-length instructions, much like you would
see in an assembly language. If statements and loops are replaced by branch
instructions. Mathematical equations are broken down into single operations. For
example, the equation x = (a + b) * (c + d) / (e - f) is broken into this:
add t0, a, b
add t1, c, d
mul t0, t0, t1
sub t1, e, f
div x, t0, t1
Those simple instructions are then turned into bytecode. That seems simple at
first, since all you basically need to do is pick a number that corresponds to the
opcode (add, for example, is an opcode), and replace the word by the number,
right? But there are other concerns. In the bytecode, how do you tell whether the
arguments are variables or constants? How do you represent floating-point
numbers? And just how do you translate a mathematical formula into fixed-length
instructions in the first place? Things like this are all questions that need to be
addressed before you can have a working compiler, and they all take a bit more
time to explain than I have in this article.
The final part of the scripting engine, the interpreter, is relatively easy to write.
After all, your compiler has already done the hard work of breaking down complex
instructions into small, simple components. The interpreter's job is to open a
script file, find out how many lines it contains, and then create some storage
space for the script, either with an array or a linked list, and load the script into
memory. Once that's done, each line in the script consists of an opcode number
and a series of arguments. An easy way to execute the line is to use the opcode
number as an index into a function table. You then have one function for
executing each opcode. The interpreter just needs to be set up to use that
function table to call the appropriate function, and then you can go ahead and
build the rest of the interpreter, the part that actually acts on the commands, one
little piece at a time. Since most of those functions will be things like
assignments, arithmetic, and branches, it's really no problem. Things like moving
NPCs are of course not trivial, but those type of actions are part of your game
program, not the scripting engine. The scripting engine is only responsible for
calling them.
For allowing your scripts to call functions already in your game program, you
could give your scripting engine a function called AddFunction(), or something
similar, which simply takes a function pointer and adds it to its opcode list. You'd
also have to make a small data file with these assignments for the interpreter to
read, so it knows how to translate those functions, but that's not a big deal either.
I know this overview was very general and doesn't give you any code towards
actually getting something like this on its feet, but hopefully it gives you some
ideas.

The String Table

Remember way back in article 2, when we looked at resources, and I told you
that you could use a string table to represent all kinds of data in a game? Well I
thought I'd give you an idea as to what use you can make of a string table in an
RPG. RPGs especially benefit from something like this, since most of them end up
having lots of text.
The first and most obvious use for a string table is to store narration and/or
character dialogue. Those two things together will make up a very large part of
the text needed in an RPG, and so it's important to have an organized way to
store it. You could put it in a file -- but then anyone playing your RPG would be
able to change the dialogue however they wanted. Your beautiful ending scene
could become a recreation of "All your base are belong to us," and we can't have
that, can we? :) You could store the strings directly in your scripts somehow, but
then you'd have to worry about how to encode them, and besides, it would break
our plan of having short, fixed-length instructions. So what's left? Use the string
table.
Storing dialogue this way helps your scripts in another way, too: it gives you a
very easy format for creating a function that displays text on the screen. For
example, look at the following script command:
showText 10, 10, 253, 5
This could be translated as "At screen position (10, 10), show five lines of text,
beginning with line 253 in the string table." The only problem here is that you're
going to have a lot of dialogue, but you only have one string table, so it's easy to
get lost in there. There are a couple things you can do to get around that. The
first is pretty easy: just group your text into logical sections and use a few
#define statements to define where the different sections are located, so you can
get to them easily. You might have something like:
#define ST_OPENINGSCENETEXT 35
#define ST_FIRSTTOWNNPCS
253
#define ST_SECONDTOWNNPCS
428
But if you have a really big project going, that's not going to be enough. In that
case, what you might want to do is to write a utility that lets you add text to all
sorts of little partitions that you can create, so it gives the effect of having many
string tables, but then the program combines them all into one and writes the
resource script for you. This can be very convenient, and it's quite easy to do. All
you need is a way to create divisions of text, almost like a directory tree on your
computer, and a way for the program to tell you what string table index it assigns
to the text you write down, so that you don't even have to look at the string table
to easily access any dialogue in the game.
Lists of items, magic spells, menu options, etc. are also good candidates for the
string table, since you can just define a constant and then use offsets to locate
the relevant data. For example, in Terran I have a constant called
ST_ITEMDESCRIPTIONS which is an index into the string table. When I want to pull
up a description for an item, I just load string number (ST_ITEMDESCRIPTIONS +
nItemNumber).
The last thing you might want to use a string table for is filenames, be they for
image files, map files, scripts, or anything else. That way you can have all your
filenames together, so they are easy to find and change if you need to do so. You
can again use numeric constants to represent the filenames by acting as indices
into the string table. When my game needs to load the script for initializing a
game, it calls the script loading function with a value of SCRID_GAMEINIT, which
of course just tells the loader where it can find the filename.

Closing
And so it ends. Congratulate yourself if you've been with me since the beginning
of the series; we've come from displaying a blank window on a screen to being

just about ready to take on most of the features of a simple, albeit complete
DirectX-based RPG, or whatever other type of games you're interested in writing.
I have to quit writing for awhile, but if you want to get more info on any part of
game development instead of striking out on your own, GameDev.Net has all the
resources you need.
As for the future, I'm thinking about getting a series on scripting going once I
have more time (read: in May when classes are done with). Some people have
even suggested writing a book based on this general outline. At first I didn't know
about that, but it's starting to sound like an interesting idea. If I do something
like that, it will of course be much more detailed, and cover a myriad things I
didn't get to here, like scripting engines, responsive NPCs, tips for other game
genres like basic shooters, and using DirectX 8 for graphics so you can get
hardware acceleration for things like alpha-blending. If you'd be interested in
seeing something like that, let me know, so I have an idea as to what people
would think of it.
Thanks to the many, many people who sent in positive feedback on the series;
this has gone over even better than I had hoped for! If you still have questions
about anything in these articles, as always, feel free to E-mail me at
ironblayde@aeon-software.com. I'm on ICQ less and less these days, so E-mail is
your best bet. Well, happy coding, everyone, and I'll see you later.

Вам также может понравиться