Unity定制,将属性暴露在Inspector面板上

发表于2015-07-15
评论0 2.7k浏览
unity的很多编辑器功能都是通过特性Attribute实现。
那么我们要自己扩展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; }
        }
}
最后还是附上 工程吧:  



如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引