您的位置:首页 > 移动开发 > Unity3D

Unity自定义ScriptableObject属性显示的三种方式

2017-05-26 20:25 871 查看

1. 继承Editor,重写OnInspectorGUI方法

Editor官方文档

效果



实现

定义一个测试类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;
}


//指定类型
[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可实现Editor的嵌套。

创建一个类TestClass2,它包含一个TestClass的属性。

[CreateAssetMenu]
public class TestClass2 : ScriptableObject
{
public TestClass data;
}


创建一个Test2Class的asset。它的Inspector面板的默认显示:



它并没有把TestClass的属性显示出来,如果要查看TestClass的属性,必须双击,跳到相应界面,但这样有看不到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的默认UI
base.OnInspectorGUI();

GUILayout.Space(20);
var data = ( (TestClass2)target ).data;
if(data != null)
{
//创建TestClass的Editor
if (cacheEditor == null)
cacheEditor = Editor.CreateEditor(data);
GUILayout.Label("this is TestClass2");
cacheEditor.OnInspectorGUI();
}
}
}




这样就可以直接在TestClass2的面板中直接查看和编辑TestClass的属性。

2. 使用PropertyDrawer

PropertyDrawer官方文档

如果想修改某种特定类型的显示,使用继承Editor的方式就会变得很麻烦,因为所有使用特定类型的asset都需要去实现一个自定义的Editor,效率非常低。这种情况就可以通过继承PropertyDrawer的方式,对指定类型的属性,进行统一显示。

效果

为Inspector面板中的所有string属性添加一个选择文件按钮,选中文件的路径直接赋值给该变量。



实现

[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();
}
}


加了一个PropertyDrawer之后,Inspector面板中的**所有**string变量都会额外添加一个Select按钮。

注意事项

PropertyDrawer只对可序列化的类有效,非可序列化的类没法在Inspector面板中显示。

OnGUI方法里只能使用GUI相关方法,不能使用Layout相关方法。

PropertyDrawer对应类型的所有属性的显示方式都会修改,例如创建一个带string属性的MonoBehaviour:



3. 使用PropertyAttribute

PropertyAttribute官方文档

如果想要修改部分类的指定类型的属性的显示,直接使用PropertyDrawer就无法满足条件,这时可以结合PropertyAttribute和PropertyAttribute来实现需求。

效果

为部分指定类的int或float属性的显示添加滑动条,滑动条的上下限可根据类和属性自行设置。



实现

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的序列化规则

这几种显示方式对Serializable Class都可以使用,并不需要一定是ScriptableObject。只是在编辑器下,ScriptableObject来保存临时数据比较常用,所以使用ScriptableObject来做例子。

demo下载

转载请注明出处:http://blog.csdn.net/wuwangxinan/article/details/72773297
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity