2024-02-25 Unity 编辑器开发之编辑器拓展7 —— Inspector 窗口拓展

文章目录

  • 1 SerializedObject 和 SerializedProperty
  • 2 自定义显示步骤
  • 3 数组、List 自定义显示
    • 3.1 基础方式
    • 3.2 自定义方式
  • 4 自定义属性自定义显示
    • 4.1 基础方式
    • 4.2 自定义方式
  • 5 字典自定义显示
    • 5.1 SerizlizeField
    • 5.2 ISerializationCallbackReceiver
    • 5.3 代码示例

1 SerializedObject 和 SerializedProperty

​ 在 Unity 中,可以完全自定义某一个脚本在 Inspector 窗口的相关显示。

​ SerializedObject 和 SerializedProperty 主要用于在 Unity 编辑器中操作和修改序列化对象的属性,通常在自定义编辑器中使用,以创建更灵活、可定制的属性面板。

​ 只需记住简单的规则:

  • SerializedObject:代表脚本对象。

    参考文档:https://docs.unity.cn/cn/2022.1/ScriptReference/SerializedObject.html。

  • SerializedProperty:代表脚本对象中的属性。

    参考文档:https://docs.unity.cn/cn/2022.1/ScriptReference/SerializedProperty.html。

2 自定义显示步骤

  1. 单独为某一个脚本实现一个自定义脚本,并且脚本需要继承 Editor。

    一般该脚本命名为:自定义脚本名 + Editor。

image-20240225145626645
  1. 在该脚本前加上特性。
    命名空间:UnityEditor
    特性名:CustomEditor(想要自定义脚本类名的 Type)
using UnityEditor;
using UnityEngine;

// 通过该特性,可以为 Lesson22 脚本自定义 Inspector 窗口中的显示
[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    ...
}
  1. 声明对应 SerializedProperty 序列化属性对象,主要通过它和自定义脚本中的成员进行关联。

    可以利用继承 Editor 后的成员 serializedObject 中的 FindProperty("成员变量名") 方法关联对应成员。

    一般在 OnEnable 函数中初始化。当选中对象后并在 Inspector 窗口进行显示时,OnEnable 函数会被执行;同理,选择其他对象使 Inspector 窗口取消显示时,OnDisable 函数会被执行。

using UnityEditor;
using UnityEngine;

// 通过该特性,可以为 Lesson22 脚本自定义 Inspector 窗口中的显示
[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty atk;
    private SerializedProperty def;
    private SerializedProperty obj;

    private void OnEnable() {
        // 关联序列化属性
        atk = serializedObject.FindProperty("atk");
        def = serializedObject.FindProperty("def");
        obj = serializedObject.FindProperty("obj");
    }
}
  1. 重写 OnInspectorGUI 函数。

    该函数控制 Inspector 窗口中显示的内容,只需在其中重写内容便可自定义窗口。

    注意:其中的逻辑需要包裹在这两句代码之间:

    serializedObject.Update(); // 更新序列化对象的表示形式 ... serializedObject.ApplyModifiedProperties(); // 应用属性修改

using UnityEditor;
using UnityEngine;

// 通过该特性,可以为 Lesson22 脚本自定义 Inspector 窗口中的显示
[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty atk;
    private SerializedProperty def;
    private SerializedProperty obj;
    
    private bool foldOut;

    private void OnEnable() {
        // 关联序列化属性
        atk = serializedObject.FindProperty("atk");
        def = serializedObject.FindProperty("def");
        obj = serializedObject.FindProperty("obj");
    }
    
    // 该函数控制了 Inspector 窗口中显示的内容
    // 只需要在其中重写内容便可以自定义窗口
    public override void OnInspectorGUI() {
        // base.OnInspectorGUI(); 不要调用父类的方法,而是去自定义
        
        serializedObject.Update(); // 更新序列化对象的表示形式
        
        // 自定义Inspector窗口的内容
        foldOut = EditorGUILayout.BeginFoldoutHeaderGroup(foldOut, "基础属性");
        if (foldOut) {
            if (GUILayout.Button("测试自定义 Inspector 窗口")) {
                Debug.Log(target.name); // 获取 Lesson22 脚本对象
            }
            EditorGUILayout.IntSlider(atk, 0, 100, "攻击力");
            def.floatValue = EditorGUILayout.FloatField("防御力", def.floatValue);
            EditorGUILayout.ObjectField(obj, new GUIContent("敌对对象"));
        }
        EditorGUILayout.EndFoldoutHeaderGroup();
        
        serializedObject.ApplyModifiedProperties(); // 应用属性修改
    }
}
image-20240225150537536

3 数组、List 自定义显示

3.1 基础方式

EditorGUILayout.PropertyField(SerializedProperty对象, 标题);

​ 该 API 会按照属性类型默认处理控件绘制的逻辑。

using UnityEditor;
using UnityEngine;

// 通过该特性,可以为 Lesson22 脚本自定义 Inspector 窗口中的显示
[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty strs;
    private SerializedProperty ints;
    private SerializedProperty gameObjects;
    private SerializedProperty listObjs;

    private void OnEnable() {
        // 默认得到的数组和 List 容量为空
        strs = serializedObject.FindProperty("strs");
        ints = serializedObject.FindProperty("ints");
        gameObjects = serializedObject.FindProperty("gameObjects");
        listObjs = serializedObject.FindProperty("listObjs");
    }
    
    public override void OnInspectorGUI() {
        serializedObject.Update();
        
        EditorGUILayout.PropertyField(strs, new GUIContent("字符串数组"));
        EditorGUILayout.PropertyField(ints, new GUIContent("整形数组"));
        EditorGUILayout.PropertyField(gameObjects, new GUIContent("游戏对象数组"));
        EditorGUILayout.PropertyField(listObjs, new GUIContent("游戏对象List"));

        serializedObject.ApplyModifiedProperties();
    }
}
image-20240225152856784

3.2 自定义方式

​ 利用 SerializedProperty 中数组相关的 API 来完成自定义。

API说明
arraySize获取数组或 List 容量。
InsertArrayElementAtIndex(索引)为数组在指定索引插入默认元素(容量会变化)。
DeleteArrayElementAtIndex(索引)为数组在指定索引删除元素(容量会变化)。
GetArrayElementAtIndex(索引)获取数组中指定索引位置的 SerializedProperty 对象。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

// 通过该特性,可以为 Lesson22 脚本自定义 Inspector 窗口中的显示
[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty strs;
    private SerializedProperty ints;
    private SerializedProperty gameObjects;
    private SerializedProperty listObjs;

    private int count;

    private void OnEnable() {
        // 默认得到的数组和 List 容量为空
        strs = serializedObject.FindProperty("strs");
        ints = serializedObject.FindProperty("ints");
        gameObjects = serializedObject.FindProperty("gameObjects");
        listObjs = serializedObject.FindProperty("listObjs");

        // 初始化当前容量,否则每次开始都是 0
        count = listObjs.arraySize;
    }

    public override void OnInspectorGUI() {
        serializedObject.Update();

        // 容量设置
        count = EditorGUILayout.IntField("List容量", count);
        
        // 移除尾部内容,从后往前移除
        for (int i = listObjs.arraySize - 1; i >= count; i--)
            listObjs.DeleteArrayElementAtIndex(i);
        
        // 根据容量绘制需要设置的每一个索引位置的对象
        for (int i = 0; i < count; i++) {
            // 如果数组或 List 容量不够,通过插入的形式扩容
            if (listObjs.arraySize <= i)
                listObjs.InsertArrayElementAtIndex(i);
        
            SerializedProperty indexPro = listObjs.GetArrayElementAtIndex(i);
            EditorGUILayout.ObjectField(indexPro, new GUIContent($"索引{i}"));
        }

        serializedObject.ApplyModifiedProperties();
    }
}
image-20240225153514603

4 自定义属性自定义显示

4.1 基础方式

EditorGUILayout.PropertyField(SerializedProperty对象, 标题);

​ 需要为自定义类添加 Serializable 特性。

using System;
using UnityEngine;

[Serializable]
public class MyCustomPro
{
    public int   i;
    public float f;
}

public class Lesson22 : MonoBehaviour
{
    public MyCustomPro myCustom;
}
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty myCustom;

    private void OnEnable() {
        myCustom = serializedObject.FindProperty("myCustom");
    }

    public override void OnInspectorGUI() {
        serializedObject.Update();
        
        EditorGUILayout.PropertyField(myCustom, new GUIContent("我的自定义属性"));

        serializedObject.ApplyModifiedProperties();
    }
}
image-20240225154307517

4.2 自定义方式

​ 使用如下方法寻找自定义属性的成员:

  1. SerializedProperty.FindPropertyRelative(属性)
  2. SerializedObject.FindProperty(属性.子属性)
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty myCustomI;
    private SerializedProperty myCustomF;

    private void OnEnable() {
        // myCustomI = myCustom.FindPropertyRelative("i");
        // myCustomF = myCustom.FindPropertyRelative("f");

        myCustomI = serializedObject.FindProperty("myCustom.i");
        myCustomF = serializedObject.FindProperty("myCustom.f");
    }

    public override void OnInspectorGUI() {
        serializedObject.Update();
        
        myCustomI.intValue = EditorGUILayout.IntField("自定义属性中的I", myCustomI.intValue);
        myCustomF.floatValue = EditorGUILayout.FloatField("自定义属性中的F", myCustomF.floatValue);

        serializedObject.ApplyModifiedProperties();
    }
}
image-20240225154447107

5 字典自定义显示

5.1 SerizlizeField

​ SerizlizeField 特性让私有字段可以被序列化(能够在 Unity 的 Inspector 窗口被看到)。

5.2 ISerializationCallbackReceiver

​ 该接口是 Unity 用于序列化和反序列化时执行自定义逻辑的接口,实现该接口的类能够在对象被序列化到磁盘或从磁盘反序列化时执行一些额外代码。

​ 接口中函数:

  • OnBeforeSerialize: 在对象被序列化之前调用。
  • OnAfterDeserialize: 在对象从磁盘反序列化后调用。

​ 由于需要用两个 List 存储 Dictionary 的键值对,所以需要在

  • OnBeforeSerialize 序列化之前:将 Dictionary 里的数据存入 List 中进行序列化。
  • OnAfterDeserialize 反序列化之后:将 List 中反序列化出来的数据存储到 Dictionary 中。

5.3 代码示例

using System.Collections.Generic;
using UnityEngine;

public class Lesson22 : MonoBehaviour, ISerializationCallbackReceiver
{
    public Dictionary<int, string> myDic = new Dictionary<int, string>() { { 1, "123" }, { 2, "234" } };

    [SerializeField]
    private List<int> keys = new List<int>();

    [SerializeField]
    private List<string> values = new List<string>();

    public void OnAfterDeserialize() {
        myDic.Clear();
        for (int i = 0; i < keys.Count; i++) {
            if (!myDic.ContainsKey(keys[i]))
                myDic.Add(keys[i], values[i]);
            else
                Debug.LogWarning("字典Dictionary容器中不允许有相同的键");
        }
    }

    public void OnBeforeSerialize() {
        keys.Clear();
        values.Clear();
        foreach (var item in myDic) {
            keys.Add(item.Key);
            values.Add(item.Value);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Lesson22))]
public class Lesson22Editor : Editor
{
    private SerializedProperty keys;
    private SerializedProperty values;

    private int dicCount;

    private void OnEnable() {
        keys = serializedObject.FindProperty("keys");
        values = serializedObject.FindProperty("values");

        dicCount = keys.arraySize;
    }

    public override void OnInspectorGUI() {
        serializedObject.Update();

        dicCount = EditorGUILayout.IntField("字典容量", dicCount);
        // 容量变少时,把多的删了
        for (int i = keys.arraySize - 1; i >= dicCount; i--) {
            keys.DeleteArrayElementAtIndex(i);
            values.DeleteArrayElementAtIndex(i);
        }

        for (int i = 0; i < dicCount; i++) {
            // 如果容量不够,扩容
            if (keys.arraySize <= i) {
                keys.InsertArrayElementAtIndex(i);
                values.InsertArrayElementAtIndex(i);
            }
            // 自定义键值对的修改
            SerializedProperty indexKey   = keys.GetArrayElementAtIndex(i);
            SerializedProperty indexValue = values.GetArrayElementAtIndex(i);
            EditorGUILayout.BeginHorizontal();
            indexKey.intValue = EditorGUILayout.IntField("字典的键", indexKey.intValue);
            indexValue.stringValue = EditorGUILayout.TextField("字典的值", indexValue.stringValue);
            EditorGUILayout.EndHorizontal();
        }

        serializedObject.ApplyModifiedProperties();
    }
}
image-20240225180102531

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Git Windows安装教程

Git的下载 去 Git 官网下载对应系统的软件了&#xff0c;下载地址为 git-scm.com 或者 gitforwindows.org git-scm 是 Git 的官方&#xff0c;里面有不同系统不同平台的安装包和源代码gitforwindows.org 里只有 windows 系统的安装包 安装 使用许可声明 选择安装目录 选择安…

springBoot整合Redis(一、Jedis操作Redis)

在springboot环境下连接redis的方法有很多&#xff0c;首先最简单的就是直接通过jedis类来连接&#xff0c;jedis类就相当于是redis的客户端表示。 但是因为现在比较常用的是&#xff1a;StringRedisTemplate和RedisTemplate&#xff0c;所以jedis只做简单的介绍。 一、Jedis…

勒索攻击新趋势,DarkSide解密工具

勒索攻击新趋势 2020年通过勒索病毒攻击已经成为网络犯罪分子热崇追捧的一种方式&#xff0c;全球几乎每天都有企业被勒索病毒攻击勒索&#xff0c;而且勒索的金额也越来越高&#xff0c;从几万美元到几千万美元不等&#xff0c;越来越多的黑客组织使用勒索病毒对企业发起攻击…

【Java系列】JDK 1.8 新特性之 Lambda表达式

目录 1、Lambda表达式介绍2、从匿名类到Lambda转换3、Lambda表达式 六种语法格式语法格式一&#xff1a;无参数、无返回值&#xff0c;只需要一个Lambda体语法格式二&#xff1a;lambda有一个参数、无返回值​语法格式三&#xff1a;Lambda只有一个参数时&#xff0c;可以省略&…

Linux之安装jdk,tomcat,mysql,部署项目

目录 一、操作流程 1.1安装jdk 1.2安装tomcat&#xff08;加创建自启动脚本&#xff09; 1.3 安装mysql 1.4部署项目 一、操作流程 首先把需要用的包放进opt文件下 1.1安装jdk 把jdk解压到/usr/local/java里 在刚刚放解压包的文件夹打开vim /etc/profile编辑器&#xff0c…

【前端素材】推荐优质后台管理系统Dashy平台模板(附源码)

一、需求分析 后台管理系统&#xff08;或称作管理后台、管理系统、后台管理平台&#xff09;是一种专门用于管理网站、应用程序或系统后台运营的软件系统。它通常由一系列功能模块组成&#xff0c;为管理员提供了管理、监控和控制网站或应用程序的各个方面的工具和界面。以下…

prometheus监控带安全认证的elasticsearch

1.下载elasticsearch_exporter wget 下载二进制包并解压、运行&#xff1a; wget https://github.com/prometheus-community/elasticsearch_exporter/releases/download/v1.3.0/elasticsearch_exporter-1.3.0.linux-amd64.tar.gz tar -xvf elasticsearch_exporter-1.3.0.lin…

【Prometheus】概念和工作原理介绍

目录 一、概述 1.1 prometheus简介 1.2 prometheus特点 1.3 prometheus架构图 1.4 prometheus组件介绍 1、Prometheus Server 2、Client Library 3、pushgateway 4、Exporters 5、Service Discovery 6、Alertmanager 7、grafana 1.5 Prometheus 数据流向 1.6 Pro…

MATLAB环境下基于洗牌复杂演化的图像分割算法

智能优化算法因其较强的搜索解能力而得到了大量的应用&#xff0c;在这些计算智能算法中&#xff0c;群体智能优化算法因其高效性、有效性以及健壮性等优点而得到了科研人员的青睐。这类算法借鉴生物群体的合作特性&#xff0c;主要解决大规模复杂的分布式问题&#xff0c;研究…

WPF Style样式设置

1.本window设置样式 <Window x:Class"WPF_Study.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expressi…

论文阅读NAM:Normalization-based Attention Module

Abstarct 识别不太显著的特征是模型压缩的关键。然而&#xff0c;在革命性的注意力机制中却没有对其进行研究。在这项工作中&#xff0c;我们提出了一种新的基于归一化的注意力模块&#xff08;NAM&#xff09;&#xff0c;它抑制了不太显著的权重。它对注意力模块应用了权重稀…

YOLOV9论文概述

YOLOV9论文阅读 摘要信息瓶颈可逆函数可编程梯度信息 (PGI)辅助可逆分支多级辅助信息 GELAN实验设计参数设置实验结论消融实验模块深度辅助可逆分支和多层辅助信息PGI综合 可视化结果结论 摘要 今天的深度学习方法侧重于如何设计最合适的目标函数&#xff0c;以便模型的预测结果…

Seata分布式事务实战AT模式

目录 分布式事务简介 典型的分布式事务应用场景 两阶段提交协议(2PC) 2PC存在的问题 什么是Seata&#xff1f; Seata的三大角色 Seata AT模式的设计思路 一阶段 二阶段 Seata快速开始 Seata Server&#xff08;TC&#xff09;环境搭建 db存储模式Nacos(注册&配…

H5 个人引导页官网型源码

H5 个人引导页官网型源码 源码介绍&#xff1a;源码无后台、无数据库&#xff0c;H5自检测适应、无加密&#xff0c;直接修改可用。 源码含有多选项&#xff0c;多功能。可展示自己站点、团队站点。手机电脑双端。 下载地址&#xff1a; https://www.changyouzuhao.cn/1434.…

HTML+CSS+JS:轮播组件

效果演示 一个具有动画效果的卡片元素和一个注册表单&#xff0c;背景为渐变色&#xff0c;整体布局简洁美观。 Code <div class"card" style"--d:-1;"><div class"content"><div class"img"><img src"./i…

java接受命令行输入

在Java中&#xff0c;你可以使用​​Scanner​​类来接受命令行输入。以下是一个简单的例子&#xff0c;演示如何从命令行接受输入&#xff1a; import java.util.Scanner;public class CommandLineInputExample {public static void main(String[] args) {// 创建一个Scanner…

019 Spring Boot+Vue 电影院会员管理系统(源代码+数据库+文档)

部分代码地址&#xff1a; https://github.com/XinChennn/xc019-cinema 一、系统介绍 cinema项目是一套电影院会员管理系统&#xff0c;使用前后端分离架构开发包含管理员、会员管理、会员卡管理、电影票、消费记录、数据统计等模块 二、所用技术 后端技术栈&#xff1a; …

width:100%和width:auto有啥区别

项目中使用了with属性&#xff0c;突然好奇auto 和 100% 的区别&#xff0c;特地搜索实践总结了一下观点 一、 width属性介绍二、 代码带入三、 分析比较四、 总结 一、 width属性介绍 width 属性用于设置元素的宽度。width 默认设置内容区域的宽度&#xff0c;但如果 box-siz…

国创证券:迎政策助力,工业母机概念爆发,华中数控、宏德股份等涨停

工业母机概念26日盘中强势拉升&#xff0c;到发稿&#xff0c;华中数控、宏德股份“20cm”涨停&#xff0c;德恩精工、纽威数控涨超16%&#xff0c;科德数控、拓斯达等涨超10%&#xff0c;秦川机床、沈阳机床等亦涨停。 音讯面上&#xff0c;2月23日举行的中央财经委员会第四次…

C语言第三十一弹---自定义类型:结构体(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、结构体内存对齐 1.1、为什么存在内存对齐? 1.2、修改默认对齐数 2、结构体传参 3、结构体实现位段 3.1、什么是位段 3.2、位段的内存分配 3.3、…