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

发布时间:2023年12月20日

应美术要求,实现一个在编辑环境下,不运行,可以实例化预制体的脚本
在这里插入图片描述
效果如上图所示
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 update
    public 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
在这里插入图片描述

即可(标签见上图)

文章来源:https://blog.csdn.net/qq_45498613/article/details/135027144
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。