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;
}
自定义属性绘制器
网友评论