If you have a long running operation that relies on the not-thread-safe Unity API, use Coroutines to split it over multiple frames and keep your application responsive.
Coroutines also help performing expensive actions every nth frame instead of running that action each frame.
Coroutines help distribute long running operations over multiple frames to help keep up the framerate of your application.
Routines that paint or generate terrain procedurally or generate noise are examples that may need the Coroutine treatment.
for (int y = 0; y < heightmap.Height; y++)
{
for (int x = 0; x < heightmap.Width; x++)
{
// Generate pixel at (x, y)
// Assign pixel at (x, y)
// Process only 32768 pixels each frame
if ((y * heightmap.Height + x) % 32 * 1024) == 0)
yield return null; // Wait for next frame
}
}
The code above is an easy to understand example. In production code it is better to avoid the per-pixel check that checks when to
yield return
(maybe do it every 2-3 rows) and to pre-calculatefor
loop length in advance.
Coroutines help you perform expensive actions less frequently, so that it isn't as big a performance hit as it would be if performed every frame.
Taking the following example directly from the Manual:
private void ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
return true;
}
return false;
}
private IEnumerator ProximityCheckCoroutine()
{
while(true)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
Proximity tests can be optimized even further by using the CullingGroup API.
A common mistake developers make is accessing results or side effects of coroutines outside the coroutine. Coroutines return control to the caller as soon as a yield return
statement is encountered and the result or side effect may not be performed yet. To circumvent problems where you have to use the result/side effect outside the coroutine, check this answer.