孙广东 2015、7、12Unity的很多编辑器功能都是通过特性Attribute实现。那么我们要自己扩展Inspector也是要自己写Attribute。先说说为什么要这样做?为了编写面向对象程序,封装特性 更优雅。下面的脚本使 属性 (即有 getter/setter 的成员) 的内容可以在Unity的Inspector上显示。这样就可以保密类的 字段,并限制所有的外部访问,只能通过 属性 访问,看看代码吧!using System;
[AttributeUsage(AttributeTargets.Property)] public class ExposePropertyAttribute : Attribute {}在 "Assets/Editor" 的脚本using UnityEditor;
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public static class ExposeProperties
{
public static void Expose(PropertyField[] properties)
{
var emptyOptions = new GUILayoutOption[0];
EditorGUILayout.BeginVertical(emptyOptions);
foreach (PropertyField field in properties)
{
EditorGUILayout.BeginHorizontal(emptyOptions);
if (field.Type == SerializedPropertyType.Integer)
{
var oldValue = (int)field.GetValue();
var newValue = EditorGUILayout.IntField(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.Float)
{
var oldValue = (float)field.GetValue();
var newValue = EditorGUILayout.FloatField(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.Boolean)
{
var oldValue = (bool)field.GetValue();
var newValue = EditorGUILayout.Toggle(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.String)
{
var oldValue = (string)field.GetValue();
var newValue = EditorGUILayout.TextField(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.Vector2)
{
var oldValue = (Vector2)field.GetValue();
var newValue = EditorGUILayout.Vector2Field(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.Vector3)
{
var oldValue = (Vector3)field.GetValue();
var newValue = EditorGUILayout.Vector3Field(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.Enum)
{
var oldValue = (Enum)field.GetValue();
var newValue = EditorGUILayout.EnumPopup(field.Name, oldValue, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
else if (field.Type == SerializedPropertyType.ObjectReference)
{
UnityEngine.Object oldValue = (UnityEngine.Object)field.GetValue();
UnityEngine.Object newValue = EditorGUILayout.ObjectField(field.Name, oldValue, field.Info.PropertyType, emptyOptions);
if (oldValue != newValue)
field.SetValue(newValue);
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
public static PropertyField[] GetProperties(object obj)
{
var fields = new List
();
PropertyInfo[] infos = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo info in infos)
{
if (!(info.CanRead && info.CanWrite))
continue;
object[] attributes = info.GetCustomAttributes(true);
bool isExposed = false;
foreach (object o in attributes)
if (o.GetType() == typeof(ExposePropertyAttribute))
{
isExposed = true;
break;
}
if (!isExposed)
continue;
var type = SerializedPropertyType.Integer;
if (PropertyField.GetPropertyType(info, out type))
{
var field = new PropertyField(obj, info, type);
fields.Add(field);
}
}
return fields.ToArray();
}
}
public class PropertyField
{
object obj;
PropertyInfo info;
SerializedPropertyType type;
MethodInfo getter;
MethodInfo setter;
public PropertyInfo Info
{
get { return info; }
}
public SerializedPropertyType Type
{
get { return type; }
}
public String Name
{
get { return ObjectNames.NicifyVariableName(info.Name); }
}
public PropertyField(object obj, PropertyInfo info, SerializedPropertyType type)
{
this.obj = obj;
this.info = info;
this.type = type;
getter = this.info.GetGetMethod();
setter = this.info.GetSetMethod();
}
public object GetValue() { return getter.Invoke(obj, null); }
public void SetValue(object value) { setter.Invoke(obj, new[] {value}); }
public static bool GetPropertyType(PropertyInfo info, out SerializedPropertyType propertyType)
{
Type type = info.PropertyType;
propertyType = SerializedPropertyType.Generic;
if (type == typeof(int))
propertyType = SerializedPropertyType.Integer;
else if (type == typeof(float))
propertyType = SerializedPropertyType.Float;
else if (type == typeof(bool))
propertyType = SerializedPropertyType.Boolean;
else if (type == typeof(string))
propertyType = SerializedPropertyType.String;
else if (type == typeof(Vector2))
propertyType = SerializedPropertyType.Vector2;
else if (type == typeof(Vector3))
propertyType = SerializedPropertyType.Vector3;
else if (type.IsEnum)
propertyType = SerializedPropertyType.Enum;
else if (typeof(MonoBehaviour).IsAssignableFrom(type))
propertyType = SerializedPropertyType.ObjectReference;
return propertyType != SerializedPropertyType.Generic;
}
}using UnityEngine;
public class ExposableMonobehaviour : MonoBehaviour {}在 "Assets/Editor" 文件夹下的脚本using UnityEditor;
using UnityEngine;
using System.Collections;
[CustomEditor(typeof(ExposableMonoBehaviour), true)]
public class ExposableMonobehaviourEditor : Editor
{
ExposableMonoBehaviour m_Instance;
PropertyField[] m_fields;
public virtual void OnEnable()
{
m_Instance = target as ExposableMonoBehaviour;
m_fields = ExposeProperties.GetProperties(m_Instance);
}
public override void OnInspectorGUI()
{
if (m_Instance == null)
return;
this.DrawDefaultInspector();
ExposeProperties.Expose(m_fields);
}
}【注】这将允许类从 ExposableMonobehaviour 继承,将增强暴露 setter 和 getter 方法。没有明显的性能问题。你可能已经看到上面的代码中,只有以下类型目前是暴露的 (尽管是容易实现其他的类型):- Integer
- Float
- Boolean
- String
- Vector2
- Vector3
- Enum
- Object Reference (Monobehavior)【使用:】现在创建一个类扩展 ExposableMonobehaviour,并使用属性。属性必须有 getter 和 setter 访问器和 [ExposeProperty] 特性设置,否则该属性将不会被显示。当你play时为了要保存属性值,您必须添加 [SerializeField] 到属性的字段。不幸的是,这把字段 暴露了到编辑器,因此您必须显式隐藏,使用 [HideInInspector]。示例:随便挂在一个对象上using UnityEngine;
public class MyType : ExposableMonoBehaviour
{
[HideInInspector, SerializeField] int m_SomeInt;
[HideInInspector, SerializeField] float m_SomeFloat;
[HideInInspector, SerializeField] bool m_SomeBool;
[HideInInspector, SerializeField] string m_Etc;
[HideInInspector, SerializeField] MonoBehaviour m_Obj;
[ExposeProperty] public int SomeInt
{
get { return m_SomeInt; }
set { m_SomeInt = value; }
}
[ExposeProperty] public float SomeFloat
{
get { return m_SomeFloat; }
set { m_SomeFloat = value; }
}
[ExposeProperty] public bool SomeBool
{
get { return m_SomeBool; }
set { m_SomeBool = value; }
}
[ExposeProperty] public string SomeString
{
get { return m_Etc; }
set { m_Etc = value; }
}
[ExposeProperty] public MonoBehaviour SomeScript
{
get { return m_Obj; }
set { m_Obj = value; }
}
}