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

相关文章

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

Windows的CMD窗口如何查看并杀死nginx进程

《Windows的CMD窗口如何查看并杀死nginx进程》:本文主要介绍Windows的CMD窗口如何查看并杀死nginx进程问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录Windows的CMD窗口查看并杀死nginx进程开启nginx查看nginx进程停止nginx服务

Python使用smtplib库开发一个邮件自动发送工具

《Python使用smtplib库开发一个邮件自动发送工具》在现代软件开发中,自动化邮件发送是一个非常实用的功能,无论是系统通知、营销邮件、还是日常工作报告,Python的smtplib库都能帮助我们... 目录代码实现与知识点解析1. 导入必要的库2. 配置邮件服务器参数3. 创建邮件发送类4. 实现邮件

基于Python开发一个有趣的工作时长计算器

《基于Python开发一个有趣的工作时长计算器》随着远程办公和弹性工作制的兴起,个人及团队对于工作时长的准确统计需求日益增长,本文将使用Python和PyQt5打造一个工作时长计算器,感兴趣的小伙伴可... 目录概述功能介绍界面展示php软件使用步骤说明代码详解1.窗口初始化与布局2.工作时长计算核心逻辑3

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

如何基于Python开发一个微信自动化工具

《如何基于Python开发一个微信自动化工具》在当今数字化办公场景中,自动化工具已成为提升工作效率的利器,本文将深入剖析一个基于Python的微信自动化工具开发全过程,有需要的小伙伴可以了解下... 目录概述功能全景1. 核心功能模块2. 特色功能效果展示1. 主界面概览2. 定时任务配置3. 操作日志演示