Tricks of the Windows Game Programming Gurus:Windows GDI, Controls, and Last-Minute Gift Ideas

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

#### Low-Level Timing

Although creating timers is at least one way to keep track of time, the technique suffers from a few faults: First, timers send messages, and second, timers aren’t that accurate. Finally, in most game loops you want to force the main body of the code to run at a specific frame rate and no higher; this is achieved by locking the frame rate via timing code. Timers aren’t very good at this. What’s really needed is a way to query a system clock of sorts and then perform differential tests to see how much time has elapsed. The Win32 API has such a function, and it’s called `GetTickCount()`:

    DWORD GetTickCount(void);

`GetTickCount()` returns the number of milliseconds since Windows was started. That may not seem useful as an absolute reference, because you have none, but it’s perfect as a differential reference. All you have to do at the top of any code block that you want to time is query the current tick count and then at the end of the loop query again, and take the difference. Whammo, you have the time difference in milliseconds. For example, here’s how you would make sure that a chunk of code runs at exactly 30 fps or with a delay of 1/30fps = 33.33 milliseconds:

    // get the starting time

    DWORD start_time = GetTickCount();

    // do work, draw frame, whatever

    // now wait until 33 milliseconds has elapsed

    while ((GetTickCount() - start_time) < 33);

That’s what I’m talking about, baby! Of course, sitting in a busy loop is a waste of time performing the `while()` logic, but you can always branch off and test every now and then, so you don’t waste cycles. The point is that with this technique you can force time constraints on chunks of code.

> Note: Obviously, if your PC can’t run at 30 fps, the loop will take longer. However, if during a free run of your code the loop ran from 30–100 fps, the preceding code would lock it to 30 fps always. That’s the point!

As an example, take a look at `DEMO4_7.CPP` on the CD. It basically locks the frame rate to 30 fps and updates a little screen saver with lines on each frame. Following is the code from the `WinMain()` that does the work:

    // get the dc and hold onto it

    hdc = GetDC(hwnd);

    // seed random number generator

    srand(GetTickCount());

    // endpoints of line

    int x1 = rand()%WINDOW_WIDTH;

    int y1 = rand()%WINDOW_HEIGHT;

    int x2 = rand()%WINDOW_WIDTH;

    int y2 = rand()%WINDOW_HEIGHT;

    // intial velocity of each end

    int x1v = -4 + rand()%8;

    int y1v = -4 + rand()%8;

    int x2v = -4 + rand()%8;

    int y2v = -4 + rand()%8;

    // enter main event loop, but this time we use PeekMessage()

    // instead of GetMessage() to retrieve messages

    while(TRUE)

        {

        // get time reference

        DWORD start_time = GetTickCount();

        // test if there is a message in queue, if so get it

        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

           {

           // test if this is a quit

           if (msg.message == WM_QUIT)

               break;

           // translate any accelerator keys

           TranslateMessage(&msg);

           // send the message to the window proc

           DispatchMessage(&msg);

           } // end if

           // is it time to change color

           if (++color_change_count >= 100)

              {

              // reset counter

              color_change_count = 0;

              // create a random colored pen

              if (pen)

                 DeleteObject(pen);

              // create a new pen

              pen = CreatePen(PS_SOLID,1,

                    RGB(rand()%256,rand()%256,rand()%256));

              // select the pen into context

              SelectObject(hdc,pen);

              } // end if

           // move endpoints of line

           x1+=x1v;

           y1+=y1v;

           x2+=x2v;

           y2+=y2v;

           // test if either end hit window edge

           if (x1 < 0 || x1 >= WINDOW_WIDTH)

              {

              // invert velocity

              x1v=-x1v;

              // bum endpoint back

              x1+=x1v;

              } // end if

           if (y1 < 0 || y1 >= WINDOW_HEIGHT)

              {

              // invert velocity

              y1v=-y1v;

              // bum endpoint back

              y1+=y1v;

              } // end if

           // now test second endpoint

           if (x2 < 0 || x2 >= WINDOW_WIDTH)

              {

              // invert velocity

              x2v=-x2v;

              // bum endpoint back

              x2+=x2v;

              } // end if

           if (y2 < 0 || y2 >= WINDOW_HEIGHT)

              {

              // invert velocity

              y2v=-y2v;

              // bum endpoint back

              y2+=y2v;

              } // end if

           // move to end one of line

           MoveToEx(hdc, x1,y1, NULL);

           // draw the line to other end

           LineTo(hdc,x2,y2);

           // lock time to 30 fps which is approx. 33 milliseconds

           while((GetTickCount() - start_time) < 33);

           // main game processing goes here

           if (KEYDOWN(VK_ESCAPE))

              SendMessage(hwnd, WM_CLOSE, 0,0);

        } // end while

    // release the device context

    ReleaseDC(hwnd,hdc);

    // return to Windows like this

    return(msg.wParam);

    } // end WinMain

Other than the timing aspect of the code, there is some other logic that you should take some time to review: the collision logic. You’ll notice that there are two ends of the line segment, each with a position and velocity. As the segment moves, the code tests whether it has collided with the edge of the window client area. If so, the segment is bounced off the edge, creating the illusion of a bouncing line.

If you just want to delay your code, use a Win32 API function called `Sleep()`. Just send it the time delay in milliseconds you wish to delay and the function will. For example, to delay 1.0 second, you would say `Sleep(1000)`.

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.