1. What is a Coroutine?
A coroutine is a special type of function that can be paused and resumed during execution, making it ideal for scenarios where you need to wait for a condition or a certain amount of time. In Unity, coroutines are commonly used to handle time delays, animations, network requests, and other tasks that shouldn’t block the main thread.
2. Basic Usage
(1) Starting a Coroutine:
To start a coroutine, use the StartCoroutine() method. Coroutines typically return an IEnumerator type.
Example: StartCoroutine( MyCoroutine() );
IEnumerator MyCoroutine()
{
print("Starting");
yield return new WaitForSeconds(2); // Wait for 2 seconds
print("Finished");
}
(2) Defining a Coroutine:
Inside a coroutine, you can use the yield statement to pause execution until a condition is met or a specified time has passed.
3. Different yield Options:
Unity provides several types of yield instructions, the most common being:
– yield return null; : Continues execution in the next frame.
– yield return new WaitForSeconds(float seconds); : Waits for the specified amount of time.
– yield return new WaitForEndOfFrame(); : Continues execution after the current frame ends.
– yield return new WaitUntil(Func predicate); : Waits until a specified condition is true.
4. Advantages of Coroutines
(a) Non-blocking Execution:
Coroutines allow you to run long tasks, like waiting or animations, without blocking the main thread, keeping the game running smoothly.
(b) Easy to Write and Understand:
Using coroutines makes code cleaner and avoids callback hell, with logic that closely resembles sequential execution.
(c) Flexibility:
Coroutines can distribute tasks across multiple frames, making them suitable for handling complex asynchronous logic.
5. Important Considerations
(a) Lifecycle:
The coroutine’s lifecycle is tied to the MonoBehaviour component that starts it. If the component is disabled or destroyed, the coroutine will automatically stop.
(b) Performance:
Although coroutines are efficient, having too many running at once may impact performance, especially when a large number of coroutines are updated every frame.
6. Example Application
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Test : MonoBehaviour
{
bool IsWaitingA;
bool IsWaitingB;
// Start is called before the first frame update
void Start()
{
IsWaitingA = true;
IsWaitingB = true;
StartCoroutine(Task());
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
IsWaitingA = false;
}
if (Input.GetKeyDown(KeyCode.B))
{
IsWaitingB = false;
}
}
IEnumerator Task()
{
while (IsWaitingA)
{
print("Waiting A");
yield return null;
}
print("IsWaitingA ------ > OK");
while (IsWaitingB)
{
print("Waiting B");
yield return null;
}
print("IsWaitingB ------ > OK");
}
}
The above code demonstrates how to use coroutines to manage multiple waiting states. User input controls the flow of the coroutine, making the logic in the game more responsive.
7. Code Breakdown
(1) Field Definitions:
bool IsWaitingA: Controls whether the coroutine is waiting for event A.
bool IsWaitingB: Controls whether the coroutine is waiting for event B.
(2) Start Method:
When the game starts, both IsWaitingA and IsWaitingB are initialized to true, indicating that both events are in a waiting state.
The Task coroutine is started.
(3) Update Method:
Each frame checks whether the A or B keys are pressed.
If the A key is pressed, IsWaitingA is set to false, meaning it no longer waits for event A.
If the B key is pressed, IsWaitingB is set to false, meaning it no longer waits for event B.
(4) Task Coroutine:
– First, it enters the while (IsWaitingA) loop:
– As long as IsWaitingA is true, it prints “Waiting A“.
– yield return null; pauses execution, waiting for the next frame.
– Once the A key is pressed and IsWaitingA is set to false, the loop ends, and it prints “IsWaitingA —— > OK”.
– Next, it enters the while (IsWaitingB) loop:
– As long as IsWaitingB is true, it prints “Waiting B“.
– Again, yield return null; pauses execution until the next frame.
– Once the B key is pressed and IsWaitingB is set to false, the loop ends, and it prints “IsWaitingB —— > OK”.
8. Execution Summary
(1) On Start:
The script initializes both waiting states to true and starts the Task coroutine.
(2) Waiting for A:
– The coroutine first enters the while (IsWaitingA) loop, continuously printing “Waiting A”.
– Once the A key is pressed, IsWaitingA becomes false, and the coroutine exits the loop, printing “IsWaitingA —— > OK”.
(3) Waiting for B:
– The coroutine then enters the while (IsWaitingB) loop, continuously printing “Waiting B”.
– Once the B key is pressed, IsWaitingB becomes false, and the coroutine exits the loop, printing “IsWaitingB —— > OK”.
This example illustrates how coroutines can be used to manage asynchronous state transitions in a game, controlled by user input. The logic is clean, sequential, and easy to follow, making coroutines a powerful tool for handling non-blocking tasks in Unity.