flood wrote:Ahigh wrote:
pseudocode:
Code: Select all
unsigned int msec( void )
{
return real_time_clock_in_milliseconds;
}
void update( void )
{
const unsigned int game_update_dtms = 4;
unsigned int ms = msec();
while( game_update_ms < ms ) // iterates multiple times per visual frame
{
ms = msec();
// state of input can be different on each iteration
grab_latest_input_state( game_update_ms );
game_update( game_update_dtms );
save_position_of_all_game_objects( game_update_ms );
game_update_ms += game_update_dtms;
}
// interpolate from previous two game state as visuals are on average game_update_dtms/2 older than game state
assert( ms <= game_update_ms );
interpolate_visually_displayed_position_of_all_game_objects_for_visuals( ms );
draw_everything(); // always drawing position of game objects at or behind their most current game state at 4ms boundaries
}
You typically also have a maximum number of iterations and some skew for when the game gets paused and what-not but that's beyond the scope of this discussion.
Not quite understanding this. How does the program know when to leave the game update while loop?
it seems what's wanted is something like
(g for game update)
g-g-g-draw frame 1-g-g-draw frame 2-g-g-g-draw frame 3 etc...
Let me answer with a specific example, and maybe that will help.
I typically update the game update loop with a fixed delta time in integral milliseconds.
So let's just say I choose to update the game at every 4 milliseconds.
I have a separate thread with a dummy window that takes USB input data from the keyboard and from the mouse. This thread operates at a maximum of 1000hz when the mouse is moving violently. But normally operates at a rate much lower than that when there is not much data coming in. It is basically an interrupt driven thread. It wakes up on hardware interrupt, takes input data, stuff it into a memory resident FIFO and then it's done.
The graphics and game update are in a single thread. Each time the graphics draws, the time stamp for the graphics is between 0 and 4 milliseconds BEHIND the game update time stamp. So graphics, in general, renders an AVERAGE of 2ms behind the latest input data. This is because graphics intrinsically has to be clamped to the nearest 16ms, and my latest input data may be up to 4ms "fresher" and therefore should not be applied to the latest graphics output.
So the main loop doing game update and graphics update is going to do something like
game_update( 4 ); game_update( 8 ); game_update( 12 ); game_update( 16 ); game_update( 20 );
graphics_update( 16.66 );
game_update( 24 ); game_update( 28 ); game_update( 32 ); game_update( 36 );
graphics_update( 33.33 );
game_update( 40 ); game_update( 44 ); game_update( 48 ); game_update( 52 );
graphics_update( 50.00 );
game_update( 56 ); game_update( 60 ); game_update( 64 ); game_update( 68 );
graphics_update( 66.66 );
Each time the graphics updates, it looks at the position and orientation of each game object for the previous TWO game_update iterations and it interpolates between those two positions to get an accurate portrayal of where the object should be for that visual frame. This is to get perfectly smooth temporal movement data.
So the first graphics update has a time stamp of 16.66 ms. 16.66 ms - 16 ms = .66 ms. 0.66 / 4.0 = 0.165
So you blend 16.5% of game_update( 20 ) 's positions and (100% - 16.5% =) 83.5% of game_update( 16 ) to get an interpolated position that represents 0.66 ms away from game_update( 16 )'s resulting position and orientation for each game object and 3.33 ms away from game_update( 20 )'s resulting position and orientation.
You generally want to use integral delta-time on the game_update loop and more accurate floating point on the graphics to get the smoothest possible movements from your interpolated results.
I hope this is helpful. You would be amazed how many game programmers consider these details unimportant. But the top fast-moving game programmers all understand this stuff, I can assure you. When I worked for Red 5 Studios with the former leads from the World of Warcraft, nobody cared. So it's not required for a game that makes history. Only for games that require serious hand-to-eye coordination and rapid, I guess, "twitch skill" as a component of gameplay. If you just kill boars all day, this is just a bunch of garbage that doesn't matter.