前言(可以不看)
早之前写自定义GroupLayout的时候就想要有个Inspector界面属性显隐控制(条件控制)的东西了,当时还完全没有想到该怎么写,最近又遇到了相关的东西就去尝试写了个,经过测试之后能够支持一部分的条件下隐藏某一属性了。
实现
1.自定义Attribute
这个我也不知道怎么搞照猫画老虎写了个,先继承PropertyAttribute
然后里面加上自定义的属性,写上构造函数就行了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum CompareType
{
NotEqul = 0,
Less = 1 << 0,
Equl = 1 << 1,
Greater = 1 << 2,
LessAndEqul = Less | Equl,
GreaterAndEqul = Greater | Equl,
}
public enum CalculateType
{
None = 0,
/// <summary>
///或
/// <summary>
Or = 1 << 0,
/// <summary>
///与
/// <summary>
And = 1 << 1,
/// <summary>
///异或
/// <summary>
Xor = 1 << 2,
/// <summary>
/// 取反运算,有点问题,取反是单目运算暂时废弃
/// </summary>
Not = 1 << 3,
}
public class PropertyActiveAttribute : PropertyAttribute
{
//字段路径
public string FieldPath { get; set; }
//说明一下,逻辑是这样的
//获取字段路径所在位置的字段,然后如果需要计算则与计算的值进行一个计算
//然后使用计算得到的值和比较的值进行对应的比较,如果为真那么会隐藏掉
//比较类型
public CompareType Compare { get; set; }
//比较的值
public object CompareValue { get; set; }
//计算的类型
public CalculateType Calculate { get; set; } = CalculateType.None;
//计算的值
public object CalculateValue { get; set; }
public PropertyActiveAttribute(string fieldPath, object value, CompareType compare = CompareType.Equl)
{
FieldPath = fieldPath;
CompareValue = value;
Compare = compare;
}
public PropertyActiveAttribute(string fieldPath, CalculateType calculate, object calculateValue, object value, CompareType compare = CompareType.Equl)
{
FieldPath = fieldPath;
Calculate = calculate;
CalculateValue = calculateValue;
CompareValue = value;
Compare = compare;
}
}
2. UnityEditor的CustomPropertyDrawer特性
用来拓展编辑器,可以对某一类型或者带有某一特性的字段进行自定义的绘制
该类需要继承PropertyDrawer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Reflection;
using System;
[CustomPropertyDrawer(typeof(PropertyActiveAttribute), false)]
public class PropertyActiveEditor : PropertyDrawer
{
//字段路径
private string field;
//比较的值
private object compareValue;
//比较类型
private CompareType compare;
//计算的值
private object calculateValue;
//计算类型
private CalculateType calculate;
//是否隐藏
private bool flag;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
//attribute是父类的,传过来的是属性值
PropertyActiveAttribute attr = attribute as PropertyActiveAttribute;
//对应变量赋值
field = attr.FieldPath;
compareValue = attr.CompareValue;
compare = attr.Compare;
calculate = attr.Calculate;
calculateValue = attr.CalculateValue;
//获取当前属性所在的类,例如当前属性是a.b,那么就是获取a
var parent = property.GetActualObjectParent();
//获取用于条件判断的字段信息,值以及类型
var ComparePropertyField = parent.GetType().GetField(field);
var ComparePropertyValue = ComparePropertyField?.GetValue(parent);
var ComparePropertyType = ComparePropertyValue?.GetType();
//如果计算的类型不为None说明要计算
object calculateRes = ComparePropertyValue;
if (calculate != CalculateType.None)
{
//目前仅支持int或enum(其实写这个主要为了enum判断)
if (CanChangeToInt(ComparePropertyValue) && CanChangeToInt(calculateValue))
{
switch (attr.Calculate)
{
case CalculateType.Or:
{
calculateRes = (int)ComparePropertyValue | (int)calculateValue;
}
break;
case CalculateType.And:
{
calculateRes = (int)ComparePropertyValue & (int)calculateValue;
}
break;
case CalculateType.Xor:
{
calculateRes = (int)ComparePropertyValue ^ (int)calculateValue;
}
break;
default:
{
return EditorGUI.GetPropertyHeight(property);
}
}
}
else
{
return EditorGUI.GetPropertyHeight(property);
}
}
if (calculateRes == null) return EditorGUI.GetPropertyHeight(property);
//等于和不等于直接调用object的Equals
if (compare == CompareType.Equl)
{
flag = calculateRes.Equals(compareValue);
}
else if (compare == CompareType.NotEqul)
{
flag = !calculateRes.Equals(compareValue);
}
else if (ComparePropertyType==compareValue.GetType()&&IsNumberType(ComparePropertyType))
{
//其他比较需要使用Comparer来比较两个object
switch (compare)
{
case CompareType.Less:
{
flag = Comparer.DefaultInvariant.Compare(calculateRes, compareValue) < 0;
}
break;
case CompareType.Greater:
{
flag = Comparer.DefaultInvariant.Compare(calculateRes, compareValue) > 0;
}
break;
case CompareType.LessAndEqul:
{
flag = Comparer.DefaultInvariant.Compare(calculateRes, compareValue) <= 0;
}
break;
case CompareType.GreaterAndEqul:
{
flag = Comparer.DefaultInvariant.Compare(calculateRes, compareValue) >= 0;
}
break;
}
}
if (flag) return 0;
else
{
return EditorGUI.GetPropertyHeight(property);
}
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
//不隐藏再进行一个绘制
if (!flag)
{
EditorGUI.PropertyField(position, property, label, true);
}
}
//判断是否是数值类型
public static bool IsNumberType(Type type)
{
//IsPrimitive就是系统自带的类,IsValueType就是值类型,再排除char剩下的就是int,float这些了
return (type.IsPrimitive && type.IsValueType && type != typeof(char));
}
//。。。。
public static bool CanChangeToInt(object value)
{
try
{
int i = (int)value;
return true;
}
catch
{
return false;
}
}
public static bool CanChangeToDouble(object value)
{
try
{
double i = (double)value;
return true;
}
catch
{
return false;
}
}
}
3.额外代码
获取属性的父物体
#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
public static class UnityEditorHelper
{
public static T GetActualObject<T>(this SerializedProperty property)
{
try
{
if (property == null)
return default(T);
var serializedObject = property.serializedObject;
if (serializedObject == null)
{
return default(T);
}
var targetObject = serializedObject.targetObject;
//if (property.depth > 0)
//{
var slicedName = property.propertyPath.Split('.').ToList();
List<int> arrayCounts = new List<int>();
for (int index = 0; index < slicedName.Count; index++)
{
arrayCounts.Add(-1);
var currName = slicedName[index];
if (currName.EndsWith("]"))
{
var arraySlice = currName.Split('[', ']');
if (arraySlice.Length >= 2)
{
arrayCounts[index - 2] = Convert.ToInt32(arraySlice[1]);
slicedName[index] = string.Empty;
slicedName[index - 1] = string.Empty;
}
}
}
while (string.IsNullOrEmpty(slicedName.Last()))
{
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
return DescendHierarchy<T>(targetObject, slicedName, arrayCounts, 0);
// }
// return default(T);
}
catch
{
return default(T);
}
}
public static object GetActualObjectParent(this SerializedProperty property)
{
try
{
if (property == null)
return default;
//获取当前序列化的Object
var serializedObject = property.serializedObject;
if (serializedObject == null)
{
return default;
}
//获取targetObject,这里的targetObject就是
//我不好描述直接举个例子:a.b.c.d.e.f,比如serializedObject就是f,那么targetObject就是a
var targetObject = serializedObject.targetObject;
//还是上面的例子propertyPath其实就是a.b.c.d.e.f
//但是如果其中某一个是Array的话假设是b那么就会变成a.b.Array.data[x].c.d.e.f
//其中x为index
var slicedName = property.propertyPath.Split('.').ToList();
List<int> arrayCounts = new List<int>();
//根据"."分好后还需要获取其中的数组及其index保存在一个表中
for (int index = 0; index < slicedName.Count; index++)
{
arrayCounts.Add(-1);
var currName = slicedName[index];
if (currName.EndsWith("]"))
{
var arraySlice = currName.Split('[', ']');
if (arraySlice.Length >= 2)
{
arrayCounts[index - 2] = Convert.ToInt32(arraySlice[1]);
slicedName[index] = string.Empty;
slicedName[index - 1] = string.Empty;
}
}
}
//清除数组导致的空
while (string.IsNullOrEmpty(slicedName.Last()))
{
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
//如果和属性名称相同则清除
if (slicedName.Last().Equals(property.name))
{
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
//如果空了那么返回targetObject为当前的父对象
if (slicedName.Count == 0) return targetObject;
//继续清除数组,防止父对象也是数组
while (string.IsNullOrEmpty(slicedName.Last()))
{
int i = slicedName.Count - 1;
slicedName.RemoveAt(i);
arrayCounts.RemoveAt(i);
}
//如果空了那么返回targetObject为当前的父对象
if (slicedName.Count == 0) return targetObject;
//获取父物体
return DescendHierarchy<object>(targetObject, slicedName, arrayCounts, 0);
}
catch
{
return default;
}
}
//自己看
static T DescendHierarchy<T>(object targetObject, List<string> splitName, List<int> splitCounts, int depth)
{
if (depth >= splitName.Count)
return default(T);
var currName = splitName[depth];
if (string.IsNullOrEmpty(currName))
return DescendHierarchy<T>(targetObject, splitName, splitCounts, depth + 1);
int arrayIndex = splitCounts[depth];
var newField = targetObject.GetType().GetField(currName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (newField == null)
{
Type baseType = targetObject.GetType().BaseType;
while (baseType != null && newField == null)
{
newField = baseType.GetField(currName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
baseType = baseType.BaseType;
}
}
var newObj = newField.GetValue(targetObject);
if (depth == splitName.Count - 1)
{
T actualObject = default(T);
if (arrayIndex >= 0)
{
if (newObj.GetType().IsArray && ((System.Array)newObj).Length > arrayIndex)
actualObject = (T)((System.Array)newObj).GetValue(arrayIndex);
var newObjList = newObj as IList;
if (newObjList != null && newObjList.Count > arrayIndex)
{
actualObject = (T)newObjList[arrayIndex];
}
}
else
{
actualObject = (T)newObj;
}
return actualObject;
}
else if (arrayIndex >= 0)
{
if (newObj is IList)
{
IList list = (IList)newObj;
newObj = list[arrayIndex];
}
else if (newObj is System.Array)
{
System.Array a = (System.Array)newObj;
newObj = a.GetValue(arrayIndex);
}
}
return DescendHierarchy<T>(newObj, splitName, splitCounts, depth + 1);
}
}
#endif
4. 使用
[Flags]
public enum TestEnum
{
a=1,
b=1<<1,
c = 1<<2,
d = 1<<3
}
public class Test : MonoBehaviour
{
public float speed = 5.0f;
//当小于10.0f时就不会显示,超过时会显示超速
[PropertyActive("speed", 10.0f, CompareType.Less)]
public string OverSpeed = "超速了";
public TestEnum enumtest = TestEnum.c;
//当enumtest中没有a时就隐藏了
[PropertyActive("enumtest", CalculateType.And, TestEnum.a, 0, CompareType.Equl)]
public string NotDouble = "测试计算";
}
结语
CSDN上的都要积分烦死了,还是自己写爽。