Unity3d开发中Custom Attributes(C#)的应用

来源:互联网 发布:war包解析源码 编辑:程序博客网 时间:2024/04/30 03:21

1. Unity3d中Attributes应用

特性(Attribute):  官方解释:特性是给指定的某一声明的一则附加的声明性信息。 允许类似关键字的描述声明。它对程序中的元素进行标注,如类型、字段、方法、属性等。特性是一种类,这些类继承于System.Attribute类,用于对类、属性、方法、事件等进行描述,主要用在反射中。但从面向对象的级别看,其实Attribute是类型级别的,而不是对象级别。


在Unity编程中,我们经常使用到Attributes

1)序列化一个自定义数据类,该类才会显示在Inspector,内容才能被存储。

[System.Serializable]public class CustomData{   public Color color;   public Vector3 position}
2) Plugins功能,允许使用unity脚本调用C、C++、ObjectC方法。
[DllImport ("__Internal", CharSet=CharSet.Ansi)]public static extern void CustomPluginFunction();

2. Unity3d中的Custom Attributes的应用。

自定义特性(Custom Attribute): 通过定义一个特性类,可以创建您自己的自定义特性。该特性类直接或间接地从Attribute派生,有助于方便快捷地在元数据中标识特性定义。 

以下列出常用的几项,更多信息请参考Unity Scripting API : UnityEngine -> Attributes

1)添加一个项目到Unity的菜单栏。

[MenuItem ("MyMenu/Do Something")]public static function DoSomething() {   Debug.Log ("Doing Something...");}

2)[ContextMenu(name)]
添加一个选项名字为name的选项到component的context menu中,选择name选项会运行特性关联的方法。

[ContextMenu(MyMenu/Do Something;)]public static function DoSomething() {   Debug.Log ("Doing Something...");}

3)[HideInInspector] and [SerializeField]
第一个是在Inspector隐藏公共变量,第二个是在Inspector显示私有变量。

public class ExampleClass : MonoBehaviour {    [HideInInspector]    public int a = 5;    [SerializeField]    private int b = 1;}

4) [Range(float, float)]

限制变量的范围,改变变量用划动条操作。

3. 使用Custom Attributes和Reflection,在Inspector中映射方法名。

在开发中,我们有时需求在脚本中可以配置一个事件的回调函数,当事件触发时,回调这个方法。方法名一般以字符串的形式写在变量中。 这样很容易出错。如果是一个下拉的选择框,这样就不容易出错了。实现这个功能我们需要用到Custom Attributes,Reflection和Unity3d的Property Drawers技术。

1)  我们要定义一个TriggerEvent类,这个类存储要发送目标对象和发送的方法名。

TriggerEventAttribute是一个自定义的特性类,其中变量_name用来存储回调方法的名字。

using UnityEngine;using System.Collections;[System.Serializable]public class TriggerEvent {public GameObject _target;public string _method;public void Invoke(){if (_target != null && !string.IsNullOrEmpty(_method))_target.SendMessage(_method, SendMessageOptions.RequireReceiver);}}public class TriggerEventAttribute : System.Attribute{public string _name;public TriggerEventAttribute(){}public TriggerEventAttribute(string name){_name = name;}}

2)  使用PropertyDrawer处理TriggerEvent类在Inspector中的显示

OnGUI函数三个参数的意思分别为:第一个绘制组件的起点坐标,第二个参数返回一个property,通过它我们能得到TriggerEvent类中定义变量的属性。第三个参数是这个属性使用的标签,返回的是定义TriggerEvent类变量的名字。

[CustomPropertyDrawer(typeof(TriggerEvent))]public class TriggerEventDrawer : PropertyDrawer {public override void OnGUI (Rect position, SerializedProperty property, GUIContent label){EditorGUI.BeginProperty(position, label, property);Rect prefabRect = new Rect(position.x, position.y + position.height / 3, position.width * 0.25f, position.height / 3);Rect valueRect = new Rect(prefabRect.x + prefabRect.width/2, prefabRect.y, position.width * 0.75f, position.height / 3);GUI.Label(prefabRect, "Target", EditorStyles.miniLabel);SerializedProperty targetProperty = property.FindPropertyRelative("_target");targetProperty.objectReferenceValue = EditorGUI.ObjectField(valueRect, targetProperty.objectReferenceValue, typeof(GameObject), true);prefabRect.y += prefabRect.height + 2;valueRect.y += valueRect.height + 2;SerializedProperty methodProperty = property.FindPropertyRelative("_method");GUI.Label(prefabRect, "Method", EditorStyles.miniLabel);List<string> names = new List<string>();List<string> methodNames = new List<string>();names.Add("None");methodNames.Add(null);int index = 0;GameObject obj = targetProperty.objectReferenceValue as GameObject;if (obj != null){MonoBehaviour[] components = obj.GetComponents<MonoBehaviour>();foreach(MonoBehaviour component in components){Type type = component.GetType();MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);foreach (MethodInfo method in methods){object[] attributes = method.GetCustomAttributes(typeof(TriggerEventAttribute), true);if (attributes.Length > 0){ParameterInfo[] parameters = method.GetParameters();bool addMethod = false;if(parameters.Length <= 1){addMethod = true;}if(addMethod){TriggerEventAttribute attribute = (TriggerEventAttribute)attributes[0];if (string.IsNullOrEmpty(attribute._name)){string name = ObjectNames.NicifyVariableName(method.Name);names.Add(type.Name + ": " + name);}else{names.Add(type.Name + ": " + attribute._name);}if (methodProperty.stringValue == method.Name)index = methodNames.Count;methodNames.Add(method.Name);}}}}if (names.Count > 1){EditorGUI.BeginChangeCheck();index = EditorGUI.Popup(valueRect, index, names.ToArray());if (EditorGUI.EndChangeCheck())methodProperty.stringValue = methodNames[index];}else{GUI.Label(valueRect, "None");}}else{GUI.Label(valueRect, "None");}EditorGUI.EndProperty();}public override float GetPropertyHeight(SerializedProperty property, GUIContent label){return base.GetPropertyHeight(property, label) * 3;}}

3)如何使用

如下图所示,TriggerEventComponent中target中拖入要通知的gameobject,在下面的Method变量中就会显示目标gameobject中,标记了[TriggerEvent] 属性的方法。

using UnityEngine;using System.Collections;public class TriggerEventComponent : MonoBehaviour {public TriggerEvent _trigger;[ContextMenu("Trigger")]void Trigger(){_trigger.Invoke();}}
using UnityEngine;using System.Collections;public class DoSomething : MonoBehaviour {[TriggerEvent]void DoSomething1(){Debug.Log("DoSomething1");}[TriggerEvent("DoSomethingTest")]void DoSomething2(){Debug.Log("DoSomething2");}}

通过以上的思路,我们也可以实现,向Inspector中一个string类型变量输入一个标识,找到对应的枚举类型后,才输入生效;Custom Attributes可以看做一个标识,我再代码中加入这个标识,我们可以在检索出所有标识代码所附属的prefab。

0 0
原创粉丝点击