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

孙广东   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

Unity定制,将属性暴露在Inspector面板上的相关文章

一键展开关闭unity的 Inspector面板上的所有组件

孙广东  2015.12.11 using UnityEngine; using System.Collections.Generic; using System.Collections; using UnityEditor; using System.Reflection; using System; /// <summary> /// 描述:全部 展开/收起 Inspector面板的所有组件. /// author: 孙广东 /// todo 还可以深度定制,添加两个按钮切换 /// &l

Unity3d Inspector面板实现set/get访问器

     简单说一下属性和字段的区别:字段就是成员变量,而属性确实提供给外部访问内部成员变量的接口.之所以会有属性的出现,就是为了避免外部对类的成员的直接访问,通俗的说就是OOP中的封装思想. using UnityEngine; using System.Collections; public class DemoTest : MonoBehaviour { private int _score; public int Score { get { return _score; } set {

拓展编辑器(二十)_面板拓展(Inspector面板

我们的Unity脚本挂在游戏对象上时,右侧会出现它的详细信息面板,这些信息是根据脚本中声明的public可序列化变量而来的.此外,也可以通过EditorGUI来对它进行绘制,让面板更具可操作性. Inspector面板: EditorGUI和GUI的用法几乎完全一致,目前来说前者多用于编辑器开发,后者多用于发布后调试编辑器.即他们都是起辅助作用的.EditorGUI提供丰富的组件非常丰富,常用的绘制元素包括文本,按钮,图片和滑动框等.做一个好的编辑器,是离不开EditorGUI的. 如图所示,我

讲解如何在Unity的Inspector面板实现类似摄像机层次遮罩的多选效果

首先,我们现在的需求是这样的,我定义了一个脚本,里面有一个枚举,还有一个Public的枚举类型变量,现在我们就可以在Unity的工程里的Inspector面板中通过手动的选择来控制这个枚举变量到底选择什么了,就像我们Unity中的摄像机自带的那种可以选择遮罩层次的感觉,但是现在的问题是,我们只能选择一个,并不能多选,现在我们要解决的就是,给这个枚举变量写一段让它可以多选的代码. 效果图如下: 这里的变量名我改成了自定义的中文,其实在代码中大家可以根据自己的需求更改 和上一篇一样,我先说一下这个工

unity中可以使一个私有的变量在Inspector面板中显示出来 和 SerializeField的使用

[HideInInspector] 可以使一个公有的变量在Inspector面板中隐藏起来 [SerializeField] 可以使一个私有的变量在Inspector面板中显示出来

Unity3D之如何扩展脚本的Inspector面板

关于扩展编辑器 Unity给我们提供了很好的可视化编程环境,但对于我们有些更懒蛋的程序猿来说,还不够方便.所以Unity也给我们提供了很友好的扩展方式. 今天来说一下如何自定义一个脚本的inspector面板. unity中所有编辑器相关的脚本,都放在Editor文件夹,所以建一个先. 假设有个脚本ABC.cs 我们想自定义它的Inspector面板. 现在上代码: // 要自定义的脚本 public class ABC : Monobehaviour{ public bool a; publi

解决在自定义编辑器下,无法用Inspector面板的赋值作为参数显示Gizmos的问题

之前在给策划童鞋写一个小脚本,为了让他们使用时能很快了解用法,自定义了编辑器,重写了一下脚本的inspector面板布局和显示逻辑. 但发现一个问题,就是一旦自定义了Editor,Gizmo的绘图包含通过编辑器赋值的参数就不能使用了.也就是说,假如我想Gizmos.DrawLine(from * param, to * param);其中 param 为编辑器赋值参数,这时Gizmo会一直使用默认值. 我想这也很好理解,扩展的东西Gizmo当然是不知道的.于是google了一下,几经波折(主要是

Unity Interface Serialization-Expose Interface field In Inspector

Unity has some quirks about their inspector, so as a preface they are listed here: If you add a [Serializable] attribute to a class, Unity's Inspector will attempt to show all public fields inside that class. Any class extending Monobehaviour automat

Unity处理Hierarchy面板上的一些特殊的对象的特殊标识

孙广东  2015.5.27 转载请注明出处:http://blog.csdn.net/u010019717 要实现下面的效果:.其实主要是要标识一些比较特殊的对象(或者是具有特殊组件的对象) 实现方式就是监听Unity的一些事件被 EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged; EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItem