What are Coroutines?
Coroutines allow functions to pause and resume execution later. They are used for performing multiple tasks with delays, or executing after a certain wait time.
Basic Concepts
- IEnumerator: Return type of coroutine functions
- StartCoroutine(): Function to start a coroutine
- yield: Keyword to pause a coroutine
- WaitForSeconds: Wait for specified time
1. Basic Coroutine Structure
Creating Coroutine Function
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
void Start()
{
// Start coroutine
StartCoroutine(MyCoroutine());
}
// Coroutine function (return type: IEnumerator)
IEnumerator MyCoroutine()
{
Debug.Log("Coroutine started!");
// Wait 2 seconds
yield return new WaitForSeconds(2f);
Debug.Log("Executed after 2 seconds!");
}
}
Coroutine Features
- Pausable: Can pause execution with
yield - Resumable: Unity automatically resumes execution
- Independent of Update(): Works separately from Update()
2. WaitForSeconds - Wait for Time
WaitForSeconds waits for a specified time (in seconds).
Basic Usage
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
void Start()
{
// Start coroutine
StartCoroutine(SpawnFireballs());
}
IEnumerator SpawnFireballs()
{
for (int i = 0; i < 10; i++)
{
// Create fireball
Instantiate(fireballPrefab);
// Wait 1 second
yield return new WaitForSeconds(1f);
}
}
}
Wait for Random Time
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
public float spawnDelayMin = 0.5f;
public float spawnDelayMax = 2f;
void Start()
{
StartCoroutine(SpawnFireballsWithDelay());
}
IEnumerator SpawnFireballsWithDelay()
{
for (int i = 0; i < 10; i++)
{
// Create fireball
Instantiate(fireballPrefab);
// Wait if not last fireball
if (i < 9)
{
// Wait random time (0.5 to 2 seconds)
float delay = Random.Range(spawnDelayMin, spawnDelayMax);
yield return new WaitForSeconds(delay);
}
}
}
}
3. WaitForSecondsRealtime - Wait for Real Time
WaitForSecondsRealtime waits for real time, unaffected by Time.timeScale.
Difference from Time.timeScale
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
void Start()
{
StartCoroutine(TestCoroutine());
}
IEnumerator TestCoroutine()
{
Debug.Log("Start!");
// Affected by Time.timeScale (waits when game is paused)
yield return new WaitForSeconds(2f);
Debug.Log("WaitForSeconds complete!");
// Not affected by Time.timeScale (waits even when game is paused)
yield return new WaitForSecondsRealtime(2f);
Debug.Log("WaitForSecondsRealtime complete!");
}
}
Practical Example
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
public float spawnDelayMin = 0.5f;
public float spawnDelayMax = 2f;
IEnumerator SpawnFireballsWithDelay()
{
for (int i = 0; i < 10; i++)
{
Instantiate(fireballPrefab);
if (i < 9)
{
// Wait for real time even if game is paused
float delay = Random.Range(spawnDelayMin, spawnDelayMax);
yield return new WaitForSecondsRealtime(delay);
}
}
}
}
4. WaitForEndOfFrame - Wait for Frame End
WaitForEndOfFrame waits until the current frame is completely finished.
Usage Example
using UnityEngine;
using System.Collections;
public class Screenshot : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(TakeScreenshot());
}
}
IEnumerator TakeScreenshot()
{
// Wait until current frame is completely rendered
yield return new WaitForEndOfFrame();
// Take screenshot
ScreenCapture.CaptureScreenshot("screenshot.png");
Debug.Log("Screenshot saved!");
}
}
5. Stopping Coroutines
StopCoroutine() - Stop Specific Coroutine
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
Coroutine myCoroutine;
void Start()
{
// Start coroutine and save reference
myCoroutine = StartCoroutine(MyCoroutine());
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// Stop coroutine
if (myCoroutine != null)
{
StopCoroutine(myCoroutine);
}
}
}
IEnumerator MyCoroutine()
{
while (true)
{
Debug.Log("Running...");
yield return new WaitForSeconds(1f);
}
}
}
StopAllCoroutines() - Stop All Coroutines
void OnDestroy()
{
// Stop all coroutines on this object
StopAllCoroutines();
}
6. Practical Examples
Example 1: Delayed Fireball Spawning
using UnityEngine;
using System.Collections;
public class FireballSpawner : MonoBehaviour
{
public GameObject fireballPrefab;
public int fireballCount = 10;
public float spawnDelayMin = 0.5f;
public float spawnDelayMax = 2f;
void Start()
{
StartCoroutine(SpawnFireballsWithDelay());
}
IEnumerator SpawnFireballsWithDelay()
{
// Don't spawn if fireballs already exist
if (spawnedFireballs.Count > 0)
{
yield break; // End coroutine
}
// Spawn specified number of fireballs
for (int i = 0; i < fireballCount; i++)
{
// Spawn one fireball
SpawnSingleFireball();
// Wait random time if not last fireball
if (i < fireballCount - 1)
{
float delay = Random.Range(spawnDelayMin, spawnDelayMax);
yield return new WaitForSecondsRealtime(delay);
}
}
}
void SpawnSingleFireball()
{
Vector3 spawnPosition = GetRandomSpawnPosition();
GameObject fireball = Instantiate(fireballPrefab, spawnPosition, Quaternion.identity);
fireball.SetActive(true);
}
Vector3 GetRandomSpawnPosition()
{
float randomX = Random.Range(-5f, 5f);
return new Vector3(randomX, 8f, 0);
}
}
Example 2: Fade In/Out
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class FadeEffect : MonoBehaviour
{
public Image fadeImage;
public float fadeDuration = 1f;
void Start()
{
StartCoroutine(FadeIn());
}
// Fade in (gradually brighten from black screen)
IEnumerator FadeIn()
{
float elapsed = 0f;
Color color = fadeImage.color;
while (elapsed < fadeDuration)
{
elapsed += Time.deltaTime;
color.a = 1f - (elapsed / fadeDuration);
fadeImage.color = color;
yield return null; // Wait until next frame
}
color.a = 0f;
fadeImage.color = color;
}
// Fade out (gradually darken from bright screen)
IEnumerator FadeOut()
{
float elapsed = 0f;
Color color = fadeImage.color;
while (elapsed < fadeDuration)
{
elapsed += Time.deltaTime;
color.a = elapsed / fadeDuration;
fadeImage.color = color;
yield return null;
}
color.a = 1f;
fadeImage.color = color;
}
}
Example 3: Repeating Execution
using UnityEngine;
using System.Collections;
public class RepeatingAction : MonoBehaviour
{
void Start()
{
StartCoroutine(RepeatEverySecond());
}
IEnumerator RepeatEverySecond()
{
while (true)
{
Debug.Log("Execute every second!");
// Wait 1 second
yield return new WaitForSeconds(1f);
}
}
}
7. yield return null
yield return null waits until the next frame.
Usage Example
using UnityEngine;
using System.Collections;
public class SmoothMovement : MonoBehaviour
{
public Vector3 targetPosition;
public float speed = 2f;
void Start()
{
StartCoroutine(MoveToTarget());
}
IEnumerator MoveToTarget()
{
while (Vector3.Distance(transform.position, targetPosition) > 0.1f)
{
// Move a little each frame
transform.position = Vector3.MoveTowards(
transform.position,
targetPosition,
speed * Time.deltaTime
);
// Wait until next frame
yield return null;
}
}
}
8. Precautions
1. MonoBehaviour Required
// ✅ Coroutines can only be used in classes that inherit MonoBehaviour
public class Example : MonoBehaviour
{
IEnumerator MyCoroutine() { }
}
// ❌ Cannot use in regular classes
public class Example
{
IEnumerator MyCoroutine() { } // Error!
}
2. StartCoroutine() Required
// ✅ Correct way
StartCoroutine(MyCoroutine());
// ❌ Wrong way (coroutine won't run)
MyCoroutine();
3. Auto-Stop on Object Destruction
// Coroutines automatically stop when GameObject is destroyed
void OnDestroy()
{
// Can also stop explicitly
StopAllCoroutines();
}
4. Nested Coroutines
IEnumerator OuterCoroutine()
{
Debug.Log("Outer coroutine started");
// Start inner coroutine (wait until complete)
yield return StartCoroutine(InnerCoroutine());
Debug.Log("Executed after inner coroutine complete");
}
IEnumerator InnerCoroutine()
{
yield return new WaitForSeconds(2f);
Debug.Log("Inner coroutine complete");
}
9. Summary
Coroutines vs Regular Functions
| Feature | Regular Function | Coroutine |
|---|---|---|
| Execution | Executes immediately | Can pause and resume |
| Time Control | Not possible | Possible (using yield) |
| Return Type | void, int, etc. | IEnumerator |
Main yield Types
yield return new WaitForSeconds(time): Wait for specified timeyield return new WaitForSecondsRealtime(time): Wait for real timeyield return new WaitForEndOfFrame(): Wait for frame endyield return null: Wait until next frame
Best Practices
- Use StartCoroutine(): Coroutines must be started with StartCoroutine()
- Use appropriate yield: Choose yield type according to usage
- Handle stopping: Stop with StopCoroutine() when needed
- Consider performance: Too many coroutines can cause performance issues
Practice Problems
-
Write a coroutine that prints "Hello" every second.
-
Write a coroutine that prints "3 seconds elapsed!" 3 seconds after the player presses spacebar.
-
Write a coroutine that gradually fades an object by setting alpha to 0.