2
\$\begingroup\$

Please see my original question here:

Using System Time to compute delay times that correctly deal with app pauses?

In summary, I am attempting to to introduce a delay (say of 5 seconds or so) before a sprite takes a particular action (say a jump) - but this needs to be preserved when the user pauses the game or the system kills the app and it is re-launched. I tried to carry out the suggestion in the answer to my previous question (to save the time on app pause and then compare it to current time when resumed to see how long the app was inactive for, then to add this to the time that I check against, but this comes with it's own problems and I'm still not sure of the best approach).

  • When the app is re-started following the system trashing it to reclaim resources, the time (now correctly adjusted to include time the app was sitting in the background). I reset & compare the time when restoring the bundle. However, it then takes 4 or 5 seconds to re-load everything (this is an OpenGL app, so resources are loaded in onSurfaceCreated which comes after the Bundle is restored) so by the time the game starts, vital seconds have already passed (so, lets say when resuming there should be 4 seconds remaining before a sprite appears, it will still appear instantly).

  • As mentioned, when the app goes into the background, I'm storing the time and then comparing it to the time when the bundle is restored to get the 'app not active' time - however, what happens in cases when the user (or system) sends the app into the background but the app stays active, and is then re-launched? the Bundle is then never restored, so I can't grab the restore time to do the comparisons.

So how best to deal with sprite delays which won't break when the app is in the background and not running? Basically I seem to need a way to have a timer that can be stopped when the app is paused or not active. (The countdown method I described in the original question seems to be the closest thing, ie, just keep a value of say 100 and decrease it each time a game-tic occurs, this would be a lot easier and tidier, however, it would also mean if the game were to ever run slower than 60 ticks per seconds, the delays would be longer), but is there someway I could use 'real' time rather than something anchored to my tics per second that wont' carry on merrily when the game isn't running?


Code for delay based on System time (this is in my game logic update)

//Timer is initially set to 0, so grab time to start timer
if (timer == 0)
timer = System.currentTimeMillis();

//Delay completed? (5 Seconds)
if (System.currentTimeMillis()>Timer+5000){
//Reset for next delay
levelTimer = System.currentTimeMillis();
//Carry out required action here (example, jump or more sprite)
}

Code to reset and re-adjust the timer (currently in onRestoreInstanceState)

timer+=(System.currentTimeMillis()-pauseDuration);
\$\endgroup\$
1
  • \$\begingroup\$ Do you have hooks into all the events you want? (That is, can you register your application for a notification that "app is going into the background", "app is being killed to reclaim memory," "app has finished loading resources"? If you do, that makes this problem much easier to solve. \$\endgroup\$
    – user1430
    Feb 26, 2014 at 17:10

2 Answers 2

1
\$\begingroup\$

You can always implement a clock by yourself which is updated together with the real time clock. If you implement your own clock, you can even scale it for a slow-motion effect, or speed it up, as seen in many tower defense games.

Update: Okay, you may want to consider using a set of 3 variables to keep track of time:

  • current time (now)
  • previous time (prev)
  • elapsed time (delta)

Updating those variables looks like this:

prev = now;
now = System.currentTimeMillis();
delta = now - prev;

You manage those values for the real time clock. For your game clock, you also need a scale factor to speed up or slow down animations (real time scale = 1). The game clock update depends on the real time clock. You can then define various operations on your game clock, such as start(), stop(), setScale(double), etc. However, there are a few things to be aware of:

  • Your updates should depend on the game clocks elapsed time (dynamic time step).
  • You need an accumulator for your game clock to keep track of real elapsed time which is below 1 unit of elapsed game time.

Additionally, you should use a timer abstraction to schedule events such as your delay. All timers are updated using the game clock, therefore pausing/deactivating the game will pause the timers.

\$\endgroup\$
2
  • \$\begingroup\$ Hi @Matthias, thanks for this. I've added some code to show how I achieve the re-adjustment and the delay. I'm not sure how I could slip the re-adjustment code into the game loop as you suggest. It would surely keep adding to the timer on each loop? I also like your suggestion of implementing my own timer because it means I could simply stop the timer when the game stops. Could you please elaborate on both methods maybe with some code examples? Thanks, much appreciate! \$\endgroup\$ Feb 26, 2014 at 17:57
  • \$\begingroup\$ I will elaborate on this tomorrow. \$\endgroup\$
    – Matthias
    Feb 26, 2014 at 22:19
0
\$\begingroup\$

I find it easiest to keep a delta time per frame updated in the onDrawFrame() of my surface view's renderer. This way it always reflects only the time that has elapsed since my last draw call, and I can accumulate this value as needed for delays and timing.

The following is a quick example of how I usually implement this:

public class GameRenderer implements GLSurfaceView.Renderer {
    public final static float NANO_SCALE = 1000000000.0f;
    public final static float DELTA_LIMIT = 0.1f;

    protected long startTime = System.nanoTime();
    protected float deltaTime = DELTA_LIMIT;

    public void onSurfaceCreated(GL10 gl, EGLConfig config)  {
       // do surface creation / re-creation here
       startTime = System.nanoTime();
    }

    public void onDrawFrame(GL10 gl)  {
       // do the following before drawing your frame
       deltaTime = ( System.nanoTime() - startTime ) / NANO_SCALE;
       if ( deltaTime > DELTA_TIME_LIMIT )
          deltaTime = DELTA_TIME_LIMIT;
       startTime = System.nanoTime();

       // draw your frame here and use deltaTime as needed :)
    }
}

This is a very basic idea of what I mean, but the important part is that you update the startTime last in onSurfaceCreated so that it is called after initialization or reloading is completed. Another thing is to ensure that you never get "very large values" so we check the delta time and limit it to some maximum value (1/10th of a second in the example). This prevents slowdowns from causing large time values which could cause chaos for things like physics, collision detection, etc.

Once you have this in place you can use the delta time to calculate active run times, for example:

public class GameRenderer implements GLSurfaceView.Renderer {

   float timeBeforeNextSprite = 0.0f;

   public void onDrawFrame(GL10 gl)  {
      // assuming deltaTime is calculated as in above example

      timeBeforeNextSprite += deltaTime;
      if ( timeBeforeNextSprite >= 5.0f )  {
         // draw your sprite here
         timeBeforeNextSprite = 0.0f;
      }
   }
}

Now of course you may want to make the deltaTime "global" so it can be accessed from anywhere you may need it, but I'll leave the actual implementation details to you.

This is only one way to do it, and you can implement it in a different place than the surface view, but the idea remains the same: restart your timing when the game starts/restarts and calculate delta time each game tick - also, make sure you limit the delta time to a reasonable value to prevent problems.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .