美文网首页UnityEditor
3.0创建自定义的查看器Inspector

3.0创建自定义的查看器Inspector

作者: 莫忘初心_倒霉熊 | 来源:发表于2021-03-07 17:20 被阅读0次

1.创建自定义的Inspector查看器

1.1使用CustomEditor

自定义的Inspector查看器脚本需放在Editor文件夹下,并继承自Editor,如图:


脚本路径结构

Level.cs脚本如下:

using UnityEngine;

public class Level : MonoBehaviour
{
    public int totalTime = 60;
    public float gravity = 10.0f;
    public AudioClip audioClip;
    public Sprite sprite;
}

MyInspectorEditor.cs如下:

using UnityEditor;
using UnityEngine;

//允许同时修改多个被选中的GameObject中组件属性
[CanEditMultipleObjects]
[CustomEditor(typeof(Level))]
public class MyInspectorEditor : Editor
{
    private Level _target;
    public int _targetTotalTime;
    
    private void OnEnable()
    {
        Debug.Log("OnEnable");
        _target = (Level) target;
        _targetTotalTime = _target.totalTime;
    }
    public override void OnInspectorGUI()
    {
        //绘制文本  EditorStyles.boldLabel粗体
        EditorGUILayout.LabelField("Data", EditorStyles.boldLabel);
        //垂直布局
        EditorGUILayout.BeginVertical("box");
        //水平布局
        EditorGUILayout.BeginHorizontal("box");
        //绘制int类型
        _target.totalTime = EditorGUILayout.IntField("Total Time", Mathf.Max(0, _target.totalTime));
        //绘制float类型
        _target.gravity = EditorGUILayout.FloatField("Gravity", _target.gravity);
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.BeginHorizontal("box");
        //绘制object类型
        _target.audioClip = (AudioClip)EditorGUILayout.ObjectField("audioClip", _target.audioClip, typeof(AudioClip), false);
        _target.sprite = (Sprite) EditorGUILayout.ObjectField("Sprite", _target.sprite, typeof(Sprite), false);
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
        
        _targetTotalTime =  EditorGUILayout.IntField("Target Total Time", Mathf.Max(0, _targetTotalTime));
        //当_targetTotalTime修改时,按钮激活
        bool oldEnabled = GUI.enabled;
        GUI.enabled = _targetTotalTime != _target.totalTime;
        //绘制button      EditorGUIUtility.singleLineHeight单行高度
        bool b = GUILayout.Button("修改totalTime",GUILayout.Height(2 * EditorGUIUtility.singleLineHeight));
        if (b)
        {
            //弹出确定对话框
            if (EditorUtility.DisplayDialog("Level Creator", "是否修改totalTime", "是", "否"))
            {
                _target.totalTime = _targetTotalTime;
            }
            //失去焦点,刷新输入的值
            GUI.FocusControl("");
        }
        GUI.enabled = oldEnabled;
        //绘制所有属性的inspector默认效果
        //DrawDefaultInspector();
        //绘制某一个object的inspector默认效果
        //Editor.CreateEditor(_target.sprite).DrawDefaultInspector();
    }
    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }

    private void OnDestroy()
    {
        Debug.Log("OnDestroy");
    }
}

绘制结果如下:


Inspector Editor

自定义Inspector Editor与自定义Editor Window是同样的方法调用,这篇文章介绍自定义EditorWindow https://www.jianshu.com/p/2754fa80f9df,涉及到的Scroll View,Tab,下拉框等复杂的控件同样可以适用于Inspector Editor,这里就不赘述了,有兴趣的可以看一下EditorWindow的文章。

1.2 使用SerializedObject和SerializedProperty

using UnityEditor;
using UnityEngine;

//允许同时修改多个被选中的GameObject中组件属性
[CanEditMultipleObjects]
[CustomEditor(typeof(Level))]
public class MyInspectorEditor : Editor
{
    private Level _target;
    public int _targetTotalTime;
    private SerializedObject _mySerializedObject;
    private SerializedProperty _mySerializedPropertyTotalTime;
    private SerializedProperty _mySerializedPropertyGravity;
    private SerializedProperty _mySerializedPropertyAudioClip;
    private SerializedProperty _mySerializedPropertySprite;
    
    private void OnEnable()
    {
        Debug.Log("OnEnable");
        _target = (Level) target;
        _mySerializedObject = new SerializedObject(_target);
        _mySerializedPropertyTotalTime =_mySerializedObject.FindProperty("totalTime");
        _mySerializedPropertyGravity =_mySerializedObject.FindProperty("gravity");
        _mySerializedPropertyAudioClip =_mySerializedObject.FindProperty("audioClip");
        _mySerializedPropertySprite =_mySerializedObject.FindProperty("sprite");
        _targetTotalTime = _target.totalTime;
    }
    public override void OnInspectorGUI()
    {
         //表示更新序列化物体
         _mySerializedObject.Update();
         
         //绘制文本  EditorStyles.boldLabel粗体
         EditorGUILayout.LabelField("Data", EditorStyles.boldLabel);
         //垂直布局
         EditorGUILayout.BeginVertical("box");
         //水平布局
         EditorGUILayout.BeginHorizontal("box");
         //绘制int类型
        //_target.totalTime = EditorGUILayout.IntField("Total Time", Mathf.Max(0, _target.totalTime));
         EditorGUILayout.PropertyField(_mySerializedPropertyTotalTime);
         //绘制float类型
         // _target.gravity = EditorGUILayout.FloatField("Gravity", _target.gravity);
         EditorGUILayout.PropertyField(_mySerializedPropertyGravity);
         EditorGUILayout.EndHorizontal();
         EditorGUILayout.BeginHorizontal("box");
         //绘制object类型
         // _target.audioClip = (AudioClip)EditorGUILayout.ObjectField("audioClip", _target.audioClip, typeof(AudioClip), false);
         EditorGUILayout.PropertyField(_mySerializedPropertyAudioClip);
         // _target.sprite = (Sprite) EditorGUILayout.ObjectField("Sprite", _target.sprite, typeof(Sprite), false);
         EditorGUILayout.PropertyField(_mySerializedPropertySprite);
         EditorGUILayout.EndHorizontal();
         EditorGUILayout.EndVertical();
        
        _targetTotalTime =  EditorGUILayout.IntField("Target Total Time", Mathf.Max(0, _targetTotalTime));
         //当_targetTotalTime修改时,按钮激活
         bool oldEnabled = GUI.enabled;
         GUI.enabled = _targetTotalTime != _target.totalTime;
         //绘制button      EditorGUIUtility.singleLineHeight单行高度
         bool b = GUILayout.Button("修改totalTime",GUILayout.Height(2 * EditorGUIUtility.singleLineHeight));
         if (b)
         {
             //弹出确定对话框
             if (EditorUtility.DisplayDialog("Level Creator", "是否修改totalTime", "是", "否"))
             {
                 _target.totalTime = _targetTotalTime;
             }
             //失去焦点,刷新输入的值
             GUI.FocusControl("");
         }
         GUI.enabled = oldEnabled;
        
        //应用修改的属性值,不加的话,Inspector面板的值修改不了
        _mySerializedObject.ApplyModifiedProperties();
    }
    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }

    private void OnDestroy()
    {
        Debug.Log("OnDestroy");
    }
}

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

2. Unity属性绘制器

属性绘制器不需要继承Editor,所以无需放到Editor文件夹中。

using UnityEngine;

public class MyPropertyDrawer : MonoBehaviour
{
    //限制范围
    [Range(0,100)]
    public int intValue = 30;
    [Range(0,100)]
    public float floatValue = 10.0f;
    //多行显示
    [Multiline(3)] 
    public string stringMultiline = "This text is using a multiline property\n drawer";
    //带有滑动条的多行显示
    [TextArea(2,3)]
    public string stringTextArea = "This text\n is using\n text area \nproperty drawer";
    
    //上下文交互菜单
    [ContextMenu("ContextMenuCallback")]
    public void ContextMenuCallback()
    {
        Debug.Log("ContextMenuCallback");
    }
    
    //上下文交互Item菜单
    [ContextMenuItem("ContextMenuItem", "ContextMenuItem")]
    public int intContextMenuItem = 100;

    public void ContextMenuItem()
    {
        intContextMenuItem = 0;
    }
}

效果如图:


ContextMenu.png
ContextMenuItem.png

3.Unity装饰绘制器

依旧使用上面的脚本,介绍Header,Tooltip,Space三个Unity装饰绘制器,代码如下:


public class MyPropertyDrawer : MonoBehaviour
{
    //分组
    [Header("Group A")]
    //限制范围
    [Range(0,100)]
    public int intValue = 30;
    //空格
    [Space(30)]
    [Range(0,100)]
    public float floatValue = 10.0f;
        
    //鼠标悬浮属性,弹出tip
    [Tooltip(("this is a tooltip"))]
    //多行显示
    [Multiline(3)] 
    public string stringMultiline = "This text is using a multiline property\n drawer";
    //带有滑动条的多行显示
    [TextArea(2,3)]
    public string stringTextArea = "This text\n is using\n text area \nproperty drawer";
    
    //上下文交互菜单
    [ContextMenu("ContextMenuCallback")]
    public void ContextMenuCallback()
    {
        Debug.Log("ContextMenuCallback");
    }
    
    //上下文交互Item菜单
    [ContextMenuItem("ContextMenuItem", "ContextMenuItem")]
    public int intContextMenuItem = 100;

    public void ContextMenuItem()
    {
        intContextMenuItem = 0;
    }
}

效果如图:


装饰绘制器

4. 创建自定义的属性绘制器

上文介绍了Unity提供的默认属性绘制器,那么我们如何创建自定义的属性绘制器呢?
步骤如下:


脚本路径结构

4.1 创建自定义属性

自定义属性脚本属于运行时脚本,不要放到Editor文件夹下,并继承PropertyAttribute,并约定脚本名为属性名Attribute,使用时只需要添加[属性名]即可,不需要写后缀Attribute。代码如下:

using UnityEngine;

public class MyTimeAttribute : PropertyAttribute
{
    public readonly bool DisplayHours;

    public MyTimeAttribute(bool displayHours = false)
    {
        DisplayHours = displayHours;
    }
}

4.2 创建自定义属性绘制器

自定义的属性绘制器脚本需放在Editor文件夹下,并继承自PropertyDrawer,代码如下:

using UnityEngine;
using UnityEditor;

//将属性绘制器与属性绑定
[CustomPropertyDrawer(typeof(MyTimeAttribute))]
public class TimeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent content)
    {
        if (property.propertyType == SerializedPropertyType.Integer)
        {
            property.intValue = EditorGUI.IntField(new Rect(position.x, position.y, position.width, position.height / 2), content, Mathf.Max(0, property.intValue));
            EditorGUI.LabelField(new Rect(position.x, position.y + position.height / 2, position.width, position.height / 2), "", TimeConverter(property.intValue));
        }
        else
        {
            EditorGUI.HelpBox(position, "To use the Time attribute, <" + content.text + "> must be an int!", MessageType.Error);
        }
    }

    //绘制的高度
    public override float GetPropertyHeight(SerializedProperty property, GUIContent content)
    {
        return EditorGUI.GetPropertyHeight(property) * 2;
    }

    //转换时间
    private string TimeConverter(int totalSeconds)
    {
        MyTimeAttribute myTime = attribute as MyTimeAttribute;
        if (myTime != null)
        {
            if (myTime.DisplayHours)
            {
                int hours = totalSeconds / (60 * 60);
                int minutes = (totalSeconds % (60 * 60)) / 60;
                int seconds = totalSeconds % 60;
                return string.Format("{0}:{1}:{2}(h:m:s)", hours, minutes.ToString().PadLeft(2, '0'), seconds.ToString().PadLeft(2, '0'));
            }
            else
            {
                int minutes = totalSeconds / 60;
                int seconds = totalSeconds % 60;
                return string.Format("{0}:{1}(m:s)", minutes, seconds.ToString().PadLeft(2, '0'));
            }
        }
        return string.Empty;
    }
}   

4.3 使用自定义属性绘制器

using UnityEngine;

public class TimeDrawerDemo : MonoBehaviour
{
    [MyTime]
    public int TimeMinutes = 3600;
    [MyTime(true)]
    public int TimeHours = 3600;

    [MyTime] public float TimeError = 3600f;
}
自定义属性绘制器

相关文章

网友评论

    本文标题:3.0创建自定义的查看器Inspector

    本文链接:https://www.haomeiwen.com/subject/nzhnxltx.html