How to do very accurate time measurement
The game I am making aims for measuring human reflexes. For this purpose I must use very accurate counters and timers (accuracy of 1ms).
I have some questions about the best way to write this code.
A stopwatch (Stopwatch class) starts when a button is pressed. After some time, the user must press another button as fast as possible. When this happens, the stopwatch stops and sends the time to a database. The signal to stop the timer is given by an onclick event in the Button component. When in the pipeline (Awake- Start- FixedUpdate- Update.....) does unity read out if this button is pressed? Or how does the 'onclick' work exactly, is this really accurate?
When the player doesn't press the button for 250 ms, the button changes color. I can't get this accurate. At the moment I use a coroutine with "Yield return new WaitForSeconds(.250f). I checked the accuracy of this with a Stopwatch and found out that the next line of code was executed after about 257ms instead of 250. I know the cause of this problem is that every coroutine is only executed once every frame. Is there a better method than using coroutines? Somebody told me to use event handlers and threads but I'm not sure this will solve it...
Answer by phxvyper · Mar 31, 2016 at 10:09 PM
Unfortunately, in Unity, deltaTime is dependent upon your frame rate. You can define a preferred frame rate of 1000 or higher (1 frame every 1 ms) that way you can get the accuracy you need, but this doesnt guarantee that you'll get a constant frame rate of 1000 or more.
See Application.targetFrameRate for more information. If you want it to render as fast as it possibly can, then set this value to -1. If you don't have much to render or process, and your cpu and graphics card are average or better, then you should easily get 1000+ FPS. Otherwise, you're at a loss.
UPDATE:
WillNode's suggestion to use FixedUpdate unfortunately comes to the same flaws the Update can. FixedUpdate doesn't always necessarily update every 50 ms, or every 1 ms if you set it to that in your Time Manager.
As suggested by Eric5h5 in his reply to this question, you should stray away from Update and FixedUpdate for ms-perfect timing, and instead use InvokeRepeating.
Answer by WillNode · Apr 01, 2016 at 12:28 AM
For these situations is better to use FixedUpdate instead, as it can do and be called multiple times more accurate than limited-to-devices regular Update. Fixed Update called on every 50 ms, you can change that to 1 ms in Time Manager (but seriously, 1 ms? i did better to set as minimum as 5 ms, since 1 ms is too fast for mobiles).
Coroutines do call and resume in regular Update calls. Luckily, there's a WaitForFixedUpdate which resumes after a FixedUpdate call so: don't use WaitForSeconds, use WaitForFixedUpdate instead.
And for Inputs like Button Click, etc, Happens internally on regular update. you can't do anything with that, so to keep the user convenience, it's better to substract the time as long as last Update be called (deltaTime):
float time = 0;
IEnumerator fixedWait()
{
//Since this called first at update loop, call Time.time
time = Time.time;
//Fixed time after it
while (time.fixedTime < time + .250f)
{
yield return WaitForFixedUpdate(); //Resumed as soon as FixedUpdate call.
}
Debug.LogWarning("Timeout:" + (Time.fixedTime - time).ToString());
//Timeout, Do anything...
}
//User press the button
public void CatchClick()
{
StopAllCoroutine();
//Unfornatunely, buttons do call on regular update...
//So give the user some tolerance as long as deltaTime
//or half of deltaTime, third, is up to you...
float finalTime = Time.time - time - Time.deltaTime;
Debug.Log(finalTime)
}
//User Start the count
public void StartCount()
{
//StartCoroutine will call at regular update...
StartCoroutine(fixedWait())
}
PS : see how it's ordered : http://docs.unity3d.com/Manual/ExecutionOrder.html
Answer by eppz · Mar 09, 2017 at 08:56 PM
I would definitely don't rely on any runloop (Update, nor FixedUpdate). Do not poll.
On iOS, every touch have a timestamp. Which is extremely accurate. See https://developer.apple.com/reference/uikit/uitouch/1618144-timestamp
Write a native plugin that bridges that into Unity. And just compare those values.
Your answer
Follow this Question
Related Questions
Need coroutine to render a texture value clamped from 0 to 1 over time 1 Answer
Best way to get code to run at very precise timing? 0 Answers
My code is not working - HELP - IEnumerator Coroutines 0 Answers
Issues with video player coroutine 2 Answers
Coroutine, Timer Local vs Class variable difference 0 Answers