シングルトン:ゲーム全体が届く一つの GameManager
シングルトンとは? GameManager の書き方は?
シングルトンとは、生存中の一つのオブジェクトを指す static フィールドを備えた形です。これでどのスクリプトも参照を持たずに GameManager.Instance を呼べます。Awake で代入し、Start で読み、シーン再読込で入ってくる二体目を破棄し、マネージャーが本当にシーンより長生きすべきときだけ DontDestroyOnLoad を呼びます。
スコアは、倒した敵からも、ポーズメニューからも、セーブファイルからも上がらないといけません。しかしそのどれが、スコアを持つ manager を所有しているわけでもない。定番の答えが singleton です——オブジェクト 1 個と、それを指す static フィールド 1 個あれば、どのスクリプトからも名前だけで到達できます。
パターンの全容
using UnityEngine;
public class GameManager : MonoBehaviour
{
// The one field the rest of the game reaches it through.
public static GameManager Instance { get; private set; }
public int Score { get; private set; }
void Awake()
{
// Coming back to a scene that already contains one? This is the spare copy.
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
// Only for a manager that must outlive the scene. Leave it out otherwise.
DontDestroyOnLoad(gameObject);
}
void OnDestroy()
{
// Do not leave the static field pointing at a destroyed object.
if (Instance == this) Instance = null;
}
public void AddScore(int points) => Score += points;
}ゲーム内のどこからでも、今はたった一行で足ります: GameManager.Instance.AddScore(10)。Inspector でドラッグして割り当てる public フィールドも要らなければ、シーンの中でそのオブジェクトを探す必要もありません。
これを正しく動かす 4 つのルール
Instance は Awake で代入し、Start で読み取ります。Unity はシーン内のすべての Awake を最初の Start より前に実行し終えるので、Start で manager を読むスクリプトは必ず見つけられます。Awake の中で互いに読み合うのは、勝てないレースです。
消すのは複製の方で、本物ではありません。メニューをロードして戻ってくると manager が二つになりますが、去らないといけないのは後に到着した方です。さもないと既に配った参照がすべて、死んだオブジェクトを指してしまいます。
DontDestroyOnLoad が効くのはルートオブジェクトだけです。子オブジェクトに付けると Unity は警告を出力して無視し、manager はそのままシーンと共に死んでしまいます。
癖だからと DontDestroyOnLoad を安易に付けないでください。この manager が一つの シーンにだけ属するなら、そのシーンごと死なせましょう。自分が数えていたレベルより長く生き残った manager こそが、点が勝手に繰り越されたり敵が二重に数えられたりする原因です。
エディターならではの落とし穴
Play Mode への移行を速めたいあまり Domain Reload を切ると、static フィールドは前回停止したときの値をそのまま抱えています——Instance が前回のセッションのオブジェクトを指したままで、最初のフレームに例外が飛びます。明示的にリセットしておけば、この近道は安全に使えます。
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetStatics() => Instance = null;シングルトンを避けるべきとき
二つ同時に存在し得るもの——画面分割のプレイヤー、敵、武器——は singleton ではありません。無理やり singleton にするのは、いつか書き直すと自分に約束するようなものです。
manager を必要とするスクリプトが 1 本だけなら、Inspector でドラッグして割り当てる public フィールドの方がシンプルです。何と何が繋がっているか、Inspector を見ればすぐ分かります。
Instance を越しに互いへ手を伸ばし合う manager は、気づけばテストも再利用もできなくなります。プロジェクトに 2〜3 個なら普通ですが、十個近くなると、そのゲームに構造はもう残っていません。
これを作るレッスン
画面、パネル、そして GameManager
この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — 基礎 のレッスン 10。