Unity inspector绘制按钮与Editor下生成与销毁物体的方法 反射 协程 Editor

应美术要求,实现一个在编辑环境下,不运行,可以实例化预制体的脚本
在这里插入图片描述
效果如上图所示
1.去实现一个简单的 行、列实例化物体脚本
2.在Inspector下提供按钮
3.将方法暴露出来(通过自定义标签实现

需求一
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FX_init : MonoBehaviour
{// Start is called before the first frame updatepublic GameObject PreGameObject;public int numCol = 4;public float spaceCol = 2.0f;public int numRow = 4;public float spaceRow = 2.0f;private List<GameObject> cubeTemp;void Start(){SetInit();}public void SetInit(){for (int i = 0; i < numCol; i++){for (int j = 0; j< numRow; j++){float xPos = i * spaceRow;float yPos = j * spaceCol;GameObject newObject = Instantiate(PreGameObject, new Vector3(xPos, yPos, 0), Quaternion.identity);cubeTemp.Add(newObject);}}}public void Clear(){for (int i = 0; i < cubeTemp.Count; i++){DestroyImmediate(cubeTemp[i]);}}
}

上述是,一个简单的for循环实例化脚本,用数组存储他们方便后续销毁

using UnityEngine;[System.AttributeUsage(System.AttributeTargets.Method)]
public class InspectorButtonAttribute : PropertyAttribute
{public readonly string Name;public InspectorButtonAttribute(){}public InspectorButtonAttribute(string name){Name = name;}
}

InspectorButtonAttribute继承自PropertyAttribute。在Unity中,PropertyAttribute用于在检视器中自定义显示属性。
Name字段,用于指定按钮在检视器中显示的名称。
构造函数:提供了两个构造函数,一个是无参数的,另一个带有一个字符串参数。通过这两个构造函数,可以选择性地为按钮指定名称。如果没有指定名称,将使用方法的名称作为按钮的显示名称。
这个属性的主要目的是为了在方法上提供标记,使这些方法能够在Unity检视器中以按钮的形式显示出来。在InspectorButton自定义编辑器中,通过检查方法上是否有InspectorButtonAttribute来确定哪些方法应该被显示为按钮,以及按钮的显示名称是什么。

Base class to derive custom property attributes from. Use this to create custom attributes for script variables.

A custom attributes can be hooked up with a custom PropertyDrawer class to control how a script variable with that attribute is shown in the Inspector.
PropertyAttribute

using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(MonoBehaviour), true)]
[CanEditMultipleObjects]
public class InspectorButton : Editor
{public override void OnInspectorGUI(){base.OnInspectorGUI();var mono = target as MonoBehaviour;if (mono == null)return;var methods = mono.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic |BindingFlags.Instance | BindingFlags.Static).Where(method =>Attribute.IsDefined(method, typeof(InspectorButtonAttribute))).ToArray();foreach (var method in methods){var attr = method.GetCustomAttribute<InspectorButtonAttribute>();DrawButton(method, attr.Name);}}private void DrawButton(MethodInfo methodInfo, string methodName){if (string.IsNullOrEmpty(methodName))methodName = methodInfo.Name;EditorGUILayout.BeginHorizontal();if (GUILayout.Button(methodName,GUILayout.ExpandWidth(true))){foreach (var targetObj in targets){var mono = targetObj as MonoBehaviour;if (mono == null)continue;var val = methodInfo.Invoke(mono, new object[] { });if (val is IEnumerator coroutine)mono.StartCoroutine(coroutine);else if (val != null)Debug.Log($"{methodName}调用结果: {val}");}}EditorGUILayout.EndHorizontal();}
}

1.重写了OnInspectorGUI方法,以在默认检视器下方添加自定义GUI元素(按钮)。
// 获取目标 MonoBehaviour 的类型
var methods = mono.GetType()
// 获取该类型的所有方法
.GetMethods(
// 指定搜索标志,以获取公共、非公共、实例和静态方法
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static
)
// 使用 LINQ 过滤出标记了 InspectorButtonAttribute 的方法
.Where(method =>
Attribute.IsDefined(method, typeof(InspectorButtonAttribute))
)
// 将结果转换为数组
.ToArray();

mono.GetType(): 获取 target(即 MonoBehaviour 对象)的类型信息。

.GetMethods(…): 获取该类型的所有方法。BindingFlags 参数指定了搜索标志,其中包括公共方法、非公共方法、实例方法和静态方法。
2.使用反射检索带有InspectorButtonAttribute的方法。假定此属性不在提供的代码中定义,但假定它是一个自定义属性,用于标记应显示为按钮的方法。

.Where(method => Attribute.IsDefined(method, typeof(InspectorButtonAttribute))): 使用 LINQ 过滤出那些标记了 InspectorButtonAttribute 的方法。Attribute.IsDefined 方法用于检查方法是否应用了指定的属性。

.ToArray(): 将过滤后的方法结果转换为数组,以便后续使用。

  1. 方法调用
    var val = methodInfo.Invoke(mono, new object[] { });
    if (val is IEnumerator coroutine)
    mono.StartCoroutine(coroutine);
    else if (val != null)
    Debug.Log($“{methodName}调用结果: {val}”);
    使用反射(methodInfo.Invoke)调用方法。如果方法返回IEnumerator,则假定它是协程,并使用StartCoroutine启动。非空返回值记录到Unity控制台。

遇到的报错
在这里插入图片描述

在Editor下直接调用GammeObject的destory方法有如下报错
Destroy may not be called from edit mode! Use DestroyImmediate instead.
Destroying an object in edit mode destroys it permanently.
UnityEngine.Object:Destroy (UnityEngine.Object)
大意是:不能从编辑模式调用销毁!请改用 DestroyImmediate。
在编辑模式下销毁对象会永久销毁它。
问题解决
所以改用DestroyImmediate
在这里插入图片描述

即可(标签见上图)

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

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

相关文章

【单调栈】【区间合并】LeetCode85:最大矩形

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的知识点 单调栈 区间合并 题目 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面积。 示例 1&#xff1…

Kubernetes与Docker:容器编排的未来

在当今快速变化的技术领域&#xff0c;容器化技术已经成为现代应用开发的核心。Docker 提供了一种轻量、可移植、自包含的容器化解决方案&#xff0c;而 Kubernetes&#xff08;简称K8s&#xff09;则崛起为容器编排的事实标准。本文将深入研究 Kubernetes 和 Docker 的关系&am…

例如,用一个DatabaseRow类型表示一个数据库行(容器),用泛型Column<T>作为它的键

以下是一个简单的示例&#xff0c;演示如何使用泛型的Column<T>作为DatabaseRow的键&#xff0c;表示一个数据库行&#xff08;容器&#xff09;&#xff1a; // 列定义 class Column<T> {private String columnName;private T value;public Column(String column…

智能优化算法应用:基于静电放电算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于静电放电算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于静电放电算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.静电放电算法4.实验参数设定5.算法结果6.…

UniGUI 之UniDBGrid

目录 1]DataSource设置 2]显示MEMO类型里的文字 3]显示悬浮提示 4]显示当前记录及总记录数 5]读取所有记录&#xff0c;及分页 6]在前面加上序号列 7]不显示标题栏 8]列排序 9]编辑 和 更新 数据 10]获得某单元格里的内容 11]标题别名 12]将某列设置为CheckBox格式 13]列标题…

列表优先于数组

在Java中&#xff0c;列表&#xff08;List&#xff09;通常优于数组&#xff0c;因为列表提供了更灵活的操作和动态调整大小的能力。下面是一个例子&#xff0c;展示了为什么在某些情况下使用列表比数组更好&#xff1a; import java.util.ArrayList; import java.util.List;…

命令调用先构建hashTable

GPT 代码改 #include <stdio.h> #include <stdlib.h> #include <string.h>#define TABLE_SIZE 256struct Node {char *key;void *value;struct Node *next; };struct HashTable {struct Node *table[TABLE_SIZE]; };void initHashTable(struct HashTable *ha…

用CC三维建模建出的OSGB格式,用模方打不开,显示该路径包含OSGB瓦块数量0,是什么原因?

答&#xff1a;模方只识别tile命名的模型文件&#xff0c;此模型是不分块输出&#xff0c;要平面切块重新跑。 模方是一款针对实景三维模型的冗余碎片、水面残缺、道路不平、标牌破损、纹理拉伸模糊等共性问题研发的实景三维模型修复编辑软件。模方4.1新增自动单体化建模功能&…

字符设备驱动模块的编译

一. 简介 本文继上一篇文章的学习&#xff0c;上一篇文章学习了字符设备驱动框架的初步编写。文章地址如下&#xff1a; 字符设备驱动框架的编写-CSDN博客 本文对上一篇编写的驱动模块初步框架进行编译。 二. 字符设备驱动模块的编译 上一篇文章&#xff0c;编写了字符设备…

Kubernetes 的用法和解析 -- 2

一.集群常用指令 1.1 基础控制指令 # 查看对应资源: 状态 $ kubectl get <SOURCE_NAME> -n <NAMESPACE> -o wide [rootkube-master ~]# kubectl get pods -n kuboard -o wide# 查看对应资源: 事件信息 $ kubectl describe <SOURCE_NAME> <SOURCE_NAME_R…

Linux:符号和符号表

文章目录 什么是符号&#xff1f;什么是符号表&#xff1f;全局符号和本地符号1. 全局符号&#xff1a;symtab符号表 2. 本地符号&#xff1a; 符号在汇编阶段符号在链接阶段1.由模块 m 定义并能被其他模块引用的全局符号。2.由其他模块定义并被模块 m 引用的全局符号。3.只被模…

如何实现一个 RPC 框架?

如果让你自己设计 RPC 框架你会如何设计&#xff1f; 一般情况下&#xff0c; RPC 框架不仅要提供服务发现功能&#xff0c;还要提供负载均衡、容错等功能&#xff0c;这样的 RPC 框架才算真正合格的。 为了便于小伙伴们理解&#xff0c;我们先从一个最简单的 RPC 框架使用示意…