UnityのSerialized Fieldとはなにか?
Unityを使っているとSerialized Fieldを使う例がたまに出てきます。
Privateに設定した変数はインスペクタには表示されませんが、Serialized Fieldに指定することで表示できるとのことです。
...が、表示するだけであればPrivate変数をインスペクタ表示用に準備したPublic変数に毎フレーム代入すればよいような気もします。
Serialized Fieldがなぜ使われれるのか、ちょっと調べてみました。
そもそもシリアライズってなんのこと?
シリアライズとは、Unity上ででオブジェクトの状態(変数の値など)をファイル(シーン、Prefab、アセットなど)に保存できる形式に変換する仕組み のことだそうです。
通常、public変数はUnityのシリアライズ対象となるため、エディタ上で値を設定すれば、その設定値はシーンファイルやPrefabに保存され、Unityを再起動しても保持されます。
一方、private変数の場合、通常はインスペクタに表示されず、値の設定することもできません。またシリアライズされず、値はコード内で設定したもののみが保持されます。
このため、エディタ上で変数を設定して再起動後も値を保持したい場合は、publicとして宣言するか、privateの場合は[SerializedField]を使用する必要があるわけです。
Serialized Fieldの使い方
using UnityEngine;
public class Example : MonoBehaviour
{
// private変数ですが、[SerializeField]属性を付けることでインスペクタに表示されます。
[SerializeField]
private int score = 10;
// GameObject型の変数も同様に、インスペクタから参照を設定できます。
[SerializeField]
private GameObject targetObject;
void Start()
{
// エディタ上で設定されたscoreの値を利用
Debug.Log("Score: " + score);
if (targetObject != null)
{
Debug.Log("Target Object Name: " + targetObject.name);
}
}
}
このような形で、変数宣言の一行前に [SerializeField] と記せばSerializeFieldが有効になります。簡単です。
Serialized Fieldを使うと何がうれしい?
① プログラマ以外がパラメータをさわる開発でうれしい
変数を外部からアクセス可能な変数(public)に設定すると、書き換えが自由になるため誤動作の原因となります。そのため、変数をprivateに設定し、外部からのアクセスはpublicに設定した関数経由でのみ許可するカプセル化が一般的です。しかしインスペクタに該当の変数の値は表示されず、インスペクタからの書き換えもできません。
Serialized Fieldを使えば変数はprivateのままで、Unityエディタのインスペクタに表示されます。
つまり、プログラマーにとっては内部実装を守りつつ、デザイナーにとってはコードに手を加えずにパラメータを直感的に調整できる、というイイトコドリとなります。
② データの永続性がうれしい
変数の値がエディタで直接編集できると、ゲームの挙動を確認・調整しやすくなりますが、Serialized Fieldであれば、前述のようにprivate変数であってもpublic変数同様に保持されます。
エディタ上で設定した値はPrefab(.prefab)やシーンなどに保存され、Unityが自動的に読み込んでくれるようになるそうです。
これにより、再起動やビルド後も設定が保持され、プロジェクト全体の一貫性が保たれます。
C++やPythonでは、同様の永続化を自前で実装する必要があるため、この仕組みは非常に手間を省ける点です。
インスタンスとシリアライズの関係
インスタンスの変数もシリアライズされるか?
ゲームオブジェクトにスクリプトがアタッチされると、そのスクリプトの「インスタンス」が作られます。
インスタンス生成時に、保存されているシリアライズされたデータから各フィールドの値が復元され、各インスタンスの変数に反映されます。
複数のゲームオブジェクトに同じスクリプト(クラス)がアタッチされていた場合でも、各インスタンスはシリアライズされた値を持ちます。エディタ上で異なる値を設定すれば、それぞれのインスタンスが異なる動作や状態を持つことができます。
コード側の初期値 vs シリアライズされた値
スクリプト内でフィールドに初期値を設定していても、エディタで値を変更すると、その変更値がシリアライズされ、インスタンス生成時にそちらが優先されます。つまり、コードに記述した初期値は、エディタで設定した値があれば上書きされます。とのことです。
結論
private変数をインスペクタで編集させたい場合には、[SerializeField]を指定すればOK。
調べながら書いた記事なので間違いや他のメリット、デメリットがあればぜひご指摘ください。
参考
この記事を書き終わってから改めて調べ直したら...よいまとめ記事がありました。
最初の検索でなぜ見逃したのか不明ですが、ありがとうございます。
本家マニュアル