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.
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.
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:
//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.
//Player 1 logicplayer1->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.
//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.
//Player 1 logic player1->handle_collision(); player1->set_status(); player1->move(); //Player 2 logicplayer2->handle_collision();player2->set_status(); player2->move();
Now we check collision for player 2. Since player 1 is stunned no collision is detected
//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.
Now that the logic module is done, this is what gets shown on screen.
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 collisionplayer1->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.
//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.
//Handle collision player1->handle_collision(); player2->handle_collision(); //Set statusplayer1->set_status();player2->set_status(); //Move players player1->move(); player2->move();
Since we know 1st player got hit, we stun first player.
//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.
//Handle collision player1->handle_collision(); player2->handle_collision(); //Set status player1->set_status(); player2->set_status(); //Move playersplayer1->move(); player2->move();
Now both players fly back as they should.
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.
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.