0
\$\begingroup\$

In my Android app I have the requirement to introduce small (for slowing down animation) and larger delays (for the purpose of delaying the appearance of a character for example).

I've experimented with 2 different ways, those being:

long grabTime;
int delayInMillis;

public void doStuff(){

//Initialise timeGrab variable
    if (grabTime==0)
        grabTime = System.currentTimeMillis();

//Has the desired number of Milliseconds passed?
    if (System.currentTimeMillis()>timeGrab+delayInMillis)

//If so, then do something here

//And re-grab the time ready for the next time
    grabTime = System.currentTimeMillis();

}

Where I have a batch of sprites (where each one will have a different delay) I find it easier to do this

long grabTime;
int spritesDelay = new int[numberOfSprites];
int forLoopCounter;

//Populate the array with desired delays (in seconds)
spritesDealay[0]=1;
spritesDealay[1]=2;
spritesDealay[2]=3;
spritesDealay[3]=4;


public void doStuff(){

    //Initialise timeGrab variable
    if (grabTime==0)
        grabTime = System.currentTimeMillis();

    //Has 1 second second passed?
    if (System.currentTimeMillis()>(timeGrab+1000)){

    //If so, reduce delay counters by 1

    for (forLoopCounter = 0;forLoopCounter<spritesDelay.length;forLoopCounter++){

        spritesDelay[forLoopCounter]--;
    }

    //And re-grab the time ready for the next 1 second count
        grabTime = System.currentTimeMillis();
    }

    //Now run through all the sprites and check to see if any of the timers have expired

    for (forLoopCounter = 0;forLoopCounter<numberOfSprites;forLoopCounter++){

        if (spriteDelay[forLoopCounter]<=0){

    //Do stuff here

    //If action has completed then reset the delay

    if (*Some criteria has been met*){

    spriteDelay[forLoopCounter]=4; //put in either explicit value for all sprites or restore previously backed up spriteDelay array (Not shown here)


            }

        }


    }
}

In my app (a game) - I am using the second method to have some sprites moving in an up/down motion, each successive one with a slight delay on the previous one (think of a wave like motion).

The problem

This does work, however, when I pause my game (which simply halts game updates and continues rendering with a 'paused' graphic over the screen), on resuming play, things are out of sync slightly (because obviously the system time doesn't stop when the game is paused, so the first check after un-pausing to see if the second has elapsed will always be true providing the game was paused for longer than 1 second).

After the sprites have all moved through one motion, then they do sync up again and everything is OK.

However I'm not happy with the remainder of the current cycle being out of sync.

Alternatives?

Another way I've thought of doing this is hold an int that counts up each time my game update method is run (my game is 'limited' to 60 logic updates per second), then when it reaches 60, I would know a second has passed. This has the advantage that the counting will stop when the game is paused.

However, if the game were ever to be run on a device that couldn't handle 60 logic updates per second then it would slow down counting and throw everything off. (My ticks per seconds are separated from my rendering per second which runs flat out so although the FPS may drop, the logic updates per second should carry on at 60. I find it unlikely that a device wouldn't be able to handle 60 game updates per second (as I've tested this on a Galaxy Ace version 1 and that was fine at 60) but I'd like my app to be as robust as possible and don't really want to risk the timings being thrown off by a slow device however unlikely.

So I'm a bit stuck. Is there another system timer I can use which can be controlled? (switched off when required) or would my 'logic-updates-per-second' method be the best bet?

\$\endgroup\$
1
\$\begingroup\$

Try keeping track of elapsed time instead of having a target time to accomplish something. This way, when the game is paused you can capture the time it was paused and unpaused so you know how much elapsed time to ignore.

If I got my math right this might look something like the code below:

//Normal gameplay update
currentTime = System.currentTimeMillis();
elapsedTime += currentTime - previousTime;

if (elapsedTime > 1000) 
{
    //dostuff
    elapsedTime = 0; //reset so we can do it again
}

previousTime = currentTime;

//On Pause
pauseTime = System.currentTimeMillis(); 

//On Unpause
pauseDuration = System.currentTimeMillis() - pauseTime;
previousTime = previousTime + pauseDuration;
\$\endgroup\$
  • \$\begingroup\$ Hi @ToddersLegrande, please see my update - cheers :-) \$\endgroup\$ – BungleBonce Feb 26 '14 at 16:26
  • \$\begingroup\$ If you have a follow-up question, please post it as a separate question; don't edit it into your original question (this isn't a discussion forum). See the help center for more information. \$\endgroup\$ – Josh Feb 26 '14 at 16:31
  • \$\begingroup\$ @JoshPetrie, it isn't a follow up question. How is it a follow up question? I tested the suggestion and it didn't work so I edited it into the existing question? Please elaborate. \$\endgroup\$ – BungleBonce Feb 26 '14 at 16:37
  • \$\begingroup\$ You added 1700+ characters of new information that invalidates existing answers (and you did it at the top of the question). That turns the post into a discussion over time, making it impossible for users to supply reasonable answers. That sort of discussion is not what this site is for. It is for asking specific questions and getting specific answers. If you need to amend your question so much that it would invalidate answers that usually means you need to post a new question. Alternatively, the help center has a list of discussion forums you could try. \$\endgroup\$ – Josh Feb 26 '14 at 16:44
  • \$\begingroup\$ Fair enough, I don't mind submitting a new question but I carried out more testing in response to an answer that was given. The question is essentially the same - how do I introduce delays. Thanks for the info though, I didn't realise I had to submit a new question each time I carried out testing! I will bear in mind! Cheers \$\endgroup\$ – BungleBonce Feb 26 '14 at 16:49
0
\$\begingroup\$

I do not like the vague language used to describe this particular part of Java. It appears to be layered on top of an operating system provided method for acquiring system time (e.g. time relative to January 1st 1970), and thus the granularity may be as poor as 1 second. But my actual concern is that there is no guarantee of monotonic return value - that is, it may produce a paradoxical situation where the returned value in a later call actually indicates a time earlier than the first call. This will happen if it reads the "wall clock" and the user or a system service (e.g. periodic internet time server sync.) decides to change the time.

I would strongly suggest that you consider using nanoTime (...) instead. While this also makes no guarantee that the returned value will be monotonic, it will give better granularity than currentTimeMillis (...). And since this timer is not based on the wall clock, sources of non-monotonic returned values are fewer. There is still the possibility that this timer could be implemented using something stupid like a CPU rdtsc instruction (which varies depending on which CPU/core the executing thread is scheduled on), but most platforms provide high-precision monotonic timers these days and Oracle is probably smart enough to use them instead.

\$\endgroup\$
  • \$\begingroup\$ its dalvik and not oracle.. \$\endgroup\$ – v.oddou Feb 27 '14 at 2:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.