Unity定制,将属性暴露在Inspector面板上
发表于2015-07-15
unity的很多编辑器功能都是通过特性Attribute实现。
那么我们要自己扩展Inspector也是要自己写Attribute。
先说说为什么要这样做?
为了编写面向对象程序,封装特性 更优雅。下面的脚本使 属性 (即有 getter/setter 的成员) 的内容可以在Unity的Inspector上显示。
这样就可以保密类的 字段,并限制所有的外部访问,只能通过 属性 访问,
看看代码吧!
在 "Assets/Editor" 的脚本
在 "Assets/Editor" 文件夹下的脚本
【注】
这将允许类从 ExposableMonobehaviour 继承,将增强暴露 setter 和 getter 方法。没有明显的性能问题。
你可能已经看到上面的代码中,只有以下类型目前是暴露的 (尽管是容易实现其他的类型):
- Integer
- Float
- Boolean
- String
- Vector2
- Vector3
- Enum
- Object Reference (Monobehavior)
【使用:】
现在创建一个类扩展 ExposableMonobehaviour,并使用属性。属性必须有 getter 和 setter 访问器和 [ExposeProperty] 特性设置,否则该属性将不会被显示。
当你play时为了要保存属性值,您必须添加 [SerializeField] 到属性的字段。不幸的是,这把字段 暴露了到编辑器,因此您必须显式隐藏,使用 [HideInInspector]。
示例:随便挂在一个对象上
最后还是附上 工程吧:
那么我们要自己扩展Inspector也是要自己写Attribute。
先说说为什么要这样做?
为了编写面向对象程序,封装特性 更优雅。下面的脚本使 属性 (即有 getter/setter 的成员) 的内容可以在Unity的Inspector上显示。
这样就可以保密类的 字段,并限制所有的外部访问,只能通过 属性 访问,
看看代码吧!
[Applenoxss]
1 2 | using System; [AttributeUsage ( AttributeTargets.Property ) ] public class ExposePropertyAttribute : Attribute { } |
在 "Assets/Editor" 的脚本
[C#]
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | 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; } } |
[C#]
1 2 3 | using UnityEngine; public class ExposableMonobehaviour : MonoBehaviour {} |
在 "Assets/Editor" 文件夹下的脚本
[C#]
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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]。
示例:随便挂在一个对象上
[C#]
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 Somenoxss { get { return m_Obj; } set { m_Obj = value; } } } |