Timing in a game is absolutely vital. Your game, whether you like it or not,
will slow down and speed up seemingly at random. As another program stops
running a resource-consuming operation, your game will suddenly speed up, possibly
confusing your player and spoiling the game.
This lesson will cover a simple technique to holding your game's speed to a constant
rate.
Because of the complexity of an advanced timer, such as one used in multiplayer
games, we will start off with a very simple timer that limits the framerate to a
specific quantity. For our program, we will use 1/40th of a second (or 25
milliseconds per frame) as a limit to our framerate.
This basic timer is actually quite simple, and only consists of two lines of code:
// find out the starting time of each loop
DWORD starting_point = GetTickCount();
// check PeekMessage(), render a frame, etc.
// wait until 1/40th of a second has passed
while ((GetTickCount() - starting_point) < 25);
There really isn't much to go over here, as it is mostly just simple C++ code with
a function in the middle. Let's start by finding out what that function is.
GetTickCount() is a function that returns the number of milliseconds that have elapsed
since Windows started. Of course, that doesn't immediately seem useful, but
if we can store the current tick-count now, then once we're done rendering we can
check to see how many milliseconds have gone by, and wait until its time to run
the next loop.
So how does this all fit together? We have as our first step, creating a DWORD
called starting_point. We will initialize this with the return value of GetTickCount().
We will then check PeekMessage(), handle any messages and render A SINGLE FRAME.
Once this frame is rendered, we get to our next statement:
Now we check to see how many milliseconds have elapsed during the message handling and frame rendering. Most likely, it was less than 25, and so our program
just continues to re-check the elapsed time until 25 milliseconds have gone by.
Let's add this new code to the program we've been working on. Below you see
the updated program with the latest additions in bold.
[
Show Code]
// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Our First Windowed Program", // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
500, // width of the window
400, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// Enter the infinite message loop
while(TRUE)
{
// find out the starting time of each loop
DWORD starting_point = GetTickCount();
// Check to see if any messages are waiting in the queue
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If the message is WM_QUIT, exit the while loop
if (msg.message == WM_QUIT)
break;
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// Run game code here
// ...
// ...
// wait until 1/40th of a second has passed
while ((GetTickCount() - starting_point) < 25);
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch(message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application
entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc (hWnd, message, wParam, lParam);
}
I'd say we're doing a fairly good job. You now have under your belt all the
Windows programming tools you will need for game programming. With what you
know, you can create a window, prepare it for a game, and have everything ready
for the DirectX code.
So, now the real fun begins! Let's dive into DirectX and build ourselves a
3D game!
Next Tutorial: Direct3D Basics
GO! GO! GO!
© 2006-2009 DirectXTutorial.com. All Rights Reserved.