当初はiPhoneXのSafeArea対応のための便利スクリプトを用意する予定でしたが、その過程で調べたHideFlagsの挙動が大変興味深かったため、そちらを纏めてみました。
前提
- Unity 2017.2.0p2 MacOS
- シーン上のGameObjectのhideFlagsに設定する
- "オブジェクト" = エディター上から見えるGameObjectとコンポーネントのまとまり (≒GameObject)
HideFlagsとは
HideFlagsはUnityのObjectが持つhideFlagsプロパティを設定するフラグです。
hideFlagsを設定すると、例えばオブジェクトをヒエラルキー上から不可視にしたりすることなどが出来ます。
HideFlagsの挙動
hideFlagsプロパテイ
- GameObjectのhideFlagsに値をセットすると、その値はGameObjectの持つ全てのコンポーネントにその値が上書きされる
- コンポーネントのhideFlagsに値をセットしても、その値はGameObjectや他のコンポーネントには影響しない
- GameObjectにコンポーネントを追加すると、その時のGameObjectのhideFlagの値が自動的にセットされる
以下の調査はGameObjectとコンポーネントのどちらにHideFlagsをセットされた時の挙動か区別していない点に注意してください。
HideFlags.HideInHierarchy
- オブジェクトがHierarchyビューに表示されなくなる
- オブジェクトをSceneビューから選択できなくなる
- スクリプトからSelection.objectに入れる事で選択状態にすることはできる
HideFlags.HideInInspector
- コンポーネントがInspectorビューに表示されなくなる
- オブジェクトの名前やActiveなどは表示されたまま操作可能
HideFlags.DontSaveInEditor
- オブジェクトがシーンに保存されなくなる
- オブジェクトを編集したり、ヒエラルキーを変更してもシーンにDirtyがつかなくなる
- アプリをビルドしたとき、オブジェクトがシーンに含まれなくなる
- 保存されていないシーン上で設定されていてもビルドに含まれない
-
DontSaveInBuild
と同じ
- Prefabにも保存されなくなる
- バグでPrefabに含まれてしまうバージョンもある (Issue Tracker)
- ルートに
DontSaveInEditor
が設定されたオブジェクトをPrefab化しようとするとエラーを出してくれる
HideFlags.NotEditable
- オブジェクトを選択した時のInspectorビューの表示が全て灰色になり編集できなくなる
- ヒエラルキーの移動は可能
- コンポーネントの値のコピーは可能
- Sceneビュー上でハンドルが表示されなくなる
HideFlags.DontSaveInBuild
- アプリをビルドしたとき、オブジェクトがシーンに含まれなくなる
- 保存されていないシーン上で設定されていてもビルドに含まれない
-
DontSaveInEditor
とは異なり、シーンには保存される
HideFlags.DontUnloadUnusedAsset
- オブジェクトやそのコンポーネントに影響は見られない
HideFlags.DontSave
-
DontSaveInBuild
とDontSaveInEditor
とDontUnloadUnusedAsset
の複合
HideFlags.HideAndDontSave
-
HideInInspector
以外の全ての複合
複合的な条件下で起きる現象
以下のいずれかの条件を満たすと、オブジェクトは「シーンに属さないオブジェクト」に変化しました。
-
[条件1] シーン直下にあり、
HideInHierarchy
,DontSaveInEditor
,DontSaveInBuild
が同時に設定される -
[条件2] シーン直下にあり、
DontSaveInEditor
が設定され、所属するシーンがアンロードされる
実験の結果、こうなったオブジェクトには以下のような特徴があるようです。
- 通常のオブジェクトと同様に動作する
- Sceneビュー、Gameビューにも表示される
-
Object.FindObjectOfType
で取得できない -
Scene.GetRootGameObjects
で取得できない -
GameObject.scene
が無効なシーンを指す -
シーンのアンロードで削除されない
- リコンパイル後も削除されない
- 選択してもGUI上から削除できない
- 明示的なDestroyやDestroyImmediateで削除できる
- [条件1] を満たさないHideFlagsが設定されるとその時アクティブなシーンの配下に戻る
要はリークオブジェクトになります。
消失したオブジェクトを探すには?
前述の通り、消失したオブジェクトはシーンから直接探すことはできません。
ですが、固有の名前やコンポーネントがついている場合は Resources.FindObjectsOfTypeAll
から探すことができます。
ただし、エディターは内部的に同様の「シーンに属さないオブジェクト」を複数保持しているため、 GameObject.scene
の値だけでこれを判別することはできません。
また、 生成時にstatic変数に参照を取っておくのは良い手ではありません。
リコンパイル時に参照が全てクリアされてしまい、リークオブジェクトとなってしまうからです。
ランタイムでも起きるのか?
DontSaveInEditor
の付いたオブジェクトはビルドに含まれません。が、ビルドされたアプリであっても実行時に DontSaveInEditor
をセットすることで同じ現象が再現するようです。
エディターコード外でHideFlagsを使う時はプリプロセッサで明確に処理を分けておくべきです。
バグなのか?
手元のマシンに入っているUnityで軽く試してみましたが、5.4, 5.5, 5.6, 2017.1 のいずれのバージョンでも再現しました。由緒正しい挙動のようです。
Unity Issue Tracker を調べてみると似た問題がヒットしました。
Issue Tracker - FINDOBJECTOFTYPE DOES NOT FIND GAMEOBJECT WITH HIDEFLAGS.DONTSAVE
条件が甘いですが、 Object.FindObjectOfType
で取得できないのは 仕様 らしいです。
オブジェクトが消えない方を現象として報告すれば通るでしょうか?
まとめ
長年悩みのPrefabInPrefab処理や、ちょっとした便利機能のつもりで作ったシングルトン(?)クラスなど、HideFlagsを利用していそうなコードに心当たりがあれば、一度見直しをしてみる事をおすすめします。
ハックは用法用量を守って楽しいUnityライフを!
追記(2017/12/20)
初めてバグレポート送ってみました。せめて仕様かどうか判明することを期待しています。
https://issuetracker.unity3d.com/issues/gameobject-with-hideflags-dot-dontsaveineditor-is-still-visible-in-a-newly-created-scene