应美术要求,实现一个在编辑环境下,不运行,可以实例化预制体的脚本
效果如上图所示
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(): 将过滤后的方法结果转换为数组,以便后续使用。
- 方法调用
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
即可(标签见上图)