So as everyone knows, Unity comes with Update, FixedUpdate, LateUpdate functions. Update happens every frame, FixedUpdate happens every physics update, LateUpdate happens after everything in Update is done. However, all of these pre-made functions update very fast. Checking lots of things dozens of times per second slows the game down, especially on mobile.
For some purposes, I only need to check something a few times a second. To achieve this, I’ve been using Coroutines, like this:
IEnumerator CheckRotationStatus()
{
//check if object is rotating
//5 times per second is more than sufficient
OldRotation = this.transform.rotation;
yield return new WaitForSeconds(0.2f);
if (OldRotation == this.transform.rotation)
{
NowRotating = false;
}
else
{
NowRotating = true;
Debug.Log("Old " + OldRotation + " New " + this.transform.rotation);
}
StartCoroutine(CheckRotationStatus());
}
While I haven’t run into performance issues, for my own personal knowledge, is this the best way to check something often without impacting performance much? What other ways can I check something multiple times per second, but much less often than the pre-made Update functions?
It seems to me you have an infinitely recursive function here so I bet there will be trouble…
In this case, using StartCoroutine will return immediately and the caller will exit, so you shouldn’t have to worry about a stack overflow. However, calling CheckRotationStatus() is going to create a new Enumerator state machine. Not really a big deal except someone will need to clean that object up once it’s run its course. I think this will generate a little bit of garbage that isn’t really necessary.
Normally I call StartCoroutine(CheckRotationStatus()); on Start. That way it checks 5 times a second forever, without needing to be called again.
You said “calling CheckRotationStatus() is going to create a new Enumerator state machine”. Do you mean that every time I call StartCoroutine(CheckRotationStatus());, I waste more memory on garbage and it doesn’t get cleaned up? I thought since it loops it will automatically run the garbage collector.
Well the GC will run and clean it up eventually. But you have no control over when that will be, so until it comes around and cleans it up it will take up unnecessary memory.
Is there a reason you don’t just have it with a while(true) loop with the WaitForSeconds in it?
I don’t think that’s an endless recursion. That StartCoroutine() returns immediately and the function exits, the stack is removed. StartCoroutine() basically just registers the method to be called on the MonoBehaviour. If it was a “yield StartCoroutine()” then it would be an endless recursion, because yield would mean it won’t exit until the coroutine finishes.
Still, like Timelog says, while(true) would probably be cleaner. Also, as to the original question, you can just do it in Update with a timer:
Why start a new coroutine if it’s just going to run the same code again???
IEnumerator CheckRotationStatus()
{
var wait = new WaitForSeconds(0.2f);
Quaternion rot;
while(true)
{
rot = this.transform.rotation;
yield return wait;
//NowRotating = (rot != this.transform.rotation);
NowRotating = Quaternion.Angle(rot, this.transform.rotation) > Tolerance;
//where Tolerance is small... like 0.01.
//due to float error it might be better to use this... your choice
}
}
Of course other options are the code that starts/stops rotating the object can flag when it’s actively doing it, and unflag when it stops. This will be most efficient.
OR
If you’re dealing with a Rigidbody that is supposed to react to physics as opposed to driven kinematically, you can check the ‘IsSleeping’ property to know if it’s in any sort of motion. The way the physics engine works is it only deals with active rigidbodies that have any physics interacting with it… an object that is not being interacted with gets put into a sleep state until an active force comes in contact with it… at which point it’s reincluded into the physics update cycle. So if it’s sleeping, it’s not moving.
Here’s the thing about this… the physics engines treats this in a sense of linear and rotational speed. And you might want to only consider rotational speed… thing is you’re doing a float error prone equality comparison. And a simulation that has forces acting on it is probably going to have SOME degree of both happening to it… if only minor… which your equals test is going to capture.
So really… ‘IsSleeping’ is a fairly decent way to determine this status effectively. If only as prone to error as testing every 0.2 seconds.
It is recursive because it is defined in terms of itself.
It is endless because there is no exit criteria.
I agree that the stack won’t overflow because of the implementation but endless recursion is a flag to me that there may be negative side effects and it warrants some investigation. It doesn’t mean it’s definitively bad, just worth some inspection.
In this case …
Yes
No
Like Timelog said, the garbage collector will clear the references up. This has nothing to do with the loop, just the fact that you create an object but don’t keep a reference to it. Any time an object exists with no references to it, the garbage collector might clean it. Anyways, my understanding is that reducing garbage is one of the primary micro optimizations for unity games. It does get brought up a lot on these forums…
I think makeshiftwing’s code is good but I like lordofduct’s better. It retains the benefits of using a coroutine. The most interesting, to me, is encapsulating the logic.
I’m making a character move and turn, while he’s turning I don’t want him to move because that would make him turn in a big circle; so that’s why I have that script.
However I’m not sure if IsSleeping will work, as according to the docs, it applies when BOTH movement and rotation are close to zero. Since I’m just worrying about rotation, it might not be what I need. However it is a cool method that I could use elsewhere.
I’ll test the speed soon by making a few hundred (or thousand) duplicates and see what is the fastest. I’m pretty curious as on a cheap Android device, even those small details count.
Here are the results. I copied 8,000 objects into a completely blank scene (no camera, no light). PC specs in signature. FPS from the “stats” button in game view, CPU/RAM data from the Windows Task Manager, V-SYNC off in Unity’s Quality settings (to show FPS differences over 60). I had to do 8,000 objects to show the difference, since my PC is quite overpowered compared to my cheap Android tablet (Samsung Galaxy Tab 3 7.0) I’m testing my projects on.
using UnityEngine;
using System.Collections;
using System.Linq;
public class TESTScript : MonoBehaviour {
//My original method
//public int Method1;
//void Start()
//{
// StartCoroutine(Method1Coroutine());
//}
//IEnumerator Method1Coroutine()
//{
// yield return new WaitForSeconds(0.2f);
// Method1++;
// StartCoroutine(Method1Coroutine());
//}
//RESULTS: 8,000 objects. About 900FPS with strange spikes to 500 every few seconds.
//In task manager: About 11% CPU, 443.3MB RAM
//==================================================
//private float nextTime = 0f;
//public int Method2;
//void Update()
//{
// if (Time.time > nextTime)
// {
// //Check rotation stuff here
// Method2++;
// nextTime = Time.time + 0.2f;
// }
//}
//RESULTS: 8,000 objects. About 200FPS.
//In task manager: 16.5% CPU, 501.3MB RAM
//==================================================
//lord of duct reccomended this
public int Method3;
void Start()
{
StartCoroutine(CheckRotationStatus());
}
IEnumerator CheckRotationStatus()
{
var wait = new WaitForSeconds(0.2f);
while (true)
{
yield return wait;
//NowRotating = (rot != this.transform.rotation);
Method3++;
//where Tolerance is small... like 0.01.
//due to float error it might be better to use this... your choice
}
}
//RESULTS: 8,000 objects. FPS randomly between 1500-2000
//In task manager: 10% CPU, 461MB RAM
}
Pretty unofficial, but apparently lordofduct’s method is the best. While my method was lowest on RAM, the FPS suffered massively (for unknown to me reasons). Makeshiftwings’s method was the slowest, probably because of the Update function updating 200 times per second.
Again, there might be a fault with how I ran the experiment, so please let me know if something wasn’t right.
I’m honestly surprised by the results for makeshiftwing’s approach. I didn’t expect it to be much better than lordofduct’s but I didn’t expect it to be a lot worse either. Maybe there is a penalty for accessing Time.time so often? Or simply for having an OnUpdate method?
The spikes you saw on your own approach were likely the garbage collector cleaning up the extra enumerables and WaitForSecond objects. You’ll notice lordofduct creates a single Wait object before looping to avoid creating another piece of garbage each cycle.
Yeah that’s weird. I assumed that a Coroutine’s iterator got called every frame the same way Update does, which should mean that they’re mostly the same. But maybe WaitForSeconds() does some trick where it doesn’t call back from C++ land into C# until after the wait time is over.
I would think the overhead is simply that it is calling from C++ into C#, and thus would be the same as a Coroutine that was just “while(true) yield return null;”, right? And the apparent performance increase is that the waiting in WaitForSeconds is done in C++ without calling back into C#? It would be baffling to me if somehow Update() was just generally slower than a Coroutine on a component for no apparent reason.
My idea is that because I unlocked V-SYNC, makeshiftwings was checking Time.time every frame. Doing this hundreds of times a second greatly slowed things down. However it was a pretty cool concept
@Eric5h5 has a fair point, so here are results from Android Kit Kat (4.4), Samsung Galaxy Tab 3 7.0. All background apps closed. RAM was cleared in Android’s Task manager before each test. System data is from Cool Tool - system stats app. Unfortunately, Cool Tools shows available RAM, not used ram (I couldn’t find anything that would show RAM like the Windows task manager). Since it’s all relative, it shouldn’t be a big deal. Just remember that more RAM available is better.
Important**:** There are 100 objects with the script on Android, compared to the 8,000 I ran on Windows 10. I didn’t want to overload it too hard, as the CPU with 100 objects was running at almost 80% with little RAM left. Everything else (clear scene, no light, no camera) is the same as in the PC test.
First, my method:
public int Method1;
void Start()
{
StartCoroutine(Method1Coroutine());
}
IEnumerator Method1Coroutine()
{
yield return new WaitForSeconds(0.2f);
Method1++;
StartCoroutine(Method1Coroutine());
}
CPU: 77%
RAM LEFT: 356MB
Makeshiftwings’s method:
private float nextTime = 0f;
public int Method2;
void Update()
{
if (Time.time > nextTime)
{
//Check rotation stuff here
Method2++;
nextTime = Time.time + 0.2f;
}
}
CPU: Fluctuating between 77-85%
RAM LEFT: 274MB
lordofduct’s method:
public int Method3;
void Start()
{
StartCoroutine(CheckRotationStatus());
}
IEnumerator CheckRotationStatus()
{
var wait = new WaitForSeconds(0.2f);
while (true)
{
yield return wait;
//NowRotating = (rot != this.transform.rotation);
Method3++;
//where Tolerance is small... like 0.01.
//due to float error it might be better to use this... your choice
}
}
CPU: 71-77%
RAM LEFT: 392MB
I tried to get FPS from FPS Meter app, but all of them showed 51-60. Either they all had the same FPS, or something didn’t work right.
The results are pretty much the same as for Windows 10. It’s not scientific, but on 2 different devices on 2 platforms the results were the same.