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

2024-02-26 01:44

本文主要是介绍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

这篇关于2024-02-25 Unity 编辑器开发之编辑器拓展7 —— Inspector 窗口拓展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/747346

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

基于Python开发PPTX压缩工具

《基于Python开发PPTX压缩工具》在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,不便于传输和存储,所以本文将使用Python开发一个PPTX压缩工具,需要的可以了解下... 目录引言全部代码环境准备代码结构代码实现运行结果引言在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

五大特性引领创新! 深度操作系统 deepin 25 Preview预览版发布

《五大特性引领创新!深度操作系统deepin25Preview预览版发布》今日,深度操作系统正式推出deepin25Preview版本,该版本集成了五大核心特性:磐石系统、全新DDE、Tr... 深度操作系统今日发布了 deepin 25 Preview,新版本囊括五大特性:磐石系统、全新 DDE、Tree

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情