Creating an Engine for Games for Windows

Building a game engine organizes code in your games so that the game-specific code is isolated from the general Windows application code. Read on to see how to design and develop a basic game engine for Windows game programming. (From Beginning Game Programming, by Michael Morrison, 2005, Sams, ISBN: 0672326590.)

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 53
October 13, 2004
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

MorrisonNote - While Pong gets credit for being the first publicly identifiable video game, I think it's safe to say that Space Invaders was the first video game to really captivate the world's imagination. Released in 1978 by Taito, Space Invaders was the original vertical shooter that set the stage for many an alien shoot-em-up to come. Space Invaders enjoyed such frenzied popularity in Japan that the country encountered a coin shortage and had to quadruple its supply of yen. Space Invaders still lives on today in quite a few modern games, as well as in revamped 3D versions of the classic original. In 2004, the original stand-up Space Invaders arcade game was re-released so that it could be enjoyed by a new generation of gamers葉o ensure that the game would continue to rake in quarters, the cost was increased to 50 cents per play.

A game is a specialized type of program, but it is still just a program written in a programming language. This means that you can create a game just as you would any other Windows program; for an example of a minimal graphical Windows program, check out the Skeleton application in Appendix C, "A Windows Game Programming Primer."

Certain game-specific tasks must be carried out in all games. Therefore, it would be quite helpful to organize the code in your games so that the game-specific code is isolated from the general Windows application code. In isolating this code, it might also be useful to build in some cool features that apply solely to games. The idea I'm suggesting is that of a game engine, which is a grouping of program code that performs tasks common to games. This chapter guides you through the design and development of a game engine that you'll build on throughout the remainder of the book.

In this chapter, you'll learn:

  • The importance of a game engine in game development

  • How to design and develop a basic game engine for Windows game programming

  • How to create an example that demonstrates the power of the game engine

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

What is a Game Engine?

Think about a few different games you like, and try to think of them in terms of how they might be designed under the hood. More importantly, see if you can figure out any common design elements that would apply to all the games. For example, do all the games have a background, a title screen, a geographical map, and background music? If so, it's possible that they are designed around the concept of a game engine. Game engines are particularly useful in situations in which you plan on creating more than one game, and you don't want to have to reinvent the wheel each time around. The idea is that you figure out what common functionality all games use, and you write it once and stick it in the game engine.

Another significant benefit of a game engine for Windows games is that it allows you to hide the messy details of Windows-specific code that doesn't necessarily have anything to do with a game. For example, virtually all the code in the Skeleton application in Appendix C has nothing to do with a game, but it's required of every Windows application. Rather than have you cut and paste this generic code to create a new game, I prefer hiding it in a game engine where you never have to fool with it again. You know it's there, but by not having to look at it, you're free to focus on the more important and fun parts of your game code.

In case you're wondering, there's nothing magical or mysterious about a game engine. A game engine represents an organization of the code for a game so that general application tasks are separated from game-specific tasks. The benefit to the game developer is that you can add features to a game engine that you will be able to reuse in all of your future games. Additionally, using a game engine allows you to simplify the code for your games and focus your attention on the game code that matters most. Once you get accustomed to using a game engine, you'll wonder how games could be created any other way. In reality, most commercial game developers do have their own custom game engines that they've developed over years of learning what common features most games require. Your own game engine will likely improve and expand upon the game engine in this book as you design and develop more demanding games.

Pondering the Role of a Game Engine

It is the responsibility of a game engine to handle the chores of setting up a game, making sure that it runs properly, and then shutting it down. Although it is true that these tasks are required of any program, certain aspects of initializing, running, and cleaning up after games are truly unique to games. Therefore, it is important for a game engine to address the unique needs of games and help make the process of building games around the engine as simple and straightforward as possible. With a well-designed game engine, you'll find that creating a game requires a lot less code than if you had not relied on a game engine. The idea is to develop certain core game routines once, stick them in the game engine, and then never bother with them again unless absolutely necessary. For example, once you've written code to load an image and draw it on the screen, there is never a reason to rewrite the code again. Loading and drawing images is a basic feature required of all game engines.


Note - As time and resources allow, you might choose to optimize certain algorithms within your game engine to help make it run more efficiently. Generally speaking, the first goal is to get a game feature working properly and then worry about optimizing it later as you have time.


SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Breaking a Game Down into Events

Every Windows program can be broken down into events, which are things that take place while a program is running, such as mouse clicks and window resizes. Just as Windows programs have events that they must handle, games have their own unique set of events that must be taken into consideration during development. The initialization process of a game can be considered an event, and its responsibilities are to load graphics and sounds for the game, clear the playing field, zero out the score, and so on. Similarly, user input carries over to games as well, meaning that mouse clicks and key presses are events that games certainly must concern themselves with. Additionally, keep in mind that, in Windows, it's possible for some games to be minimized or otherwise placed into the background, which means that you'll probably want to pause the game. This activation and reactivation process can be represented by a couple of events.


Note - Many commercial Windows games run only in full-screen mode, which means that they can't be tiled visually with other running Windows applications. Even so, these games are technically still running alongside other programs and, therefore, must be able to pause themselves if another application takes center stage.


Although many other events could certainly factor into a game engine, the following are some of the core events applicable to just about any game:

  • Initialization

  • Start

  • End

  • Activation

  • Deactivation

  • Paint

  • Cycle

The initialization event occurs when a game is first launched and gives a game a chance to perform critical initial setup tasks, including creating the game engine itself. The start and end events correspond to the start and end of a game, and they provide good places to perform initialization and cleanup tasks associated with a specific game session. The activation and deactivation events come into play when a game is minimized or sent to the background and then later restored. The paint event is sent when a game needs to draw itself and is similar to the Windows WM_PAINT message. Finally, the cycle event enables a game to perform a single game cycle, which is very important, as you learn next.

Establishing the Timing for Games

If you've never taken a look at a game from a programming perspective, it might surprise you to learn how all the movement and the animation in a game are orchestrated. You will learn all the details of animated graphics in Chapter 9, "Making Things Move with Sprite Animation," but for now, I want to touch on the importance of game timing as it applies to animation and other facets of games. Every game, except extremely simple card games, relies on some sort of timing mechanism to enable the game to break down its execution into frames or cycles. A cycle of a game is one slice of time, which usually corresponds to a snapshot of the game's graphics and data. If you think of a game as a movie playing on a VCR or DVD player, pressing Pause allows you to view a single cycle. Stepping forward one frame in the video is like moving to the next cycle of the game. In any given cycle, a game takes care of updating its graphics, as well as performing any other calculations and processing related to how characters and objects are moving and interacting with each other.

A good way to get a grasp on the importance of game cycles is to take a practical game as an example. The classic Space Invaders game was mentioned in the opener of this chapter, so let's use it as an example to demonstrate game cycles. When Space Invaders first starts, the ship is created, along with several rows of alien invaders. Each of these objects has an initial position and velocity. If Space Invaders had no timing or game cycles, the game would be forever frozen in its initial state, as if you had pressed a permanent Pause button when the game started. We know that this isn't the case, however, because the game starts out with the aliens slowly moving across the screen. If you were to view Space Invaders a cycle at a time, you would notice that, in each cycle, the aliens are only moved slightly. This is because there happen to be quite a few cycles taking place in a given period of time, which gives the effect of smooth motion. Figure 2.1 shows a few hypothetical cycles of Space Invaders and how the aliens move ever so slightly, along with a shot fired by the player's ship.

Morrison

Figure 2.1. A few cycles of a hypothetical Space Invaders game reveals how the objects in the game change slightly with each cycle.

Figure 2.1 reveals how each cycle of the Space Invaders game reflects a small change in the state of the objects in the game. Therefore, the role of a game cycle is to update the status of all the objects in the game and then reflect these changes by updating the graphics shown on the screen. Judging by how fast things are visibly changing in most games, can you guess how often game cycles take place? Even the most sluggish of games includes no less than 12 cycles per second, which is incidentally the minimum rate required to trick your eyes into thinking that they are seeing movement instead of a series of changing images. As a comparison, televisions display 30 different images (cycles) per second, whereas motion pictures rely on 24 images per second. You learn much more about the significance of different rates of animation in Chapter 9. For now, it's important to understand that just about every game is highly dependent on periodic cycles.


Note - A single screen of graphics in a game is known as a frame. Because a new screen of graphics is drawn during each game cycle, the speed of games is often measured in frames per second, or fps. Because the discussion in this chapter is centered on cycles, as opposed to frames, I refer to game speeds in cycles per second. However, cycles per second, frames per second, and even images per second are really the same measurement.


The more cycles a game can run through in a given amount of time, the smoother the game appears to run. As an extreme example, compare the "smoothness" of a slideshow to a motion picture. The slideshow abruptly moves from one still image to another with no transition or sense of smooth movement, whereas a motion picture shows fluid motion as if you were experiencing it in real-time. Similarly, a game with only a few cycles per second will appear choppy, whereas a higher number of cycles per second will result in a much smoother game. A larger number of cycles per second also gives you more flexibility in speeding up or slowing down a game to arrive at a perfect speed.

Knowing that more cycles result in smoother graphics and better flexibility, you might think that you could crank up the cycles per second really high. As with most things in life, there is a trade-off when it comes to game cycles and game efficiency. The problem lies in the fact that the amount of processing taking place in a game in each cycle is often considerable, which means that to perform numerous cycles per second, your computer's processor and graphics card have to be able to keep up. Even with the blazingly fast computers prevalent these days, there are practical limitations as to how fast most computers can perform game processing. In reality, most games will fall in the range of 15 to 20 cycles per second, with a maximum speed surpassing that of a motion picture at 30 cycles per second. Except for some rare situations, the minimum speed you should shoot for is 12 cycles per second.


Note - Commercial 3D games are always pushing the envelope of what is possible given the current computer hardware available. The number of cycles per second for a modern 3D game is the most common measurement used to determine if the game is being too ambitious in terms of how much it is taxing the processing capabilities of the computer. In an ideal world, you would design games with beautifully detailed graphics and not worry about whether the game brings a user's system to a screeching halt. In reality, game designers are always walking a tight rope to make games look incredibly good yet still maintain a decent frame rate (ideally, 30fps or more).


Now that you understand how the timing of a game is expressed in terms of cycles, you can probably see why a cycle is a type of game event. It works like this: When a game first starts, you initialize the game engine with the game speed in cycles per second. Let's say that you go with 12 cycles per second. The game engine is then responsible for setting up and managing a timer that fires a cycle event 12 times each second. The game code receives these cycle messages and handles them by updating the objects in the game and redrawing the game screen. You can think of a cycle event as a snooze alarm that keeps going off over and over; except in this case, it's going off 12 times a second. Your game clearly isn't getting much sleep!


Note - Speaking of sleep, another role of a game engine is to put a game to sleep whenever it is no longer the active window. In practical terms, putting a game to sleep simply means that the game engine stops sending cycle messages. Because no cycle messages are being sent, the game is effectively paused.


SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Developing a Game Engine

You now understand enough about what a game engine needs to accomplish that you can start assembling your own. In this section, you create the game engine that will be used to create all the games throughout the remainder of the book. Not only that, but also you'll be refining and adding cool new features to the game engine as you develop those games. By the end of the book, you'll have a powerful game engine ready to be deployed in your own game projects.

If you're accustomed to reading books that give you every little piece of code to type in and try out as you go, I should caution you that I don't list every line of code in this book. Certain pieces of code just aren't that important in the context of game programming (but are nonetheless required of all Windows programs), and I'd rather not burden you with long code listings when you can learn what you need to learn from a smaller snippet of code. The CD-ROM accompanying the book has the complete source code for every example in the book, so the code is there for you to sift through if you want to explore every little nuance. You'll also need the complete source code to build the examples; more on this later.

The point to this little discussion is that it isn't necessary for you to take in every line of code in order to learn how to reuse the code in this book to create your own games. Therefore, understand as you work through the rest of the book that the focus is on showing you the most interesting and significant code so that you have the knowledge to run with it and carry out your own game creations. Having said all of this, I do show you all the code for the game engine once I've presented the bits and pieces that go into it.

The Game Event Functions

The first place to start in creating a game engine is to create handler functions that correspond to the game events mentioned earlier in the chapter. When an event occurs in a game, the corresponding event handler function will be called, which gives your game a chance to respond accordingly. The following are these functions, which should make some sense to you because they correspond directly to the game events:

BOOL GameInitialize(HINSTANCE hInstance);
void GameStart(HWND hWindow);
void GameEnd();
void GameActivate(HWND hWindow);
void GameDeactivate(HWND hWindow);
void GamePaint(HDC hDC);
void GameCycle();

The first function, GameInitialize(), is probably the only one that needs special explanation simply because of the argument that gets sent into it. I'm referring to the hInstance argument, which is of type HINSTANCE. This is a Win32 data type that refers to an application instance. An application instance is basically a program that has been loaded into memory and is running in Windows. If you've ever used Alt+Tab to switch between running applications in Windows, you're familiar with different application instances. The HINSTANCE data type is a handle to an application instance, and it is very important because it enables a program to access its resources since they are stored with the application in memory.

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

The GameEngine Class

The GameEngine Class

The game event handler functions are actually separated from the game engine itself, even though there is a close tie between them. This is necessary because it is organizationally better to place the game engine in its own C++ class. This class is called GameEngine and is shown in Listing 2.1.


Note - If you were trying to adhere strictly to object-oriented design principles, you would place the game event handler functions in the GameEngine class as virtual methods to be overridden. However, although that would represent good OOP design, it would also make it a little messier to assemble a game because you would have to derive your own custom game engine class from GameEngine in every game. By using functions for the event handlers, you simplify the coding of games at the expense of breaking an OOP design rule. Such are the trade-offs of game programming.


Listing 2.1 The GameEngine Class Definition Reveals How the Game Engine Is Designed

class GameEngine
{
protected:
// Member Variables
static GameEngine* m_pGameEngine;
HINSTANCE          m_hInstance;
HWND               m_hWindow;
TCHAR              m_szWindowClass[32];
TCHAR              m_szTitle[32];
WORD               m_wIcon, m_wSmallIcon;
int                m_iWidth, m_iHeight;
int                m_iFrameDelay;
BOOL               m_bSleep;
public:
// Constructor(s)/Destructor
GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle,
WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480);
virtual ~GameEngine();
// General Methods
static GameEngine* GetEngine() { return m_pGameEngine; };
BOOL               Initialize(int iCmdShow);
LRESULT            HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
LPARAM lParam);
// Accessor Methods
HINSTANCE GetInstance() { return m_hInstance; };
HWND      GetWindow() { return m_hWindow; };
void      SetWindow(HWND hWindow) { m_hWindow = hWindow; };
LPTSTR    GetTitle() { return m_szTitle; };
WORD     GetIcon() { return m_wIcon; };
WORD      GetSmallIcon() { return m_wSmallIcon; };
int     GetWidth() { return m_iWidth; };
int     GetHeight() { return m_iHeight; };
int     GetFrameDelay() { return m_iFrameDelay; };
void     SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 /
iFrameRate; };
BOOL      GetSleep() { return m_bSleep; };
void      SetSleep(BOOL bSleep) { m_bSleep = bSleep; };
};

The GameEngine class definition reveals a subtle variable naming convention that you might or might not be familiar with. This naming convention involves naming member variables of a class with an initial m_ to indicate that they are class members. Additionally, global variables are named with a leading g_ to indicate that they are globals. This convention is useful because it helps you to immediately distinguish between local variables, member variables, and global variables in a program. The member variables for the GameEngine class all take advantage of this naming convention.

The GameEngine class defines a static pointer to itself, m_pGameEngine, which is used for outside access by a game program. The application instance and main window handles of the game program are stored away in the game engine using the m_hInstance and m_hWindow member variables. The name of the window class and the title of the main game window are stored in the m_szWindowClass and m_szTitle member variables. The numeric IDs of the two program icons for the game are stored in the m_wIcon and m_wSmallIcon members. The width and height of the game screen are stored in the m_iWidth and m_iHeight members. It's important to note that this width and height correspond to the size of the game screen, or play area, not the size of the overall program window, which is larger to accommodate borders, a title bar, menus, and so on. The m_iFrameDelay member variable indicates the amount of time between game cycles in milliseconds. Finally, m_bSleep is a Boolean member variable that indicates whether the game is sleeping (paused).


Note - If the naming convention of preceding variable names with characters to indicate their variable type is unfamiliar to you, take a quick look at "Unconventional Coding Conventions" in Appendix C. The idea is to use one or two characters to convey the data type of a variable; for example, m_iWidth is a member variable of type integer (i), whereas m_szTitle is a member variable that is a null-terminated string (string with a trailing zero).


The GameEngine constructor and destructor are defined after the member variables, as you might expect. The constructor is very important because it accepts arguments that dramatically impact the game being created. More specifically, the GameEngine() constructor accepts an instance handle, window classname, title, icon ID, small icon ID, width, and height. Notice that the iWidth and iHeight arguments default to values of 640 and 480, respectively, which is a reasonable minimum size for game screens. The ~GameEngine() destructor doesn't do anything, but it's worth defining in case you need to add some cleanup code to it later.

I mentioned that the GameEngine class maintains a static pointer to itself. This pointer is accessed from outside the engine using the static GetEngine() method. The Initialize() method is another important general method in the GameEngine class, and its job is to initialize the game program once the engine is created. The HandleEvent() method is responsible for handling standard Windows events within the game engine and is a good example of how the game engine hides the details of generic Windows code from game code.

The remaining methods in the GameEngine class are accessor methods used to access member variables; these methods are all used to get and set member variables. The one accessor method to pay special attention to is SetFrameRate(), which sets the frame rate or number of cycles per second of the game engine. Because the actual member variable that controls the number of game cycles per second is m_iFrameDelay, which is measured in milliseconds, it's necessary to perform a quick calculation to convert the frame rate in SetFrameRate() to milliseconds.

The source code for the GameEngine class provides implementations for the methods described in the header that you just saw, as well as the standard WinMain() and WndProc() functions that tie into the game engine. The GameEngine source code also initializes the static game engine pointer, like this:

GameEngine *GameEngine::m_pGameEngine = NULL;

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Source Code for the WinMain Function

Listing 2.2 contains the source code for the game engine's WinMain() function.

Listing 2.2 The WinMain() Function in the Game Engine Makes Calls to Game Engine Functions and Methods and Provides a Neat Way of Separating Standard Windows Program Code from Game Code

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG       msg;
static int iTickTrigger = 0;
int       iTickCount;
if (GameInitialize(hInstance))
{
// Initialize the game engine
if (!GameEngine::GetEngine()->Initialize(iCmdShow))
return FALSE;
// Enter the main message loop
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Make sure the game engine isn't sleeping
if (!GameEngine::GetEngine()->GetSleep())
{
// Check the tick count to see if a game cycle has elapsed
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount +
GameEngine::GetEngine()->GetFrameDelay();
GameCycle();
}
}
}
}
return (int)msg.wParam;
}
// End the game
GameEnd();
return TRUE;
}

Although this WinMain() function is similar to those found in every Windows application, there is an important difference. The difference has to do with the fact that this WinMain() function establishes a game loop that takes care of generating game cycle events at a specified interval. The smallest unit of time measurement in a Windows program is called a tick, which is equivalent to one millisecond and is useful in performing accurate timing tasks. In this case, WinMain() counts ticks in order to determine when it should notify the game that a new cycle is in order. The iTickTrigger and iTickCount variables are used to establish the game cycle timing in WinMain().

The first function called in WinMain() is GameInitialize(), which gives the game a chance to be initialized. Remember that GameInitialize() is a game event function provided as part of the game-specific code for the game; therefore, it isn't a direct part of the game engine. A method that is part of the game engine is Initialize(), which is called to get the game engine itself initialized. From there, WinMain() enters the main message loop for the game program. The else part of the main message loop is where things get interesting. This part of the loop first checks to make sure that the game isn't sleeping and then uses the frame delay for the game engine to count ticks and determine when to call the GameCycle() function to trigger a game cycle event. WinMain() finishes up by calling GameEnd() to give the game program a chance to wrap up the game and clean up after itself.

The other standard Windows function included in the game engine is WndProc(), which is very simple because the HandleEvent() method of the GameEngine class is responsible for processing Windows messages:

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route all Windows messages to the game engine
return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}

All WndProc() really does is pass along all messages to HandleEvent(), which might at first seem like a waste of time. However, the idea is to allow a method of the GameEngine class to handle the messages so that they can be processed in a manner that is consistent with the game engine.

Speaking of the GameEngine class, now that you have a feel for the support functions in the game engine, we can move right along and examine specific code in the GameEngine class. Listing 2.3 contains the source code for the GameEngine() constructor and destructor.

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Initializing Variables

Listing 2.3 The GameEngine::GameEngine() Constructor Takes Care of Initializing Game Engine Member Variables, Whereas the Destructor is Left Empty for Possible Future Use

GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
// Set the member variables for the game engine
m_pGameEngine = this;
m_hInstance = hInstance;
m_hWindow = NULL;
if (lstrlen(szWindowClass) > 0)
lstrcpy(m_szWindowClass, szWindowClass);
if (lstrlen(szTitle) > 0)
lstrcpy(m_szTitle, szTitle);
m_wIcon = wIcon;
m_wSmallIcon = wSmallIcon;
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iFrameDelay = 50;  // 20 FPS default
m_bSleep = TRUE;
}
GameEngine::~GameEngine()
{
}

The GameEngine() constructor is relatively straightforward in that it sets all the member variables for the game engine. The only member variable whose setting might seem a little strange at first is m_iFrameDelay, which is set to a default frame delay of 50 milliseconds. You can determine the number of frames (cycles) per second for the game by dividing 1,000 by the frame delay, which in this case results in 20 frames per second. This is a reasonable default for most games, although specific testing might reveal that it needs to be tweaked up or down. Keep in mind that you should always shoot for the highest frame rate (lowest frame delay) possible that allows your game to run smoothly; you don't want to see a game slowing down because it can't keep up with a high frame rate.

The Initialize() method in the GameEngine class is used to initialize the game engine. More specifically, the Initialize() method now performs a great deal of the messy Windows setup tasks, such as creating a window class for the main game window and then creating a window from the class. Listing 2.4 shows the code for the Initialize() method.

Listing 2.4 The GameEngine::Initialize() Method Handles Some of the Dirty Work that Usually Takes Place in WinMain()

BOOL GameEngine::Initialize(int iCmdShow)
{
WNDCLASSEX  wndclass;
// Create the window class for the main window
wndclass.cbSize     = sizeof(wndclass);
wndclass.style     = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc  = WndProc;
wndclass.cbClsExtra   = 0;
wndclass.cbWndExtra   = 0;
wndclass.hInstance   = m_hInstance;
wndclass.hIcon     = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm    = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor    = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName  = NULL;
wndclass.lpszClassName = m_szWindowClass;
// Register the window class
if (!RegisterClassEx(&wndclass))
return FALSE;
// Calculate the window size and position based upon the game size
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL)
iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;
// Create the window
m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW |
WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth,
iWindowHeight, NULL, NULL, m_hInstance, NULL);
if (!m_hWindow)
return FALSE;
// Show and update the window
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);
return TRUE;
}

This code is similar to the Skeleton program example found in Appendix C, and it should be familiar to you if you've done any Windows programming using the Win32 API. An important thing to note in this code is how it determines the game application window size, which is calculated based on the size of the game client area. The GetSystemMetrics() Win32 function is called to get various standard window sizes, such as the width and height of the window frame, as well as the menu height. The position of the game application window is then calculated so that the game is centered on the screen.

The window styles used to describe the main game window are WS_POPUPWINDOW, WS_CAPTION, and WS_MINIMIZEBOX, which result in a window that is not resizable and can't be maximized; however, it does have a menu and can be minimized.

The Initialize() method is a perfect example of isolating generic Windows program code and moving it into the game engine. Another example of this approach is the HandleEvent() method, which is shown in Listing 2.5.

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

HandleEvent Method

Listing 2.5 The GameEngine::HandleEvent() Method Receives and Handles Messages that Are Normally Handled in WndProc()

LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
LPARAM lParam)
{
// Route Windows messages to game engine member functions
switch (msg)
{
case WM_CREATE:
// Set the game window and start the game
SetWindow(hWindow);
GameStart(hWindow);
return 0;
case WM_SETFOCUS:
// Activate the game and update the Sleep status
GameActivate(hWindow);
SetSleep(FALSE);
return 0;
case WM_KILLFOCUS:
// Deactivate the game and update the Sleep status
GameDeactivate(hWindow);
SetSleep(TRUE);
return 0;
case WM_PAINT:
HDC     hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hWindow, &ps);
// Paint the game
GamePaint(hDC);
EndPaint(hWindow, &ps);
return 0;
case WM_DESTROY:
// End the game and exit the application
GameEnd();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}

The HandleEvent() method contains a switch statement that picks out Windows messages and responds to them individually. The HandleEvent() method also makes calls to game engine functions whose implementations are specific to each different game. First, the WM_CREATE message is handled, which is sent whenever the main game window is first created. The handler code for this message sets the window handle in the game engine and then calls the GameStart() game event function to get the game initialized.

The WM_SETFOCUS and WM_KILLFOCUS messages inform the game whenever its window receives (activated) or loses (deactivated) the input focus, respectively. If the game window is being activated (gains focus), the GameActivate() function is called, and the game is awoken. Similarly, if the game window is being deactivated (loses focus), the GameDeactivate() function is called, and the game is put to sleep.

The remaining messages in the HandleEvent() method are pretty straightforward in that they primarily call game functions. The WM_PAINT message handler calls the standard Win32 BeginPaint() function followed by the GamePaint() function. The EndPaint() function is then called to finish up the painting process; you learn a great deal more about BeginPaint() and EndPaint() in the next chapter. Finally, the WM_DESTROY handler calls the GameEnd() function and then terminates the whole program.

To help put all this game engine code into perspective, check out Listings 2.6 and 2.7, which contain the complete GameEngine.h header and GameEngine.cpp source code files. Please note that you don't need to understand every little nuance of this code in order to build games based on it. It will make much more sense as you get comfortable using it to build games throughout the book.

Listing 2.6 The Complete GameEngine.h Header Code

#pragma once
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Include Files
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
#include <windows.h>
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Windows Function Declarations
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
int WINAPI    WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Game Engine Function Declarations
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
BOOL GameInitialize(HINSTANCE hInstance);
void GameStart(HWND hWindow);
void GameEnd();
void GameActivate(HWND hWindow);
void GameDeactivate(HWND hWindow);
void GamePaint(HDC hDC);
void GameCycle();
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// GameEngine Class
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
class GameEngine
{
protected:
// Member Variables
static GameEngine* m_pGameEngine;
HINSTANCE      m_hInstance;
HWND        m_hWindow;
TCHAR        m_szWindowClass[32];
TCHAR        m_szTitle[32];
WORD        m_wIcon, m_wSmallIcon;
int         m_iWidth, m_iHeight;
int         m_iFrameDelay;
BOOL        m_bSleep;
public:
// Constructor(s)/Destructor
GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle,
WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480);
virtual ~GameEngine();
// General Methods
static GameEngine* GetEngine() { return m_pGameEngine; };
BOOL        Initialize(int iCmdShow);
LRESULT       HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
LPARAM lParam);
// Accessor Methods
HINSTANCE GetInstance() { return m_hInstance; };
HWND      GetWindow() { return m_hWindow; };
void      SetWindow(HWND hWindow) { m_hWindow = hWindow; };
LPTSTR    GetTitle() { return m_szTitle; };
WORD    GetIcon() { return m_wIcon; };
WORD    GetSmallIcon() { return m_wSmallIcon; };
int    GetWidth() { return m_iWidth; };
int       GetHeight() { return m_iHeight; };
int     GetFrameDelay() { return m_iFrameDelay; };
void      SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 /
iFrameRate; };
BOOL      GetSleep() { return m_bSleep; };
void     SetSleep(BOOL bSleep) { m_bSleep = bSleep; };
};

Listing 2.7 The Complete GameEngine.cpp Source Code

#include "GameEngine.h"
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Static Variable Initialization
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
GameEngine *GameEngine::m_pGameEngine = NULL;
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Windows Functions
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG     msg;
static int iTickTrigger = 0;
int     iTickCount;
if (GameInitialize(hInstance))
{
// Initialize the game engine
if (!GameEngine::GetEngine()->Initialize(iCmdShow))
return FALSE;
// Enter the main message loop
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Make sure the game engine isn't sleeping
if (!GameEngine::GetEngine()->GetSleep())
{
// Check the tick count to see if a game cycle has elapsed
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount +
GameEngine::GetEngine()->GetFrameDelay();
GameCycle();
}
}
}
}
return (int)msg.wParam;
}
// End the game
GameEnd();
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route all Windows messages to the game engine
return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// GameEngine Constructor(s)/Destructor
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
// Set the member variables for the game engine
m_pGameEngine = this;
m_hInstance = hInstance;
m_hWindow = NULL;
if (lstrlen(szWindowClass) > 0)
lstrcpy(m_szWindowClass, szWindowClass);
if (lstrlen(szTitle) > 0)
lstrcpy(m_szTitle, szTitle);
m_wIcon = wIcon;
m_wSmallIcon = wSmallIcon;
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iFrameDelay = 50;  // 20 FPS default
m_bSleep = TRUE;
}
GameEngine::~GameEngine()
{
}
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Game Engine General Methods
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
BOOL GameEngine::Initialize(int iCmdShow)
{
WNDCLASSEX  wndclass;
// Create the window class for the main window
wndclass.cbSize       = sizeof(wndclass);
wndclass.style     = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc  = WndProc;
wndclass.cbClsExtra   = 0;
wndclass.cbWndExtra   = 0;
wndclass.hInstance    = m_hInstance;
wndclass.hIcon      = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm      = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor      = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName  = NULL;
wndclass.lpszClassName = m_szWindowClass;
// Register the window class
if (!RegisterClassEx(&wndclass))
return FALSE;
// Calculate the window size and position based upon the game size
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL)
iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;
// Create the window
m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW |
WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth,
iWindowHeight, NULL, NULL, m_hInstance, NULL);
if (!m_hWindow)
return FALSE;
// Show and update the window
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);
return TRUE;
}
LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,
_ LPARAM lParam)
{
// Route Windows messages to game engine member functions
switch (msg)
{
case WM_CREATE:
// Set the game window and start the game
SetWindow(hWindow);
GameStart(hWindow);
return 0;
case WM_SETFOCUS:
// Activate the game and update the Sleep status
GameActivate(hWindow);
SetSleep(FALSE);
return 0;
case WM_KILLFOCUS:
// Deactivate the game and update the Sleep status
GameDeactivate(hWindow);
SetSleep(TRUE);
return 0;
case WM_PAINT:
HDC     hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hWindow, &ps);
// Paint the game
GamePaint(hDC);
EndPaint(hWindow, &ps);
return 0;
case WM_DESTROY:
// End the game and exit the application
GameEnd();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}

You've now seen all the code for the game engine, which successfully hides generic Windows code while providing a basic framework for games. Let's now take a look at an example that puts this embryonic game engine to work.


Note - If you're a bit intimidated by seeing this much code in only the second chapter of the book, let me ease your concerns by explaining that building games on top of the game engine is more straightforward than building the game engine itself. You'll find that game code is made much simpler thanks to the work put into the game engine.


SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Put the Engine to Work

Building the Blizzard Example

The game engine you created in this chapter allows you to create games without having to repeat boilerplate Windows code that is required of every Windows program. In this section, you use the game engine to create a new example called Blizzard that demonstrates the absolute minimum requirements of a Windows game. You'll quickly realize that the Blizzard example is very easy to understand because the game engine hides most of the mess associated with Windows programs.

You'll be glad to know that the Blizzard example isn't just a remake of the Skeleton example from Appendix C with the game engine thrown in. Because the game engine includes support for establishing a game loop complete with timed game cycles, it only makes sense to take advantage of that feature. Therfore, the Blizzard example demonstrates the power of game cycles and how they make it possible to get interesting graphical effects with little effort. More specifically, you find out how to rapidly draw graphical icons (snowflakes) at random locations on the game screen and see firsthand how the speed of the game loop impacts the performance of the game.


Note - All the code for the Blizzard example擁ncluding project files for Visual Studio/C++ .NET, Borland C++Builder, and Bloodshed Dev-C++擁s located on the accompanying CD-ROM. I encourage you to use the project files that I've provided because they contain the necessary settings for the examples to compile and link properly. Windows programs (especially games) can sometimes be tricky to set up in terms of getting the compiler and linker settings just right.


Writing the Program Code

The Blizzard example is divided into two source files: the Blizzard.h header file and the Blizzard.cpp source code file. Listing 2.8 contains the code for the Blizzard.h header file, which is relatively simple. Keep in mind that all the code for the Blizzard example is available on the accompanying CD-ROM.


Note - If you haven't yet installed a C++ development environment for Windows, please take a look at Appendix A, "Selecting a Game Development Tool." The accompanying CD-ROM includes source code files and project files for several popular development environments, such as Microsoft Visual C++ .NET, Borland C++Builder, and Bloodshed Dev-C++. It also includes executable versions of all the examples found in the book so that you can run them and test them out, even if you don't have a development tool installed yet.


Listing 2.8 The Blizzard.h Header File Simply Imports a Few Header Files and Declares the Important Global Game Engine Pointer

//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Blizzard Application
// C++ Header - Blizzard.h
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
#pragma once
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Include Files
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
#include <windows.h>
#include "Resource.h"
#include "GameEngine.h"
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Global Variables
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
GameEngine* g_pGame;

This header file includes the familiar windows.h, as well as Resource.h and GameEngine.h. After importing these header files, a global game engine pointer, g_pGame, is defined. This pointer is very important because it will provide the Blizzard example access to the game engine.

The Resource.h header file is somewhat of a helper file that establishes identifiers for all the resources used throughout the program. In this case, the only resources are two icons of different sizes (16x16 and 32x32). Listing 2.9 contains the code for the Resource.h header file for the Blizzard example.

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Resource.h Header File

Listing 2.9 The Resource.h Header File Declares Identifiers for Icons Used in the Blizzard Example

//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Icons          Range : 1000 - 1999
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
#define IDI_BLIZZARD    1000
#define IDI_BLIZZARD_SM   1001

Resources are typically numbered in certain ranges to make it easier to distinguish them from each other and to help ensure that they have unique values. As you get into more interesting examples throughout the book, you'll be adding additional types of resources to the Resource.h header file, such as bitmap images and wave sound resources.

Speaking of resources, the icons in the Blizzard example are listed in the Blizzard.rc resource script, which is shown in Listing 2.10.

Listing 2.10 The Blizzard.rc Resource Script Lists Icon Resources Used in the Blizzard Example

#include "Resource.h"
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Icons
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
IDI_BLIZZARD    ICON     "Res\\Blizzard.ico"
IDI_BLIZZARD_SM  ICON     "Res\\Blizzard_sm.ico"

The Blizzard.rc resource script is where the resource identifiers in the Resource.h file are linked to actual resource files. In this example, the resource files are icon files, which is evident by their .ICO file extension. When you compile the Blizzard example, the resources listed in the Blizzard.rc resource script are compiled into binary form and are linked into the Blizzard.exe executable.

Now that you've gotten a look at the helper files that go into the Blizzard example, let's move on to the main source code. The main Blizzard program is fleshed out in the Blizzard.cpp source code file, which is shown in Listing 2.11

Listing 2.11 The Blizzard.cpp Source Code File Reveals how Straightforward the Program Code for a Minimal Windows Program (Game) Becomes when a Game Engine Is Used

//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Blizzard Application
// C++ Source - Blizzard.cpp
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Include Files
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
#include "Blizzard.h"
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
// Game Engine Functions
//覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧-
BOOL GameInitialize(HINSTANCE hInstance)
{
// Create the game engine
g_pGame = new GameEngine(hInstance, TEXT("Blizzard"),
TEXT("Blizzard"), IDI_BLIZZARD, IDI_BLIZZARD_SM);
if (g_pGame == NULL)
return FALSE;
// Set the frame rate
g_pGame->SetFrameRate(15);
return TRUE;
}
void GameStart(HWND hWindow)
{
// Seed the random number generator
srand(GetTickCount());
}
void GameEnd()
{
// Cleanup the game engine
delete g_pGame;
}
void GameActivate(HWND hWindow)
{
HDC  hDC;
RECT rect;
// Draw activation text on the game screen
GetClientRect(hWindow, &rect);
hDC = GetDC(hWindow);
DrawText(hDC, TEXT("Here comes the blizzard!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hWindow, hDC);
}
void GameDeactivate(HWND hWindow)
{
HDC  hDC;
RECT rect;
// Draw deactivation text on the game screen
GetClientRect(hWindow, &rect);
hDC = GetDC(hWindow);
DrawText(hDC, TEXT("The blizzard has passed."), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(hWindow, hDC);
}
void GamePaint(HDC hDC)
{
}
void GameCycle()
{
HDC  hDC;
HWND hWindow = g_pGame->GetWindow();
// Draw the snowflake icon at random positions on the game screen
hDC = GetDC(hWindow);
DrawIcon(hDC, rand() % g_pGame->GetWidth(), rand() % g_pGame->GetHeight(),
(HICON)(WORD)GetClassLong(hWindow, GCL_HICON));
ReleaseDC(hWindow, hDC);
}

The really interesting thing about the code for the Blizzard example is how the only functions present in the code are the game event functions described in GameEngine.h (refer to Listing 2.6). The first of these functions is GameInitialize(), whose responsibility is to get the program started off on the right foot. More specifically, the GameInitialize() function creates a GameEngine object and assigns it to the g_pGame global variable. The GameInitialize() function then sets the frame rate for the game to 15 frames per second, which is a little slower than the default setting of 20 frames per second. This change is primarily to demonstrate how you will often change the default frame rate for games depending on their specific needs.

The GameStart() function is next, and its job is to initialize game data and start a game. In the case of the Blizzard example, there really isn't any game data, so the only code in GameStart() is code to seed a random number generator. I mentioned earlier that the Blizzard example draws snowflake icons at random positions on the screen. In order to successfully generate random numbers for these positions, you have to seed the random number generator. This is accomplished with a call to the standard C library function, srand().


Note - Random number generators don't truly generate random numbers. Instead, they generate a pattern of numbers that has the appearance of being random if you make sure that the pattern is selected differently each time a program is run. This is accomplished by seeding the random number generator with an unpredictable value. It turns out that the internal Windows tick count changes so rapidly that if you look at it at any given instant, you will generally get a unique value, which is sufficient to seed the random number generator and get "random" results.


Similar to the GameStart() function, the GameEnd() function is designed to clean up game data once a game is over. In this case, the GameEnd() function is only required to clean up the game engine.

The GameActivate() and GameDeactivate() functions are very similar to each other in the Blizzard example. Both are here just to demonstrate how you can respond to game activations and deactivations, and they do so by drawing text on the game screen. For example, the GameActivate() function obtains the client rectangle for the game window and then uses it as the basis for drawing a line of text centered on the game screen. I realize that some of this graphics code probably looks a little strange, but don't worry too much about it because the next chapter gives you the whole scoop on how to draw graphics in Windows. Speaking of strange graphics code, the GamePaint() function is responsible for painting the game screen, but in this case, all the painting takes place in the GameCycle() function, so GamePaint() does nothing.

The GameCycle() function is the last function in the Blizzard example and is, without a doubt, the most interesting. The job of this function is to draw a snowflake icon at a random location on the game screen. This might not seem like a big deal, but keep in mind that you set the frame rate to 15 frames per second, which means that the GameCycle() function is getting called 15 times every second; this means that 15 icons get drawn in random locations every second! The first step in the GameCycle() function is to obtain a window handle for the main game window; this window handle is important because it allows you to draw on the game screen. The drawing actually takes place when the Win32 DrawIcon() function is called to draw the Blizzard icon. The standard rand() function is called to determine a random location on the game screen, and the icon is extracted from the game window class using the Win32 GetClassLong() function.


Note - I used a little trick in this example to avoid the task of loading and drawing bitmap images. Because a Windows application's icon is automatically loaded into its window class, I chose to draw the icon, as opposed to loading a bitmap image and drawing it. You learn how to load and draw a bitmap image in Chapter 4, "Drawing Graphical Images," but for now, drawing an icon proved much simpler.


Although I admittedly threw you a few curves with the graphics code in the Blizzard example, hopefully, you were able to follow along with most of the code. You were also hopefully able to see the benefit of relying on the game engine to take care of a lot of the dirty work associated with Windows game programming.

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

Testing the Finished Product

When you run the Blizzard example, you are presented with a game screen that rapidly fills up with snowflake icons, as shown in Figure 2.2.

Morrison

Figure 2.2. The Blizzard example demonstrates how the game engine makes it possible to focus solely on the game-specific aspects of a Windows program.

It doesn't take too long for the Blizzard screen to fill up with snowflake icons. This has a lot to do with the fact that you have the game set up so that it runs through 15 game cycles per second. You could dramatically slow down or speed up the icons being drawn by altering the frame rate of the game in the GameInitialize() function.

Another interesting point to make about the Blizzard example is how it isn't smart enough to repaint the snowflake icons if the window gets invalidated, as shown in Figure 2.3. In other words, if you minimize the program or activate another window in front of it, the game screen will get cleared. This happens because the GamePaint() function doesn't have any code to redraw the snowflake icons in response to the game screen needing a repaint. The repaint problem in the Blizzard example is addressed in the next chapter when you explore Windows graphics in more detail.

Morrison

Figure 2.3. The timing aspect of the game engine causes the Blizzard example to fill up quite quickly with randomly placed Blizzard icons.

Summary

This chapter took an important leap forward in your game development path by guiding you through the design and development of a basic game engine for Windows games. You learned about the importance of a game engine, as well as what code goes into creating a fully functioning game engine. Although features certainly need to be added to the game engine to make it more useful in the context of games, the elements are in place for creating Windows games with much less effort than if you didn't create the engine. It's okay if you don't yet fully appreciate the role of the game engine in making your life easier as a game programmer. But trust me; you will eventually come to appreciate the game engine for how it simplifies game development and allows you to focus on the most important facets of game design.

Chapter 3, "Learning to Draw Basic Graphics," explains the mysteries behind the graphics code that you saw in the Blizzard example. You learn some of the fundamental graphics techniques that will carry you forward throughout the rest of the book. You also create a pretty neat example that demonstrates your new graphics knowledge.

Extreme Game Makeover

Many of the chapters throughout the book contain this section, which includes ideas about how to improve upon the program examples and games. You might be thinking that the Blizzard example is about as limited as an example can be, but part of an "extreme makeover" is showing how to turn one thing into something unexpectedly different. In the case of the Blizzard example, you can turn it into a uniquely different program by changing the snowflake icon and the manner in which the icon is drawn in the GameCycle() function. Just follow these steps:

  1. Modify the Blizzard.ico and Blizzard_sm.ico to a completely different theme, such as a fireball.

  2. Add a POINT global variable to the program that is used to "remember" the position of the fireball; this position can initially be randomly calculated.

  3. Each time through the GameCycle() function, alter the position of the fireball so that it appears to move in a particular direction over time.

The end result of these changes is a program that shows a fireball gracefully moving across the screen leaving trails of fire, as opposed to randomly appearing snowflakes.

SamsThis chapter is from Beginning Game Programming, by Michael Morrison (Sams, ISBN: 0672326590). Check it out at your favorite bookstore today.

Buy this book now.

blog comments powered by Disqus
CODE EXAMPLES ARTICLES

- Bipartite Graphs
- Connectivity in Graphs
- The Ford-Fulkerson Algorithm
- Critical Paths
- The Bellman-Ford and Roy-Floyd Algorithms
- Shortest Path Algorithms in Graphs
- Minimum Spanning Tree
- Articulation Edges and Vertexes
- Circles and Connectivity in Graphs
- Depth-First Search in Graphs
- Breadth-First Search in Graphs
- The Prufer Code and the Floyd-Warshall Algor...
- An Insight into Graphs
- Coding a Custom Object with WSC
- Creating a Custom Object with WSC

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 7 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials