【Attribute】Inspector视图枚举字段范围限定特性

news2024/7/4 1:47:09

简介

        为了提升枚举的复用性,有时候我们可以通过限定枚举字段的范围来避免定义新的枚举类型,例如有一个代表方向的枚举(包括None,Left,Up,Right,Down),全局方向(Left,Up,Right,Down),水平方向(Left,Right),竖直方向(Up,Down)。

代码示例(C#)

EnumRangeAttribute.cs

using System;
using System.Linq;
using UnityEngine;

/// <summary>
/// 枚举范围限定特性
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class EnumRangeAttribute : PropertyAttribute
{
    /// <summary>
    /// 枚举最小值
    /// </summary>
    public int mMin { get; }

    /// <summary>
    /// 枚举最大值
    /// </summary>
    public int mMax { get; }

    /// <summary>
    /// 枚举名称合集
    /// </summary>
    public string[] mEnumNames { get => enumNames?.ToArray(); }

    /// <summary>
    /// 枚举值合集
    /// </summary>
    public int[] mEnumValues { get => enumValues?.ToArray(); }

    /// <summary>
    /// 枚举范围特性模式
    /// </summary>
    public EnumRangeMode mEnumRangeMode { get; }

    private string[] enumNames;
    private int[] enumValues;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="min">枚举最小值</param>
    /// <param name="max">枚举最大值</param>
    public EnumRangeAttribute(int min, int max)
    {
        mMin = min;
        mMax = max;
        mEnumRangeMode = EnumRangeMode.MinAndMax;
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="enumNames">枚举名称合集 (大小写敏感)</param>
    public EnumRangeAttribute(params string[] enumNames)
    {
        this.enumNames = enumNames;
        mEnumRangeMode = EnumRangeMode.EnumNames;
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="enumValues">枚举值合集</param>
    public EnumRangeAttribute(int[] enumValues)
    {
        this.enumValues = enumValues;
        mEnumRangeMode = EnumRangeMode.EnumValues;
    }

    /// <summary>
    /// 枚举范围特性模式
    /// </summary>
    public enum EnumRangeMode
    {
        MinAndMax, EnumNames, EnumValues
    }
}

EnumRangeAttributeDrawer.cs

using UnityEngine;
using UnityEditor;
using System;
using System.Linq;

/// <summary>
/// 枚举范围限定特性绘制器
/// </summary>
[CustomPropertyDrawer(typeof(EnumRangeAttribute))]
public class EnumRangeAttributeDrawer : PropertyDrawer
{
    private EnumRangeAttribute enumRangeAttribute; // 枚举范围特性
    private Type enumType; // 枚举类型
    private string[] rawEnumNames; // 枚举名称原始合集
    private string[] displayNames; // 下拉菜单显示名称合集
    private int selectedIndex; // 当前所选中的选项索引
    private int preIndex; // 所选中的选项索引的副本
    private bool isLockGUI; // 是否锁定GUI的绘制
    private bool isInit; // 是否初始化
    private string warningText; // 警告信息
    private string preWarningText; // 警告信息副本

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUI.GetPropertyHeight(property, label, true);
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (fieldInfo.FieldType.IsEnum)
        {
            Init(property);
            if (!isLockGUI)
            {
                selectedIndex = EditorGUI.Popup(position, label, selectedIndex, displayNames.Select(n => new GUIContent(n)).ToArray());
                if (selectedIndex != preIndex)
                {
                    preIndex = selectedIndex;
                    property.enumValueIndex = IndexOf(displayNames[selectedIndex]);
                }
            }
        }
        else warningText = $"Warning:The type of the field '{fieldInfo.Name}' marked with 'EnumRange' attribute is not 'Enum'.";
        if (!warningText.Equals(preWarningText))
        {
            Debug.LogWarning(warningText);
            preWarningText = warningText;
        }
    }

    // 初始化
    private void Init(SerializedProperty property)
    {
        if (!isInit)
        {
            isLockGUI = true;
            isInit = true;

            enumRangeAttribute = (EnumRangeAttribute)attribute;
            if (enumRangeAttribute == null)
            {
                warningText = $"Warning:The field '{fieldInfo.Name}' is not marked 'EnumRange' attribute.";
                return;
            }

            enumType = fieldInfo.FieldType;

            rawEnumNames = property.enumNames;
            if (rawEnumNames == null || rawEnumNames.Length == 0)
            {
                warningText = $"Warning:The enum's names of the field '{property.name}' is null or empty.";
                return;
            }

            if (!InitDisplayNames()) displayNames = rawEnumNames;

            selectedIndex = Array.FindIndex(displayNames, n => n.Equals(rawEnumNames[property.enumValueIndex]));
            if (selectedIndex == -1) selectedIndex = 0;
            preIndex = selectedIndex;

            warningText = string.Empty;
            preWarningText = string.Empty;
            isLockGUI = false;
        }
    }

    // 初始化枚举下拉菜单显示名称合集
    private bool InitDisplayNames()
    {
        switch (enumRangeAttribute.mEnumRangeMode)
        {
            case EnumRangeAttribute.EnumRangeMode.MinAndMax:
                return MinAndMaxInit();
            case EnumRangeAttribute.EnumRangeMode.EnumNames:
                return EnumNamesInit();
            case EnumRangeAttribute.EnumRangeMode.EnumValues:
                return EnumValuesInit();
        }
        return false;
    }

    // MinAndMax模式初始化
    private bool MinAndMaxInit()
    {
        if (enumRangeAttribute.mMin > enumRangeAttribute.mMax) return false;
        var v_values = Enum.GetValues(enumType).Cast<int>();
        if (v_values.Count() == 0) return false;
        v_values = v_values.Where(val => val >= enumRangeAttribute.mMin && val <= enumRangeAttribute.mMax);
        if (v_values.Count() == 0) return false;
        var v_names = v_values.Select(val => Enum.GetName(enumType, val));
        if (v_names.Count() == 0) return false;
        displayNames = v_names.ToArray();
        return true;
    }

    // EnumNames模式初始化
    private bool EnumNamesInit()
    {
        if (enumRangeAttribute.mEnumNames == null || enumRangeAttribute.mEnumNames.Length == 0) return false;
        var v_names = enumRangeAttribute.mEnumNames.Where(n => Array.FindIndex(rawEnumNames, en => en.Equals(n)) != -1);
        if (v_names.Count() == 0) return false;
        displayNames = v_names.ToArray();
        return true;
    }

    // EnumValues模式初始化
    private bool EnumValuesInit()
    {
        if (enumRangeAttribute.mEnumValues == null || enumRangeAttribute.mEnumValues.Length == 0) return false;
        var v_values = Enum.GetValues(enumType).Cast<int>();
        if (v_values.Count() == 0) return false;
        v_values = v_values.Where(val => enumRangeAttribute.mEnumValues.Contains(val));
        if (v_values.Count() == 0) return false;
        var v_names = v_values.Select(val => Enum.GetName(enumType, val));
        if (v_names.Count() == 0) return false;
        displayNames = v_names.ToArray();
        return true;
    }

    // 返回指定名称的枚举在原始枚举合集中的索引
    private int IndexOf(string enumName)
    {
        int index = Array.FindIndex(rawEnumNames, n => n.Equals(enumName));
        return index == -1 ? 0 : index;
    }
}

效果截图

 如果这篇文章对你有帮助,请给作者点个赞吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1511306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在RTMP推送端和RTMP播放端支持Enhanced RTMP H.265(HEVC)

技术背景 时隔多年&#xff0c;在Enhancing RTMP, FLV With Additional Video Codecs And HDR Support&#xff08;2023年7月31号正式发布&#xff09;官方规范出来之前&#xff0c;如果RTMP要支持H.265&#xff0c;大家约定俗成的做法是扩展flv协议&#xff0c;CDN厂商携手给…

掌握未来数据管理:MongoDB学习网站全攻略!

介绍&#xff1a;MongoDB是一个开源的文档型数据库系统&#xff0c;以其灵活性和可扩展性而闻名。以下是对MongoDB的详细介绍&#xff1a; 基本概念&#xff1a;MongoDB与传统的关系型数据库不同&#xff0c;它使用BSON&#xff08;类似JSON&#xff09;格式存储数据&#xff0…

案例分析篇05:数据库设计相关28个考点(9~16)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

LeetCode[题解] 1261. 在受污染的二叉树中查找元素

首先我们看原题 给出一个满足下述规则的二叉树&#xff1a; root.val 0如果 treeNode.val x 且 treeNode.left ! null&#xff0c;那么 treeNode.left.val 2 * x 1如果 treeNode.val x 且 treeNode.right ! null&#xff0c;那么 treeNode.right.val 2 * x 2 现在这个…

细粒度IP定位参文2(Corr-SLG):A street-level IP geolocation method (2021年)

[2]S. Ding, F. Zhao, and X. Luo, “A street-level IP geolocation method based on delay-distance correlation and multilayered common routers,” Secur. Commun. Netw., vol. 2021, no. 1, pp. 1–10, 2021. 智能设备的地理位置可以帮助提供多媒体内容提供商和5G网络中…

linux设置systemctl启动

linux设置nginx systemctl启动 生成nginx.pid文件 #验证nginx的配置&#xff0c;并生成nginx.pid文件 /usr/local/nginx/sbin/nginx -t #pid文件目录在 /usr/local/nginx/run/nginx.pid 设置systemctl启动nginx #添加之前需要先关闭启动状态的nginx&#xff0c;让nginx是未…

静态时序分析:SDC约束命令set_output_delay详解

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 目录 指定延迟值 指定端口、引脚列表 指定参考时钟 简单使用 指定时钟下降沿 指定参考端口、引脚 包含源、网络延迟 指定电平敏感 指定上升、下降沿 指…

DVWA靶场-暴力破解

DVWA是一个适合新手锻炼的靶机&#xff0c;是由PHP/MySQL组成的 Web应用程序&#xff0c;帮助大家了解web应用的攻击手段 DVWA大致能分成以下几个模块&#xff0c;包含了OWASP Top 10大主流漏洞环境。 Brute Force——暴力破解 Command Injection——命令注入 CSRF——跨站请…

网工内推 | 数据库工程师,最高35k*14薪,OCP认证优先,带薪年假

01 洛轲智能 招聘岗位&#xff1a;数据库工程师 职责描述&#xff1a; 1. 负责数据库备份及恢复策略制定&#xff1b; 2. 负责数据库性能分析及调优&#xff1b; 3. 负责数据库相关项目的方案制定、评测、投产实施和维护管理&#xff1b; 4. 数据库日常运维工作&#xff1a; -…

手撸nano-gpt

nano GPT 跟着youtube上AndrejKarpathy大佬复现一个简单GPT 1.数据集准备 很小的莎士比亚数据集 wget https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt 1.1简单的tokenize 数据和等下的模型较简单&#xff0c;所以这里用了个…

解析Perl爬虫代码:使用WWW__Mechanize__PhantomJS库爬取stackoverflow.com的详细步骤

在这篇文章中&#xff0c;我们将探讨如何使用Perl语言和WWW::Mechanize::PhantomJS库来爬取网站数据。我们的目标是爬取stackoverflow.com的内容&#xff0c;同时使用爬虫代理来和多线程技术以提高爬取效率&#xff0c;并将数据存储到本地。 Perl爬虫代码解析 首先&#xff0…

神经网络线性量化方法简介

可点此跳转看全篇 目录 神经网络量化量化的必要性量化方法简介线性对称量化线性非对称量化方法神经网络量化 量化的必要性 NetworkModel size (MB)GFLOPSAlexNet2330.7VGG-1652815.5VGG-1954819.6ResNet-50983.9ResNet-1011707.6ResNet-15223011.3GoogleNet271.6InceptionV38…

【机器学习300问】34、决策树对于数值型特征如果确定阈值?

还是用之前的猫狗二分类任务举例&#xff08;这个例子出现在【机器学习300问】第33问中&#xff09;&#xff0c;我们新增一个数值型特征&#xff08;体重&#xff09;&#xff0c;下表是数据集的详情。如果想了解更多决策树的知识可以看看我之前的两篇文章&#xff1a; 【机器…

spring启动时如何自定义日志实现

一、现象 最近在编写传统的springmvc项目时&#xff0c;遇到了一个问题&#xff1a;虽然在项目的web.xml中指定了log4j的日志启动监听器Log4jServletContextListener&#xff0c;且开启了日志写入文件&#xff0c;但是日志文件中只记录业务代码中我们声明了日志记录器的日志&a…

CPU设计实战-协处理器访问指令的实现

目录 一 协处理器的作用与功能 1.计数寄存器和比较寄存器 2.Status寄存器 3.Cause寄存器(标号为13) 4.EPC寄存器(标号为14) 5.PRId寄存器(标号为15) 6.Config 寄存器(标号为16)-配置寄存器 二 协处理器的实现 三 协处理器访问指令说明 四 具体实现 1.译码阶段 2.执行…

3/12/24交换排序、插入排序、选择排序、归并排序

目录 交换排序 冒泡排序 快速排序 插入排序 直接插入排序 选择排序 简单选择排序 堆排序 归并排序 各种排序的时间复杂度、空间复杂度、稳定性和复杂度 快排真题2016 选排真题2022 排序算法分为交换类排序、插入类排序、选择类排序、归并类排序。 交换排序 交换排…

【智能算法】哈里斯鹰算法(HHO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2019年&#xff0c;Heidari 等人受到哈里斯鹰捕食行为启发&#xff0c;提出了哈里斯鹰算法(Harris Hawk Optimization, HHO)。 2.算法原理 2.1算法思想 根据哈里斯鹰特性&#xff0c;HHO分为探索-…

新智元 | Stable Diffusion 3技术报告流出,Sora构架再立大功!生图圈开源暴打Midjourney和DALL·E 3?

本文来源公众号“新智元”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Stable Diffusion 3技术报告流出&#xff0c;Sora构架再立大功&#xff01;生图圈开源暴打Midjourney和DALLE 3&#xff1f; 【新智元导读】Stability AI放…

chrome浏览器插件content.js和background.js还有popup都是什么,怎么通讯

popup 在用户点击扩展程序图标时&#xff08;下图中的下载图标&#xff09;&#xff0c;都可以设置弹出一个popup页面。而这个页面中自然是可以包含运行的js脚本的&#xff08;比如就叫popup.js&#xff09;。它会在每次点击插件图标——popup页面弹出时&#xff0c;重新载入。…

如何阅读“计算机界三大神书”之一 ——SICP

《计算机程序的构造和解释》&#xff08;Structure and Interpretation of Computer Programs&#xff0c;简记为SICP&#xff09;是MIT的基础课教材&#xff0c;出版后引起计算机教育界的广泛关注&#xff0c;对推动全世界大学计算机科学技术教育的发展和成熟产生了很大影响。…