【Unity】インスペクターに値が表示されない！

この記事は[Unity Advent Calendar 2022](https://qiita.com/advent-calendar/2022/unity)（その2） 20日目の記事です。
昨日は[@copo](https://qiita.com/copo)さんの[「【Unity】シェーダで穴をあけるマッピング」](https://coposuke.hateblo.jp/entry/2022/12/20/021532)でした！
こんなこともできるんだなぁと非常に楽しく拝見しました！Unityでも深淵を覗くことができそうですね！

# はじめに
業務中、なぜかインスペクターに表示されないフィールドがあり、publicかSerializeFieldをつけておけばだいたいオッケー！と思う気持ちを改めて、どういったものが表示されるのか、されないのかまとめてみました。
また、今回自分がハマったポイントと合わせてご紹介しようと思います。


# 環境
| Unity |  2021.3.13f1 |
|:-:|:-:|


# そもそもどういったものがインスペクターに表示できるのか
インスペクターに表示するには**シリアル化**の対象とする必要があります。
シリアル化とは、保存して後で再構成できる形式に変換する自動処理です。
これによってプレハブとして保存したり、インスペクターに値を設定するとUnity再起動しても同じ値が表示されていたりするわけです。


:::note 
以下条件を**全て**満たすことで、シリアル化の対象となります。
1. publicであるか、SerializeField属性を持っていること。
2. 静的(static)でないこと。
3. constではないこと。
4. 読み取り専用（readonly）でないこと。
5. シリアル化可能なフィールド型であること。
:::

> 5\. シリアル化可能なフィールド型であること。

こちらについては、以下いずれかの型である必要もあります。
- プリミティブなデータ型であること。
    - int、float、double、bool、stringなど
- Enum 型 (32 バイト以下) であること。
- 固定サイズバッファであること。
- Unity の組み込み型であること。
    - Vector2、Vector3、Rect、Matrix4x4、Color、AnimationCurveなど
- Serializable 属性をもつカスタム構造体であること。
- [UnityEngine.Object](https://docs.unity3d.com/ja/current/ScriptReference/Object.html) から派生するオブジェクトへの参照であること。
- Serializable 属性を持つカスタムクラスであること。
    - 自作したクラスはカスタムクラスと呼ばれSerializable属性を付与することでシリアル化することができます。    （[カスタムクラスのシリアライズ](https://docs.unity3d.com/ja/current/Manual/script-Serialization.html#CustomClasses)） 
- 上記のフィールド型の配列であること。
    - 一次元配列のみ、後述の多次元配列はできない。
- 上記のフィールド型の List<T> であること。


# シリアル化の条件を満たしている（はず）なのに表示されない！:angry:
よくある？表示されないパターンを挙げてみます。

## シリアル化できない型を指定している
先程の、シリアル化可能なフィールド型であること。の条件が細かく別れているので、うっかりできると思っているものができないかもしれません。
例えば以下のリストはそのままでは表示することができません。

- 多次元配列
    例）`public int[,] multiArray;`
- ジャグ配列（配列の配列）
    例）`public int[][] jaggedArray`
- ネストしたコンテナ型
    例）`public List<List<int>> nestedList;`
- 辞書
    例）`public Dictionary<int, string> dictionary;`

ただ、こちらは一応回避方法もあります。

***回避方法1.クラスまたは構造体でネストされた型をラップ***
カスタムクラス内に値を持ち、そのクラスを更にリストとして持つと表示することができます。
``` .cs
[Serializable]
public class Data
{
    public List<int> ids;
}

public List<Data> datas;
public List<List<int>> datas2
```


***回避方法2.シリアル化コールバック（[ISerializationCallbackReceiver](https://docs.unity3d.com/ja/current/ScriptReference/ISerializationCallbackReceiver.html)）***
少々長くなりそうなため割愛しますが、シリアライズ、デシリアライズ時に発火されるコールバックで例えばstringなどのシリアル化可能な値から任意の値にパースして扱うという方法になります。

## HideInInspectorまたはNonSerializableの属性が設定されている
こちらはあえてInspectorに表示されないようにする設定になります。
やっていることは、****HideInInspector****はインスペクターに表示されないようにする、****NonSerializable****はシリアル化されないようにする、と意味は違いますが結果的にはどちらもインスペクター上に表示されないようになります。

## コンパイルエラーが発生している
こちらは実行もできないため気づきやすいかもしれませんね。
コンパイルエラーが発生していると、新たに追加や変更をした箇所の更新が行われません。
Consoleの表示を確認してみましょう。

## CustomEditor属性でインスペクターで表示する内容の変更が行われている
Unityでは、CustomEditor属性を付与することでエディター表示を変更し、独自のインスペクター表示を構成することができます。
ボタンをつけてメソッドを実行したり、表示をリッチにしたりできるため便利ではあるのですが、ちょっとした罠もあります。

### 罠について
今回私がハマったポイントについて。
以下は、[Unityのカスタムエディターのドキュメント](https://docs.unity3d.com/ja/2022.2/Manual/editor-CustomEditors.html)に乗っているサンプルの一部です。
``` LookAtPoint.cs
using UnityEngine;
public class LookAtPoint : MonoBehaviour
{
    public Vector3 lookAtPoint = Vector3.zero;

    void Update()
    {
        transform.LookAt(lookAtPoint);
    }
}
```

``` LookAtPointEditor.cs
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(LookAtPoint))]
[CanEditMultipleObjects]
public class LookAtPointEditor : Editor 
{
    SerializedProperty lookAtPoint;
    
    void OnEnable()
    {
        lookAtPoint = serializedObject.FindProperty("lookAtPoint");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(lookAtPoint);
        serializedObject.ApplyModifiedProperties();
    }
}
```
GameObjectにLookAtPointEditorをアタッチすることで、このように表示されるのですが。
![LoookAtPoin1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2125059/e215a238-5d93-7ff0-60c5-700b73f56c78.png)

例えばポイントの名前を追加しようと思い、先程挙げたシリアル化の条件を満たしたフィールドを追加します。
``` LookAtPoint.cs
    // 〜略〜
    public Vector3 lookAtPoint = Vector3.zero;
    public string pointName; // 追加

    void Update()
    {
    // 〜略〜
```

もちろん表示されませんね。
![LoookAtPoin2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2125059/fc7687d0-3835-eebd-2bd6-5f480816ca88.png)(一応先ほどと違う画像です。)

LookAtPointEditor側で表示したいものを追加してあげなければなりません。
複数人で作業している時はもちろん、数ヶ月後の自分は他人なのでカスタムエディタを利用していることを忘れているかもしれません。
クラスのコメントに明記する、カスタムエディタだとわかりやすいデザインにする、など気づきやすい工夫が必要かもしれません。


# 終わりに
知っていないとなかなか気づかない問題もあるかと思います。
そのため、予めこういう場合に表示されるんだなということを知っておき、順番に原因をあたっていくことが大切だなと改めて思いました。
もしこの記事で困っている方が解決して年を越せますと幸いです。:sunrise:

明日は、 [@hoktist](https://qiita.com/hoktist)さんの[「Unity + Arduinoできっちりオンオフのできる双方向通信をしよう」](https://qiita.com/hoktist/items/8e3ae56fd5f12c93dea8#_reference-9edf4611feb00bcb7798)です！

# 参考
https://docs.unity3d.com/ja/current/Manual/script-Serialization.html

https://docs.unity3d.com/ja/current/ScriptReference/ISerializationCallbackReceiver.html

https://nekojara.city/unity-script-serialization

https://qiita.com/k_yanase/items/3bb59963a6f477f5a523