我们在 Unity 中通过 UI 菜单创建的各种控件,比如 Text, Image 等,它们的 Raycast Target 属性默认都是勾选的,Unity 会将其加入到事件的响应队列,这意味着他们会响应点击等时间,所以 UI 中这种控件数量很多时,是很影响性能的,确定不需要响应时间的都可以将改选项的勾选去掉。但是默认创建的都是全部勾选的,不过想要修改下这里面的细节又不破坏整体的使用习惯也是可行的。
实现思路:
通过翻阅源码我发现,在创建ui控件时,源码中都会走MenuOptions这个静态类中对应的方法,例如Image的创建:
这里核心的两个方法就是,GetStandardResources和PlaceUIElementRoot,前者是初始化并创建Image,后者则是创建Canvas和EventSystem的方法,具体逻辑可以去源码中的MenuOptions类查看。
至于raycastTarget=true的赋值是在Graphic类中初始化时就赋值好的,我们希望直接接入原有 UI 菜单的创建过程,并且这部分源码并不是所有接口都在外部可访问,所以直接拷贝到项目也不一定可行,但是我们可以利用C#的反射来做这件事.
话不多说直接上代码:
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
[InitializeOnLoad]
static internal class UIMenuOptionsExtend
{
private static MethodInfo m_miGetDefaultResource = null;
private static MethodInfo m_miPlaceUIElementRoot = null;
static UIMenuOptionsExtend()
{
Initialize();
}
private static void Initialize()
{
// 获取所有程序集
Assembly[] allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
Assembly uiEditorAssembly = null;
foreach (Assembly assembly in allAssemblies)
{
AssemblyName assemblyName = assembly.GetName();
if ("UnityEditor.UI" == assemblyName.Name)
{
uiEditorAssembly = assembly;
break;
}
}
if (null == uiEditorAssembly)
{
Debug.LogError("Can not find assembly: UnityEditor.UI.dll");
return;
}
// 通过反射获取UGUI底层创建UI时的两个核心方法
Type menuOptionType = uiEditorAssembly.GetType("UnityEditor.UI.MenuOptions");
m_miGetDefaultResource = menuOptionType.GetMethod("GetStandardResources", BindingFlags.NonPublic | BindingFlags.Static);
m_miPlaceUIElementRoot = menuOptionType.GetMethod("PlaceUIElementRoot", BindingFlags.NonPublic | BindingFlags.Static);
}
[MenuItem("GameObject/UI/Text", false, 2000)]
public static void AddText(MenuCommand menuCommand)
{
GameObject go = DefaultControls.CreateText((DefaultControls.Resources)m_miGetDefaultResource.Invoke(null, null));
m_miPlaceUIElementRoot.Invoke(null, new object[] { go, menuCommand });
Text text = go.GetComponent<Text>();
text.raycastTarget = false;
}
[MenuItem("GameObject/UI/Image", false, 2001)]
public static void CreatImage(MenuCommand menuCommand)
{
GameObject go = DefaultControls.CreateImage((DefaultControls.Resources)m_miGetDefaultResource.Invoke(null, null));
m_miPlaceUIElementRoot.Invoke(null, new object[] { go, menuCommand });
Image image = go.GetComponent<Image>();
image.raycastTarget = false;
}
}
此时点击菜单 GameObject/UI/Image
创建出的 Image,属性 Raycast Target 即为 false。这样就可以几乎无缝修改引擎自定义的行为,同时保留原有的使用习惯。
以上是重写了Image和Text的内部逻辑,有兴趣的朋友可以按着这个思路尝试更多的扩展!