In our current project, a large part of the functionality relies on a timer. When it boils down to it, the accuracy of the timer isn’t hugely important so long as it’s consistent, but I figured the most accurate method is likely to also be the most consistent.
There are three basic ways to implement a timer in Unity:
I’ve seen quite a bit of discussion about these different methods, but there appears to be some disagreement about what’s best and why. None of the discussions I’ve found have included the results of any tests to compare them or demonstrate accuracy or efficiency.
I’ve been avoiding Update()
where practical – using Coroutines or Invoke
instead if something doesn’t need to be checked or done for every frame of an object’s life. But with two other methods available, I didn’t know whether a Coroutine with WaitForSeconds
or an Invoke
is a better way to create a simple delay. It doesn’t really matter too much for one off events, but for repeating events, or if you have lots of them, delays can add up fairly quickly.
Here is the basic code I used to test each method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
public Text deltaTimeText; public Text invokeText; public Text updateText; public Text coroutText; public float increment = 1; private bool timerStarted; // Use this for initialization void Start() { timerStarted = true; InvokeRepeating("InvokeTimer", 0, increment); StartCoroutine(CoTimer()); } void Update () { deltaTimeText.text = Time.time.ToString("0.0"); // Update Method if (timerStarted) { updateTime += Time.deltaTime; updateText.text = (updateTime).ToString("0.0"); } } // Invoke Method void InvokeTimer() { invokeText.text = invokeTime.ToString("0.0"); invokeTime = invokeTime + increment; } // Coroutine Method IEnumerator CoTimer() { coroutTime = increment; WaitForSeconds waitTime = new WaitForSeconds(increment); while (timerStarted) { yield return waitTime; coroutText.text = coroutTime.ToString("0.0"); coroutTime = coroutTime + increment; } } |
- The
Update()
method addsdeltaTime
(the time it took to complete the last frame) to its counter, every frame. - The Invoke method is called from
Start()
, and adds the increment to its counter each time it’s called – once per increment. - The coroutine is started from
Start()
, and then usesWaitForSeconds
to pause for the increment time before adding the increment to its counter, and then repeating.
One difference to most Coroutine examples is that this one creates a WaitForSeconds
instance before starting the Coroutine timer. Most examples create a new WaitForSeconds
every increment, but it’s better to create something once and reuse it than to create and throw away over and over. There’s enough garbage being created with all those strings without adding more.
The Results
So what is the most accurate timer in Unity?
Well, it’s immediately obvious that the Coroutine isn’t remotely accurate. At one second increments, it only takes a few seconds for it to be noticeably lagging, losing 8-10 milliseconds per update, or around 1/2 a second per minute. More frequent increments cause it to lose time more quickly, as you’d expect.
The Update method behaves differently for Unity 5.3.5 and 5.4, but is much more accurate than the Coroutine. In 5.3.5, the Update version falls behind gradually, losing 1 second after around 70 minutes. In 5.4 the Update version keeps up for about an hour, but after that it can either gain or lose time, ending up a second or more behind or ahead of the actual time.
The Invoke method stays synced with the time for over an hour, and after longer periods has consistently been closer to the correct time than the Update method.
Faster increments effect the results. An increment of 1/10 of a second (or 100 milliseconds) causes the Coroutine to fall behind more quickly, and the Invoke becomes slightly less accurate, but still often more accurate than Update.
Another thing to be aware of is that losing and regaining focus really messes with the timers. I was getting wildly varied results until I realized that each time Unity lost focus, the timers would go out of whack – most obviously the Update method.
Verdict
For slower increments, anything over 100 milliseconds, Invoke definitely comes out the winner. It’s consistently as or more accurate than the Update method without needing to do calculations every frame.
For faster increments, under 100 milliseconds, Invoke becomes slightly less accurate, but we also start to lose the benefit of invoking over calculating every frame anyway. The closer the increment gets to the frame rate, the more benefit there is to using the Update method, but given how slight the difference is, I would still likely choose Invoke for anything slower than the average frame rate.
Coroutines definitely have their uses, but the overhead is too great for a timer like this unless it’s combined with other functionality that needs to occur over multiple frames.
Now I just need to test in my app with everything else going on to see if that makes a difference…
Finally an explanation clear about something that Is not clearifyed ever.
A possibile topic to add could be the resource usage of these three methods
Timing based on a known starting time, will ultimately yield the most accurate result down to within 1 frame, since it’s not an independent timer. It’ll also remove the need of summing up deltaTime.