DrawDefaultInspectorで使ってたSerializedPropertyもうちょっと掘り下げ。
以前ネタにしたDrawDefaultInspector()の実装を読み直します。
internal static bool DoDrawDefaultInspector(SerializedObject obj) { EditorGUI.BeginChangeCheck(); obj.Update(); SerializedProperty iterator = obj.GetIterator(); bool enterChildren = true; while (iterator.NextVisible(enterChildren)) { EditorGUILayout.PropertyField(iterator, true, new GUILayoutOption[0]); enterChildren = false; } obj.ApplyModifiedProperties(); return EditorGUI.EndChangeCheck(); }
ン?SerializePropertyはSerializedObject()のイテレータとして振舞います。ただ、NextVisible()で前進する時、子階層に入るかどうかしか指定できません。
「SerializePropertyは終端をどのように設定されるの?同階層の終端時、上階層に戻っちゃうの?戻っちゃわないの?どこまでイテレートするの?」
答え:基本的に終端情報は持たず、SerializedObjectの最後まで進む。
だいたいSerializedObjectってコンポーネント丸ごとの情報です。では当然ながら気になる次の疑問。
ComponentにAとBというクラスフィールドがあって、それらのクラスの中にメンバがあるが、Aのクラスメンバだけをイテレートしたい場合、NextVisible()するだけじゃBの最後までイテレーションが進んじゃう、どうしたら?
これについてはSerializedProperty.depthまたはSerializedProperty.propertyPathメンバを使います。depthはネスト階数。SerializedProperty.propertyPathについては、公式サイト曰く
Full path of the property. (Read Only)
一行。わっかりやすーい!もうちょっと書いてほしいのですが、こんな感じ?
- コンポーネントにおけるAという名前のフィールドをイテレートする時、SerializedProperty.propertyPathは"A"
- コンポーネントにおけるA.firstMemberという名前のフィールドをイテレートする時、SerializedProperty.propertyPathは"A.firstMember"
区切り文字がドットである事くらいは明記するか、文字定数を定義しておくれ…ナンカコワイヨゥ。
あえてパスを比較するなら、こんな感じ。
System.String rootPath = iterator.propertyPath + "."; // もしiteratorがコンポーネントの中で、Aという名前のフィールドだったら"A"なので、"A."ではじまるメンバだけ処理する bool enterChildren = true; while (property.NextVisible(enterChildren)) { if (property.propertyPath.StartsWith(rootPath) == false) break; // パスが不一致、離脱! EditorGUILayout.PropertyField(property, true, new GUILayoutOption[0]); enterChildren = false; }
もちろんdepth比較する方が処理も軽いです。
int rootDepth = iterator.depth; // この階層に戻ってきたら抜ける bool enterChildren = true; while (property.NextVisible(enterChildren)) { if (property.depth <= rootDepth) break; // 離脱! EditorGUILayout.PropertyField(property, true, new GUILayoutOption[0]); enterChildren = false; }
これと下記の3系統をおさえておけば、独自のインスペクタ描画はかなり出来るようになるはず。
SerializeProperty.isArray
このプロパティはシリアライズされた配列かどうか返します。配列操作については、ほにゃららArrayElementなんちゃらというメソッドが存在しているのでそれらをSeeAlso。
ClearArray(), DeleteArrayElementAtIndex(), GetArrayElementAtIndex(), InsertArrayElementAtIndex(), MoveArrayElement()
一揃いあるもののUnityはこういうところがホントなんつーかセンスがアレな感じです。
SerializeProperty.hasChildren
このプロパティに子階層が存在するか、ようするにクラス型で内部にメンバが存在してるか返します。この子階層に限り処理をする方法は上述したとおり。
SerializeProperty.isExpanded
表示上重要。配列やクラスはインスペクタで折りたためますが、開いているならtrueを返します。もちろん閉じてる時は子階層をDrawする必要はありませんが、自分で対応する必要があります。「閉じてる時にDrawするな、開いてたらDrawしないとおかしいぞ」というメンバです。
よしこれでPropertyDrawerシリーズは終了だ、と思いきや、実は配列表示カスタマイズするのに、もうひとつやんなきゃいけないトピックを忘れていました。次回に続く…。