tools-unity-unitask
UniTask async/await patterns for Unity including cancellation, lifecycle binding, and coroutine interop.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
UniTask async/await patterns for Unity including cancellation, lifecycle binding, and coroutine interop.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | tools-unity-unitask |
| description | UniTask async/await patterns for Unity including cancellation, lifecycle binding, and coroutine interop. |
UniTask provides efficient async/await support for Unity with zero allocation, proper cancellation, and Unity lifecycle integration.
// manifest.json
"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
public async UniTask LoadGameAsync()
{
await UniTask.Delay(1000); // 1 second delay
await LoadAssetsAsync();
await InitializeSystemsAsync();
}
public async UniTask<PlayerData> LoadPlayerAsync()
{
var json = await File.ReadAllTextAsync(path);
return JsonUtility.FromJson<PlayerData>(json);
}
// ForgetTask() suppresses warnings but loses error handling
LoadGameAsync().Forget();
// Better: Use UniTaskVoid for true fire-and-forget
public async UniTaskVoid StartBackgroundTask()
{
try
{
await DoWorkAsync();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
public class GameLoader : MonoBehaviour
{
private CancellationTokenSource _cts;
private void OnEnable()
{
_cts = new CancellationTokenSource();
LoadAsync(_cts.Token).Forget();
}
private void OnDisable()
{
_cts?.Cancel();
_cts?.Dispose();
_cts = null;
}
private async UniTask LoadAsync(CancellationToken ct)
{
await UniTask.Delay(1000, cancellationToken: ct);
// Work here...
}
}
public class Enemy : MonoBehaviour
{
private async UniTaskVoid Start()
{
// Automatically cancelled when GameObject destroyed
var ct = this.GetCancellationTokenOnDestroy();
while (!ct.IsCancellationRequested)
{
await UniTask.Delay(1000, cancellationToken: ct);
Patrol();
}
}
}
public async UniTask DoWorkAsync(CancellationToken externalCt)
{
// Link external token with destroy token
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
externalCt,
this.GetCancellationTokenOnDestroy()
);
try
{
await LongRunningTask(linkedCts.Token);
}
finally
{
linkedCts.Dispose();
}
}
// Timeout with exception
await task.Timeout(TimeSpan.FromSeconds(5));
// Timeout without exception
var (hasValue, result) = await task.TimeoutWithoutException(TimeSpan.FromSeconds(5));
if (!hasValue)
{
Debug.Log("Operation timed out");
}
// Timeout with CancellationTokenSource
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await task.AttachExternalCancellation(cts.Token);
// Wait one frame
await UniTask.Yield();
// Wait for end of frame
await UniTask.WaitForEndOfFrame();
// Wait for fixed update
await UniTask.WaitForFixedUpdate();
// Wait multiple frames
await UniTask.DelayFrame(10);
// Wait for specific player loop
await UniTask.Yield(PlayerLoopTiming.PreUpdate);
// Available timings:
PlayerLoopTiming.Initialization
PlayerLoopTiming.EarlyUpdate
PlayerLoopTiming.FixedUpdate
PlayerLoopTiming.PreUpdate
PlayerLoopTiming.Update
PlayerLoopTiming.PreLateUpdate
PlayerLoopTiming.PostLateUpdate
PlayerLoopTiming.TimeUpdate
// Wait until condition is true
await UniTask.WaitUntil(() => player.IsReady);
// Wait while condition is true
await UniTask.WaitWhile(() => isLoading);
// Wait until with cancellation
await UniTask.WaitUntil(
() => player.IsReady,
cancellationToken: ct
);
// Wait until with timeout
await UniTask.WaitUntil(() => player.IsReady)
.Timeout(TimeSpan.FromSeconds(10));
// Wait for button click
await button.OnClickAsync();
// Wait for trigger enter
var other = await gameObject.OnTriggerEnterAsync();
// Wait for collision
var collision = await gameObject.OnCollisionEnterAsync();
// Wait for animation event
await animator.WaitForAnimationEvent("AttackHit");
// Wait for all tasks to complete
var results = await UniTask.WhenAll(
LoadTexturesAsync(),
LoadAudioAsync(),
LoadConfigAsync()
);
// With different return types
var (textures, audio, config) = await UniTask.WhenAll(
LoadTexturesAsync(),
LoadAudioAsync(),
LoadConfigAsync()
);
// Wait for first to complete
var (winIndex, result1, result2) = await UniTask.WhenAny(
TryServerAAsync(),
TryServerBAsync()
);
// Use winner
if (winIndex == 0)
{
UseResult(result1);
}
// Process with limited concurrency
var semaphore = new SemaphoreSlim(3); // Max 3 concurrent
await UniTask.WhenAll(items.Select(async item =>
{
await semaphore.WaitAsync();
try
{
await ProcessItemAsync(item);
}
finally
{
semaphore.Release();
}
}));
// Wrap existing coroutine
await MyCoroutine().ToUniTask();
// With cancellation
await MyCoroutine().ToUniTask(cancellationToken: ct);
// From IEnumerator
IEnumerator LegacyCoroutine()
{
yield return new WaitForSeconds(1);
}
await LegacyCoroutine().ToUniTask();
// For APIs that require coroutines
StartCoroutine(MyUniTask().ToCoroutine());
// Add async triggers to GameObjects
var trigger = gameObject.GetAsyncTriggerEnterTrigger();
// Wait for trigger
var other = await trigger.OnTriggerEnterAsync();
public async UniTask<T> LoadAssetAsync<T>(string address, CancellationToken ct)
{
var handle = Addressables.LoadAssetAsync<T>(address);
try
{
return await handle.ToUniTask(cancellationToken: ct);
}
catch (OperationCanceledException)
{
Addressables.Release(handle);
throw;
}
}
public async UniTask LoadSceneAsync(string sceneName, CancellationToken ct)
{
await SceneManager.LoadSceneAsync(sceneName)
.ToUniTask(cancellationToken: ct);
}
// With progress
public async UniTask LoadSceneWithProgressAsync(string sceneName, IProgress<float> progress)
{
await SceneManager.LoadSceneAsync(sceneName)
.ToUniTask(progress: progress);
}
public async UniTask<AssetBundle> LoadBundleAsync(string url, CancellationToken ct)
{
var request = UnityWebRequestAssetBundle.GetAssetBundle(url);
await request.SendWebRequest().ToUniTask(cancellationToken: ct);
if (request.result != UnityWebRequest.Result.Success)
{
throw new Exception(request.error);
}
return DownloadHandlerAssetBundle.GetContent(request);
}
public async UniTask SafeLoadAsync()
{
try
{
await LoadDataAsync();
}
catch (OperationCanceledException)
{
// Expected when cancelled - don't log as error
Debug.Log("Load cancelled");
}
catch (Exception ex)
{
Debug.LogException(ex);
ShowErrorUI();
}
}
// Returns (isCancelled, result) instead of throwing
var (cancelled, result) = await LoadAsync()
.SuppressCancellationThrow();
if (cancelled)
{
return; // Graceful exit
}
ProcessResult(result);
// Collect all exceptions
try
{
await UniTask.WhenAll(tasks);
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
{
Debug.LogException(ex);
}
}
public async UniTask LoadWithProgressAsync(IProgress<float> progress, CancellationToken ct)
{
var items = await GetItemsAsync();
for (int i = 0; i < items.Count; i++)
{
await ProcessItemAsync(items[i], ct);
progress?.Report((float)(i + 1) / items.Count);
}
}
// Usage
var progress = new Progress<float>(p => loadingBar.value = p);
await LoadWithProgressAsync(progress, ct);
public struct LoadProgress
{
public string CurrentItem;
public int Loaded;
public int Total;
public float Percent => (float)Loaded / Total;
}
public async UniTask LoadAsync(IProgress<LoadProgress> progress)
{
var items = await GetItemsAsync();
for (int i = 0; i < items.Count; i++)
{
progress?.Report(new LoadProgress
{
CurrentItem = items[i].Name,
Loaded = i + 1,
Total = items.Count
});
await ProcessItemAsync(items[i]);
}
}
// Unity main thread operations
await UniTask.Delay(1000); // Use UniTask
await UniTask.Yield(); // Use UniTask
await SceneManager.LoadSceneAsync(s); // Use UniTask
// Fire and forget in Unity
DoWorkAsync().Forget(); // UniTask only
// Pure .NET operations (no Unity API)
await File.ReadAllTextAsync(path); // Task is fine
await Task.Run(() => HeavyComputation()); // Task for thread pool
// Converting
var result = await task.AsUniTask(); // Task to UniTask
var task = unitask.AsTask(); // UniTask to Task
// BAD: No cancellation, leaks when object destroyed
public async UniTaskVoid Start()
{
await UniTask.Delay(10000);
DoSomething(); // May crash if destroyed
}
// GOOD: Use destroy token
public async UniTaskVoid Start()
{
await UniTask.Delay(10000, cancellationToken: destroyCancellationToken);
DoSomething();
}
// BAD: Exceptions silently swallowed
DoWorkAsync().Forget();
// GOOD: Handle errors
async UniTaskVoid DoWorkSafe()
{
try
{
await DoWorkAsync();
}
catch (Exception ex) when (!(ex is OperationCanceledException))
{
Debug.LogException(ex);
}
}
DoWorkSafe().Forget();
// BAD: Blocks main thread
var result = LoadAsync().GetAwaiter().GetResult();
// GOOD: Await properly
var result = await LoadAsync();
// BAD: Allocates closure
await UniTask.Delay(1000).ContinueWith(_ => DoSomething());
// GOOD: Just await
await UniTask.Delay(1000);
DoSomething();
.AsTask() conversionsUniTask.Yield() instead of await UniTask.Delay(0)CancellationTokenSource for high-frequency operationsUniTaskCompletionSource for custom async patternsCreates an Architecture Decision Record (ADR) documenting a significant technical decision, its context, alternatives considered, and consequences. Every major technical choice should have an ADR.
Mobile-specific Unity optimization patterns for memory, battery, thermal, and performance.
End-of-story completion review. Reads the story file, verifies each acceptance criterion against the implementation, checks for GDD/ADR deviations, prompts code review, updates story status to Complete, and surfaces the next ready story from the sprint.
Unity Addressables patterns for asset loading, memory management, reference counting, and remote content delivery.
Unity animation patterns including Animancer, state machines, and performance optimization.
Behavior Designer patterns for AI behavior trees including task creation, shared variables, conditionals, and debugging.