U
10 / 22 · 10 分
FAQ

シーン切替:LoadScene、ローディング画面、そして生き残るもの

短い答え

別のシーンに切り替えるには?

すべてのシーンを File > Build Profiles の Scene List に追加し、シーン名つきで SceneManager.LoadScene を呼びます。切り替えは次のフレームで起こり、古いシーンのすべては破棄され、DontDestroyOnLoad を付けたルートオブジェクトだけが生き残ります——static フィールドや Time.timeScale も同上で、誰もリセットしてくれません。

別の scene を読み込むのは 1 行で済みます。問題は必ずその行の周りで起きます —— scene が一度もビルドに追加されていない、後ろのコードが動き続けてしまう、必要だったものが古い scene と一緒に破棄されている、のいずれかです。

ステップ 1 — scene を一覧に入れる

  1. 1

    File▸Build Profiles(Unity 6 以前は File▸Build Settings)を開いて、Scene List を見つけます。

  2. 2

    ゲームに含めたい scene をすべて開いてから Add Open Scenes を押すか、.unity ファイルを Project パネルから直接ドラッグします。

  3. 3

    index 0 にあるものが、ビルド後のゲームが起動時に入る scene です。メニューや intro を一番上に置いてください。

ステップ 2 — 読み込む

Doorway.cs — the whole of scene switching
using UnityEngine;
using UnityEngine.SceneManagement;   // without this line, SceneManager is not found

public class Doorway : MonoBehaviour
{
    public void Play()
    {
        SceneManager.LoadScene("Arena");        // by name, as spelt in the Scene List
    }

    public void Restart()
    {
        // The scene that is running, whatever it is called.
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }
}

読み込みを生き延びるものと、できないもの

  • 古い scene の GameObject はすべて破棄されます。それらへの参照は null になり、上で動いていた coroutine もその場で停止します。

  • DontDestroyOnLoad を付けた object は動き続けます —— ただし親を持たないルート object であることが条件です。定番は BGM、メモリ上に置いたセーブデータ、そして ある manager です。

  • static フィールドはどの scene にも属さないので、値がそのまま残ります。レベル番号を次の scene へ持ち越すには便利ですが、前回のプレイで残った値がまだそこに寝ている場合は罠になります。

  • Time.timeScale はグローバルです。timeScale = 0 でゲームを止めたまま 1 に戻さずメニューを読み込むと、メニューは固まった状態で開きます —— しかも理由を知らせてくれるエラーも出ません。

少し時間がかかる scene 用のローディング画面

Loader.cs — a progress bar that actually means something
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class Loader : MonoBehaviour
{
    public Slider bar;

    public void Load(string scene) => StartCoroutine(LoadRoutine(scene));

    IEnumerator LoadRoutine(string scene)
    {
        AsyncOperation op = SceneManager.LoadSceneAsync(scene);

        while (!op.isDone)
        {
            // progress stops at 0.9 while the scene waits its turn to appear,
            // so scale it back up or the bar never reaches the end.
            bar.value = Mathf.Clamp01(op.progress / 0.9f);
            yield return null;
        }
    }
}

2 つの scene を同時に

LoadSceneMode.Additive は、すでに動いている scene を置き換えずにその上へ重ねます。二度と読み直さない HUD、ハブとなる場所に隣接してストリーミングで読み込むレベルなどがこれにあたります。外すときは UnloadSceneAsync を使い、新しい object は active になっている scene の側に生成される点を覚えておいてください。

これを作るレッスン

画面、パネル、そして GameManager

この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — 基礎 のレッスン 10。