Coroutine / IEnumerator
| Since: | C# 1.0(2002) |
|---|
How to use coroutines in Unity to split processing across frames. A coroutine is a mechanism that can pause execution midway and resume in the next frame. Unity implements this internally using the IEnumerator interface. Define a method that returns an IEnumerator and start it with StartCoroutine().
Syntax
using System.Collections;
using UnityEngine;
// A method that returns IEnumerator is the body of a coroutine.
IEnumerator CoroutineName()
{
// Pause execution and return control to the next frame.
yield return null;
// Wait for the specified number of seconds.
yield return new WaitForSeconds(float seconds);
// Wait until the next FixedUpdate.
yield return new WaitForFixedUpdate();
// Wait until the condition becomes true.
yield return new WaitUntil(() => condition);
}
// Start the coroutine.
Coroutine handle = StartCoroutine(CoroutineName());
// Stop the coroutine.
StopCoroutine(handle);
// Stop all coroutines running on this GameObject.
StopAllCoroutines();
Method List
| Method / Class | Description |
|---|---|
| StartCoroutine(IEnumerator) | Starts a coroutine. Storing the returned Coroutine handle lets you stop it later. |
| StopCoroutine(Coroutine) | Stops the specified coroutine. Pass the value returned by StartCoroutine(). |
| StopAllCoroutines() | Stops all coroutines running on this component. |
| yield return null | Pauses execution until the next frame. |
| yield return new WaitForSeconds(t) | Pauses execution for t seconds. Affected by Time.timeScale. |
| yield return new WaitForSecondsRealtime(t) | Pauses execution for t seconds in real time. Not affected by Time.timeScale (continues during pause). |
| yield return new WaitUntil(() => condition) | Waits every frame until the condition becomes true. |
| yield return new WaitForFixedUpdate() | Waits until the next FixedUpdate. Use this to synchronize with physics. |
Sample Code
A sample coroutine that runs a countdown and blink effect in parallel.
CoroutineSample.cs
using System.Collections;
using UnityEngine;
public class CoroutineSample : MonoBehaviour
{
private Coroutine blinkCoroutine;
void Start()
{
// Start the countdown coroutine.
StartCoroutine(CountDownCoroutine(5));
// Store the handle so it can be stopped later.
blinkCoroutine = StartCoroutine(BlinkCoroutine(0.5f));
// Stop the blinking after 3 seconds.
StartCoroutine(DelayedAction(3f, () =>
{
StopCoroutine(blinkCoroutine);
Debug.Log("Blinking stopped.");
}));
}
// A coroutine that counts down.
IEnumerator CountDownCoroutine(int seconds)
{
for (int i = seconds; i > 0; i--)
{
Debug.Log($"Countdown: {i}");
yield return new WaitForSeconds(1f);
}
Debug.Log("Go!");
}
// A coroutine that blinks the object.
IEnumerator BlinkCoroutine(float interval)
{
Renderer rend = GetComponent<Renderer>();
while (true)
{
if (rend != null)
rend.enabled = !rend.enabled;
yield return new WaitForSeconds(interval);
}
}
// A coroutine that executes an action after a delay.
IEnumerator DelayedAction(float delay, System.Action action)
{
yield return new WaitForSeconds(delay);
action?.Invoke();
}
}
Practical Pattern: WaitUntil for Condition-Based Waiting
A pattern that waits until a game state changes. Using WaitUntil keeps per-frame polling concise.
EnemySpawner.cs
using System.Collections;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
private bool playerReady = false;
private int waveCount = 0;
void Start()
{
StartCoroutine(SpawnWaveCoroutine());
}
IEnumerator SpawnWaveCoroutine()
{
// Wait every frame until playerReady becomes true.
yield return new WaitUntil(() => playerReady);
waveCount++;
Debug.Log($"Wave {waveCount} started.");
SpawnEnemies();
yield return new WaitForSeconds(30f);
Debug.Log($"Wave {waveCount} ended.");
// Start the next wave.
StartCoroutine(SpawnWaveCoroutine());
}
void SpawnEnemies()
{
Debug.Log("Enemies spawned.");
}
public void SetPlayerReady()
{
playerReady = true;
}
}
Practical Pattern: Nested Coroutines
Using yield return StartCoroutine() inside a coroutine waits for the inner coroutine to complete before continuing.
SequenceCoroutine.cs
using System.Collections;
using UnityEngine;
public class SequenceCoroutine : MonoBehaviour
{
void Start()
{
StartCoroutine(MainSequence());
}
IEnumerator MainSequence()
{
Debug.Log("Phase 1 start.");
yield return StartCoroutine(PhaseOne()); // Waits for PhaseOne to complete.
Debug.Log("Phase 2 start.");
yield return StartCoroutine(PhaseTwo()); // Waits for PhaseTwo to complete.
Debug.Log("All phases complete.");
}
IEnumerator PhaseOne()
{
yield return new WaitForSeconds(1f);
Debug.Log("Phase 1 complete.");
}
IEnumerator PhaseTwo()
{
yield return new WaitForSeconds(2f);
Debug.Log("Phase 2 complete.");
}
}
Common Mistakes
Common Mistake 1: Coroutine Won't Stop
To stop a coroutine with StopCoroutine(), you must retain the Coroutine handle returned by StartCoroutine(). If you discard it, you can no longer stop that specific coroutine.
StopCoroutineNG.cs
using System.Collections;
using UnityEngine;
public class StopCoroutineNG : MonoBehaviour
{
private Coroutine handle;
void Start()
{
// NG: Discarding the return value means you cannot stop it later.
StartCoroutine(BlinkCoroutine(0.5f));
}
// OK: Store the return value.
void StartBlink()
{
handle = StartCoroutine(BlinkCoroutine(0.5f));
}
void StopBlink()
{
if (handle != null)
StopCoroutine(handle);
}
IEnumerator BlinkCoroutine(float interval)
{
while (true)
{
yield return new WaitForSeconds(interval);
}
}
}
Common Mistake 2: Coroutine on Inactive GameObject
Coroutines only run while the MonoBehaviour is active. If SetActive(false) is called, coroutines stop and do not resume when the object is re-enabled.
using System.Collections;
using UnityEngine;
public class InactiveCoroutineNG : MonoBehaviour
{
void Start()
{
StartCoroutine(TimerCoroutine());
// NG: Disabling the object stops the coroutine permanently.
gameObject.SetActive(false);
// OK: Stop all coroutines before disabling.
// StopAllCoroutines();
// gameObject.SetActive(false);
}
IEnumerator TimerCoroutine()
{
float elapsed = 0f;
while (true)
{
elapsed += Time.deltaTime;
Debug.Log($"Elapsed: {elapsed:F1}s");
yield return null;
}
}
}
Overview
Coroutines run on the main thread, so there are no thread-safety concerns and you can call any Unity API freely. For long-running async operations such as HTTP requests, C#'s async / await is also available (Unity 2017 and later). Coroutines and async / await can coexist.
For Unity math functions, see Mathf; for vector math, see Vector2 / Vector3.
If you find any errors or copyright issues, please contact us.