The C# async/await pattern offers a structured approach to managing asynchronous operations in Unity, providing an alternative to traditional coroutines. This method can enhance control over game logic by simplifying concurrent and sequential task execution.
Extension Method for Child Collection
A helper method gathers all child Transforms from a parenet object into a list.
using UnityEngine;
using System.Collections.Generic;
public static class TransformCollectionHelper
{
public static void FetchAllChildren(this Transform parent, List<Transform> childList)
{
for (int idx = 0; idx < parent.childCount; idx++)
childList.Add(parent.GetChild(idx));
}
}
Concurrent Rotation Using async/await
Objects rotate simultaneously. The async void method starts multiple tasks without waiting for completion.
using UnityEngine;
using System.Collections.Generic;
using System.Threading.Tasks;
public class AsyncRotationController : MonoBehaviour
{
private List<Transform> _targetObjects = new List<Transform>();
private void Begin()
{
transform.FetchAllChildren(_targetObjects);
for (int i = 0; i < _targetObjects.Count; i++)
{
PerformRotationAsync(i);
}
}
private async void PerformRotationAsync(int objectIndex)
{
float finishTime = Time.time + 1.5f;
while (Time.time < finishTime)
{
_targetObjects[objectIndex].Rotate(Vector3.up * 120 * Time.deltaTime);
await Task.Yield();
}
}
}
Sequential Rotation Using async/await
Objects rotate one after another. The await keyword pauses execution untill each task finishes.
private async void BeginSequential()
{
transform.FetchAllChildren(_targetObjects);
for (int i = 0; i < _targetObjects.Count; i++)
{
await ExecuteRotationTask(i);
}
}
private async Task ExecuteRotationTask(int objectIndex)
{
float rotationEnd = Time.time + 1.5f;
while (Time.time < rotationEnd)
{
_targetObjects[objectIndex].Rotate(Vector3.up * 120 * Time.deltaTime);
await Task.Yield();
}
}
Concurrent Rotation Using Coroutines
Traditional coroutine implementation for simultaneous rotation.
private void BeginCoroutine()
{
transform.FetchAllChildren(_targetObjects);
for (int i = 0; i < _targetObjects.Count; i++)
{
StartCoroutine(RotateCoroutine(i));
}
}
private IEnumerator RotateCoroutine(int objectIndex)
{
float stopTime = Time.time + 1.5f;
while (Time.time < stopTime)
{
_targetObjects[objectIndex].Rotate(Vector3.up * 120 * Time.deltaTime);
yield return null;
}
}
Sequential Execution with Coroutines
Coroutines require nested yield return StartCoroutine() calls for sequential flow, which can become verbose.
private IEnumerator RunSequence()
{
yield return StartCoroutine(FirstAction());
yield return StartCoroutine(SecondAction());
yield return StartCoroutine(ThirdAction());
}
private IEnumerator FirstAction()
{
yield return new WaitForSeconds(1.0f);
}
// ... Similar methods for SecondAction and ThirdAction
Coordinating Multiple Async Tasks
The Task.WhenAll method waits for a collection of tasks to complete before proceeding.
public class TaskCoordinator : MonoBehaviour
{
private List<Transform> _targetObjects = new List<Transform>();
private Task[] _rotationTasks;
private async void Initialize()
{
transform.FetchAllChildren(_targetObjects);
_rotationTasks = new Task[_targetObjects.Count];
for (int i = 0; i < _targetObjects.Count; i++)
{
_rotationTasks[i] = ExecuteRotationTask(i);
}
await Task.WhenAll(_rotationTasks);
Debug.Log("All rotation tasks completed.");
}
private async Task ExecuteRotationTask(int objectIndex)
{
float durationEnd = Time.time + 1.5f;
while (Time.time < durationEnd)
{
_targetObjects[objectIndex].Rotate(Vector3.up * 120 * Time.deltaTime);
await Task.Yield();
}
}
}