孙广东 2015、7、12
Unity的很多编辑器功能都是通过特性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<PropertyField>(); 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; } } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-07 21:43:08