Unity5 Editor ReorderableList
ReorderableList
整理使用 Unity ReorderableList 筆記,希望以後有範本可以直接複製貼上,不用每次都要重新找文章,依樣畫葫蘆重做一次。
// Path: /Scripts/ShopMenu.cs
using UnityEngine;
public class ShopMenu : MonoBehaviour
{
public Drink[] Drinks = new Drink[0];
[System.Serializable]
public struct Drink
{
public string Name;
public float Price;
public Color Color;
}
}
參考上述範例程式碼,該程式碼在 Unity Editor 中,預設的呈現編輯器如下圖:
這樣的編輯器不是不好,只是很陽春且不好瀏覽編輯,此外也不容易修改各個元素順序。 在 Unity 4.5 後,在 UnityEditor.Internal 提供一個非常好的 class,Reorderablelist,讓我們可以修改其陽春編輯器如下圖,使用近似於表格編輯方式,而且可以輕易的調整順序:
Reorderablelist 屬於 UnityEngineInternal namespace,該 class 並沒有任何的使用說明文件,不過總是有人分享其使用方法 [0]。該編輯器程式碼如下,記得 Drinks 欄位(Field)要加上 [HideInInspector]
,來隱藏預設的編輯器。
// Path: /Editor/ShopMenuEditor.cs
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
[CustomEditor(typeof(ShopMenu))]
[CanEditMultipleObjects]
public class ShopMenuEditor : Editor
{
ReorderableList list;
void OnEnable()
{
var serProp = this.serializedObject.FindProperty("Drinks");
this.list = new ReorderableList(serProp.serializedObject, serProp, true, true, true, true);
this.list.drawElementCallback = this.DrawListElement;
this.list.drawHeaderCallback = this.DrawListHeader;
}
void DrawListHeader(Rect rect)
{
var spacing = 10f;
var arect = rect;
arect.height = EditorGUIUtility.singleLineHeight;
arect.x += 15;
arect.width = 100;
EditorGUI.LabelField(arect, "Name");
arect.x += arect.width + spacing ;
arect.width = 100;
EditorGUI.LabelField(arect, "Price");
arect.x += arect.width + spacing;
arect.width = 100;
EditorGUI.LabelField(arect, "Color");
}
void DrawListElement(Rect rect, int index, bool isActive, bool isFocused)
{
var spacing = 10f;
var arect = rect;
var serElem = this.list.serializedProperty.GetArrayElementAtIndex(index);
arect.height = EditorGUIUtility.singleLineHeight;
arect.width = 100;
EditorGUI.PropertyField(arect, serElem.FindPropertyRelative("Name"), GUIContent.none);
arect.x += arect.width + spacing;
arect.width = 100;
EditorGUI.PropertyField(arect, serElem.FindPropertyRelative("Price"), GUIContent.none);
arect.x += arect.width + spacing;
arect.width = 100;
EditorGUI.PropertyField(arect, serElem.FindPropertyRelative("Color"), GUIContent.none);
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
this.serializedObject.Update();
var property = list.serializedProperty;
property.isExpanded = EditorGUILayout.Foldout(property.isExpanded, property.displayName);
if (property.isExpanded)
{
this.list.DoLayoutList();
}
this.serializedObject.ApplyModifiedProperties();
}
}
- Line 6: 標示此為 ShopMenu 物件客製化的編輯器
- Line 7: 允許多物件同時編輯
- Line 14: 使用 SerializeObject 取得 SerializedProperty,自訂編輯器必要使用這一個方便的物件
- Line 24: Unity 編輯器預設的單行高度
- Line 44: GetArrayElementAtIndex 取得第 n 個元素的 SerializedProperty,再使用該 property 去編輯該元素的 SerializedProperty
- Line 48: 使用 EditorGUI.PropertyField 來繪製該欄位的編輯功能
- Line 59: 自訂編輯器的起手式,Override 實作自己的編輯器
- Line 61: 繪製預設的編輯器
- Line 63: 開始繪製編輯器前,先更新 SerializeObject 的資料
- Line 72: 結束繪製編輯器後,將編輯器所做的修改,寫回 SerializeObject
ReorderableList 實際上提供許多 Callback functions 可以自訂更多編輯器項目,以上範例僅用到 drawElementCallback 以及 drawHeaderCallback 來繪製,其他更進一步說明,請參考 [0]。
更簡單的使用方法
ReorderableList 協助建立可重新排序的編輯器,功能是非常好用。但對於簡單的結構來說,要寫這麼多程式碼來達成這樣的編輯器,要處理排版(Layout)設定欄位寬度就一個麻煩,該些程式碼顯得又臭很長。只是希望改成行列(Rows)顯示,外加一個標頭列(Header),像是表格方式編輯方式而已。而且每增加一個參數就得要修改編輯器程式碼一次,有沒有自動化的方法?
思考半天,總算是弄出一個 Utility,用於建立能夠自動排版的 ReorderableList,程式碼以及使用方法請參考放在 Github 的 ReorderableListUtility 頁面。
透過 ReorderableListUtility,現在只要幾行就可以建立表格式編輯頁面。
// Path: /Editor/ShopMenuEditor.cs
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(ShopMenu))]
[CanEditMultipleObjects]
public class ShopMenuEditor : Editor
{
private ReorderableList list;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
this.serializedObject.Update();
ReorderableListUtility.DoLayoutListWithFoldout(this.list);
this.serializedObject.ApplyModifiedProperties();
}
private void OnEnable()
{
this.list = ReorderableListUtility.CreateAutoLayout(this.serializedObject.FindProperty("Drinks"));
}
}
但這還是有點麻煩,如果 PropertyDrawer 能夠針對 Array 進行繪製處理,那也許可以改用 PropertyAttribute 來完成…,像以下這個樣子:
// Path: /Scripts/ShopMenu.cs
using UnityEngine;
public class ShopMenu : MonoBehaviour
{
[TableStyleArrayEditor]
public Drink[] Drinks = new Drink[0];
// ...
}
沒有留言: