【Unity】HideInInspectorとSerializeFieldの興味深い関係

【Unity】HideInInspectorとSerializeFieldの興味深い関係

スクリプトを作成した時、publicなフィールドはInspectorウィンドウに表示され、そこから動的に値を変えることができます。

これはとても便利な機能なのですが、場合によってはpublicなフィールドだけどInspectorに表示させたくない! なんてこともあります。

そこで登場するのが[HideInInspector]のAttribute(属性)。これがついているフィールドはInspectorに表示されなくなるんです。

Attributeについて触れたことのある人だと、Inspectorに表示してくれる[SerializeField]とどっちが強いの? なんて疑問も浮かぶかもしれません。その点についてもこの記事の中で扱っていきます。

 

環境

macOS 10.13 High Sierra

Unity2018.1.0f2

HideInInspectorの効果

HideInInspectorは直訳すると「隠す」「Inspectorの中で」となります。言葉通り、Inspectorウィンドウに表示しないようにするAttributeです。

Attributeとは、クラスやメンバ変数(フィールド)に与える特別な動作指示のこと。[]で囲って使います。

通常、MonoBehaviourを継承して作成されたスクリプトに含まれるpublicなフィールドは、Inspectorウィンドウに表示され動的に値を変更することが可能です。

publicだと勝手に表示されてしまいますが、テストプレイ中に意図しない値に変えるとゲームの進行に悪影響を及ぼすフィールドもあるかもしれません。

特にInspectorで表示されるフィールドは、ダイレクトに値が変わるためかgetter/setterが動かず、そこで値の検証をしていると致命的。

でも外部のスクリプトからも値を参照しているのでprivateにもできない、なんて状況もあるかもしれません。

そこでこの[HideInInspector]を使うことで、外部のスクリプトからアクセスできるpublicのまま、Inspectorには表示しないようにできます。

上記のスクリプトをアタッチしたゲームオブジェクトを選択してInspectorウィンドウを見ると、[HideInInspector]をつけたlevelフィールドが表示されなくなっています。

levelフィールドは表示されない
levelフィールドは表示されない

 

levelフィールドそのものは外部のスクリプトからアクセス可能です。以下の画像はVSCodeでスクリプトを編集している画面ですが、エラーは表示されません。

publicなのでアクセスできる
publicなのでアクセスできる

 

[HideInInspector] VS [SerializeField]

publicなフィールドでもInspectorで非表示にする[HideInInspector]と、privateなフィールドでもInspectorに表示させる[SerializeField]、どっちが強いんでしょうね。

Attributeを知り始めた人なら疑問に思うポイント。

もっと詳しく知っている人なら、「強いも何も、そもそも役割が……」と、すぐにどうなるか予想がつく場所でもあります。

実際にやってみた

何は無くとも、実際に動かしてみるのが一番の近道。まずは上で使ったスクリプトを編集して、以下の4パターンを追加します。

  1. privateに[SerializeField]をつける
  2. privateに[SerializeField]をつけない
  3. privateに[SerializeField]と[HideInInspector]の両方をつける
  4. privateに[SerializeField]と[HideInInspector]の両方をつける(Attributeの順番を変える)

[SerializeField]の動作も確認しつつ、[HideInInspector]と同時に指定した場合の動作を確認します。このスクリプトのコンパイルが終わると、Inspectorウィンドウでは以下のように表示されました。

HideInInspectorの勝利
HideInInspectorの勝利

 

追加したうち、元からprivateなmpフィールドが表示されないのはいいとして、[HideInInspector]がついているものは順番に関係なく表示されません。

結果として、[HideInInspector]が指定されていればpublicだろうと[SerializeField]だろうとInspectorウィンドウで表示されないんです。

シリアライズの目的

でもちょっと待った。

そもそもシリアライズの目的って、Inspectorウィンドウに表示することがメインじゃないんです。

シリアライズの詳細についてはUnityマニュアルの『スクリプトのシリアライゼーション』をご覧いただくとして、ざっくり説明するとデータ構造やオブジェクトの状態をUnityが保存し、後で使えるように変換する処理のこと。

オブジェクトの状態を保存するから、変数の値が保持されているんです。

んで、Unity側で値を保持できるから、じゃあついでにUnity側の機能であるInspectorで変数を表示して編集できるようにしよう、となっているんです(想像)

こうすればスクリプトを変更することなく値の調整ができますもんね。

この時、Inspectorにフィールドを表示するというのはサブの機能で、[HideInInspector]はこのサブの機能に対して働いているんです。

……ってことは、シリアライズ自体はされたままのはず。

 

シリアライズされていることの確認

[HideInInspector]はInspectorに表示しないだけ。シリアライズ(オブジェクトの状態保存)を防いでいる訳ではありません。

とすると、privateのフィールドに[SerializeField]をつけたら、たとえInspectorに表示されなくともUnity内部で値を保持しているはず。

Inspectorからは変更できないのですが、Unityに内部的に値を保持させる方法はあります。

それは宣言時に初期値を設定しておくこと。と言っても実は上に掲載したスクリプトでは初期値を明示的にセットしていないので0になっています。

ここで、スクリプトを以下のように編集して、値がどうなっているか確認します。追加したのはStart()の中身。

まずは初期値をセットしないパターン。ゲームを実行すると、mpとatkの両方の値が0になっています。

初期値の0が出力された
初期値の0が出力された

 

続いて、スクリプトの中で初期値をセットします。シリアライズの対象フィールドの場合、スクリプトのコンパイル後にフィールド名が変わっていなければ、Unityが保持しているコンパイル前の値がセットされます。

上記のスクリプトなら、値が保持されていれば0が、保持されていなければ711が出力されます。

いい気分
いい気分

 

privateフィールドのmpはスクリプトの中で初期値がセットされ、その値を出力しました。

対して[SerializeField]をつけたatkは、初期値をセットするようにスクリプトを修正しても既存のコンポーネントには反映されず、以前の値を保持しています。

見事にシリアライズ(オブジェクト状態の保持)がなされていますね。

Inspectorウィンドウでは値を変えられず、スクリプトの中で初期値を設定しても以前の値が残っている状態。

……バグの元だこれ!!

実験目的以外で[SerializeField]と[HideInInspector]を共存させることはそうそう無いと思いますが、念のためご注意を。

まとめ

[HideInInspector]はInspectorへの表示関係に対して働きます。publicや[SerializeField]がついていても、Inspectorには表示されなくなります。

また、[SerializeField]をInspectorに表示させるためのAttributeだと思ってしまうとハマる可能性があるので注意。

シリアライズする対象だからこそ、結果的にInspectorに表示している、という因果関係を間違えないようにしないといけません。

なお、[ShowInInspector]といったHideの逆バージョンのAttributeは存在しないようです。

おそらくprivateなフィールドをInspectorに表示したところで、シリアライズの対象でなければ値を保存できないので無意味、ってことなんだと思います(想像)

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!