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");// 初始化当前容量,否则每次开始都是 0count = 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]);elseDebug.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

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

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

相关文章

【新三板年报文本分析】第一辑:python+selium模拟浏览器,批量实现上市公司年报链接

目录 序言函数模块介绍创建模拟浏览器对象只需要执行一次的部分需要批量执行的重复操作部分&#xff08;信息录入excel&#xff09;换页操作主函数 本地文件结构全部代码结果预览 如果直接需要结果的&#xff0c;可以直接见文末&#xff0c;获取资源。 序言 新三板年报链接&am…

emoji选择器

emoji表情 功能描述 这款聊天对话时选择表情的UI插件&#xff0c;是为了提升用户在聊天过程中的互动体验而设计的。它提供了一个直观且易于操作的界面&#xff0c;使用户能够快速地选择并插入各种表情符号&#xff0c;从而丰富他们的聊天内容&#xff0c;增加情感表达的多样性…

Unity3D 使用 Proto

一. 下载与安装 这里下载Google Protobuff下载 1. 源码用来编译CSharp 相关配置 2. win64 用于编译 proto 文件 二. 编译 1. 使用VS 打开 2. 点击最上面菜单栏 工具>NuGet 包管理器>管理解决方案的NuGet 管理包 版本一定要选择咱们一开始下载的对应版本否则不兼容&am…

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

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

幻兽帕鲁服务器多少钱?有买过的吗?

幻兽帕鲁服务器多少钱&#xff1f;太卷了&#xff0c;降价到24元1个月&#xff0c;阿里云4核16G10M游戏服务器26元1个月、149元半年&#xff0c;腾讯云4核16G游戏服务器32元、312元一年&#xff0c;华为云26元&#xff0c;京东云主机也是26元起。云服务器吧yunfuwuqiba.com给大…

VUE3环境搭建开发准备

VUE3 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;Vu…

【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…

100天精通Python(实用脚本篇)——第117天:基于selenium实现反反爬策略之代码输入账号信息登录网站

文章目录 专栏导读1. 前言2. 实现步骤3. 基础补充4. 代码实战4.1 创建连接4.2 添加请求头伪装浏览器4.3 隐藏浏览器指纹4.4 最大化窗口4.5 启动网页4.6 点击密码登录4.7 输入账号密码4.8 点击登录按钮4.9 完整代码4.10 GIF动图展示 五、总结 专栏导读 &#x1f525;&#x1f5…

IDEA安装配置以及安装配置Maven

IEDA官方下载地址&#xff0c;有专业版&#xff08;收费&#xff0c;破解&#xff09;&#xff0c;社区版&#xff08;免费&#xff09; 下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 安装配置Maven 1.解压apache-maven-3.6.3-bin.zip&#xff0c;安装maven到D盘softwar…

台湾旺泓-WH4530A三合一光距感 接近传感芯片

WH4530A是一款集成了环境光传感器&#xff08;PS&#xff09;接近传感器&#xff08;ALS&#xff09;和红外LED灯三合一为一体的光距感传感芯片&#xff0c;可以测距从0到100厘米以内范围&#xff1b;并采用I2C接口具有超高的灵敏度和精准的测距检测范围。 该WH4530A​​​​​…

真香!NineData SQL开发全面适配 GaiaDB

2 月&#xff0c;新年伊始&#xff0c;NineData 重磅发布&#xff0c;提供了对百度云原生关系型数据库 GaiaDB 的支持。 这一次的发布不仅仅是简单的数据源支持&#xff0c;而是覆盖了整个 SQL 开发能力的重要发布&#xff0c;意味着您已经可以完整地使用 NineData SQL 开发的…

MySQL 事务原理分析

事务 前提&#xff1a;并发连接访问。定义&#xff1a;事务是用户定义的一系列操作&#xff0c;这些操作要么都做&#xff0c;要么都不做&#xff0c;是一个不可分割的单位。目的&#xff1a;事务将数据库从一种一致性状态转换为另一种一致性状态&#xff0c;保证系统始终处于…

半导体物理基础-笔记(续)

源内容参考&#xff1a;https://www.bilibili.com/video/BV11U4y1k7zn/?spm_id_from333.337.search-card.all.click&vd_source61654d4a6e8d7941436149dd99026962 掺杂半导体的费米能级与温度及杂质浓度的关系图 在温度一定的条件下&#xff0c;施主杂质浓度越高&#xff0…

Oracle EBS GL 外币折算逻辑

背景 由于公司财务在10月份期间某汇率维护错误,导致帐套折算以后并合传送至合并帐套生成合并日记帐凭证的借贷金额特别大,但是财务核对的科目余额有没有问题,始终觉得合并日记帐生成会计分发有问题,需要我们给出外币折算逻辑。 基础设置 汇率 Path: GL->设置->币种-&…

pclpy 最小二乘法拟合平面

pclpy 最小二乘法拟合平面 一、算法原理二、代码三、结果1.左边原点云、右边最小二乘法拟合平面后点云投影 四、相关数据 一、算法原理 平面方程的一般表达式为&#xff1a; A x B y C z D 0 ( C ≠ 0 ) Ax By Cz D 0 \quad (C\neq0) AxByCzD0(C0) 即&#xff1a; …

Spring Boot对接RocketMQ示例

部署服务 参考RocketMq入门介绍 示例 引入maven依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.2</version></dependency>完整依赖如下&am…

C++ 游戏飞机大战, 字符型的

//#define _CRT_SECURE_NO_WARNINGS 1 用于禁止不安全函数的警告 #include<iostream> #include<stdlib.h> #include<string> #include<conio.h> #include<Windows.h> #include<time.h> #include <graphics.h> using namespace std;…

Rust升级慢,使用国内镜像进行加速

背景 rustup 是 Rust 官方的跨平台 Rust 安装工具&#xff0c;国内用户使用rustup update的时候&#xff0c;网速非常慢&#xff0c;可以使用国内的阿里云镜像源来进行加速 0x01 配置方法 1. Linux与Mac OS用户配置环境变量 修改~/.bash_profile文件添加如下内容&#xff1…

微信小程序 --- 小程序基础知识

小程序基础知识 1. 认识什么是小程序 什么是微信小程序 微信小程序是一种运行在微信内部的 轻量级 应用程序。 在使用小程序时 不需要下载安装&#xff0c;用户 扫一扫 或 搜一下 即可打开应用。它也体现了 “用完即走” 的理念&#xff0c;用户不用关心安装太多应用的问题…

力扣链表篇

以下刷题思路来自代码随想录以及官方题解 文章目录 203.移除链表元素707.设计链表206.反转链表24.两两交换链表中的节点19.删除链表的倒数第N个节点面试题 02.07. 链表相交142.环形链表II 203.移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链…