1. 繼承Editor提澎,重寫OnInspectorGUI方法
效果
實(shí)現(xiàn)
定義一個(gè)測試類TestClass诉濒,一個(gè)可序列化類DataClass
[CreateAssetMenu]
public class TestClass : ScriptableObject
{
[Range(0, 10)]
public int intData;
public string stringData;
public List<DataClass> dataList;
}
[System.Serializable]
public class DataClass
{
[Range(0, 100)]
public int id;
public Vector3 position;
public List<int> list;
}
//指定類型
[CustomEditor(typeof(TestClass))]
public class TestClassEditor : Editor
{
SerializedProperty intField;
SerializedProperty stringField;
void OnEnable()
{
//獲取指定字段
intField = serializedObject.FindProperty("intData");
stringField = serializedObject.FindProperty("stringData");
}
public override void OnInspectorGUI()
{
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
serializedObject.Update();
EditorGUILayout.IntSlider(intField, 0, 100, new GUIContent("initData"));
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(stringField);
if(GUILayout.Button("Select"))
{
stringField.stringValue = EditorUtility.OpenFilePanel("", Application.dataPath, "");
}
EditorGUILayout.EndHorizontal();
// Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
//需要在OnInspectorGUI之前修改屬性板祝,否則無法修改值
serializedObject.ApplyModifiedProperties();
base.OnInspectorGUI();
}
}
Editor嵌套
通過Edtiro.CreateEditor可實(shí)現(xiàn)Editor的嵌套悬嗓。
創(chuàng)建一個(gè)類TestClass2顾稀,它包含一個(gè)TestClass的屬性博肋。
[CreateAssetMenu]
public class TestClass2 : ScriptableObject
{
public TestClass data;
}
創(chuàng)建一個(gè)Test2Class的asset。它的Inspector面板的默認(rèn)顯示:
它并沒有把TestClass的屬性顯示出來赃春,如果要查看TestClass的屬性蜻底,必須雙擊,跳到相應(yīng)界面聘鳞,但這樣有看不到TestClass2的屬性薄辅。
如果想在Test2Class的Inspector面板中直接看到并且可以修改TestClass的屬性,可以重寫TestClass2的Editor抠璃,并在其中嵌套TestClass的Editor站楚。
[CustomEditor(typeof(TestClass2))]
public class TestClass2Editor : Editor
{
Editor cacheEditor;
public override void OnInspectorGUI()
{
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
serializedObject.Update();
//顯示TestClass2的默認(rèn)UI
base.OnInspectorGUI();
GUILayout.Space(20);
var data = ( (TestClass2)target ).data;
if(data != null)
{
//創(chuàng)建TestClass的Editor
if (cacheEditor == null)
cacheEditor = Editor.CreateEditor(data);
GUILayout.Label("this is TestClass2");
cacheEditor.OnInspectorGUI();
}
}
}
這樣就可以直接在TestClass2的面板中直接查看和編輯TestClass的屬性。
2. 使用PropertyDrawer
如果想修改某種特定類型的顯示搏嗡,使用繼承Editor的方式就會(huì)變得很麻煩窿春,因?yàn)樗惺褂锰囟愋偷腶sset都需要去實(shí)現(xiàn)一個(gè)自定義的Editor,效率非常低采盒。這種情況就可以通過繼承PropertyDrawer的方式旧乞,對指定類型的屬性,進(jìn)行統(tǒng)一顯示磅氨。
效果
為Inspector面板中的所有string屬性添加一個(gè)選擇文件按鈕尺栖,選中文件的路徑直接賦值給該變量。
實(shí)現(xiàn)
[CustomPropertyDrawer(typeof(string))]
public class StringPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect btnRect = new Rect(position);
position.width -= 60;
btnRect.x += btnRect.width - 60;
btnRect.width = 60;
EditorGUI.BeginProperty(position, label, property);
EditorGUI.PropertyField(position, property, true);
if (GUI.Button(btnRect, "select"))
{
string path = property.stringValue;
string selectStr = EditorUtility.OpenFilePanel("選擇文件", path, "");
if (!string.IsNullOrEmpty(selectStr))
{
property.stringValue = selectStr;
}
}
EditorGUI.EndProperty();
}
}
加了一個(gè)PropertyDrawer之后烦租,Inspector面板中的所有string變量都會(huì)額外添加一個(gè)Select按鈕延赌。
注意事項(xiàng)
- PropertyDrawer只對可序列化的類有效除盏,非可序列化的類沒法在Inspector面板中顯示。
- OnGUI方法里只能使用GUI相關(guān)方法挫以,不能使用Layout相關(guān)方法者蠕。
- PropertyDrawer對應(yīng)類型的所有屬性的顯示方式都會(huì)修改,例如創(chuàng)建一個(gè)帶string屬性的MonoBehaviour:
3. 使用PropertyAttribute
如果想要修改部分類的指定類型的屬性的顯示掐松,直接使用PropertyDrawer就無法滿足條件踱侣,這時(shí)可以結(jié)合PropertyAttribute和PropertyAttribute來實(shí)現(xiàn)需求。
效果
為部分指定類的int或float屬性的顯示添加滑動(dòng)條大磺,滑動(dòng)條的上下限可根據(jù)類和屬性自行設(shè)置抡句。
實(shí)現(xiàn)
public class RangeAttribute : PropertyAttribute
{
public float min;
public float max;
public RangeAttribute(float min, float max)
{
this.min = min;
this.max = max;
}
}
[CustomPropertyDrawer(typeof(RangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
// Draw the property inside the given rect
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// First get the attribute since it contains the range for the slider
RangeAttribute range = attribute as RangeAttribute;
// Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
if (property.propertyType == SerializedPropertyType.Float)
EditorGUI.Slider(position, property, range.min, range.max, label);
else if (property.propertyType == SerializedPropertyType.Integer)
EditorGUI.IntSlider(position, property, (int)range.min, (int)range.max, label);
else
EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
}
}
修改TestClass和DataClass
[CreateAssetMenu]
public class TestClass : ScriptableObject
{
[Range(0, 10)]
public int intData;
public string stringData;
public List<DataClass> dataList;
}
[System.Serializable]
public class DataClass
{
[Range(0, 100)]
public int id;
public Vector3 position;
public List<int> list;
}
其他
- 需要修改顯示的類都需要滿足Unity的序列化規(guī)則
- 這幾種顯示方式對Serializable Class都可以使用,并不需要一定是ScriptableObject量没。只是在編輯器下玉转,ScriptableObject來保存臨時(shí)數(shù)據(jù)比較常用突想,所以使用ScriptableObject來做例子殴蹄。