はじめに
Unityでデータを管理する方法をよく忘れるのでまとめました。
目次
public staticな変数
シングルトンパターン
ScriptableOjbectを使う
個人的ベストプラクティス
staticな変数に格納する
もっとも単純な方法が、staticな変数に値を格納することでどこからでも参照できるようにするというものです。
「Unity Scene 変数 共有」などと検索すると、一番ヒットしやすい内容だと思います。
public static int score = 5;
もっとも手軽に実装できる方法なので、他の部分に時間を割きたいときや、初心者で他の方法は学習負荷が高いときなどはとりあえずpublic staticで良いかもしれません。
DontDestroyOnLoadなシングルトン
ゲームオブジェクトをシングルトンにする、つまりオブジェクトが一つしかないことを確約する、と解釈しています。
主にゲームマネージャーやオーディオマネージャーなど、ゲーム内に一つだけ存在するべきオブジェクトに対して実装されるものとのことです。
// ChatGPTによるサンプルプログラム
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject); // シーン遷移時に破棄されないようにする
}
else
{
Destroy(gameObject); // 既にインスタンスが存在する場合は、重複を避けるために自身を破棄
}
}
}
ScriptableObject
個人的に一番使いやすいと考えているのがこの方法です。
Unityでは作成したクラスをコンポーネントとしてゲームオブジェクトにアタッチして使用しますが、ScriptableObjectはアセットとしてクラスを使用する方法となります。
画像やテキストファイルなどと同じようにAssetフォルダ内に作成し、それを[SerializeField]やpublicとした変数にアタッチする形で使用します。
// ChatGPTによるサンプルコード
using UnityEngine;
// ScriptableObjectを継承したクラス
[CreateAssetMenu(fileName = "NewItem", menuName = "Inventory/Item", order = 1)]
public class Item : ScriptableObject
{
public string itemName = "New Item"; // アイテムの名前
public Sprite icon = null; // アイテムのアイコン
public int price = 0; // アイテムの価格
}
この方法の良い点は、シーンやプレイ中かどうかを問わずデータを変更できる点にあると考えています。
このメリットが顕著な場合として以下を挙げます。
ゲームを実行しないとゲームオブジェクトの設定を変更できない状態
この場合、ゲームオブジェクトの初期設定や初期値といったデータをScriptableObjectに分離することで、いちいちゲームの実行/プログラムの編集を行わずともInspectorからデータを変更できます。
特定の場面でしか変更できない設定を反映させたい場合
仮にゲームの設定がタイトル画面からしかできない場合、デバッグやテストを行う際に毎回タイトル画面を経由する必要が出てくると思われます。
そこでScriptableObjectの出番です。
アセットフォルダ内のデータを参照する形になるため、変数を書き換えて実行すると、すぐに設定が変更されます。
ベストプラクティス
私は、ScriptableObjectを使用することを個人的に推奨しています。
public staticな変数、シングルトンパターンの問題点は以下になります。
どこからでも参照、操作可能な変数であるため、一見扱いやすいと思うかもしれません。
しかし、実態はその逆です。
多くのロジックでグローバル変数を参照し、値を変更していると、どこで、どのタイミングで値が書き換わったのか把握が非常に困難になります。仙場大也. 良いコード/悪いコードで学ぶ設計入門. 技術評論社. 2022, p185
このように、どこからでも参照できるということはバグの原因となりやすい、とも言えます。
もちろんどちらの方法も多くのメリットを持っていますが、実装の難易度と扱いやすさのバランスが取れているのがScirptableObjectだと思います。
おわりに
これは現時点での私の考えるベストプラクティスであるため、今後学習を進めることで考えが変わるかもしれません。