我们知道Unity的组件类中,public的变量可以直接在Inspector中编辑,而其他访问级别的变量,可以为它们添加[SerializeField]特性来实现同样的效果。但是如果我们希望一个变量改变的时候调用一个属性(Property)的set访问器该怎样实现呢?
首先我们需要自定义一个特性(参考C#语法小知识(七)特性):
using UnityEngine; using System.Collections; using System; [AttributeUsage(AttributeTargets.Field)] public class SetPropertyAttribute : PropertyAttribute { public string propertyName { get; private set;} public bool dirty { get; set;} public SetPropertyAttribute(string propertyName_) { propertyName = propertyName_; } }
为这个特性添加了一个特性AttributeUsage,表示它的用途,只用在Field(字段/成员变量)上。
SetPropertyAttribute继承自PropertyAttribute,这是UnityEngine命名空间下的一个特性,用来在编辑器的PropertyDrawer中使用,也就是我们下面要实现的。
using UnityEditor; using UnityEngine; using System; using System.Collections; using System.Reflection; [CustomPropertyDrawer(typeof(SetPropertyAttribute))] public class AccessPropertyDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // Rely on the default inspector GUI EditorGUI.BeginChangeCheck (); EditorGUI.PropertyField(position, property, label); // Update only when necessary SetPropertyAttribute setProperty = attribute as SetPropertyAttribute; if (EditorGUI.EndChangeCheck ()) { setProperty.dirty = true; } else if (setProperty.dirty) { object obj = property.serializedObject.targetObject; Type type = obj.GetType(); PropertyInfo pi = type.GetProperty(setProperty.propertyName); if (pi == null) { Debug.LogError("Invalid property name: " + setProperty.propertyName + "\nCheck your [SetProperty] attribute"); } else { pi.SetValue(obj, fieldInfo.GetValue(obj), null); } setProperty.dirty = false; } } }CustomPropertyDrawer用了指定这个类是SetPropertyAttribute的Drawer,所有添加了[SetProperty]特性的字段都会经过这个Drawer来绘制。
在BeginChangeCheck和EndChangeCheck之间使用PropertyField来绘制字段,就可以检查字段的值是否发生了变化。
但是变化并不会立即体现出来,需要等下次绘制的时候再调用属性的set访问器。
最后我们测试一下:
using UnityEngine; using System.Collections; public class NewBehaviourScript : MonoBehaviour { [SerializeField] [SetProperty("Test")] protected int _test; public int Test { get { return _test;} set { _test = value; Debug.Log ("Test"+_test);} } // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
(注:本文参考自https://github.com/LMNRY/SetProperty。哈哈哈,其实就是抄的……)
作者:ecidevilin 发表于2016/11/21 22:05:43 原文链接
阅读:41 评论:0 查看评论