◀ 10.

Unityで処理を一定時間待つ|WaitForSeconds・実時間待機と停止条件

▶
この記事の要点
  • Unityで処理を一定時間待つには、コルーチン内で yield return を使います。ゲーム全体やメインスレッドを止める待機ではありません。
  • WaitForSeconds はゲーム内の時間(scaled time)、WaitForSecondsRealtime は Time.timeScale の影響を受けない時間(unscaled time)で待ちます。
  • 呼び出しは StartCoroutine(WaitAndReport(false)); のように行います。通常の呼び出し元まで完了待ちになるわけではありません。
  • 再開はフレームの処理に従います。指定秒数ぴったりの実行は保証されず、コルーチン自体が別スレッドを作るわけでもありません。
  • Thread.Sleep をUnityのメインスレッドで呼ぶと、そのスレッドを止めます。画面更新を続けたい待機の代わりに使わないでください。

これはUnityの処理を一定時間待つ方法に関する記事です。説明の対象はUnity 6.0(6000.0)の公式資料です。待機時間の基準と、GameObjectやコンポーネントの停止条件を分けて確認します。

UnityのWaitForSecondsとWaitForSecondsRealtime:時間の基準とフレーム更新の条件

コルーチンを定義する

元の書式例の注意:次の表はメソッド部分だけです。System.Collections と UnityEngine のusing、および MonoBehaviour を継承するクラス内の定義が必要です。コメントの「3.5秒」はscaled timeで3.5秒という意味であり、常に実時間の3.5秒ちょうどではありません。完全な配置例は「開始・停止を含む完全例」を参照してください。

コルーチンの書き方

IEnumerator 関数名()
{
    yield return new WaitForSeconds(3.5f); // 3.5秒待つ
    
    //ここに待った後の処理を書きます
}

コルーチンを呼び出す

新しく書く場合は StartCoroutine(関数名()); のような IEnumerator を渡す形式を基本にします。文字列による名前解決より一般に実行時のオーバーヘッドが小さい形式です。

元の呼び出し例の注意:次の文字列形式もUnity 6000.0で提供されています。誤った構文として削除するものではありません。ただし、指定した名前のコルーチンが呼び出す側のMonoBehaviourに存在する必要があります。「関数名」は実際のメソッド名に置き換えます。停止時も文字列で開始した方式と、保存したIEnumerator/Coroutineを使う方式を混同しないでください。

コルーチンの呼び出し方

StartCoroutine("関数名");

StartCoroutine は最初のyieldまで処理を進めて戻り、通常は呼び出し元の次の処理へ進みます。別のコルーチン内で子の完了を待ちたい場合は yield return StartCoroutine(関数名()); などを使います。同じフレームに終わる複数コルーチンの完了順は、開始順と同じとは保証されません。

根拠:Unity 6000.0 StartCoroutine。

ゲーム内の秒数と実時間を選ぶ

待機命令時間の基準timeScaleが0のとき
yield return new WaitForSeconds(3.5f);scaled time。timeScaleを一定の0.5に保つ場合は、実時間でおよそ7秒にフレーム境界のずれが加わります。この待機の進行が止まり、待機後の処理へ再開しません。停止・破棄されたという意味ではありません。
yield return new WaitForSecondsRealtime(3.5f);unscaled time。timeScaleによるゲーム内時間の遅速とは独立した待機です。アプリがフレーム処理を続けていれば、timeScaleが0でも待機を進められます。
yield return null;次のフレームへ処理を譲ります。指定秒数待つ命令ではありません。timeScaleが0だから全コルーチンが止まる、とは扱いません。

ゲームの一時停止に合わせたい演出などはscaled time、ゲーム内時間とは独立して進めたいUIの遅延などはunscaled timeを検討します。途中でtimeScaleを変えた場合、単純に「秒数÷開始時のtimeScale」では説明できません。timeScaleの変更は次以降のフレームに反映されます。待機例を使うためだけに、ゲーム全体のtimeScaleやfixedDeltaTimeを変更する必要はありません。

根拠:WaitForSeconds、WaitForSecondsRealtime、Time.timeScale。

指定秒数ぴったりに再開するわけではない

WaitForSeconds は呼び出したフレームの終了から待ち始めます。そのフレームに同期ロードなどの長い処理があれば、呼び出した瞬間を基準にした待機より遅くなります。指定時間を過ぎた後の再開可能なフレームで再開するので、精密な実時間タイマーではありません。

Realtimeもフレーム処理から独立した割込みタイマーではありません。CustomYieldInstruction の待機判定は各フレームのUpdate後・LateUpdate前に行われます。OSによるアプリ中断・Editorの実行停止中にも、指定時刻に必ずコードが動くという保証には使えません。WaitForSecondsのフレーム末開始という説明を、Realtimeの内部実装まで同一だという意味に広げないでください。

根拠:WaitForSecondsの時間差に関する注意、CustomYieldInstruction。

開始・停止を含む完全例

検証範囲:以下はUnity 6.0の公式API仕様を照合した教材例です。同じソースを、手元のUnity 6000.6.0f1に付属する実アセンブリとC#コンパイラで型検査・コンパイルしました。Unity 6000.0での互換性、Editor/Playerの実行、フレームの再開時刻や停止動作は未検証です。

WaitExample.cs として独立したテストシーンの有効なGameObjectに取り付ける想定です。既定はscaled timeです。実時間待機を試す場合は、開始前にInspectorの useUnscaledTime を有効にします。開始時の値を今回の待機へ渡すため、待機途中にチェックを変えても、その待機の時間基準は切り替わりません。

この例の方針:再開始では前回分を取り消し、このコンポーネントの無効化時にも明示的に取り消します。これは OnDisable の実装によるもので、Unityが enabled = false だけでコルーチンを自動停止する、という意味ではありません。

using System.Collections;
using UnityEngine;

public class WaitExample : MonoBehaviour
{
    [SerializeField] private bool useUnscaledTime;
    private Coroutine pending;

    private void Start()
    {
        BeginWait();
    }

    public void BeginWait()
    {
        if (!isActiveAndEnabled)
        {
            return;
        }

        CancelWait();
        pending = StartCoroutine(WaitAndReport(useUnscaledTime));
    }

    private IEnumerator WaitAndReport(bool unscaled)
    {
        if (unscaled)
        {
            yield return new WaitForSecondsRealtime(3.5f);
        }
        else
        {
            yield return new WaitForSeconds(3.5f);
        }

        pending = null;
        Debug.Log("Wait completed", this);
    }

    public void CancelWait()
    {
        if (pending == null)
        {
            return;
        }

        StopCoroutine(pending);
        pending = null;
    }

    private void OnDisable()
    {
        CancelWait();
    }
}

BeginWait() を呼ぶと、その例の前回分だけを取り消して新しく待ち始めます。CancelWait() は保存してあるCoroutineをnull確認して停止します。毎フレーム新しい待機を追加する実装にはしていません。

取り消された場合、yieldの後ろにある完了ログは実行されません。必須の後始末を完了ログと同じ位置だけに置かず、明示的な停止・破棄の経路で設計します。再有効化してもStartは毎回呼ばれないため、この例は自動で待機を再開しません。再び必要なら、有効な状態で BeginWait() を呼んで新しい待機を開始します。

何をすると止まるか

操作・状態Unityの基本動作と注意
StopCoroutineそのMonoBehaviourで開始した対象を停止します。保存したIEnumeratorまたは返されたCoroutineを使い、停止のために新しいIEnumeratorを作らないでください。文字列で開始した形式との混同も避けます。
StopAllCoroutines()そのMonoBehaviourで動いているコルーチンを停止します。ゲーム全体・別コンポーネントの全コルーチンを止めるAPIではありません。
GameObjectを非アクティブにするそのGameObjectに付くコルーチンは停止します。一時的に待機時間だけを止める操作とは区別します。
実行元MonoBehaviourを破棄するコルーチンも停止します。破棄した所有元を使って後続処理を継続できるとは扱いません。
enabled = false でコンポーネントだけ無効化するそれだけではコルーチンは止まりません。完全例ではOnDisableでCancelWaitを呼ぶため、明示的に停止します。
timeScale = 0WaitForSecondsによる待機の進行停止と、StopCoroutineによる取消しは別です。Realtime待機や全コルーチンの停止とは同一視しません。

GameObjectの activeSelf はローカルな状態です。親GameObjectの状態による activeInHierarchy と、コンポーネントの enabled は区別してください。

根拠:StopCoroutine、StopAllCoroutines、GameObject.SetActive。

Thread.Sleepやビジーループで待たない理由

Thread.Sleep が止めるのは呼び出した現在のスレッドです。UnityのStart・Updateや通常のコルーチン内で呼べばメインスレッドを止め、画面更新などを妨げます。「コルーチンに入れれば別スレッドになる」「Sleepならゲーム内時間だけ止められる」は誤りです。

時刻を確認し続けるだけのビジーループも、yieldしなければメインスレッドを占有します。重い処理はyieldより前でも後でもメインスレッド上で重いままなので、コルーチン化だけで高速化・並列化が済むわけではありません。一方で、すべてのワーカースレッドにおけるSleepまで無条件に禁止する説明でもありません。

根拠:Unity 6000.0 コルーチンのマニュアル、Microsoft Thread.SleepのAPI説明。Microsoftの資料は現在のスレッドを停止するという一般仕様の確認に使っており、Unityが.NET 9で動くという意味ではありません。

子ページ

子ページはありません

同階層のページ
  1. スクリプトの作成と実行
  2. C# のクラス一覧
  3. シーンの移動方法
  4. オブジェクトの移動・回転
  5. キーボードの入力値を受け取る
  6. オブジェクトの取得とコンポーネントの取得
  7. 衝突時の処理
  8. Webページを開く
  9. コンポーネントの取得
  10. 処理を一定時間待つ
  11. コンソールへのログ出力方法
  12. 飛行機の加速と減速
  13. ジェットエンジンのエフェクトとオーディオ

最近更新/作成されたページ