Lazy Foo' Productions

SDL Forums external link SDL Tutorials Articles OpenGL Tutorials OpenGL Forums external link
Follow BlueSky Follow Facebook Follow Twitter Follow Threads
Donate
News FAQs Contact Bugs

Game Loops

Last Updated: Nov 22nd, 2012

The game loop is a key concept in real time games or any game for that matter. When starting out it can be hard to wrap your mind around. This article is here to help break down the basics.
A video game is at its heart an application that repeatedly renders to the screen. A game loop is the main loop that takes in the user input and handles it accordingly to render to the screen. Like any program, things have to happen in a certain order or the program won't run properly. If your game loop isn't structured properly, it is going to lead to a lot of headaches.
while( gameIsRunning )
{
    //Events
    //Logic
    //Rendering
}
Here you see the basic structure to a game loop. First you take in the user input, then you do your logic (motion/physics/AI/etc), and lastly you show everything on the screen.
    //Game Loop
    while( quit == false )
    {
        //Start the frame timer
        fps.start();
//Events while( SDL_PollEvent( &event ) ) { myDot.handle_input(); if( event.type == SDL_QUIT ) { quit = true; } }
//Logic myDot.move();
//Rendering SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) ); myDot.show(); if( SDL_Flip( screen ) == -1 ) { return 1; }
while( fps.get_ticks() < 1000 / FRAMES_PER_SECOND ){} }
Here is the game loop from the motion tutorial. I've colored the 3 modules to highlight how all event handling is in one place, all logic is in next place and all the rendering is together is at the end. Keeping everything in the right module is the key to a good main loop. Sounds easy enough, but there are a few things you want to be careful of when making your game loop.

First thing you have to remember is not to mix logic with event handling.
void Dot::handle_input()
{
    if( event.type == SDL_KEYDOWN )
    {
        switch( event.key.keysym.sym )
        {
            case SDLK_UP: yVel -= DOT_HEIGHT / 2; break;
            case SDLK_DOWN: yVel += DOT_HEIGHT / 2; break;
            case SDLK_LEFT: xVel -= DOT_WIDTH / 2; break;
            case SDLK_RIGHT: xVel += DOT_WIDTH / 2; break;    
        }
    }
    else if( event.type == SDL_KEYUP )
    {
        switch( event.key.keysym.sym )
        {
            case SDLK_UP: yVel += DOT_HEIGHT / 2; break;
            case SDLK_DOWN: yVel -= DOT_HEIGHT / 2; break;
            case SDLK_LEFT: xVel += DOT_WIDTH / 2; break;
            case SDLK_RIGHT: xVel -= DOT_WIDTH / 2; break;    
        }        
    }
	
    x += xVel;

    if( ( x < 0 ) || ( x + DOT_WIDTH > SCREEN_WIDTH ) )
    {
        x -= xVel;    
    }
    
    y += yVel;
    
    if( ( y < 0 ) || ( y + DOT_HEIGHT > SCREEN_HEIGHT ) )
    {
        y -= yVel;    
    }
}
This right here will obviously not work. The reason is you don't know how many events are going to have to be handled. If there are no events to handle the dot isn't going to move at all. Event handling is just supposed to set variables properly so the logic section of the code knows the user's input and can act accordingly.

The logic part of the game loop is the probably the hardest to structure. It contains so many things like AI, collision detection, motion/physics, state changes, etc. The trick is to keep each section of the logic separated from the others, but be careful on how you separate the logic.

Let's take this game of Super Street Fighter 2 Turbo for example:
street fighter 2

//Player 1 logic
player1->handle_collision();
player1->set_status();
player1->move();

//Player 2 logic
player2->handle_collision();
player2->set_status();
player2->move();
Let's say we have our logic set up like this with the 1st player logic going first then the 2nd player logic going after that. Sound good, right? Well let's see what happens when we run it.

Here our two fighters just hit each other.
precollision

//Player 1 logic
player1->handle_collision();
player1->set_status(); player1->move(); //Player 2 logic player2->handle_collision(); player2->set_status(); player2->move();
First we handle the collision for player one.
1
//Player 1 logic
player1->handle_collision();
player1->set_status();
player1->move(); //Player 2 logic player2->handle_collision(); player2->set_status(); player2->move();
Since he got hit, player one is now stunned.
//Player 1 logic
player1->handle_collision();
player1->set_status();
player1->move();
//Player 2 logic player2->handle_collision(); player2->set_status(); player2->move();
Now player one flies back.
3
//Player 1 logic
player1->handle_collision();
player1->set_status();
player1->move();

//Player 2 logic
player2->handle_collision();
player2->set_status(); player2->move();
Now we check collision for player 2. Since player 1 is stunned no collision is detected
4
//Player 1 logic
player1->handle_collision();
player1->set_status();
player1->move();

//Player 2 logic
player2->handle_collision();
player2->set_status(); player2->move();
Now player 2 will keep going. He will also hit player 1 again in the next frame.
5
Now that the logic module is done, this is what gets shown on screen.

So because of badly structured logic, instead of the hitting each other player 2 will score two hits. This is obviously not what is wanted.
//Handle collision
player1->handle_collision();
player2->handle_collision();

//Set status
player1->set_status();
player2->set_status();

//Move players
player1->move();
player2->move();
Here we have the logic organized by the type of logic it is. Now let's run the code.
//Handle collision
player1->handle_collision();
player2->handle_collision(); //Set status player1->set_status(); player2->set_status(); //Move players player1->move(); player2->move();
First we check the collision for player one.
1
//Handle collision
player1->handle_collision();
player2->handle_collision();
//Set status player1->set_status(); player2->set_status(); //Move players player1->move(); player2->move();
Then we check the collision for player two.
2
//Handle collision
player1->handle_collision();
player2->handle_collision();

//Set status
player1->set_status();
player2->set_status(); //Move players player1->move(); player2->move();
Since we know 1st player got hit, we stun first player.
2
//Handle collision
player1->handle_collision();
player2->handle_collision();

//Set status
player1->set_status();
player2->set_status();
//Move players player1->move(); player2->move();
Because we checked both players for collision before setting status, we know to stun second player also.
4
//Handle collision
player1->handle_collision();
player2->handle_collision();

//Set status
player1->set_status();
player2->set_status();

//Move players
player1->move(); player2->move();
Now both players fly back as they should.
5
So always remember to have all the collision detection in one place, all the motion together in its place, all state change code in its place, etc, etc.

Yes, I realize the health bars are off. You also might have noticed that it shows only one player playing against the CPU and I never bothered putting an AI section in the logic. My photoshop skills are pretty limited so you'll have to excuse my complete lack of artistic ability.
//Render
clear_screen();
show_background();
show_objects();
update_screen();
Above you see what a typical rendering module looks like. Keeping the rendering part of your game loop modular shouldn't be too hard. There are cases where rendering can be more complicated and things have to change rendering order. Just remember to keep any rendering specific code modular and you should be fine.

So just remember to keep everything in its proper place and you should be fine.
If you have any suggestions to improve this article, It would be great if you contacted me so I can improve this article.