【Unity-Tool】特性扩展:条件控制Inspector属性显隐

0 前言

今天遇到了一个需求,以下举例进行描述:
现有一个自定义类型Test,它有一个枚举类型变量speciesType和一个float类型变量height。

Test.cs

using UnityEngine;
public enum SpeciesType
{
    People,
    Null,
}
public class Test : MonoBehaviour
{
    [SerializeField]
    public SpeciesType speciesType;
    public float height = 160.0f;
}

height变量仅在speciesType==SpeciesType.People时用来描述身高,也就是说,height变量仅在条件满足时起作用。
因此,我希望height变量仅在条件满足时才显示在Inspector窗口中。

理想的情况:
当speciesType的值为SpeciesType.People时:

当speciesType的值为其他时:
在这里插入图片描述

1 工具相关代码

1.UnityEditorHelper.cs
该脚本主要是为了辅助获取对应的属性值。

#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
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;

            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);
        }
        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

2.PropertyActiveAttribute.cs
定义特性。

using UnityEngine;

namespace InspectorEx
{
    public enum CompareType
    {
        Equal,
        NonEqual,
        Less,
        LessEqual,
        More,
        MoreEqual,
    }

    /// <summary>
    /// 特性:条件判断Inspector窗口属性的显隐
    /// </summary>
    public class PropertyActiveAttribute : PropertyAttribute
    {
        public string field;
        public CompareType compareType;
        public object compareValue;

        public PropertyActiveAttribute(string fieldName, CompareType type, object value) {
            field = fieldName;
            compareType = type;
            compareValue = value;
        }
    }
}

3.PropertyActiveEditor.cs
该脚本实现了条件控制Inspector属性显隐(主要通过重写PropertyDrawer的GetPropertyHeight和OnGUI方法实现)。

using System;
using System.Collections;
using UnityEditor;
using UnityEngine;

namespace InspectorEx
{
    [CustomPropertyDrawer(typeof(PropertyActiveAttribute), false)]
    public class PropertyActiveEditor : PropertyDrawer
    {
        private bool isShow = false;

        /// <summary>
        /// 修改属性占用高度
        /// </summary>
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
            var attr = attribute as PropertyActiveAttribute;
            var field = attr.field;
            var compareType = attr.compareType;
            var compareValue = attr.compareValue;

            var parent = property.GetActualObjectParent();
            var fieldInfo = parent.GetType().GetField(field);
            var fieldValue = fieldInfo?.GetValue(parent);

            isShow = IsMeetCondition(fieldValue, compareType, compareValue);
            if (!isShow) return 0;
            return base.GetPropertyHeight(property, label);
        }

        /// <summary>
        /// 符合条件
        /// </summary>
        private bool IsMeetCondition(object fieldValue, CompareType compareType, object compareValue) {
            if (compareType == CompareType.Equal) {
                return fieldValue.Equals(compareValue);
            }
            else if (compareType == CompareType.NonEqual) {
                return !fieldValue.Equals(compareValue);
            }
            else if (IsValueType(fieldValue.GetType()) && IsValueType(compareValue.GetType())) {
                switch (compareType) {
                    case CompareType.Less:
                        return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) < 0;
                    case CompareType.LessEqual:
                        return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) <= 0;
                    case CompareType.More:
                        return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) > 0;
                    case CompareType.MoreEqual:
                        return Comparer.DefaultInvariant.Compare(fieldValue, compareValue) >= 0;
                }
            }
            return false;
        }

        /// <summary>
        /// 是否是值类型
        /// </summary>
        private bool IsValueType(Type type) {
            //IsPrimitive就是系统自带的类,IsValueType就是值类型,再排除char剩下的就是int,float这些了
            return (type.IsPrimitive && type.IsValueType && type != typeof(char));
        }

        /// <summary>
        /// 绘制
        /// </summary>
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
            if (isShow) EditorGUI.PropertyField(position, property, label, true);
        }
    }
}

2 结果

1.代码

using InspectorEx;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SpeciesType
{
    People,
    Null,
}
public class Test : MonoBehaviour
{
    [SerializeField]
    public SpeciesType speciesType;

    [PropertyActive("speciesType",CompareType.Equal,SpeciesType.People)]
    public float height = 160.0f;
}

2.效果
同0中描绘的理想情况。
在这里插入图片描述
在这里插入图片描述

参考文章

### 回答1: #提问者没有完整的问题,无法给出全面的回答,请完善问题# 如果您要问的是如何使用Python和Unity进行Socket通信,那么我可以为您提供一些信息。Socket通信是指通过网络连接在不同的计算机上进行数据传输的过程。Python和Unity可以通过Socket协议实现实时的数据传输和通信。 对于Python和Unity之间的Socket通信,Python负责服务器端,Unity负责客户端。Python服务器将数据发送到Unity客户端,Unity客户端接收并解析数据。最常用的Socket协议是TCP(Transmission Control Protocol)协议,它提供了单一的连接,并将数据包使用特殊的数据结构进行编码和解码。 在开始使用Python和Unity进行Socket通信之前,需要安装Python的Socket模块和Unity的网络功能模块。然后,需要编写Python服务器和Unity客户端的代码,实现数据的发送和接收。 Python Socket模块是Python用于网络编程的核心模块。它提供了内置的套接字对象,可用于创建和操作Socket。在此基础上,可以编写一个Python服务器端程序,并绑定到IP地址和端口上。 Unity客户端使用C#,其自带的网络功能模块可以用于发送和接收数据。在Unity中,需要创建网络Socket连接,并进行连接、发送和接收操作。 总的来说,Python和Unity之间的Socket通信需要使用TCP协议,并需要编写Python服务器和Unity客户端的代码,才能实现数据的实时传输和通信。在实际工程中,还需要考虑数据格式、安全性等问题。 ### 回答2: #Python-Unity-Socket通信:Unity (C#)的实现 Python-Unity-Socket通信可以让Python代码与Unity (C#)代码实现良好的互动。通过Socket通信,Python和Unity都可以发送和接收网络数据,实现各种场景和功能。 实现Python-Unity Socket通信的步骤如下: 1. 在Python中建立Socket服务器(Server),等待Unity连接 2. 在Unity中建立Socket客户端(Client),连接到Python Socket服务器 3. Python和Unity互相发送和接收数据 在Python中,建立Socket服务器的代码如下: ``` import socket host = '127.0.0.1' port = 8888 s = socket.socket() s.bind((host, port)) s.listen(1) conn, addr = s.accept() print("连接地址:" + str(addr)) while True: data = conn.recv(1024).decode() if not data: break print("接收数据:" + data) conn.sendall(data.encode()) conn.close() ``` 在Unity中,建立Socket客户端的代码如下: ``` using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Net.Sockets; using System.Net; using System; public class SocketClient : MonoBehaviour { public string host = "127.0.0.1"; public int port = 8888; private TcpClient client; // Use this for initialization void Start () { try { client = new TcpClient(); client.Connect(host, port); } catch (Exception e) { Debug.Log(e); } string message = "Hello Python"; byte[] data = System.Text.Encoding.UTF8.GetBytes(message); NetworkStream stream = client.GetStream(); stream.Write(data, 0, data.Length); data = new byte[1024]; string responseData = string.Empty; int bytes = stream.Read(data, 0, data.Length); responseData = System.Text.Encoding.UTF8.GetString(data, 0, bytes); Debug.Log("收到服务器消息:" + responseData); stream.Close(); client.Close(); } } ``` 以上代码实现了Unity向Python发送一条消息,并接收Python回传的消息。 Python和Unity之间还可以通过Socket发送和接收其他类型的数据,如音频、视频等。需要注意的是,Python和Unity发送和接收数据的格式需要保持一致,可以使用Json、Protobuf等数据格式来统一。 ### 回答3: # 应该题目描述不太准确,这里按照理解给出回答 python-unity-socket-communication,是指使用Python语言和Unity游戏引擎之间进行网络Socket通信。 具体实现中,需要在Unity中编写C#代码,通过Socket连接Python服务器,实现网络通信。 在Unity中,首先需要使用Socket创建一个客户端连接到Python服务器,并通过该连接向Python服务器发送请求和接收响应。同时,需要编写代码来解析Python服务器返回的数据,以便Unity游戏引擎正确地使用它们。 在Python服务器中,需要编写Socket服务器程序,以便监听来自Unity客户端的请求,并对其做出响应。在响应Unity客户端请求时,需要将Python语言中的数据格式转换为Unity游戏引擎所需的格式。 总之,Python-Unity-Socket通信是一种常用且强大的网络通信方式,可以帮助Unity游戏开发人员高效地与Python服务器进行通信,以实现更为复杂的游戏功能和应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值