C++: Easiest way to implement an in-game Time/Date system?...

Started by
32 comments, last by Sean_Seanston 9 years, 3 months ago

In theory, it is better.

Implementation details are a mess, though.

Yeah, it didn't work and then I changed a few things around and it seemed to be working ok at that point. Of course, that's just doing the game loop and before I've actually changed my game logic to work with the new fixed timestep. Even still it seems to be keeping pretty correct time.

Most importantly, I managed to just set frameTime directly to a seconds value, which made things much handier than trying to convert back from seconds to QPC ticks...

Also, why the floating point? You complain about the accuracy thinking long will not work for you with 32-bit precision, so you move to a 64-bit type ... then you discard it and go with a double that has even less bits of precision. Just stick with integers (either 32-bit or 64-bit), it will make your life easier.

Also, where do the magic numbers come from? Do the 0.25 and the 4 have any significance?

Well I based it directly off of the example you linked earlier:

gafferongames.com/game-physics/fix-your-timestep/

There he uses a double for the frameTime, which he has in seconds.

Also 0.25 comes from the example. As for the 4, it was there to (hopefully) make for a simpler calculation than multiplying by 0.25, by just dividing by 4 (and therefore avoiding floating points).

Figured I'd try to get it working with my code but as close to the example as possible before looking at if some different values would be more appropriate.

It now looks like:


while( Engine::instance()->isRunning() )
{
	//Get ticks
	LONGLONG newTime = getQPC();

	//Get time between updates in seconds
double frameTime =  double( newTime - currentTime )/ double ( getQPFreq() );

	//If more than 0.25 seconds between updates
        if( frameTime > 0.25 )
	{
		//Cap to 0.25 seconds
                frameTime = 0.25;
	}
	//Set currentTime for next frame
        currentTime = newTime;

	//Add frameTime seconds to accumulator
        accumulator += frameTime;

	//While
        while ( accumulator >= dt )
        {
		Engine::instance()->handleEvents();
		Engine::instance()->update( dt );

                accumulator -= dt;
        }

	//Calculate alpha interpolation value
        const double alpha = accumulator / dt; //

	//Render
	Engine::instance()->render( alpha );
}

frameTime calculation looks a bit messy, but it seems to be giving reasonable values. I suppose I'll look into changing it to a LONGLONG that holds nanoseconds, and then changing accumulator and dt similarly...

Is it very much a bad idea to leave it as is?

One more thing though: in the example he has the following before the rendering function:


State state = currentState * alpha + 
previousState * ( 1.0 - alpha );

Clearly state is going to go out of scope after the rendering is done, so I assume even though he doesn't pass state into the render function, that we're supposed to assume that render is using the value of state as the basis for rendering?

Must be, it's just at first I thought it looked like he might have been doing things a little differently to what I was used to in that regard (it made me think he was actually storing the in-between value for purposes other than pure rendering), but I think it's the same now. i.e. He keeps track of the current and previous states, and then just draws between them based on the alpha value and nothing else happens.

Advertisement

Yes, the example on Gaffer On Games is an example of fixed time step, but it is also for a different type of game.

In his type of game working with time in seconds is a workable. In your type of game, it is probably better to use an integer at a regular simulation time interval.

But meh, whatever.

It is not directly bad. Using a double with 1.0 as a second will work for a long time. You've got 15 decimal digits to work through, and if you're operating at milliseconds, that gives you 30,000 years of simulation time before your program suffers a catastrophic death from running out of simulation time. Using a 64-bit number to indicate milliseconds would give you over 50 million years instead of 30 thousand years before catastrophic failure, but both are a long time for a life simulator.

Just be careful not to use float, only double. A float would give you about two weeks before you hit catastrophic failure where time steps were greater than rounding error.

Yes, the example on Gaffer On Games is an example of fixed time step, but it is also for a different type of game.

In his type of game working with time in seconds is a workable. In your type of game, it is probably better to use an integer at a regular simulation time interval.

It did strike me as kind of strange to use a scale as big as seconds, even with the decimal point, alright.

Guess I'll just change it to an unsigned int keeping track of microseconds. Would probably be less fiddly to add ticks without messing with a dt value in decimal form anyway...

It is not directly bad. Using a double with 1.0 as a second will work for a long time. You've got 15 decimal digits to work through, and if you're operating at milliseconds, that gives you 30,000 years of simulation time before your program suffers a catastrophic death from running out of simulation time. Using a 64-bit number to indicate milliseconds would give you over 50 million years instead of 30 thousand years before catastrophic failure, but both are a long time for a life simulator.

Just be careful not to use float, only double. A float would give you about two weeks before you hit catastrophic failure where time steps were greater than rounding error.

Though for these particular variables, frameTime is just representing the time between updates and is capped at 0.25, while accumulator should never get large either because its value comes from frameTime and continually has dt subtracted from it, so the range shouldn't matter much should it?

The counter for the epoch being another matter of course.

The example's t variable is pointless though, right? OR... I've just realized... I suppose it represents the equivalent of the epoch in the case of my in-game time?

Therefore... when I implement some kind of time and date classes/systems and use an epoch to count real-life seconds (in-game minutes), THAT is actually my "t", if I understand it now.

I've updated the loop to use integers. Here it is in case there are any huge glaring problems:


int dt = 40 * 1000;

LONGLONG currentTime = getQPC();
int accumulator = 0;

while( Engine::instance()->isRunning() )
{
	//Get ticks
	LONGLONG newTime = getQPC();

	//Get time between updates in MICROseconds
	int frameTime = int(( newTime - currentTime )/(getQPFreq()/1000000.0));

	//If more than 0.25 seconds between updates
	if( frameTime > 250000 )
	{
		//Cap to 0.25 seconds
		frameTime = 250000;
	}
	//Set currentTime for next frame
    currentTime = newTime;

	//Add frameTime seconds to accumulator
    accumulator += frameTime;

	//While
    while ( accumulator >= dt )
    {
	Engine::instance()->handleEvents();
	Engine::instance()->update( dt );

        accumulator -= dt;
    }

	//Calculate alpha interpolation value
	const double alpha = ( double ) accumulator / ( double ) dt;

	//Render
	Engine::instance()->render( alpha );
}

I'm specifically not 100% confident about casting frameTime to an int... though things seem to be working fine.

This topic is closed to new replies.

Advertisement