UnityTips:使用反射调用内部方法拓展编辑器
大家都知道Unity是一个C/C++的游戏引擎,C#只是Unity提供的脚本层。因此大部分功能都是通过C#来调用底层的C++代码的。而一些朋友可能不知道的是,其实Unity的C#代码中也有很多方法是我们不能直接调用的非公共方法,一个常见的情景是在拓展Editor时,除了Unity提供的公共方法之外,我们还可以通过反射的方式,调用一些Unity提供的内部方法来实现一些特殊的功能。
至于Unity的C#代码都有哪些内容,各位可以在这里找到答案:
https://github.com/MattRix/UnityDecompiled
当然C#部分的代码并没有什么实际的操作,但是它能告诉我们有哪些方法我们可以调用。
一个有趣的例子就是如何在Editor中完全关闭Gizmo呢?一个很简单但是我们正常情况下无法调用的方法就是来干这事的。
internal static extern void SetGizmoEnabled(int classID, string scriptClass, int gizmoEnabled);
所以,我们就可以通过C#的反射机制,在自己的脚本中调用Editor内封装的非公共方法来实现。
using System;
using System.Collections;
using System.Reflection;
using UnityEditor;
public class SceneViewGizmos
{
#region 方法
[MenuItem("temp/Disable All Gizmos")]
private static void DisableAllSceneGizmos()
{
ToggleGizmos(false);
}
[MenuItem("temp/Enable All Gizmos")]
private static void EnableAllSceneGizmos()
{
ToggleGizmos(true);
}
private static void ToggleGizmos(bool gizmosOn)
{
int val = gizmosOn ? 1 : 0;
Assembly asm = Assembly.GetAssembly(typeof(Editor));
Type type = asm.GetType("UnityEditor.AnnotationUtility");
if (type == null)
{
return;
}
MethodInfo getAnnotations = type.GetMethod("GetAnnotations", BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo setGizmoEnabled = type.GetMethod("SetGizmoEnabled", BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo setIconEnabled = type.GetMethod("SetIconEnabled", BindingFlags.Static | BindingFlags.NonPublic);
var annotations = getAnnotations.Invoke(null, null);
foreach (object annotation in (IEnumerable)annotations)
{
Type annotationType = annotation.GetType();
FieldInfo classIdField = annotationType.GetField("classID", BindingFlags.Public | BindingFlags.Instance);
FieldInfo scriptClassField = annotationType.GetField("scriptClass", BindingFlags.Public | BindingFlags.Instance);
if (classIdField != null && scriptClassField != null)
{
int classId = (int)classIdField.GetValue(annotation);
string scriptClass = (string)scriptClassField.GetValue(annotation);
setGizmoEnabled.Invoke(null, new object[] { classId, scriptClass, val });
setIconEnabled.Invoke(null, new object[] { classId, scriptClass, val });
}
}
}
#endregion
}