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…

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.

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 : Unity - Manual: Order of execution for event functions

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 timestamp | Apple Developer Documentation

Write a native plugin that bridges that into Unity. And just compare those values.