很多專案在測試時都會需要測試資料,為了專案測試特別做了一個測試資料的編輯器讓企劃去填入測試參數去測試,這次用到了自定義的Reorderable List來實作
一般的Reorderable List,這裡用Inspector做範例
一般簡單的Reorderable List很直覺,就是把list參數一排一排的列出來,每一列高度也是如圖的一個參數的預設高度,預設的Header是Serialized Property,每個element的Label也都是"Element X"
using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEditorInternal; using System; public class Test : MonoBehaviour { public List<string> list = new List<string>(); } [CustomEditor(typeof(Test))] public class TestEditor : Editor { SerializedProperty m_List; ReorderableList m_ReorderableList; private void OnEnable() { m_List = serializedObject.FindProperty("list"); m_ReorderableList = new ReorderableList(serializedObject, m_List, true, true, true, true); m_ReorderableList.drawElementCallback = DrawElement; } public override void OnInspectorGUI() { serializedObject.Update(); m_ReorderableList.DoLayoutList(); serializedObject.ApplyModifiedProperties(); } void DrawElement(Rect rect, int index, bool isActive, bool isFocused) { SerializedProperty element = m_List.GetArrayElementAtIndex(index); EditorGUI.PropertyField(rect, element); } }
幾個基礎的點介紹一下:
- new ReorderableList(serializedObject, m_List, true, true, true, true) :
serializedObject (序列化物件)這裡是Editor把Test類別轉為序列化物件,m_List為Test類別裡的List參數,後面就是功能的開啟,分別是拖拉,Header的顯示,增加和移除按鈕
- void DrawElement(Rect rect, int index, bool isActive, bool isFocused):
實作繪製List項目,參數分別m_ReorderableList的Rect,項目的index,是否Active和是否Focuse,把實作fuction給drawElementCallback繪製
- serializedObject.Update():
重新進行資料更新
- m_ReorderableList.DoLayoutList():
繪製List
- serializedObject.ApplyModifiedProperties():
資料更動進行寫入
但在我的測試資料中,並不只有這些參數,而參數的檢查也需要自己實作,這樣簡單的實作就不夠用了,就需要稍微複雜一點的實作
依據上方再作修改
using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEditorInternal; using System; using System.Text.RegularExpressions; public class DebugDataSettings : MonoBehaviour { public List<DebugData> m_DebugDatas = new List<DebugData>(); } [System.Serializable] public class DebugData { public string m_StageCollectionMoney; public List<int> m_CurMachinesLvList = new List<int>(); } [CustomEditor(typeof(DebugDataSettings))] public class TestEditor : Editor { SerializedProperty m_list; ReorderableList reorderableList; private Regex m_Regex = new Regex("(^[0-9]*[1-9][0-9]*$)|(^([0-9]{1,}[.][0-9]*)$)"); private void OnEnable() { m_list = serializedObject.FindProperty("list"); m_ReorderableList = new ReorderableList(serializedObject, serializedObject.FindProperty("m_DebugDatas") , true, true, true, true) { //自定義列表名稱 drawHeaderCallback = (Rect rect) => { GUI.Label(rect, "Stage Data"); }, //自定Element的高度 elementHeightCallback = ElementHeightCallback, //自定義繪製列表元素 drawElementCallback = DrawListElement, //當添加新元素時的回調函數,自定義新元素的值 onAddCallback = AddListEvent, //當刪除元素時候的回調函數,實現刪除元素時,有提示框跳出 onRemoveCallback = RemoveListEvent }; } public override void OnInspectorGUI() { serializedObject.Update(); var styleTitle = new GUIStyle(GUI.skin.label); styleTitle.normal.textColor = new Color(248 / 255f, 71 / 255f, 126 / 255f, 1); reorderableList.DoLayoutList(); serializedObject.ApplyModifiedProperties(); } // 計算每個Element的高度 private float ElementHeightCallback(int index) { // Set the height of each row dynamically depending on the height of the names entries. float space = 10; SerializedProperty element = m_ReorderableList.serializedProperty.GetArrayElementAtIndex(index); SerializedProperty prop1 = element.FindPropertyRelative("m_StageCollectionMoney"); SerializedProperty prop2 = element.FindPropertyRelative("m_CurMachinesLvList"); return EditorGUI.GetPropertyHeight(prop1) + space + EditorGUI.GetPropertyHeight(prop2) + EditorGUIUtility.standardVerticalSpacing; } //繪製列表元素 void DrawListElement(Rect rect, int index, bool isActive, bool isFocused) { float space = 10; var arect = rect; //根據index獲取對應元素 var serElem = m_ReorderableList.serializedProperty.GetArrayElementAtIndex(index); var prop1 = serElem.FindPropertyRelative("m_StageCollectionMoney"); var prop2 = serElem.FindPropertyRelative("m_CurMachinesLvList"); // By default, the array dropdown is offset to the left which intersects with // the drag handle, so we can either indent the array property or inset the rect. EditorGUI.indentLevel++; var rectTemp = arect; rectTemp.height = EditorGUI.GetPropertyHeight(prop1); string temp = EditorGUI.DelayedTextField(rectTemp, $"Collection Money:", prop1.stringValue); if (!m_Regex.Match(temp).Success) { EditorUtility.DisplayDialog("Error", "Money Format Incorrectly !", "Cancel"); } else { prop1.stringValue = temp; } arect.y += EditorGUI.GetPropertyHeight(prop1) + space; EditorGUI.PropertyField(arect, prop2, new GUIContent($"Machines Level:"), true); EditorGUI.indentLevel--; } //當添加新元素時的回調函數,自定義新元素的值 private void AddListEvent(ReorderableList list) { if (list.serializedProperty != null) { list.serializedProperty.arraySize++; list.index = list.serializedProperty.arraySize - 1; SerializedProperty item = list.serializedProperty.GetArrayElementAtIndex(list.index); } else { ReorderableList.defaultBehaviours.DoAddButton(list); } } //當刪除元素時候的回調函數,實現刪除元素時,有提示框跳出 private void RemoveListEvent(ReorderableList list) { if (EditorUtility.DisplayDialog("Warnning", "Do you want to remove this element?", "Remove", "Cancel")) { ReorderableList.defaultBehaviours.DoRemoveButton(list); } } }
除了剛剛的基本介紹外,自定義了Header名稱,項目高度,項目繪製,增加事件,刪除事件
- 項目高度的部分依項目內容的高度做改變
- 在項目繪製的部分做值輸入的規則檢查,這裡用正則表達式做規則檢查
Regex m_Regex = new Regex("(^[0-9]*[1-9][0-9]*$)|(^([0-9]{1,}[.][0-9]*)$)")
m_Regex.Match(temp).Success 來檢查字串是否為正小數或正整數,不是的話跳出錯誤提示窗,字串回到還沒改之前
- 刪除事件,實現了在刪除項目時,跳出提示窗確認是否刪除
ReorderableList.defaultBehaviours.DoRemoveButton(list) 來做刪除
最後成效:
留言列表