Unityで学ぶ async/await ― TextMeshProで非同期処理を実装する方法

TL;DR

  • Thread.Sleep などの同期待機は UIをフリーズさせる
  • Unity では async/await を使うことで 待機中でもUIが動き続ける
  • Task.Delay を使えば簡単に「待機付き処理」が書ける
  • TextMeshPro (TMP) を使うと見やすいラベル更新が可能
  • 処理中はボタンを無効化して 二重クリック防止 を実装すると安全

はじめに

C# の学習を進めていると「同期処理」と「非同期処理」の違いに直面します。

Windows Forms では Thread.Sleep を使うと UI が固まり、ユーザー体験が悪化します。

Unity でも同じで、重い処理や強制待機をそのまま実行するとゲームがフリーズしてしまいます。

そこで登場するのが async/await 構文です。

この記事では、Unity で async/await を使い ボタンを押すと数秒待機してラベルが変わるアプリ を TextMeshPro を使って実装します。


同期処理の例(ダメなパターン)

まず、典型的な「UIが固まる」処理を見てみましょう。

// Windows Forms 版
private void button1_Click(object sender, EventArgs e)
{
    label1.Text = "処理中...";
    Thread.Sleep(5000); // 5秒間待機
    label1.Text = "処理が完了しました";
}

この場合、ラベルの文字は変わりますが、5秒間はボタンやフォーム全体が応答不能になります。

Unity で同じことをやっても「ゲームが一時停止したように固まる」ので実用的ではありません。


Unityでの非同期処理 ― async/await を使う

Unity では以下のように書き換えられます。

サンプルコード(TextMeshPro版)

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class AsyncAwaitSampleTMP : MonoBehaviour
{
    [Header("UI (インスペクターでアサイン)")]
    [SerializeField] private Button button1;
    [SerializeField] private TMP_Text label1;

    private CancellationTokenSource _cts;
    private bool _isProcessing;

    private void Awake()
    {
        _cts = new CancellationTokenSource();
    }

    private void OnEnable()
    {
        button1.onClick.AddListener(OnButtonClick);
    }

    private void OnDisable()
    {
        button1.onClick.RemoveListener(OnButtonClick);
    }

    private void OnDestroy()
    {
        _cts.Cancel();
        _cts.Dispose();
    }

    private async void OnButtonClick()
    {
        if (_isProcessing) return;

        _isProcessing = true;
        button1.interactable = false;
        label1.text = "処理中...";

        try
        {
            // UIを止めずに5秒間待機
            await Task.Delay(5000, _cts.Token);
            label1.text = "処理が完了しました";
        }
        catch (TaskCanceledException)
        {
            label1.text = "処理はキャンセルされました";
        }
        finally
        {
            _isProcessing = false;
            button1.interactable = true;
        }
    }
}

セットアップ手順(Unityエディター)

  1. Canvas を作成
    • Hierarchy → 右クリック → UI → Canvas
  2. UI を配置
    • Canvas 内に Button (TextMeshPro) を追加 → 名前は StartButton
    • Canvas 内に Text (TextMeshPro) を追加 → 名前は StatusLabel
  3. スクリプトを配置
    • 空の GameObject(例:GameController)を作成
    • 上記スクリプトをアタッチ
    • インスペクターで button1 に StartButton、label1 に StatusLabel をドラッグ

実行結果

  • ボタンを押すと「処理中…」が表示
  • 5秒間待機(UIは操作可能)
  • 「処理が完了しました」に更新
  • 処理中はボタンが押せないので二重実行も防止

コルーチンとの比較

Unity ではコルーチンでも同じことができます。

IEnumerator SampleCoroutine()
{
    label1.text = "処理中...";
    yield return new WaitForSeconds(5);
    label1.text = "処理が完了しました";
}
  • コルーチン → Unity独自の仕組み。ゲーム開発でよく使う。
  • async/await → C# 標準の非同期構文。外部APIやファイルI/Oとも相性が良い。

どちらを使うかは用途次第ですが、C#の知識を活かしたいなら async/await が便利です。


まとめ

  • Thread.Sleep のような同期待機は Unity でも UI を止めてしまう
  • async/await を使うと 処理を待ちながらUIを更新できる
  • Task.Delay はコルーチンの WaitForSeconds に相当
  • TextMeshPro を組み合わせることで見やすいUIを構築可能
  • ボタン無効化やキャンセル処理を入れると実用的

訪問数 6 回, 今日の訪問数 6回

Unity,非同期

Posted by hidepon