Developer

【初心者Unity】[SerializeField]ってなに?
2021.12.23
Lv1

【初心者Unity】[SerializeField]ってなに?

1.はじめに

Unityのサンプルコード等を見ていると、変数の定義部分に[SerializeField]と書かれていることがあります。

// 例えばこんな感じ
[SerializeField] int num;

どうやらこれを記述するとInspectorから変数にアクセスできるようになりそうだ、ということはわかるものの、その意味やpublicとの違いはわからない…という方もいらっしゃるのではないでしょうか。

そうした疑問に答えるべく、本記事では、[SerializeField]の理解の元となるシリアライズ、およびpublicとの使い分けについて解説していきます。
頻出の機能ですので、是非使いこなせるようになりましょう!

2.シリアライズとは

[SerializeField] について理解するためには、シリアライズという用語について理解する必要があります。

C#におけるシリアライズとは、オブジェクトをバイト列に変換する操作を意味します。
「シリアル化」や「直列化」などと表記されることもあります。

変換後のバイト列は、データベースやファイルに保存することができます。
逆に言うと、オブジェクトを外部の記憶装置に保存するためには、一度バイト列等に変換する必要があります。
ちなみに、データベースやファイルに保存することは永続化と呼びます。
プレハブなどは永続化の代表的な例でしょう。

逆に、データベースやファイルに保存したバイト列をオブジェクトに変換することもできます。
この操作をデシリアライズと呼びます。

Unityにおいて重要な理解は、シリアライズ可能なフィールドはInspectorから操作が可能になる、ということです。

3.[SerializeField]とは

[SerializeField] は、そのフィールドがシリアライズ可能であることを表す属性です。
属性というのは、あまり深く考えずに、例えば変数等に何らかの性質を付加するもの、という程度の認識で大丈夫です。
今回はその「何らかの性質」が「シリアライズ可能」に当たる訳ですね。

使用場面としては、当該のフィールドにInspectorからアクセスしたい場合などが一般的です。
上で触れたように、シリアライズ可能なフィールドはInspectorから操作が可能になる、という特性があるためです。

例として、以下のようなスクリプトをゲームオブジェクトにアタッチします。

public class Script : MonoBehaviour
{
    [SerializeField] string sampleField;
}

[SerializeField] が付与されたフィールドがInspectorに表示されることが確認できます。

もっとも、[SerializeField] を付ければ何でもInspectorからアクセス可能になる訳ではありません。

以下は、シリアライズができない例です。
これらを記述した場合、エラーは出ないものの、Inspectorにも表示されません。

public class Script : MonoBehaviour
{
    // staticなフィールド
    [SerializeField] static string sampleField_1;

    // プロパティ
    [SerializeField] string sampleField_2 { get; set; }

    // UnityEngine.Objectを継承していない自作クラス型
    [SerializeField] Sample sampleField_3;
}

class Sample { }

4.publicとの使い分け

単にInspectorからフィールドにアクセスするだけであれば、publicアクセス修飾子を付与する方法でも同様のことができます。
これは、Unityがpublicなフィールドをシリアライズの対象であると認識するためです。
では、publicではなく[SerializeField] を使うケースとはどのようなものでしょうか。

以下のシナリオを考えてみます。

・AさんとBさんが共同でゲームを開発しています。
・AさんはScriptAを、BさんはScriptBをそれぞれ担当しています。
・AさんはScriptA内のフィールド:fieldAに対して、Inspectorからアクセスしたいと考えました。
・ただし、fieldAの値は他のクラスから変更できないようにしたいです。
・BさんはScriptB内で、ScriptAを使用しています。
・Aさんは、fieldAの値を変更して欲しくないことをBさんに伝えられていません。

publicアクセス修飾子を使用した場合
InspectorからfieldAにアクセスする方法について、
まずは、publicアクセス修飾子を使用した場合を考えてみましょう。

ScriptAは以下のようになります。

using UnityEngine;

public class ScriptA : MonoBehaviour
{
    public int fieldA;
}

Bさんの作業の様子を見てみます。
VisualStudioの補完機能で、ScriptAオブジェクトのサジェストにfieldAが表示されています。

これを見たBさんはこう考えるかもしれません。
「アクセスが許可されている、ということはfieldAは書き換え可能なんだな。」

これを防ぐためにはアクセス修飾子をprivate等に変更する必要がありますが、それではInspectorからアクセスすることができなくなってしまいます。

[SerializeField] を使用した場合
次に、[SerializeField] を使用した場合を考えてみましょう。
ScriptAを以下のように書き換えます。

using UnityEngine;

public class ScriptA : MonoBehaviour
{
    [SerializeField] private int fieldA;
}

privateなフィールドに対して [SerializeField] を付与しています。
この場合でも、fieldAにはInspectorからアクセスすることができます。

また、アクセス修飾子はあくまでprivateなので、ScriptBからの書き換えを防ぐこともできます。

このように、
・Inspectorからアクセスしたい
・他のクラスからの書き換えを防ぎたい

という、相反する要求がなされる場合は、publicアクセス修飾子ではなく [SerializeField] を使用する必要があるのです。

5.(おまけ)publicだけどInspectorには表示したくない!

上の例の逆パターンです。
アクセス修飾子はpublicだけど、Inspectorからはアクセスできないようにしたい!という場合はどうすれば良いのでしょうか。

こうしたケースに適した属性が用意されています。
[System.NonSerialized]です。

先ほどのScriptAで試してみます。

using UnityEngine;

public class ScriptA : MonoBehaviour
{
    public int fieldA;
    [System.NonSerialized] public int fieldB;
}

見事、publicなfieldBをInspectorに表示しないことに成功しました。
…こうしなくてはならないケースがあまり想像できませんが、探してみるとあるものですね。

6.おわりに

[SerializeField] について解説しました。

フィールドをInspectorから弄ることができると何かと便利です。
ゲーム実行中も値を変更できるし、ビルドの待ち時間もないし…。

一方で、アクセス修飾子も重要な機能です。
中々一人で実装していると気にならないかもしれませんが、安全なコーディングをする上で非常に使い勝手が良いです。

[SerializeField] は、こうした2つの機能が無理なく共存するための懸け橋となる機能です。
見慣れていないと何やら難しげに感じてしまうかもしれませんが、敬遠せずに使いこなしてあげてくださいね!

 

連載目次リンク

「初心者のための」Unityゲーム制作 目次
 

Unity実践編 - 目次リンク

実践Unityゲームプログラミング 連載目次