Experiments In Game Programming
Main
 Home
XNA - C#
 Coming Soon!
 Level Editing - GTK
DirectX 9 - C++
 Downloads
 Disclaimer
 Introduction
Part 1 - DirectX
 1 - Breakout
 2 - Create DX
 3 - 2d Images
 4 - 3d Models
 5 - Cameras & Lights
 6 - Animation Timing
 7 - Keyboard/Mouse
 8 - Sound
Part 2 - Breakout
 1 - Art and Sounds
 2 - The Menu
 3 - Starting Breakout
 4 - The Level
 5 - The Paddle
 6 - The Ball
 7 - Finishing Touches

Untitled Document

Chapter 6

 

Timing & Animation

 

 

A difficult part of game programming is setting up proper timing. We can't simply update our object positions every time we render a frame, because fast computers would move the objects faster than slower computers. Because of this we need to be able to update our objects based on timing. In this chapter we will setup a Frame Timer to control animation based on time.

Topics Covered in this Chapter:
  • Animation Timing
  • Creating A Frame Timer
  • Creating an Animation Test Class

Frame based animation is easy, at every frame you adjust the position of the object based on what ever logic you are using. Time based animation is more complicated because the distance the objects move is based on how many timer intervals (usually fractions of a second) have passed since the last time a call to update the object was made. The FrameTimer is my alternative and lets you program using frame timing (easy), but maintains a timer based frame rate. What the frame timer does is calculate how many frames need to be updated based on how much time has passed. For example if you set the frame timer to maintain 60 frames per second, for every 60th of a second that passes it will report that a frame needs to be updated. The frame timer is designed to be called every time the game update loop begins; The number of frames that need to be updated is passed to the objects that need to be updated, and they are updated accordingly. The results being, if you have a slower computer that can only maintain 30 fps, each update is asked to move the game objects 2 frames worth of distance. If you have a faster computer that can maintain 120 fps, the the game objects are only requested to move every 2 frames. This maintains a constant movement rate on any computer you run it on. Of course the drawback to this method is if your computer can't maintain more than 1 frame per second the whole thing would freeze up. However the game wouldn't be playable on a system like that anyway.

Take a look at the frame timer from the chapter 6 code. The frame timer is actually a very simple class with 2 functions, init, and framesToUpdate. Init takes one parameter, the frame rate to maintain. Init uses the performance counter to do timing. The performance counter is different with every chip, so the first thing we do is find out what the frequency of the timer is, that is, how many counts per second. Then to determine how many counts per frame, we divide that frequency by the frame rate.

void FrameTimer::init(int fps){
     QueryPerformanceFrequency(&timerFreq);
     QueryPerformanceCounter(&timeNow);
     QueryPerformanceCounter(&timePrevious);
    
     //init fps time info
     Requested_FPS=fps;

     //The number of intervals in the given timer, per frame at the requested      rate.
     intervalsPerFrame = ( (float)timerFreq.QuadPart / Requested_FPS );

}

When framesToUpdate is called the performance counter is called to get the current count. The intervals since the last time the framesToUpdate function was called are calculated. Then, using the intervals per frame we calculated above, it calculates how many frames need to be updated. We then record the current time for use the next time framesToUpdate is called, and then return the number of frames that need to be updated.

int FrameTimer::framesToUpdate(){
     int framesToUpdate = 0;
     QueryPerformanceCounter(&timeNow);

     intervalsSinceLastUpdate = (float)timeNow.QuadPart - (float)timePrevious.QuadPart;
    
     framesToUpdate = (int)( intervalsSinceLastUpdate / intervalsPerFrame );

     //If we are not updateing any frames, keep the old previous timer count
     if (framesToUpdate != 0){
         QueryPerformanceCounter(&timePrevious);
     }

     return framesToUpdate;
}

That's all there is to the frame timer. The only thing you need to keep in mind when programming using the frame timer is that you are designing your game to run at a certain frame rate, if you wished to increase or decrease that frame rate, adjusting the frame timer after you write your game would just make it run faster or slower.

In order to use the frame timer, we need to setup a controller class for the object we wish to animate. The controller will receive the frames to update and change the position of its object accordingly. The controller I have written is called AnimationTest. It is a very simple controller that moves the sphere model back and forth on the X axis between 25 and -25 units. This class can be used as an example for a controller class, but is not part of the reusable classes we are building in this part of the book.

The AnimationTest class starts with its init function, which loads the sphere model, and sets the starting position of the model.
bool AnimationTest::init(LPDIRECT3DDEVICE9 device){
     model = new Model();
     model->loadModel(device,"sphere.x");
     if (!model){
         return false;
     }
     position = D3DXVECTOR3(0.0f,0.0f,0.0f);
     model->setPosition(position);
     direction = 1;
}

The next function is updateFrames. UpdateFrames takes the number of frames requested to be updated. This value will be the value returned by the frame timer. When we write the controller classes we do so based on the frame rate we know we will be setting in the frame timer. It would be possible to easily set a frame rate option and adjust the movements made per frame based on that frame rate. I prefer to set a preferred rate of 60 fps and program to that. In updateFrames the frame loop calculates what changes would be made per frame, and once all the calculations have been made it actually makes its changes. In this example it doesn't look important to loop each frame update, and the changes could be calculated as just a multiple of the frame rate. However, later when doing collision detection it becomes important to do each update frame by frame.
void AnimationTest::updateFrames(int numberOfFrames){
     //for a simple test of animation the model will be moved from +25x to -25x
     //for each frame to update, calculate changes to the model position
     for (int i = 0; i<numberOfFrames; i++){
         if (direction == 1){
             position.x+=0.3f; //change the x position by 0.3 units per frame
         } else {
             position.x-=0.3f; //change the x position by 0.3 units per frame
         }
         if (position.x > 25.0f || position.x < -25.0f){
             direction = direction * -1;
         }
     }
     //update the model position
     model->setPosition(position);
}


 The last function in AnimationTest is render. Render actually just propagates the render call to the model.
void AnimationTest::render(LPDIRECT3DDEVICE9 device){
     model->render(device);
}

 Now we have a FrameTimer, as well as a class to test animation using it. The next step is to add both a FrameTimer and the AnimationTest to the GameMain. By now you should understand we need to add both the class definitions to the GameMain header as well as to the top of the GameMain. So, I will skip to GameMain's Init. I have removed the test model from the previous chapter and replaced it with AnimationTest, as well as added a call to create a FrameTimer.
bool GameMain::init(HWND wndHandle)
{
     ...

     //create a new animation test object
     animTest = new AnimationTest();
     animTest->init(dxManager->getD3DDevice());


     //create a new frame timer
     timer = new FrameTimer();
     timer->init(60); //init to maintain 60 frames per second


     return true;
}

 Lastly, we need to add an update, and render call to the update function. The timer is called, and the frames that need to be updated are passed to the AnimationTest object. Once the object has been updated, the render call renders the AnimationTest object in its new position.
void GameMain::update(void)
{
     int framesToUpdate;
     //call our update function
     framesToUpdate = timer->framesToUpdate();
     animTest->updateFrames(framesToUpdate);

     // begin rendering
     dxManager->beginRender();
     //game render calls go here

     textManager->drawText("Hello World", 30, 30, 300, 300);

     testSurface->setSize(100);
     testSurface->setPosition(60,80);
     testSurface->render(dxManager->getD3DDevice());
    
     animTest->render(dxManager->getD3DDevice());

    
     //end render
     dxManager->endRender();

}

 That concludes the changes required to test the timer. Compiling and running the project should now result in a single sphere that moves from one side of the screen to the other. The sphere should take just over 3 seconds to get from one side to the other no matter what computer it is running on.



Summing Up - Chapter 6

In this chapter we have created a FrameTimer; a timing system that returns the frames we should update based on how much time has passed since we last updated. This allows us to program our game in a frame based method, which is easier than writing our controllers to work on a time based model. This method still reliably maintains the same game speed on different speed computers. Before moving on, play with the frame rates, see what happens when you increase or decrease the frame rate. Also, try adjusting the distance updated in the AnimationTest. You should be able to increase the smoothness of the movement by reducing the distance moved and increasing the frame rate. Of course this will only work if your computer can keep up.
 ©2008 David Whittaker