Skip to main content
GameDev.net gamedev.net
🔒 Locked

How does a game loop work?

Started by ElyasMachera Sep 8, 2011 at 2:17 PM 19 replies 8.9k views
Original Post
ElyasMachera
ElyasMachera
Can someone please explain the concept of a game loop to me? I am really slow on this kind of stuff.

I am really trying to understand the concepts of game update and draw. And frames per second.

A frame is like a picture yeah? So if I flip through many frames I can get something like an animation. I think the recommended is 60fps?

Right now, all I have is


while(!keypressed)
{
input
draw
clearsprite
}



So anytime the loop is run input is required, then the changes - if any - are applied to the game objects, maybe they changed position screen. The backbuffer is cleared to reflect later change of positions or whatever.

But the thing is..what is the number of frames in this case? I haven't set any number..so I assume the number of frames is how fast the while loop can run depending on the computer?

If I was to introduce the concept of frames per second how would I do it? I can't find tutorials on this. I am using C++ thanks.
NB: Can you please make explanations simple. Think of it like explaining to a 2 year old .
JTippetts
JTippetts
Gaffer on Games/Fix Your Timestep
The old FlipCode fixed timestep loop


The problem, as you have deduced for yourself, is that you can't count on the computer to be exactly as fast or as slow as you need it to be. The two above linked articles detail common approaches to solving the problem of game loops. My own personal preference leans toward fixing the logical framerate and allowing the visual frame rate to run as fast as it can, in the manner of the Flipcode loop.

The trick with this approach is to use a timer class to track elapsed time, and when enough time has elapsed, perform a logic update. Outside of this timed loop, the main loop continues to run, updating input and drawing the frame. In order to achieve a smooth framerate, each visible "thing" in the world retains a memory of its last position/orientation/etc... as well as a memory of its current position/orientation/etc... An interpolant is calculated based on how long it is until the next logic update, and this is used to smoothly interpolate between last and current state for rendering. The above links should explain the concepts of game loops pretty well.
Pickleninja
Pickleninja
Each game is going to run at different speeds due to the processor / memory of the machine. So your main game loop (depending on the number of actions) will loop anywhere from thousands to tens of thousands (or more) times per second.

This couple possibly relate to thousands of frames per second if you don't have much animation.

To maintain a constant FPS in your game, you need to implement something called "time stepping". In a nutshell, this is a simple counter that says "wait before drawing the next frame".


timeCounter = 0;

while(mainGameLoopRunning()){
// find out how many times per second this game is looping.
cyclesPerSecond = getCyclesPerSecond();

// if you've looped more than what's allowed in a second (note) this only works for 1 frame per second,
// I'd assume you'd divide by 60 if you wanted 60fps
if(timeCounter > cyclesPerSecond){
updateAnimation();
timeCounter = 0;
}
timeCounter++;
}


Here's a couple good examples of what I was talking about.

http://blog.alexjeffery.org/files/pulpcore/fixed_time_step/
http://www.flipcode.com/archives/Main_Loop_with_Fixed_Time_Steps.shtml

P.S. It's been about a year since I implemented a time step in my game... and I don't have my code in front of me, but I think that little pseudo code is correct-ish



-pickle


ElyasMachera
ElyasMachera
Thanks guys,
I will read the stuff on the links provided, if I am still confounded I will be back.
Thanks again .
ElyasMachera
ElyasMachera
Hello again,
I still don't understand it..I actually when to sleep in depression.
The deWitter one, http://www.koonsolo....tters-gameloop/, was kind of OK to follow but he lost me here:
I quote
[font="Verdana,"]"FPS
FPS is an abbreviation for Frames Per Second. In the context of the above implementation, it is the number of times display_game() is called per second.
Game Speed
Game Speed is the number of times the game state gets updated per second, or in other words, the number of times update_game() is called per second."
How is it that game speed, the number of times updage_game() is not the same as the amount of times display_game() is called? It doesn't make sense to me. If draw() or display_game() is called 60times per second (60fps) It should mean the calculations (the logic?) is also called 60 times per second?
You can't skip one and call the other right? Can someone explain this, before I post my other questions. Thanks[/font]
arthursouza
arthursouza
There are calculations which wont be directly connected to graphics, that can be processed in shorter amounts of time than your frame ratio, if your system is fast. What I mean is, the AI could process a pathfinding, which would take some time in a slower machine, but a much faster machine could finish processing that pathfinding before you have to draw the next frame.

I'm not sure if I made myself clear. And this is my point of view on your question, so of course I'm not sure if its the right one.

Sorry for the iffy english.

Lets say you keep track of the amount of interval you want to take between frames, to achive a fixed amount of 60 fps.

In the meantime, a much faster computer would be able to process much more, and you would probably have to step over drawing a couple of times, but could still do other calculations.
SimonForsman
SimonForsman

Hello again,
I still don't understand it..I actually when to sleep in depression.
The deWitter one, http://www.koonsolo....tters-gameloop/, was kind of OK to follow but he lost me here:
I quote
[font="Verdana,"]"FPS
FPS is an abbreviation for Frames Per Second. In the context of the above implementation, it is the number of times display_game() is called per second.
Game Speed
Game Speed is the number of times the game state gets updated per second, or in other words, the number of times update_game() is called per second."
How is it that game speed, the number of times updage_game() is not the same as the amount of times display_game() is called? It doesn't make sense to me. If draw() or display_game() is called 60times per second (60fps) It should mean the calculations (the logic?) is also called 60 times per second?
You can't skip one and call the other right? Can someone explain this, before I post my other questions. Thanks[/font]


you can skip one and call the other, all you need is a simple if statement.

Normally games will update at a fixed rate and render as fast as possible.

for example:

long timeSinceLastUpdate = getTimeInMilliSeconds();
const long updateDelay = 10; //shitty name but ... (run one update for every 10 ms)
while(running) {
long currentTime = getTimeInMilliSeconds();
while (currentTime>timeSinceLastUpdate+updateDelay) {
getInputStuff();
updateGameState();
timeSinceLastUpdate+=10;
}
renderGame();
}


That game loop uses a while loop for updates so it can do multiple updates for each render if needed.

To be really solid it should have a cap on how many updates it will do on a single render (to avoid getting a downward spiral if the cpu lags behind)

This could be as simple as:


long lastUpdateTime = getTimeInMilliSeconds();
const long updateDelay = 10; //shitty name but ... (run one update for every 10 ms)
const int updateCap = 5;
while(running) {
long currentTime = getTimeInMilliSeconds();
int updates = 0;
while (currentTime>lLastUpdateTime+updateDelay) {
getInputStuff();
updateGameState();
lastUpdateTime+=10;
updates++;
if (updates>=updateCap) {
lastUpdateTime = currentTime;
}
}
renderGame();
}


this editor indents like shit btw ...
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
ElyasMachera
ElyasMachera
Lol, I know I can add a if man, but I don't want it now..not yet. I just want to understand the basics, before I can grasp the timing logic.

Let's imagine, theoretically 2 computers a dell dimension 2400 and Sony Vaio

the 2400 can compute a pathfinding algorithm in 6secs, while the Vaio half the time.
The 2400 runs the while loop, that contains the update(exec pathFinding) 400times per sec, but Vaio can do it 1000times per sec.
So for the 2400 doesn't it mean, that the update() and draw() is called 400times? Which means the fps and game speed is 400? FPS is definitely 400! But why is the update not 400?
But the Vaio runs the loop 1000, so 1000fps and game speed is also 1000?
If this logic is flawed can you please correct me, if possible not with code.
Remember 2yr old
Infernal0010
Infernal0010
Your understanding is that the game logic updates and screen renderings are done together at the same rate. Correct?

This is where you are getting mixed up. At one time this may have been the norm, but now, people usually decouple the game updating from the game rendering.
Why?

because your game will render at different speeds at different resolutions and on different hardware. You do not want this render time difference affecting the game updates.

This goes back to using a fixed timestep vs realtime timestep. With a fixed timestep, objects will move slower on a slower rendering machine, on a faster rendering machine, they will move way too fast because in the fixed timestep situation you assume a given amount of time will pass to render and calculate and you specify this value. If in reality it takes less time, it renders and calculates too fast and the whole game speeds up.

You can witness this in very old DOS games run on any newer processor. They run way too fast because of the fixed timesteps they use which were tuned to run on 66MHz machines.

today, we use realtime timesteps.
Now you have to balance simulation accuracy (HIGH Game update FPS required) vs rendering detail/quality (HIGH render/display FPS required)

the typical balance is to minimize the complex physics math in the game update to 30-60fps and use the rest of the time to render the scene as fast as possible (older games like quake3 now render at 200+fps, but only update game logic at the most at 60fps)

That's why the game_update() and game_display() in newer games are decoupled in their calling times.
Your realtime timestep is counted.. when enough milliseconds have elapsed since the last game_update() you call game_update(). in some situations where you can spend too much time rendering frames at maximum speed, you may throttle the rendering with an accumulator to call the rendering less often.

Hope this helps.


ElyasMachera
ElyasMachera

Your understanding is that the game logic updates and screen renderings are done together at the same rate. Correct?

This is where you are getting mixed up. At one time this may have been the norm, but now, people usually decouple the game updating from the game rendering.
Why?

because your game will render at different speeds at different resolutions and on different hardware. You do not want this render time difference affecting the game updates.

This goes back to using a fixed timestep vs realtime timestep. With a fixed timestep, objects will move slower on a slower rendering machine, on a faster rendering machine, they will move way too fast because in the fixed timestep situation you assume a given amount of time will pass to render and calculate and you specify this value. If in reality it takes less time, it renders and calculates too fast and the whole game speeds up.

You can witness this in very old DOS games run on any newer processor. They run way too fast because of the fixed timesteps they use which were tuned to run on 66MHz machines.

today, we use realtime timesteps.
Now you have to balance simulation accuracy (HIGH Game update FPS required) vs rendering detail/quality (HIGH render/display FPS required)

the typical balance is to minimize the complex physics math in the game update to 30-60fps and use the rest of the time to render the scene as fast as possible (older games like quake3 now render at 200+fps, but only update game logic at the most at 60fps)

That's why the game_update() and game_display() in newer games are decoupled in their calling times.
Your realtime timestep is counted.. when enough milliseconds have elapsed since the last game_update() you call game_update(). in some situations where you can spend too much time rendering frames at maximum speed, you may throttle the rendering with an accumulator to call the rendering less often.

Hope this helps.





Thanks, that was somewhat helpful, that was my thought. I will try and reread some of the logic and see if I can make sense of it. If not, I will repost on this, maybe you guys can walk me through it. Thanks a lot guys.
ElyasMachera
ElyasMachera
Hello again,
I decided that the only way to understand this timing stuff..it is to try and do a simple timing implementation myself, and try to understand it. I can't grasp the logic just by reading the examples

So this is what I have, and I need help understanding what is happening even if I wrote it




float startTimer = 0.0f ;
float frameTimer = 1.0f/60 ;// 60 frames per second.


while(gamerunning())
{
while(startTimer < 1000)
{

player->update() ;
startTimer+=frameTimer ; // so for every 0.016ms update the game logic....
}
startTimer = 0.0f ;
player->draw(backBuffer) ; //draw the new endpoint?
}



I recognize that even though I did startTimer < 1000, it doesn't necessarily mean that the update logic ran for 1 sec. It might run exactly second, on a higher processor finish in the 0.5secs ,or take 2 seconds on a slow processor. But in theory and for my sake, let's assume it did run for 1sec.
When I ran it, it actually drew reasonably well...I thought it was going to be choppy, since I was calling update 60 times and drawing once. I don't know why it drew reasonably well.
if the initial position X is at 0 and I kept pressing right..until the loop ended, it didn't end up at the other end of the screen or outside the screen window..can someone please explain why?
arthursouza
arthursouza
[font="Arial"][color="#1C2837"][color="#000000"]startTimer[color="#666600"]+=[color="#000000"]frameTimer [color="#666600"]; [color="#880000"]// so for every 0.016ms update the game logic....

This would mean you are telling the game that player->update() took 0.016ms, and you cant be sure of that.
You are assuming, and thats not good.

This is a little closer to what you should do.



// Update Method

float TimePassed += GetTimeSinceLastUpdate();

Update();

if(TimePassed >= DrawingDelay)
{
DrawGraphics();
TimePassed = 0f;
}


You must have an actual timer, counting how much time has passed since the last update.
Now, lets assume you ARE using an actual timer.

With your logic, you are assuming that player->update() will take a certaing amount of time, you are not using an actual timer to see how long it has actually taken, so your drawing will sometimes take longer, and sometimes happen faster.[/font]
[font="Arial"]Could you explain a little bit further what you mean with the last question?[/font]

[font="Arial"]
[/font]
[font="Arial"]#. Im sorry for taking a while to realize you had already understood that. I was trying to explain further the point I made earlier, and ended up repeating what other people have already said.[/font]
_orm_
_orm_
Something else you may want to consider as part of your design is passing the time between logic updates to the update function of your game objects.


float logicTime = 1.0f/25 ;// 25 logical ticks per second is really all you will ever need.
float elapsedTime = 0;

while(gamerunning())


{
elapsedTime += TimeBetweenFrames();
if(elapsedTime > logicTime)
{
player->Update(elapsedTime); // because the true elapsed time between logical updates can vary, you want the game objects to know about it so they can decide what to do with it.

elapsedTime = 0;

}
player->draw(backBuffer) ;

}
haegarr
haegarr
The problems with timing are these:
* You want a minimum rendering rate to give the player the illusion of a smooth flow.
* You want to have a fixed timestep for physic simulations, because the underlying math works fine with fixed timesteps (only few methods work with variable timestep).
* You don't want to waste time; e.g. AI isn't required to decide 50 times per second what to do.
* You don't want input to be missed, because that would frustrate the player soon.

To handle all this in a single loop with unknown rate, you have to measure the time that has elapsed. E.g.


while ( running ) {
now = GetSystemTime() ;
elapsed = now - justNow;
// do something
justNow = now;
}

So, knowing the current moment in time "now", the previous moment in time "justNow", and hence the elapsed period "elapsed", you can drive your simulation. A fast computer may "do something" at 0.2, 0.4, 0.6, ... while a slow computer way "do something" at 0.3, 0.6, 0.9 ... seconds. However, both will reach e.g. 0.6 seconds, the former one within 3 steps and the latter one within 2 steps.

Now think of your simulations being defined in real time units. E.g. an animation with its timeline given in seconds. Assume that the animation has a keyframe with position P1 at 0s, and another keyframe with position P2 at 6s. The fast computer asks for a position at times 0.2s and 0.4s which need to be interpolated as usual, and then it hits P2 at 0.6s directly. The slow computer asks for the position at 0.3s which needs to be interpolated, and then hits P2 at 0.6s directly. Notice that both computers use P2 at the same time. Although the computers run differently fast, the animation is played in the same rate.

In reality the "elapsed" time will differ from loop pass to pass, but that still works fine, assuming that a minimum rate is reached at all.

Now coming to the fix timestep of physic simulation. The principle is simple: You use an inner loop that passes as often as the fixed timestep fits into the current "elapsed".


delta = 0;
while ( running ) {
now = GetSystemTime() ;
elapsed = now - justNow;

delta += elapsed;
while ( delta > 0 ) {
// do physic simulation step
delta -= physicsTimestep;
}

justNow = now;
}

The devil is in the details, of course. E.g. it is a sheer fortuity if delta will be reduced to 0. Instead, it will be slightly less than zero in general. Then you can do an interpolation of the current physic state and the previous one in order to compute the active state.
ElyasMachera
ElyasMachera
Guys, I believe I understand...sorry for being so slow..

When we say a the game runs 60fps, it means we must draw the frame 60 times in a second. So a frame drawn to the screen - this is the part I didn't fully get - has a time slice of 16ms... In other words a frame must be drawn in the time allocated which is 16ms, right?

But the problem is maybe the update and draw takes too long so the the greedy frame uses more than 16ms..


So:
StartTime = 160ms, endTime = 200ms,
duration of update and draw is elapsedTime = 40ms. But a frame should have taken 16ms
So it was 24ms overtime, which means there is now a lag yeah?
And that sucks..big time....the only thing I may do is allocate a bigger frame rate, so that each frame may have sufficient time to finish update and drawing without stealing precious time from the next frame draw.

But if
startTime = 160ms, endTime = 170ms
then elapsedTime = 10ms!
The update and draw finished quickly, and if there was no code to say sleep(6ms) the next draw will be called, and it will be fast!

The best thing then is to use a variable time step.

We calculate the elapsed time the draw and update takes and use that as our speed.
so if it took more time:
10/16 * 2px that is elapsedTime/frameDuration * velocity will give me the amount of units I should move the player by instead of using the speed of 2px.

I think that is pretty much the idea of timing the game loop. My brain is still trying to cope with the concept, but I believe I am getting there!
Please correct me if I am wrong.
arthursouza
arthursouza
(Wish I was more used with english, my brain gets a little confused trying to read and explain some things in english)

[color=#1C2837][size=2]But if [color=#1C2837][size=2]startTime = 160ms, endTime = 170ms
then elapsedTime = 10ms!

[color=#1C2837][size=2]The update and draw finished quickly, and if there was no code to say sleep(6ms) the next draw will be called, and it will be fast![/quote]


No, because what you will define a minimum time step in the beginning, and if the process if too fast, you will your time counter is AT LEAST higher than that minimum time step before drawing the next frame. So it will never be too fast. And if it goes too slow, you might have a problem in your logic that should be improved, or you will have todivide your update in multiple steps (think about this later, first try to implement these fixed time steps based on the actual elapsed time)

I will give you a real example. In my game, I usually have 60 fps. But I dont have enough animation frames on my sprites to change them 60 times a second! So what do I do then?

Well, its simple.

use the same example given above.



float updateTime = 100f;

float animationTimer = 0f;

void Update()
{
animationTimer += TimeElapsed();

if(animationTimer >= updateTime)
{
Animate();
}
}




This will never be too fast. Its pretty simple actually, just clear your thoughts, and go over it from the beginning, because it seems like you already have your mind set into a way you think things are supposed to work (like defining how much time things are going to take, instead of defining WHEN are YOU going to do something) and thats confusing you a bit.
ElyasMachera
ElyasMachera

(Wish I was more used with english, my brain gets a little confused trying to read and explain some things in english)

[color="#1C2837"]But if [color="#1C2837"]startTime = 160ms, endTime = 170ms
then elapsedTime = 10ms!

[color="#1C2837"]The update and draw finished quickly, and if there was no code to say sleep(6ms) the next draw will be called, and it will be fast!



No, because what you will define a minimum time step in the beginning, and if the process if too fast, you will your time counter is AT LEAST higher than that minimum time step before drawing the next frame. So it will never be too fast. And if it goes too slow, you might have a problem in your logic that should be improved, or you will have todivide your update in multiple steps (think about this later, first try to implement these fixed time steps based on the actual elapsed time)

I will give you a real example. In my game, I usually have 60 fps. But I dont have enough animation frames on my sprites to change them 60 times a second! So what do I do then?

Well, its simple.

use the same example given above.



float updateTime = 100f;

float animationTimer = 0f;

void Update()
{
animationTimer += TimeElapsed();

if(animationTimer >= updateTime)
{
Animate();
}
}




This will never be too fast. Its pretty simple actually, just clear your thoughts, and go over it from the beginning, because it seems like you already have your mind set into a way you think things are supposed to work (like defining how much time things are going to take, instead of defining WHEN are YOU going to do something) and thats confusing you a bit.
[/quote]


No problem, I also find it hard to understand people's explanation, and I know English lol.
But I don't understand why you say it is wrong... Using a fixed fps of 60fps so the frame may draw at 16ms

But it could happen that the frame finished updating the logic and drawing at just 4ms.
which means the remaining time of the time slice is 12ms...
So I start to update the next logic and then draw ahead of time....Which is fast? It makes sense to me.. but I don't know why you say it is wrong
Since we want each frame to have a duration of 16ms the way I can control it is to spend the remaining 12ms doing something else, and when it is up continuing update and draw...
So the update or game speed is dependent on the fps.
I thought I was making progress

But this is highly efficient because I am limiting at a constant fps and I have to use sleep, better to use a dynamic fps....which I am having a bit of trouble understand. You are right..

I need to clear my head and think about it.

To calculate the amount of frames to move..let me imagine a scenario where every 16ms I move the position by 100px

So, position+=100 ;

So I am moving 100px/16ms... I believe that is 6000px per second? Using sleep.

But to get rid of it, I will have to calculate the elapsed time between update() and draw().







ElyasMachera
ElyasMachera
It got too long so I will continue from here...


while(true)
{
start_time = clock() ;

player->update(timeDelta)
player->draw(backBuffer) ;
end_time = clock() ;

elapsedTime = end_time - start_time




So, to calculate how much time elapsed for the next frame..the timeDelta will be position = velocity * timeDelta

where timeDelta = elapsedTime/16 * 100.

So If the duration was 10ms instead of 16ms
it would be 10/16 * 100 ..62.5 instead of the original 100px.
if the elapsed time was 16ms
then 16/16 * 100...exactly 100 units...But there is a bug in the code..which I am trying to solve
arthursouza
arthursouza

But it could happen that the frame finished updating the logic and drawing at just 4ms.
which means the remaining time of the time slice is 12ms...
So I start to update the next logic and then draw ahead of time....Which is fast? It makes sense to me.. but I don't know why you say it is wrong


I'm not saying its wrong, because I'm in no position to say that. I'm also a beginner (:

But in my way of doing things, I wont do it like this.

"So I start to update the next logic and then draw ahead of time....Which is fast? It makes sense to me.. but I don't know why you say it is wrong "

I woudnt update ahead of the time. I would keep, like in my previous examples, a variable indicating the needed delay between two calls of the draw() method.

if(timeElapsed > 16)
{
draw();
}

Just an example, so you would never draw "too fast". But you would also need a way of not drawing too slow, and your idea of keeping a delta value to determine how much movement would be added based on the time it took to draw is nice, and I believed it has been advised by someone before on this thread.

The problem with that is you wont have a smooth animation. Your characters will, sometimes, jump.
ElyasMachera
ElyasMachera
if(timeElapsed > 16)
draw()

That is limiting the draw rate isn't it? But what you want is to draw as fast as possible....
You are right it may jump....that's one of the drawbacks..but I can't even get it working..well I can but there is something I don't understand about it..

From what I was told and understand

I calculate elapsedTime after update and draw...

But the thing is initially when I pass the timeDelta the elapsedTime would be 0.0f which is wrong

So I am confused... I am reading that dewitters page again, to see if I can understand it.

And btw, as a fellow beginner I am impressed how quickly you've grasped it

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.