frame independent animation/motion

Started by
6 comments, last by DontPanic 15 years, 9 months ago
I wasn't too sure what to name this as the subject but here is the "problem" Basically I cap my frame rate at the moment and have come to the point I ask myself the question. Frame independent motion or not. For reference my game is a 2d sdl/opengl shoot em up. I just made an animation class with a manager class without thinking much and each individual animation I make runs on a timer. Looking at it now that decision leads me towards frame independent motion as if the player moves across the screen slower on a lower fps the animations will all still go full speed. Basically I've become a bit lost how to structure this all. I'm new too this stuff as before I made everything linked to the fps so half fps is half speed everything. I don't think that suits this game as getting a slowdown is an advantage making the game easier and so easier to get hi-scores. That lead me too look at frame independent movement. Is it worth doing or is it simpler to just not bother and it'll be fine So if I make it that as the game loops it works out if it is going half speed and therefore makes a float. I could pass that too anything that has motion and they will use that in the motion calculations to make themselves compensate for this slowdown in the frame rate (half fps means making the movement double so it seems like they always move the same). I think I've grasped this and got it right but should that float be passed to each animation to regulate them or is their individual timers perfectly fine?
Advertisement
Don't let the complexity of existing frame-independent implementations overwhelm you. The basic idea behind it is in fact extremely simple if you look at it from a high level.

The core of the idea is to separate the view (how the game is shown to the player) from the model (how game objects interact). What will happen is that every time a new frame is required, the view will ask the model, how does the world look like at time T? And the model will then compute what the world looks like at time T, and respond by providing a batch of data that the view will then transform into sprites and sounds.

Using this approach, the game will always run at the same speed regardless of the frame rate: if ten seconds have elapsed since the start of the game, the view will ask the model for the state of the world exactly ten seconds after the game started, regardless of how many frames have been rendered in-between.

The game-loop in such a structure would look like this:

loop:  t = time_since_game_started()  model.update(t)  view.render(model)


The view part is very easy to get done, since it basically reads through the model to determine how the world should be drawn. The actual difficult part is to design the model. There are basically three main ways of doing so.

  1. The fixed-step approach: you decide that every second, a fixed number of update steps should happen. Say, for instance, 30 updates per second. Then, you store the number of updates that have already happened, and write your code like this:

    model.update(t):  while ( model.steps_done < t * 30 ):    model.step()    ++ model.steps_done


    This will guarantee that you will have done the correct number of update steps at any given point in time.

  2. The delta-time approach: if an object moves at a speed of x pixels per second, and T seconds have elapsed since the last update, then it will have moved T * x pixels. So, you have to store the time of the last update, and compute the time since that update to move your objects around. You then have to do a "step" of the non-linear actions: collision detection, state changes, etc. The code looks like this:

    model.update(t):  model.things.update(t - model.last_time)  model.step()  model.last_time = t


  3. The event-based approach: you have a list of events (each event has to occur at a certain time). When told to update to time T, you execute in the right order all events up to time T. These events may, of course, add other events to the list, and these will also be executed if they must happen before time T. If you have any continuous effects (such as movement) you may either have them cause change-position events occur at a fixed time interval, or have them be functions of time. Either way, the code looks like this:

    model.update(t):  while (model.next_event.time < t):    model.next_event.execute()


I personally advise using either the first or the last solutions, as they are deterministic. The first, in particular, is very easy to use.
I think I'm still a bit muddled but you have helped. My current loop is basic:

while game's running
{
start timer

game.HandleEvents();
game.Update();
game.Draw();

//Cap the frame rate
if (time < 1000/30)
{
delay((1000/30) - time);
}
}

I'm looking at option 1 from your examples but I am struggling how too integrate it

when you go to say move an object, you move it by how much it would have been able to go in that time, i.e d = v*t

you keep track of how much time has passed since the last frame (t) and move the object the distance d = v*t (where v is its speed) in the direction its going.

Quote:Original post by ToohrVyk
Don't let the complexity of existing frame-independent implementations overwhelm you. The basic idea behind it is in fact extremely simple if you look at it from a high level.


Unfortunately the confusion often comes in at the low level. The examples you've given will probably require some sort of interpolation or extrapolation when the view comes to query the model for its current state, and how to manage that is not immediately obvious.
I was going down the route of using delta time. So taking your recommendation against it I'm looking at the fixed step approach. Having slept on it I can see why it is simpler, and how if the game is running slow the game will still seem to update correctly but the frame rate would be ugly. But what disadvantages does it have against delta time? Why the different approaches if they all get around to doing the same thing?

ibebrett seems to be talking about delta time independent movement if I am understanding correctly.

[edit]
Kylotan, I'm under the impression that the fixed step approach that was outlined would keep everything going at a constant rate collision wise etc. It looks to me like saying:
Every time we loop to update the screen we see how far the game should have got too and then perform that many logic loops to get it to that point. Now we can update.
So it keeps the game speed independent from the display speed and processing speed. Now if all I do is keep the game speed the same as my display speed and all animation looks good wouldn't it be avoiding interpolation and extrapolation? I'm quite uninformed as you can tell but I thought they basically fill in the gaps between the game/logic loops. For example if a bullet travels fast between the update calls but I let the display function operate as fast as possible, we are loosing smoothness that could be there in-between while we display many frames? That could be done by interpolating and so predicting where the bullet would be between logic updates. So it doesn't have to be a problem if I keep the game logic <= fps?

[edit2]
I've gone away and had a shot at implementing a game loop:
        const int m_FPS = 25;	const int m_SKIP = 1000/m_FPS;	const int m_MAXSKIP = 5;        CTimer update;	Uint32 nextGameTick = update.GetTicks();	unsigned int frame = 0;	float interpolation = 0;        while(game.Running())	{		frame = 0;		while(update.GetTicks() > nextGameTick && frame < m_MAXSKIP)		{			game.HandleEvents();			game.Update();                        nextGameTick += m_SKIP;			++frame;		}		interpolation = float(update.GetTicks() + m_SKIP - nextGameTick ) / float(m_SKIP);		game.Draw(interpolation);        }


[Edited by - DontPanic on July 22, 2008 5:36:11 PM]
Quote:Original post by DontPanic
Kylotan, I'm under the impression that the fixed step approach that was outlined would keep everything going at a constant rate collision wise etc. It looks to me like saying:
Every time we loop to update the screen we see how far the game should have got too and then perform that many logic loops to get it to that point. Now we can update.
So it keeps the game speed independent from the display speed and processing speed.

The problem is that you'll typically do your logic updates less frequently than your visual updates. This means you'll see 2 visual updates that look identical, then a change and 2 more that look identical, etc. So you typically need to be able to query the model for its position at a given time, often involving storing 2 copies of all the data and linearly interpolating, or something like that.

Quote:Now if all I do is keep the game speed the same as my display speed and all animation looks good wouldn't it be avoiding interpolation and extrapolation?

But that defeats the entire object of separating out the updates. That's effectively just capping your frame rate as you said initially.
I see what you are saying about capping the frame rate. Luckily I have begun using interpolation as can be seen from my code in my last post. To me that seems to be the perfect game loop but maybe I've made a silly mistake I haven't seen yet. The idea is that on a fast computer I get high frame rate and therefore a smoother game to the eye but the logic remains a constant rate. On a slower computer the frame rate will dip but hopefully the games logic wont take that long therefore it should still be playable. My thinking is even if the frame rate is about 20 fps the games logic should still run fine (as it is less demanding) taking player input and the rest of it 25 times a second.

Have I got it all wrong and my loop will do nothing like that?

I like to think I've learnt something from this thread, thank you

This topic is closed to new replies.

Advertisement