I discovered that Unity Coroutines are reasonably unreliable if you need accurate time-elapse measurements.
For example, when I’ve used WaitForSeconds(float) it doesn’t actually wait for the specified time instead it seems to check every frame whether or not the specified time has elapsed or not. That means there is always a margin of error roughly equal to the time between frames. On low frame rates this seems to be very evident.
I’m considering writing a custom yield where instead of checking for elapsed time it also checks if the elapsed time is closer to the current frame than it would be on the next frame based on the time since the last. eg, if the time passed is 1 second, but it is waiting for 1.0001 seconds, it shouldn’t wait another frame where the time elapsed may be 1.019 which would be less accurate.
I’m wondering if anyone more experienced has a good solution to coroutines. Ideally it can still use the MonoBehaviour API (eg still on the Main thread).
It feels like what you’re trying to accomplish is not technically difficult, but I would seriously question whether its conceptually reasonable. Your game should be tolerant of extremely variable elapsed time between frames. Maybe you can explain exactly what you’re trying to do that you feel requires this kind of precision?
Over the past two years i’ve had quite a few instances. As Kurt-Dekker affirms, it’s not precise.
A good example is if I iterate through a loop and used very short wait intervals the margin of error grows with each iteration. Such as if in each loop I WaitForSeconds(0.05f); and it’s off by 0.01f then if I iterate a hundred times then what should have been 5 seconds could vary upwards to 6 seconds. This fluctuates with FPS of course so it’s also unpredictable. But consider that for a moment, that’s 20% off what it should be. A substantial difference.
I should also point out that 0.05 seconds is not that small, at 60 FPS one frame should be as low as 0.016 seconds.
Essentially I’d like to find a more accurate way to accomplish this. It doesn’t sound like a huge discrepancy but I assure you it is very noticeable at times. I’d be very interested in alternatives to coroutines. What would you have in mind?
The first alternative that comes to mind is that instead of running your method once per loop, potentially run it more than once. This is basically the idea with Unity’s FixedUpdate. If a long time passes between two frames, it will execute FixedUpdate twice (or more) the next frame, to “catch up”.
In other words, if you need something to happen every 0.05 seconds, you shouldn’t simply yield return 0.05, then run your code once. You should keep a running total of times your method has been run since some start time. Then, potentially every frame, you test how many times you code should have run by now, assuming it should run every 0.05 seconds. You might find that the code needs to be called once that frame, or twice, or more, if there was a big lag spike for some reason.
It depends on whether this is on a per-level basis, or longer. If it’s per level, you can just check the value of Time.timeSinceLevelLoad, which resets the 0 whenever you load a scene. In that case, Time.timeSinceLevelLoad tells you how much time has passed. Otherwise, you can use Time.time, or some other flavor or time, to test how much “time” has passed. You’ll need to decide whether you want your code to run when the game is paused, and use the appropriate time based on that.
Than you’d need a counter to keep track of how many times the main method has executed.
I would just store the current Time.timeSinceLevelLoad in a private variable, and take the difference between Time.timeSinceLevelLoad and that variable every time you test whether to run your code or not. This would also mean keeping track of how many times the code has run. Something like this:
private const int TargetExecutionsPerSecond = 20;
private int _executionCount = 0;
void Update() {
var targetNumberOfExecutions = (int)(Time.timeSinceLevelLoad * TargetExecutionsPerSecond);
var numberOfExecutionsToPerform = targetNumberOfExecutions - _executionCount;
for ( var i = 0; i < numberOfExecutionsToPerform; i++) {
MainMethod();
_executionCount++;
}
}
There’s probably a more elegant approach, but that’s pretty simple. The idea is that on every frame you test how many times your MainMethod should have executed by now, and compare that to the number of times it actually has executed by now, and run as many times as it takes to catch up to the target.
One other thing thing: You didn’t state specifically what kind of action you’re performing that you believe needs to be run at this precision. What are you actually using this for? Using an approach similar to FixedUpdate is a fairly uncommon thing. I’m just curious if there’s a much better way to do the thing you’re trying to do, or whether the code you’re trying to run should just be in FixedUpdate or something.
Since code that must interact with Unity can only be ran on the main thread, you’re going to be stuck with this issue of not being precise. It’s just a fact of how the engine behaves that you can interact at it between drawing of frames.
But if you wanted to get something to occur on the frame nearest to your desired time. You could technically do that with a custom yield instruction:
With this and some time source you’d just check if your time is nearing where you like. Time.unscaledTime could work, or Time.realtimeSinceStartup. The problem will come in estimating what the next frame’s deltaTime will be. You could create an Update loop that averages it over the last N frames, or you could just use the current deltaTime as an estimate. None of them will be accurate though since well… that’d be reading the future.
…
The FixedUpdate style behaviour that @dgoyette suggests is also a possibility.
But keep in mind it too isn’t actually accurate.
It reports having occurred at a constant delta time, but that delta is not real world accurate. It’s a simulation. If that fits your needs than it too could be what you could use.
…
Another option is if you’re not actually doing anything that needs to update the game/scene itself (access the Unity engine directly). You could just use a separate thread any any of the C#/.Net timing systems that are built in there that rely on the system time and can be way more accurate (while still not perfect… just a result of how computers and .net behave).
But now you’re relying on hardware level timing which may vary from platform to platform.
Time.realtimeSinceStartup actually relies on the system time and is why it has this warning:
…
Note a trend is coming up in my suggestions.
The approach really depends on what it is you’re attempting to actually do.
Which is why @dgoyette has asked a couple times what it is you’re attempting to accomplish. Not the kind of code you’re trying to write… but the actual goal you have in mind. Is this used for animation? Is this used for user input analysis for some pattern game like ‘guitar hero’? Is this used for some scientific analysis/simulation where accuracy to the nanosecond is paramount?
With that knowledge we could probably better point you in the direction you may want to look.
Thanks guys, I think you’re right it depends on each use case. Some examples from memory:
1. In the past i’ve tried writing my own animators which could read external sprite sheets, in this instance a custom yield may have sufficed but probably a combination of things would have made it better. This was a very different problem because the animations were being used very differently, it was a simulation game.
2. Another time was trying to move, or ‘animate’ something from point A to point B and have it consistently do so in the desired time. Which may come back to checking time and correcting it as it goes. As dgoyette suggested.
3. (current) But right now I’m trying to write a state machine still using unity animations and states but not controller transitions. I’m trying to handle the animation transitions on my own. I know that sounds unusual but we have good reason to try and create our own solution, it could save us a lot of time in the end.
A few problems come up trying to get a coroutine to line up on the exact same frame we intend for that specific animation. Firing events, triggering a transition, that sort of thing. It’s important to line up with the animation or else the game will feel janky. The problem here would actually be syncing with the animation, not entirely a ‘precision’ problem. So long as what we do to time the event or transition lines up with the same way in which unity’s animation does it.
That said, in my experience I’ve continued to run into cases where a regular ol’coroutine doesn’t quite cut it. I’ll try to stay true to the original question of the topic, though. In which the answer seems to be more or less (staying purely coroutines) use custom yields, which honestly seems quite straightforward.
I do have a more generic question in mind which I might be for another topic entirely. That is; what alternatives are there to coroutines and what would those benefits/caveats be.
I do have this one nagging question, and excuse me if it’s just plain ignorant, but is it possible to start a new thread asynchronously for the purpose of making the thread sleep and then once it’s finished call something to run on the main thread? Thus being able to implement a wait outside the main thread and still use MonoBehaviour?
If you need a frame of a sprite sheet to be perfectly timed for simulation purposes. Instead of moving ahead a single frame after N seconds… here you can either do the sort of FixedUpdate-like deal where you have a timer that ticks up at a fixed time and increment your frames that way. Or you store the start time and then subtract that from the current time to determine the current frame you should be one (since these usually go at a fixed frame rate). Something like:
You should be calculating your delta in movement via the Time.deltaTime per frame. This should keep you on time for A->B movement.
I don’t know the specifics of this use case. But if you need something to occur exactly at some moment in the animation, instead of waiting a time, let the Animator signal to you that it has happened. You can put animation events into your clips at the key moments you want things to happen and then react to them in your controller attached along side the Animator.
Custom yield instructions, with some finagling, will keep you on the main thread and with your own custom algorithm choose to pick between the 2 closest frames which you want to fire on, rather than always being the frame after.
You’ll never occur exactly because it has to occur on a frame and not between frames if you plan to do any communication with the Unity API that requires being on the main thread (note, not all communication with the API requires it. You should be checking the documentation for this information. And if it doesn’t exist… well… test it.)
That’s just the nature of the beast, nearest frame only. And thus if you need a simulation you simulate at a fixed rate (may it be on another thread, or simulated like how FixedUpdate works), and then when the frame goes to be updated you sync the visualization to the simulation.
You can use the C# async task engine. It’s not as well documented by Unity themselves but plenty of people on the internet talk about it. It’s technically a .Net feature separate of Unity so you mostly have to rely on that.
Here’s an article found as the first result when I googled “unity async task”:
Note the big thing here is that async tasks use a “synchronization context”. This context will help define the threading model it uses so it behaves correctly with your system. In the case of Unity we use a synchronization context that helps keep it in line with the main thread.
Sure… but whatever you do you need to wait for the next frame[i/] whenever you decide to synchronize back to the main thread. Scripting code on the main thread only runs on the main thread in respect to the ‘execution order’ @Kurt-Dekker mentioned earlier: Unity - Manual: Order of execution for event functions A simple way to do it though is to just have an ‘Update’ loop that you can queue callback delegates into. Heck before we got to newer versions of C# some of us used custom coroutine extensions that allowed us to jump our coroutine onto a custom thread and back to the main thread as a yield instruction. I wrote my own to do it, but the popular one I remember was called… I think… let me remember… Thread Ninja? Yeah, Thread Ninja: Thread Ninja - Multithread Coroutine | Tools | Unity Asset Store Again though, all of these can only get back on the main thread on the next frame. It just can’t be any other way. Which yes also means that async tasks when they spin off on a thread that needs to synchronize back, they too wait for the next available frame. (mind you this “waiting for the next frame” is relative to the thread. The custom thread is off on its own thread by definition. If it just so happens that when it tries to synchronize back… depending what ever technique you use for synchronization it could by happenstance be right in the middle of the current threads execution where the synchronization just happens to be currently happening. But that’s just chance.)
You can only make visible changes the user can see on frame boundaries.
Use the tools available on a per frame basis, but keep track of the actual time elapsed via Time.deltaTime AND DON’T DISCARD THE LEFTOVERS. When you want something to happen after 5 seconds, and your frame runs after 5.06 seconds, keep track of that extra .06 seconds and roll it into your next timed calculation. That will keep you on track over time.
Thanks @lordofduct I think that is just about everything I’ve been looking for, and more.
Understanding everything that’s going on around these things actually helps quite a lot, especially when planning a new feature of some kind.
That’s a valid point. Anything visual that is.
Since the beginning of this topic i’ve actually started doing some AI behaviours and things which I noticed get ‘dumber’ as the framerate drops lol. So I think a coroutine is not the way for that.