UnityでUIシーンとゲームシーンを分けて共存させる方法

― ゲームシーンからUIテキストを安全に変更する設計 ―

近年のUnity開発では、UIを独立したシーンとして管理する手法が一般的になっています。

これにより、UIの再利用・デバッグ・画面遷移がシンプルになり、ゲーム全体の構造も明確になります。

しかし、「UIシーンとゲームシーンを分けると、どうやってUIにアクセスすればいいのか?」という疑問を持つ方も多いでしょう。

本記事では、UIシーンを常駐させ、ゲームシーンから安全にUIを更新する構成と実装例を紹介します。


1. シーンを分けるメリット

まず、UIを別シーンに分ける理由を整理します。

項目メリット
責務分離ゲームロジックとUI制御を明確に分離できる
再利用性UIを別プロジェクト・別モード(タイトル、メニュー)でも再利用可能
ロード時間短縮UIを再ロードせず、ゲームシーンだけを切り替えられる
デバッグ効率UIだけ単体でテストできる

2. 基本構成

フォルダとシーン構成の一例を示します。

Assets/
 ├─ Scenes/
 │    ├─ UI.unity         ← 常駐(Additiveロード)
 │    ├─ GameStage1.unity
 │    └─ GameStage2.unity
 ├─ Scripts/
 │    ├─ UIManager.cs
 │    ├─ GameManager.cs
 │    └─ SceneLoader.cs

UIシーンはアプリ起動時にロードし、

DontDestroyOnLoadを使って破棄されないようにします。


3. UIManagerの実装(UIシーン)

UIシーンには、Canvas配下にText(またはTMP_Text)と

UIManagerスクリプトを配置します。

using TMPro;
using UnityEngine;

public class UIManager : MonoBehaviour
{
    public static UIManager Instance { get; private set; }

    [SerializeField] private TMP_Text scoreText;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // シーン切り替え時に破棄しない
        }
        else
        {
            Destroy(gameObject);
        }
    }

    public void SetScore(int value)
    {
        scoreText.text = $"Score: {value}";
    }
}

ポイント

  • Singletonパターンを採用して、どのシーンからでもアクセス可能に。
  • DontDestroyOnLoad() でUIシーンを常駐化。

4. GameManagerの実装(Gameシーン)

ゲームシーン側では、UIManager.Instance を介してUIを更新します。

using UnityEngine;

public class GameManager : MonoBehaviour
{
    private int score;

    public void AddScore(int value)
    {
        score += value;
        UIManager.Instance?.SetScore(score);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            AddScore(10);
        }
    }
}

スペースキーを押すたびにスコアが加算され、UI側のテキストが更新されます。

UIシーンが別でも問題なく動作します。


5. UIシーンを自動ロードする(SceneLoader)

アプリ起動時に自動でUIシーンをAdditiveロードする方法です。

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneLoader
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Init()
    {
        SceneManager.LoadScene("UI", LoadSceneMode.Additive);
    }
}

このクラスをどこかに置いておけば、

Unity起動時に必ずUIシーンがロードされます。

以降、ゲームシーンを自由に切り替えてもUIは残ります。


6. 応用:イベントベース設計(依存をさらに減らす)

上記の方法は簡単で強力ですが、

GameManager → UIManager の直接参照が残ります。

より拡張性を高めるには、イベント駆動にします。

// GameManager.cs
public static event Action<int> OnScoreChanged;

public void AddScore(int value)
{
    score += value;
    OnScoreChanged?.Invoke(score);
}
// UIManager.cs
private void OnEnable()
{
    GameManager.OnScoreChanged += SetScore;
}

private void OnDisable()
{
    GameManager.OnScoreChanged -= SetScore;
}

こうすると、UIはGameManagerの存在を知らずに動作します。

依存方向が一方通行となり、保守性が向上します。


7. 注意点とベストプラクティス

注意点対応策
UIシーンのロード忘れRuntimeInitializeOnLoadMethod で自動ロード
シングルトンの重複生成Awakeでチェック&破棄
テキスト参照の未設定[SerializeField]でInspectorから設定
シーン切り替え時のNullInstance? で安全呼び出し

8. まとめ

手法メリット想定規模
Findで直接アクセス簡単だが壊れやすい小規模・試作
Singleton + DontDestroyOnLoad安全で汎用的中規模
イベント駆動結合度が低く拡張性◎大規模・商用

9. まとめコード一式

起動時

SceneManager.LoadScene("UI", LoadSceneMode.Additive);

UIManager.cs

public void SetScore(int value)
{
    scoreText.text = $"Score: {value}";
}

GameManager.cs

UIManager.Instance?.SetScore(score);

結論

Find は便利なようで、スケールが大きくなるとすぐ破綻します。代わりに、UIは常駐シーン+シングルトン or イベントベースで構成するのが最も堅牢です。

この設計を導入すれば、「シーンをまたいでも安定してUIを更新できる」だけでなく、ロード順や依存関係のバグを未然に防げるようになります。


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