Jump to content
  • Advertisement
Sign in to follow this  
JonBonazza

Time scale for slow motion effects and game pausing

This topic is 2609 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

After finally achieving frame-rate independance (See this thread: http://www.gamedev.net/topic/613049-deltatime-issues/page__gopid__4874823#entry4874823), I am now after the implementation of a time scale.
Basically, I have a data member of my Time class that is always between 0.0f, and 1.0f. When this value is 1.0f, frame updates should behave normally, however, if the time scale is 0.0f, no frame updates should occur. Anything in between should scale the number of updates approriately. My problem is deciding what the best variable to scale by would be. I have the following update function:



public void onDrawFrame(GL10 gl)
{
float newTime = System.nanoTime() / 1000000000.0f;
Time.deltaTime = frameTime = newTime - currentTime;
//currentTime = newTime;
if(frameTime > Time.maxDeltaTime)
frameTime = Time.maxDeltaTime;

currentTime = newTime;

accumulator += frameTime;
while(accumulator > Time.fixedDeltaTime)
{
//Perform Fixed Updates here
accumulator -= Time.fixedDeltaTime;
}
Log.e("IMPACT","" + Time.deltaTime);


go.transform.position.x += 10 * Time.deltaTime;///1000000000.0f;
go.update();


++Time.frameCount;

}


I am assuming that I should just multiply Time.deltaTime by timeScale in order to scale it appropriately, however that only takes care of the variable frame update, not the fixed frame update (Physics calculations).

Should I also multiply the frameTime variable by timeScale or is there something better?

Share this post


Link to post
Share on other sites
Advertisement
Depends on how sensitive your fixed-tick simulation is to resolution shifts... if you drop the timestep too much in a typical heavy-duty physics simulation, you start getting integration errors. This is clearly undesirable.

A decent middle ground is to discretize your time-scaling factor, so instead of being an arbitrary floating point ratio, it's a number of fixed-tick steps to run per "wall clock second." If you normally run, say, 30 fixed ticks per second, then your time scale is an integer between 0 and 30 inclusive. Then your variable timestep code is multiplied by (time scale integer / 30) to get the corresponding speed adjustment.

This way you preserve all the accuracy of your physics simulation, without compromising the ability to shift time scales. It's unlikely that you'd need more than 30 distinct levels of time adjustment anyways, so unless you're specifically writing a time-warp game (in which case you need custom physics anyways to do it right) you should be good :-)

Share this post


Link to post
Share on other sites
Hmm...
Ok, I think I understand, but to be sure, is this what you mea?



public void onDrawFrame(GL10 gl)
{
float newTime = System.nanoTime() / 1000000000.0f;
Time.deltaTime = frameTime = (newTime - currentTime) * Time.timeScale / 30;
//currentTime = newTime;
if(frameTime > Time.maxDeltaTime)
frameTime = Time.maxDeltaTime;

currentTime = newTime;

accumulator += frameTime;
while(accumulator > Time.fixedDeltaTime)
{
//Perform Fixed Updates here
accumulator -= Time.fixedDeltaTime;
}
Log.e("IMPACT","" + Time.deltaTime);


go.transform.position.x += 10 * Time.deltaTime;///1000000000.0f;
go.update();


++Time.frameCount;

}


Where Time. fixedDeltaTime = 0.333f
and Time.timescale = 30 (Initially, anyway. it can obviously be changed).

Share this post


Link to post
Share on other sites
Your timer class (System apparently) should be keeping track of:
#1: The current time.
#2: The virtual current time.
#3: The time since the last update.
#4: The virtual time since the last update.

You generally accumulate time by using the time since the last update. It is actually rare to ever use the current time counters.
If you have an object manager, it would have a Tick() or Update() function which accepts the amount of time by which to update it. The time since the last update.
As a manager, it has the duty of updating all of the things in your scene that need to be updated. This is just a rough draft of a design but you should have something that encapsulates all of your objects and allows them to be updated with a single call so that you don’t have to do it yourself.

If you want to slow down the game or speed it up, simply scale the value you pass to that manager.
As mentioned, it is rarely useful to know how much time has actually passed (and as you have discovered it is extremely non-trivial to derive the actual current time by accounting for all of the changes in time speed that have occurred).
Instead, the manager will just accumulate all of the “time since last update”’s that you pass it, and within the scope of the game world that is the “current time”.


L. Spiro

Share this post


Link to post
Share on other sites
Close to the beginning of each frame I fetch the system time and compute the difference to the previous fetch. This delta is feeded into the "timebase" where (if the game isn't paused) the delta is scaled and added to the internal time variable. This value defines the virtual time as the "tick of the current frame", available as absolute value and also as difference from the previous step. All simulation is run from this virtual time. As I understand it, it is very close to what YogurtEmperor has written above (except that my timebase doesn't drive the update as YogurtEmperor's manager does).

Share this post


Link to post
Share on other sites

Your timer class (System apparently) should be keeping track of:
#1: The current time.
#2: The virtual current time.
#3: The time since the last update.
#4: The virtual time since the last update.

You generally accumulate time by using the time since the last update. It is actually rare to ever use the current time counters.
If you have an object manager, it would have a Tick() or Update() function which accepts the amount of time by which to update it. The time since the last update.
As a manager, it has the duty of updating all of the things in your scene that need to be updated. This is just a rough draft of a design but you should have something that encapsulates all of your objects and allows them to be updated with a single call so that you don’t have to do it yourself.

If you want to slow down the game or speed it up, simply scale the value you pass to that manager.
As mentioned, it is rarely useful to know how much time has actually passed (and as you have discovered it is extremely non-trivial to derive the actual current time by accounting for all of the changes in time speed that have occurred).
Instead, the manager will just accumulate all of the “time since last update”’s that you pass it, and within the scope of the game world that is the “current time”.


L. Spiro


the System class is a part of the JDK that I have no control over. I am only using it to retrieve the elapsed time in nanoseconds. My Time class is is, I think, what you are referring to as a timer class.

I have my Timer class laid out like so:



public class Time
{
public static float deltaTime = 0; //time it took to complete last frame
public static long frameCount = 0; // Number of rendered frames
public static float fixedTime = 0; //The time that the last fixed update was called
public static float fixedDeltaTime = 0.333f; //The interval, in seconds, at which fixed updates are performed
public static float maxDeltaTime = 0.25f; //The maximum time that a frame can take for fixed updates
public static float timeScale = 30.0f; //The scale at which time is passing.
}


I realize that static data members are generally bad practice (unless absolutely necessary), but because I am working with so little resources and clock speeds (Android OS), I decided against the typical private datamember/acessor and mutator method approach.

At any rate, I understand your suggestion about directly embedding the delta time as a parameter for my object manager's update method, however because I am creating a scripted component-based system, I felt it was better to leave the trasnformation scaling to the developer rather than the engine to make things easier for the developers.

With that said, the onDrawFrame() method you see above is simply for testing and does not represent what the actual onDrawFrame method would actually look like. I am just trying to make sure my understanding of the concept is correct before embedding it into the engine. This is the reason for the seemingly randomly placed variable declarations.

Am I correct in my method of scaling delta time in the above example?

Share this post


Link to post
Share on other sites
I would still recommend your time class be instance-based rather than static.
It is helpful to have one instance act as the main timer and one act as the state timer, which tells your state how much time has passed since the beginning of the state (as apposed to the overall game time).
The state can then reset its own timer at-will, so it becomes very handy for situations as follows:
“Okay, move the buttons onto the screen for this period of time. Now reset the timer and play this sequence. Okay the user has touched a button. Reset our timer and flash the buttons for 0.5 seconds. Okay now reset the timer and move the buttons off the screen for 0.5 seconds.”


fixedTime should be integral. Never keep track of total elapsed time using floats.


Why store the scale in FPS? Just store the scale as a scale. Then you don’t have to divide every frame, which is a costly operation.
If you want to provide an interface for the user to specify the game speed in FPS, you can easily provide a function that allows setting the game speed in FPS, and perform the divide inside that function only once, instead of every frame.


Which example?
If you mean the code example (not the Time class code snippet) then I would say No.
As mentioned, accumulate times, don’t derive times.


Also it was mentioned above to update your timers at the start of the frame, and use those cached values for the remainder of the frame.
I don’t know how System.nanoTime() works, but you probably can’t use it directly.
Call that function only once per frame and save the result. Use your copy of the result for the rest of the calculations you make that frame.
This is a very important point. You don’t want to update some objects by 0.166 seconds and other objects by 0.167 seconds.


L. Spiro

Share this post


Link to post
Share on other sites
1) I feel embarrassed asking this, however I have never had any formal training with integrals, so I am completely unsure of how to convert my fixedTime variable to an integral. as for it being float, I can see where this could be a bad thing and have changed it accordingly.

2) For the timeScale, you are saying that I should go back to original method of scaling, by using a value of 0.0 to 1.0?

3) As for the multiple calls of System.nanoTime(), you are correct. In fact, that was a mistake left over from previous "playing around". I have fixed that just now.

Share this post


Link to post
Share on other sites
Just store the accumulated time in microseconds using a 64-bit integer.
Seconds to micros = (SECONDS * 1000000.0f)
Micros to seconds = (MICROS * (1.0f / 1000000.0f))


L. Spiro

Share this post


Link to post
Share on other sites

Just store the accumulated time in microseconds using a 64-bit integer.
Seconds to micros = (SECONDS * 1000000.0f)
Micros to seconds = (MICROS * (1.0f / 1000000.0f))


L. Spiro


And what about for the scaling?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!