グローバル変数による値渡し
Unityでシーン間で値の受け渡しをしたいこと、多いですよね。
例えばゲームシーンからリザルトシーンにスコア情報を渡したりなどが挙げられます。
その時に最も簡便かつ危険な方法が、グローバル変数を用いる手法です。
public static class GlobalVariables
{
public static int score;
}
public class GameView : MonoBehaviour
{
private void GoToResult()
{
GlobalVariables.score = 100;
SceneManager.LoadScene("Result");
}
}
public class ResultView : MonoBehaviour
{
private void Start()
{
Debug.Log($"Score: {GlobalVariables.score}");
}
}
このような書き方をすると、scoreはどこからでも参照・代入できるようになり、
ゲーム規模が大きく・人数が増えると管理し切れなくなり、とても危険です
ここでいうグローバル変数はどこからでもアクセスできるものであり、シングルトンやPlayerPrefsも同様です。
これらを値渡しに使うのは避けましょう。
シーン間の依存による値渡し
そこで次に考えるのが、下記のようにシーン間で直接値を渡す方法です。
public class GameView : MonoBehaviour
{
private async void GoToResult()
{
await SceneManager.LoadSceneAsync("Result");
var result = GameObject.FindObjectOfType<ResultView>();
result.Initialize(100);
}
}
public class ResultView : MonoBehaviour
{
public void Initialize(int score)
{
Debug.Log($"Score: {score}");
}
}
これはグローバル変数による授受と比較すると、かなりマシではあります。
ただし、この手のシーンオブジェクト(GameView, ResultView)は肥大化するものであり、GameViewがResultViewそのものに依存するのには依然として危険性があります。
また、シーン同士が相互参照して結局スパゲティになるリスクもあります。
シーンマネージャーとパラメータに依存した値渡し
上記を踏まえて、直接シーンに依存するのは避けたいです。
ではどうするかというと、シーンマネージャーという、シーン遷移と値渡しを行うクラスを用意して、そこに代わりに行ってもらいます。
public static class GameSceneManager
{
public static async void LoadScene(string sceneName, object parameter)
{
await SceneManager.LoadSceneAsync(sceneName);
var scene = GameObject.FindObjectOfType<SceneBase>();
scene.Initialize(parameter);
}
}
public abstract class SceneBase : MonoBehaviour
{
public virtual void Initialize(object param) { }
}
public class GameView : SceneBase
{
private async void GoToResult()
{
GameSceneManager.LoadScene("Result", 100);
}
}
public class ResultView : SceneBase
{
public override void Initialize(object param)
{
Debug.Log($"Score: {param as int}");
}
}
このような実装にすることで、GameViewはResultViewのパラメータの形だけ知っておけば良いので、
疎結合な状態で安全にパラメータを渡すことが可能になります。
実際にシーンマネージャーを設計する際には、ジェネリクスを用いたりなど、より安全な構成にするのが良いでしょう。