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

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按钮。

注意事项

  1. PropertyDrawer只对可序列化的类有效,非可序列化的类没法在Inspector面板中显示。
  2. OnGUI方法里只能使用GUI相关方法,不能使用Layout相关方法。
  3. 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下载

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容