【Unity3D】Inspector界面属性显隐控制(条件控制)

前言(可以不看)

早之前写自定义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 = "测试计算";
}

没超速没选a
超速了选了a

结语

CSDN上的都要积分烦死了,还是自己写爽。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值