Movement Through Space

 

In this tutorial you will learn how to give the impression of movement through 3D space.

 

First let’s have a look at a typical game loop

 

//initialisation code

while(playing)

{

            //if a message is waiting

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

            {

                        //translate it

                        TranslateMessage(&msg);

                        //dispatch it

                        DispatchMessage(&msg);

            }

            //if no message is waiting

            else

            {

                        //update the position of player, enemies etc.

                        UpDatePosition();

                        //draw the world

                        DrawWorld();

                        //draw the models in the world

                        DrawModels();

            }

}

 

 

OpenGL and other graphics API’s have a single “camera” fixed at 0, 0, 0 on the Cartesian plane.

 

In order to give the impression that the camera is moving, the world has to be moved in the opposite direction. Therefore to give the impression that the camera is moving clockwise, the objects in the scene must be rotated anti-clockwise.

 

 

 However it is much easier to pretend that the camera is movable and the world remains fixed.

 

 

To calculate the distance that the scene must be translated every scene, the rotation of the camera and the length of a step (how far the camera must move every frame) must be know. From then a bit of trigonometry can solve the problem. A reasonable step length is 0.03f.

So to find the length of translation on the x axis we to find sin(angle of rotation) * Step length. Then the z axis translation can be calculated with Pythagoras’s theorem.  Z axis translation = sqrt(x axis^2 + step length^2).  It is important to multiply the angle of rotation by Pi/180 before calculating the translation. This is because the trig functions take radians, not degrees.

 

In code:

 

       //create some variable to aid in calculations

       float rtemp, xtemp, ztemp;

 

        //multiply the amount of rotation by pi/180 and store the value in rtemp

        rtemp = rotation * (M_PI/180);

 

        //find the x axis translation

        xtemp = sin(rtemp) * STEP;

 

        //find z axis translation using Pythagoras

        ztemp = sqrt((xtemp*xtemp) + (STEP * STEP));

 

        //increment the zposition and xposition

        zposition += ztemp;

        xposition -= xtemp;

 

 

So in the DrawWorld function you would rotate the world by the angle of rotation, then translate it to the new positions in xposition and zposition.

 

    //Clear colour buffer and depth buffer

    glClear(GL_COLOR_BIT_BUFFER | GL_DEPTH_BUFFER_BIT);

 

    //reset modal view matrix

    glLoadIdentity();

 

    //rotate the world

    glRotatef(rotation, 0.0f, 1.0f, 0.0f);

 

    //translate the world

    glTranslatef(xposition, 0.5f, zposition);

 

    //make primitives of this color

    glColor3f(0.0f, 1.0f, 0.0f);

 

    //begin drawing quads

    glBegin(GL_QUADS);

 

        //coordinates of quad

        glVertex3f(-1.0f, 0.0f, -1.0f);

        glVertex3f(-1.0f, 1.0f, -1.0f);

        glVertex3f(1.0f, 1.0f, -1.0f);

        glVertex3f(1.0f, 1.0f, -1.0f);

 

    //stop drawing

    glEnd();

 

Frame Independent Movement

 

Let’s imagine you have just downloaded a game created on a very powerful machine and you are trying to play it but the frame rate is too slow to move well. Or you have downloaded an old game of pong and the ball zips across the screen so fast you can barley see it. This is because the movement in these games is frame dependant, or the position of the camera is only updated every frame. To rid your applications of these irritating problems, frame independent movement must be implemented.

 

Frame independent movement is quite simple.  It has been established that 25 frames per second (one every 40 milliseconds) is the minimum speed that frames can be displayed before the human eye can detect individual frames. So if the cameras position is updated every 35 milliseconds then the movement will look nice and smooth.

 

WINUSERAPI UINT WINAPI SetTimer

                                                            (HWND, UINT, UINT, TIMERPROC);

 

This function sets a timer. The first parameter is the hwnd of your windows. The second parameter is the id of your timer. It is used in your callback function to tell the difference between timer messages. The third is the time (in milliseconds) between the timer going off and the fourth is usually left as null (check msdn for more info). When your timer goes off, you will receive a WM_TIMER message in your call back function. The wparam of this message is the id of the timer.

 

So the new frame independent loop will loop something like this.

 

const int ID_UPDATE //this variable is global

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

    switch(msg)

    {

        case WM_TIMER:

        {

            If(wParam == ID_UPDATE)

            {

                     UpDate();

             }

        }

        break;

        default:

            return DefWindowProc(hwnd, msg, wParam, lParam);

    }

    return 0;

}

 

 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    LPSTR cmdline, int show)

 

//SetTimer ID_UPDATE to 35 milliseconds

SetTimer(hwnd, ID_UPDATE, 35, NULL);

 

//initialisation code

while(playing)

{

            //if a message is waiting

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

            {

                        //translate it

                        TranslateMessage(&msg);

                        //dispatch it

                        DispatchMessage(&msg);

            }

            //if no message is waiting

            else

            {

                        //draw the world

                        DrawWorld();

                        //draw the models in the world

                        DrawModels();

            }

}