始めに
ここのまとめで話したSerializedObjectを使用しないパターンで実装してみようかなと思います。因みにここでも実験をしながら結果を書き残そうと思います。
今回はこのサイトを元にプログラムを書いています。 www.shibuya24.info
第一段階
前回はドロップダウンメニューの表示で断念してしまいましたが、SerializedObjectを使わないパターンではあっさりと出来てしまいました。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditorInternal; using System; [CustomEditor(typeof(Monster))] public class MonsterEditor : Editor { ReorderableList reorderableList; SerializedProperty actionsProperty; List<MonsterAction> monsterActions; private void OnEnable() { var monster = target as Monster; monsterActions = monster.actions; if (reorderableList == null) { reorderableList = new ReorderableList(monsterActions, typeof(MonsterAction)); } reorderableList.elementHeight = 68; #region DrawElementCallback // 描画処理 reorderableList.drawElementCallback = (rect, index, isActive, isFocused) => { var action = monsterActions[index]; rect.height = 30; rect.width = 200; action.test = EditorGUI.IntField(rect, action.test); }; #endregion #region AddDropdownCallback reorderableList.onAddDropdownCallback += (rect, list) => { var menu = new GenericMenu(); menu.AddItem( new GUIContent("MonsterAction"), false, () => { monsterActions.Add(new MonsterAction()); }); menu.DropDown(rect); }; #endregion var defaultColor = GUI.backgroundColor; reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, "MonsterActions"); } public override void OnInspectorGUI() { reorderableList.DoLayoutList(); } }
前回のプログラムを使いまわしているので少し無駄な部分があるかもしれません。
そして、結果はこのようになりました。
第二段階
ではここからMonsterActionを継承した派生クラスを追加していきたいと思います。
まず初めに次のように変更を加えました。
reorderableList.drawElementCallback = (rect, index, isActive, isFocused) => { var action = monsterActions[index]; rect.height = 20; rect.width = 200; action.test = EditorGUI.IntField(rect, action.test); rect.y += 20; if (action.GetType() == typeof(ShotBallAction)) { var shotBallAction = action as ShotBallAction; shotBallAction.test2 = EditorGUI.IntField(rect, shotBallAction.test); } else if (action.GetType() == typeof(TestSubMonsterAction)) { var testSubMonsterAction = action as TestSubMonsterAction; testSubMonsterAction.testString = EditorGUI.TextField(rect, testSubMonsterAction.testString); } }; ...... ....... reorderableList.onAddDropdownCallback += (rect, list) => { var menu = new GenericMenu(); menu.AddItem( new GUIContent("MonsterAction"), false, () => { monsterActions.Add(new MonsterAction()); }); menu.AddItem( new GUIContent("ShotBallAction"), false, () => { monsterActions.Add(new ShotBallAction()); }); menu.AddItem( new GUIContent("TestSubMonsterAction"), false, () => { monsterActions.Add(new TestSubMonsterAction()); }); menu.DropDown(rect); };
そして結果です。
基底クラスでは入力フィールドが1つ、派生クラスではint型もしくはstring型の入力フィールドが追加され2つ表示されるようになりました。が、このGameObjectをコピーして出来たオブジェクトのInspectorではすべて基底クラスとして判断され、派生クラスの値が表示されなくなりました。
つまり、失敗です!
クラスの型を返す仮想関数を作る
GetType()が上手く動作していないと考えて次の関数を作成しました。
public virtual Type GetActionType() { return typeof(MonsterAction); }
派生クラスはこの関数をoverrideします。
public override Type GetActionType() { return typeof(ShotBallAction); }
こうすればちゃんとした型を取得でき、派生クラスの値が表示されると思っていましたが........
失敗に終わりました。
考察
前回も話しましたが、ContextMenuを使用して実際に値が保存されているのかを確かめる関数が用意されています。コピーされる前のオブジェクトで実行するとちゃんと派生クラスのデータまで出力することが出来ました。しかし、コピーされたオブジェクトでは基底クラスのデータまでしか出力されませんでした。
このことから、内部的にも基底クラスのデータまでしか保存されていないみたいです。もしかするとSerializedObjectを使用していないのが理由なのかもしれません。
最後に
只今かなりのデッドロック状態となっています。様々なサイトを参考にしてEditor拡張に挑戦しましたが、いくつかの派生クラスをReorderableListで表示するという情報はなく、あっても単一のクラスをReorderableListで保存する方法でした。
ここまでくると、ReorderableListでは出来ないと疑ってしまいそうです。(まだわからない)
これから考えうる対策としてはデザインパターンの変更(Factory Methodがいいかな?)や別の手法を試みることだと思います。今のところではいい案が思い浮かんでいないので考えついたらもう一度初めから取り組もうかなと思います。