Unity 编辑器扩展二 Editor 自定义Inspector面板

OnInspectorGUI()是Unity的Editor类里的相关函数,通过对该方法的重写,可以自定义对Inspector面板的绘制。


image.png
//Test1.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test1 : MonoBehaviour
{
    public Text Text1;
    public bool myBool;
    public string myString;
}

//Test1Editor.cs
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Test1))]
public class Test1Editor : Editor
{
    public override void OnInspectorGUI()
    {
        Test1 test = (Test1)target;
        test.myBool = EditorGUILayout.Toggle("是否选中", test.myBool);
        test.myString = EditorGUILayout.TextField("输入文本", test.myString);
    }
}

这里要先创建Test1脚本,再创建Test1Editor,否则引用会报错。

看一下前后对比图

如图对比,重写了OnInspectorGUI之后,Inspector面板发生变化。因为没有处理Text1,重写之后已经无法设置。

以下参考
【Unity 编辑器】扩展总结三:自定义Inspector面板
Unity Editor 基础篇(二):自定义 Inspector 面板
Unity检视面板重构(OnInspectorGUI重写)
unity编辑器扩展#1 自定义窗口、面板、和属性

一、两种方式

创建目标类,挂载在场景对象中

using UnityEngine;

public enum Course
{
    Chinese,
    Mathematics, 
    English 
}

public class InspectorExample : MonoBehaviour
{
    public int intValue;
    public float floatValue;
    public string stringValue;
    public bool boolValue;
    public Vector3  vector3Value; 
    public Course enumValue = Course.Chinese;
    public Color colorValue = Color.white;
    public Texture textureValue;
}
1.绘制方式一
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(InspectorExample))]
public class InspectorExampleEditor : Editor
{
    //target指该编辑器类绘制的目标类,需要将它强转为目标类
    private InspectorExample _target { get { return target as InspectorExample; } }

    //GUI重新绘制
    public override void OnInspectorGUI()
    {
        //EditorGUILayout.LabelField("IntValue",_target.intValue.ToString(),EditorStyles.boldLabel);
        //_target.intValue = EditorGUILayout.IntSlider(new GUIContent("Slider"),_target.intValue, 0, 10);
        //_target.floatValue = EditorGUILayout.Slider(new GUIContent("FloatValue"), _target.floatValue, 0, 10);
        _target.intValue = EditorGUILayout.IntField("IntValue", _target.intValue);
        _target.floatValue = EditorGUILayout.FloatField("FloatValue", _target.floatValue);
        _target.stringValue = EditorGUILayout.TextField("StringValue", _target.stringValue);
        _target.boolValue = EditorGUILayout.Toggle("BoolValue", _target.boolValue);
        _target.vector3Value = EditorGUILayout.Vector3Field("Vector3Value", _target.vector3Value);
        _target.enumValue = (Course)EditorGUILayout.EnumPopup("EnumValue", (Course)_target.enumValue);
        _target.colorValue = EditorGUILayout.ColorField(new GUIContent("ColorValue"), _target.colorValue);
        _target.textureValue = (Texture)EditorGUILayout.ObjectField(
        "TextureValue", _target.textureValue, typeof(Texture), true);
    }
}
2.绘制方式二
using UnityEditor;

[CustomEditor(typeof(InspectorExample))]
public class InspectorExampleEditor : Editor
{
    //定义序列化属性
    private SerializedProperty intValue;
    private SerializedProperty floatValue;
    private SerializedProperty stringValue;
    private SerializedProperty boolValue;
    private SerializedProperty vector3Value;
    private SerializedProperty enumValue;
    private SerializedProperty colorValue;
    private SerializedProperty textureValue;

    private void OnEnable()
    {
        //通过名字查找被序列化属性。
        intValue = serializedObject.FindProperty("intValue");
        floatValue = serializedObject.FindProperty("floatValue");
        stringValue = serializedObject.FindProperty("stringValue");
        boolValue = serializedObject.FindProperty("boolValue");
        vector3Value = serializedObject.FindProperty("vector3Value");
        enumValue = serializedObject.FindProperty("enumValue");
        colorValue = serializedObject.FindProperty("colorValue");
        textureValue = serializedObject.FindProperty("textureValue");
    }

    public override void OnInspectorGUI()
    {
        //表示更新序列化物体
        serializedObject.Update();
        EditorGUILayout.PropertyField(intValue);
        EditorGUILayout.PropertyField(floatValue);
        EditorGUILayout.PropertyField(stringValue);
        EditorGUILayout.PropertyField(boolValue);
        EditorGUILayout.PropertyField(vector3Value);
        EditorGUILayout.PropertyField(enumValue);
        EditorGUILayout.PropertyField(colorValue);
        EditorGUILayout.PropertyField(textureValue);
        //应用修改的属性值,不加的话,Inspector面板的值修改不了
        serializedObject.ApplyModifiedProperties();
    }
}

P.S. 第二种绘制方式相较于第一种,显示的效果是差不多的。虽然脚本内容多了一点,但是方式比较简单。不用根据每个变量的数据类型选择相对应的属性API绘制。

3.使用方式二改造之前的Test1Editor
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Test1))]
public class Test1Editor : Editor
{
    //定义序列化属性
    private SerializedProperty Text1;
    private SerializedProperty myBool;
    private SerializedProperty myString;

    private void OnEnable()
    {
        //通过名字查找被序列化属性。
        Text1 = serializedObject.FindProperty("Text1");
        myBool = serializedObject.FindProperty("myBool");
        myString = serializedObject.FindProperty("myString");
        Debug.Log("test 1 editor enable");
    }

    public override void OnInspectorGUI()
    {
        //Test1 test = (Test1)target;
        //test.myBool = EditorGUILayout.Toggle("是否选中", test.myBool);
        //test.myString = EditorGUILayout.TextField("输入文本", test.myString);

        //表示更新序列化物体
        serializedObject.Update();
        EditorGUILayout.PropertyField(Text1);
        //EditorGUILayout.PropertyField(myBool);
        EditorGUILayout.PropertyField(myBool, new GUIContent("是否选中"));
        EditorGUILayout.PropertyField(myString);
        //应用修改的属性值,不加的话,Inspector面板的值修改不了
        serializedObject.ApplyModifiedProperties();
    }
}

这里PropertyField如果需要修改显示属性,可以参考Unity属性窗口编辑器,改为EditorGUILayout.PropertyField(myBool, new GUIContent("是否选中"));

4.布局
        serializedObject.Update();
        EditorGUILayout.BeginHorizontal("box");
        EditorGUILayout.PropertyField(Text1);
        //EditorGUILayout.PropertyField(myBool);
        EditorGUILayout.PropertyField(myBool, new GUIContent("是否选中"));
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.PropertyField(myString);
image.png

水平和垂直布局,注意这是一个方法对,Begin和End不能少

 EditorGUILayout.BeginVertical("box");
 //TODO
 EditorGUILayout.EndVertical();

 EditorGUILayout.BeginHorizontal("box");
 //TODO
 EditorGUILayout.EndHorizontal();
二、数组或list集合的显示方式

参考【Unity 编辑器】扩展总结七:数组或list集合的显示方式

using System.Collections.Generic;
using UnityEngine;

public class InspectorExample : MonoBehaviour
{
    //序列化
    [SerializeField] 
    public int[] intArray;
    [SerializeField]
    public List<string> stringList;
}

using UnityEditor;

[CustomEditor(typeof(InspectorExample))]
public class InspectorExampleEditor : Editor
{
    private SerializedProperty intArray;
    private SerializedProperty stringList;

    private void OnEnable()
    {
        intArray = serializedObject.FindProperty("intArray");
        stringList = serializedObject.FindProperty("stringList");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(intArray,true);
        EditorGUILayout.PropertyField(stringList, true);
        serializedObject.ApplyModifiedProperties();
    }
}
三、ReorderableList实现可排序列表

参考【Unity 编辑器】扩展总结七:数组或list集合的显示方式,原文中还有拓展:ReorderableList添加下拉菜单,这里不再转载。

using System.Collections.Generic;
using UnityEngine;

public class TargetExample : MonoBehaviour
{
   [SerializeField]
   public List<string> stringArray;
}
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

[CustomEditor(typeof(TargetExample))]
public class TargetExampleEditor : Editor
{
   private ReorderableList _stringArray;

   private void OnEnable()
   {
       _stringArray = new ReorderableList(serializedObject, serializedObject.FindProperty("stringArray")
           , true, true, true, true);

       //自定义列表名称
       _stringArray.drawHeaderCallback = (Rect rect) =>
       {
           GUI.Label(rect, "StringArray");
       };

       //自定义绘制列表元素
       _stringArray.drawElementCallback = (Rect rect,int index,bool selected,bool focused) =>
       {
           //根据index获取对应元素
           SerializedProperty item = _stringArray.serializedProperty.GetArrayElementAtIndex(index);
           rect.height = EditorGUIUtility.singleLineHeight;
           rect.y += 2;
           EditorGUI.PropertyField(rect, item, new GUIContent("Element "+index));
       };

       //当添加新元素时的回调函数,自定义新元素的值
       _stringArray.onAddCallback = (ReorderableList list) =>
       {
           if (list.serializedProperty != null)
           {
               list.serializedProperty.arraySize++;
               list.index = list.serializedProperty.arraySize - 1;
               SerializedProperty item = list.serializedProperty.GetArrayElementAtIndex(list.index);
               item.stringValue = "Default Value";
           }
           else
           {
               ReorderableList.defaultBehaviours.DoAddButton(list);
           }
       };

       //当删除元素时候的回调函数,实现删除元素时,有提示框跳出
       _stringArray.onRemoveCallback = (ReorderableList list) =>
       {
           if (EditorUtility.DisplayDialog("Warnning","Do you want to remove this element?","Remove","Cancel"))
           {
               ReorderableList.defaultBehaviours.DoRemoveButton(list);
           }
       };
   }

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

推荐阅读更多精彩内容