- Home /
How to get precise timer
Using the function WaitForSeconds doesn't seem to produce exact results. Especially when waiting for milliseconds instead of seconds. Is there some other way to let a function wait a specific time?
Example:
var waitingtime : float = 0.1;
var i : int;
@HideInInspector()
var time1 : float = 0;
@HideInInspector()
var time2 :float = 0;
@HideInInspector()
var deltatime : float = 0;
function Start(){
for (i = 1; i < 16; i++){
time1 = Time.time;
yield WaitForSeconds (waitingtime);
time2 = Time.time;
deltatime = time2-time1;
Debug.Log("Attempt No: " +i +" ;Waited time: " +deltatime +"s - it should be: " +waitingtime +"s.");
yield WaitForSeconds(1); //Pause until next round - to give debugger a bit refresh-time.
} //end for i
} //endfunc Start
This script, attached to some object produces an output similar to this, after hitting the Start-Button in Unity:
Attempt No: 01 ;Waited time: 0.3533334s - it should be: 0.1s.
Attempt No: 02 ;Waited time: 0.1527811s - it should be: 0.1s.
Attempt No: 03 ;Waited time: 0.1974149s - it should be: 0.1s.
Attempt No: 04 ;Waited time: 0.1044042s - it should be: 0.1s.
Attempt No: 05 ;Waited time: 0.1999474s - it should be: 0.1s.
Attempt No: 06 ;Waited time: 0.2078395s - it should be: 0.1s.
Attempt No: 07 ;Waited time: 0.1046262s - it should be: 0.1s.
Attempt No: 08 ;Waited time: 0.1062851s - it should be: 0.1s.
Attempt No: 09 ;Waited time: 0.1296844s - it should be: 0.1s.
Attempt No: 10 ;Waited time: 0.1247978s - it should be: 0.1s.
Attempt No: 11 ;Waited time: 0.1004639s - it should be: 0.1s.
Attempt No: 12 ;Waited time: 0.1252518s - it should be: 0.1s.
Attempt No: 13 ;Waited time: 0.1074142s - it should be: 0.1s.
Attempt No: 14 ;Waited time: 0.1060486s - it should be: 0.1s.
Attempt No: 15 ;Waited time: 0.217804s - it should be: 0.1s.
It's easy to see that it rarely pauses for the desired time (4 times out of 15 attempts).
Depending on exactly what you're trying to do, you might want to try threading.
I want a function to pause for an exact amount of time.
Edit: Exact means at least two digits after the second-value, e.g: 0.25s
Edit 2: As far as I understood the documentation, WaitForSeconds is a co-routine, running in parallel to the script while it waits for this co-routine to finish. I guess this way the developers avoided the game to freeze for the waiting-time. The script pauses but the game doesn't. Therefore WaitForSeconds also should be framerate-independent. The only thing that matters is the delay between the end of the co-routine and the next frame to continue the script. This is because the function Update gets called once per frame.
Example: Assu$$anonymous$$g Update is called 24 times per second (is this value correct?), we have 1s/24 = 0.04166666s = 41.6666 ms per frame.
This is the maximum additional time WaitForSeconds can get because of this timegap (error-tolerance). So the worst case is this: The co-routine ends exactly when a frame just got loaded and the function needs another frame to notice that. The measured time then is waitingtime + 0.041 seconds.
However, as seen in the example above, this is barely happening. The total time is a lot bigger. In some tests it also was too small. $$anonymous$$eaning, the function is crap! I need something else.
Answer by spacepilot · Jan 09, 2012 at 01:15 PM
I tried writing a custom script but I can't use it with the yield-command:
function WaitForMilliSeconds(ms : float){
var timecapture1 : float = 0;
var timecapture2 : float = 0;
ms = ms / 1000;
timecapture1 = Time.time;
timecapture2 = timecapture1;
while (timecapture2-timecapture1 < ms){ // = Until [difference] >= ms
timecapture2 = Time.time;
}//went
}//Endfunc WaitForMilliSeconds
So when I call this function with yield now:
yield StartCoroutine(WaitForMilliSeconds(100));
The compiler says:
BCE0023: No appropriate version of 'UnityEngine.MonoBehaviour.StartCoroutine' for the argument list '(void)' was found.
What am I doing wrong? Isn't it what yield and Coroutine is for? The script shall pause until the function finshes (without slowing down the whole game).
Your function WaitFor$$anonymous$$illiSeconds should have a yield to become a coroutine and be able to be called that way - but it would not work better than WaitForSeconds, because coroutines run in the update cycles, which are synced to the frame rendering process (very unpredictable, for sure).
Rumor has it the InvokeRepeating is the most precise ti$$anonymous$$g tool you have in Unity (http://unity3d.com/support/documentation/ScriptReference/$$anonymous$$onoBehaviour.InvokeRepeating.html)
This looks interesting! To avoid confusions directly at the beginning: The documentation says the command accepts a method. I'm new to object-oriented world, so please forgive my silly question: This method seems to be a function in the example. Can this function have a return-value then?
Refer previous thread about coroutines: No, it can't, and for all the same reasons (initiating function can't wait for return values, etc. etc.).
I don't know if the function may return a value, but for sure you won't be able to use it because the system calls the function, not you. If you need it to return any value, store it in a member variable (variable defined outside any function).
This was helpful. But how do I finally get a timer out of it? When changing my function into a co-routine with a lower 42ms-limit as you said, I have the advantage that it will run asynchronous without game-influence. The function could output a value into some member-variable at any time so that the main-script also can react on it whenever it wants.
Question: How do I pause the asynchronous co-routine for 10ms? Or in other words: How do I stop the InvokeRepeating-command afterwards? The example in the documentation shows how projectiles get fired. There must be a way to also stop firing. $$anonymous$$g. when the magazine is empty.
Answer by BTables · May 23, 2014 at 01:39 AM
I think you are misunderstanding what coroutines do, Unity doesn't multithread coroutines at all which is causing the inconsistent yield results. (Didn't you ever wonder why everything is magically threadsafe?)
Unity does its normal update loop of:
FixedUpdate()
Call coroutine that has yielded WaitForFixedUpdate until it yields again
Update()
Call all coroutines that have yielded for atleast long enough except WaitForFixedUpdate until it yields again
LateUpdate()
RenderStuff()
When your coroutine is called, nothing else is done until you yield, trying to use a busy wait in a coroutine will cause no more frames to be rendered until you yield again which is a very bad idea.
I haven't delved into real threading in Unity yet, but this is much more hardcore stuff that unity won't hold your hand through like coroutines. Which opens up a can of worms around thread safety and deadlocks which aren't to be taken lightly.
Here are the docs on when coroutines are called:
yield; The coroutine will continue after all Update functions have been called on the next frame.
yield WaitForSeconds(2); Continue after a specified time delay, after all Update functions have been called for the frame
yield WaitForFixedUpdate(); Continue after all FixedUpdate has been called on all scripts
yield WWW Continue after a WWW download has completed.
yield StartCoroutine(MyFunc); Chains the coroutine, and will wait for the MyFunc coroutine to complete first.
Answer by Tarlius · May 23, 2014 at 06:23 AM
Waiting for an EXACT amount of time is impossible. (shcipwa's answer gets this quite well). Although even with threads you'll still be a little out (analogue vs digital). The real question is what are you trying to achieve? The correct approach will probably be see how far "over" you went and deal with it somehow.
For example, if you are writing the 0.1 part of a clock (yes, I actually came across a clock written using logic like this! A 60s game took about 70s, varying wildly depending on the platform >.>; ), you need to carry over the missed part (or use (int)(Time.time % 0.1) or something)
Also, to get such varying results, you might have other performance problems... Are you doing some heavy calculations in this waiting time? To wait 0.31s on a 0.1s wait you are running at less than 5fps...
If you are doing physics calculations you might be able to used FixedUpdate(). The deltaTime in fixed update is fixed (but its also fake! But then so are most of the time functions...)
So back to the question: You can't. You'll have to fake it. What are you trying to do?
Answer by chernobyyl · Sep 22, 2020 at 02:52 PM
I know im 6 years to late and nobody will probably answer but would it be possible to take the time from an online timer with an api?