效果
效果如图,给标题栏右侧加上了3个按钮,用于方便分辨率切换,做分辨率适配的时候比较方便,可自行替换为其他实现。

实现
通过查看UnityEditor相关的源码可以看到,Game视图的绘制在UnityEditor.GameView中,但该类是个internal类,所以需要用反射去Hook相关的绘制回调,添加我们需要的GUI代码;分辨率的设置方法也在该类中。以下给出两种实现:
1.通过分析调用堆栈发现GameView中的GUI绘制是通过UIElement的,而GameView是继承自PlayModeView,PlayModeView继承自EditorWindow,EditorWindow持有一个rootVisualElement。
如下:
/// <summary>
/// <para>Retrieves the root visual element of this window hierarchy.</para>
/// </summary>
public VisualElement rootVisualElement
{
get
{
if (!(this is ISupportsOverlays))
return this.baseRootVisualElement;
if (!this.m_OverlaysInitialized)
{
this.baseRootVisualElement.Add(this.overlayCanvas.rootVisualElement);
this.overlayCanvas.Initialize(this);
this.m_OverlaysInitialized = true;
}
return this.overlayCanvas.windowRoot;
}
}
那么很明显了,rootVisualElement就是窗口UXML的根节点,UIElement提供的Debugger也可以看到这点。我们往里面插入想要绘制的元素即可。
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[InitializeOnLoad]
public static class GameViewExtension
{
private static int _screenIndex;
private static readonly Type GameViewType = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
private static EditorWindow _gameViewWindow;
static GameViewExtension()
{
EditorApplication.update += OnEditorUpdate;
}
private static void OnEditorUpdate()
{
EditorApplication.update -= OnEditorUpdate;
DrawCustomGameViewToolbar();
}
private static void DrawCustomGameViewToolbar()
{
_gameViewWindow = EditorWindow.GetWindow(GameViewType);
var root = _gameViewWindow.rootVisualElement;
VisualElement element = new VisualElement
{
style =
{
flexDirection = FlexDirection.Row,
justifyContent = Justify.FlexEnd,
alignSelf = Align.FlexEnd,
marginRight = 275
}
};
IMGUIContainer container = new IMGUIContainer();
container.onGUIHandler += CustomOnGUI;
element.Add(container);
if (root.childCount > 0) root.RemoveAt(0);
root.Add(element);
}
private static void CustomOnGUI()
{
GUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent("720", EditorGUIUtility.IconContent("PlayButton").image)))
{
SetSize(6 + 4);
}
if (GUILayout.Button(new GUIContent("1920", EditorGUIUtility.IconContent("PlayButton").image)))
{
SetSize(6 + 1);
}
if (GUILayout.Button(new GUIContent("3440", EditorGUIUtility.IconContent("PlayButton").image)))
{
SetSize(6 + 5);
}
GUILayout.EndHorizontal();
}
private static void SetSize(int index)
{
var sizeSelectionCallback = GameViewType.GetMethod("SizeSelectionCallback",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
sizeSelectionCallback?.Invoke(_gameViewWindow, new object[] { index, null });
}
}
2.第二种则是通过HostView,HostView负责各Editor窗口的管理,其中有各窗口的GUI、Focus、LostFocus等回调,是一个HostView.EditorWindowDelegate的委托类型,m_OnGUI中持有GameView的OnGUI方法,可往该委托中添加我们的自定义绘制方法实现Hook。不过处理有些麻烦,没有写完,给出部分示意代码:
// 使用反射Hook GameView的OnGUI 方法 主要是去Hostview中获取onGui的委托并添加自定义的委托,gameview的OnGUI方法由该委托持有
private static void OnEditorUpdate()
{
// 查找 GameView 窗口实例
var gameViewWindow = EditorWindow.GetWindow(gameViewType);
EditorApplication.update -= OnEditorUpdate;
// 获取所有 HostView 实例
var hostViewType = typeof(Editor).Assembly.GetType("UnityEditor.HostView");
var hostViews = Resources.FindObjectsOfTypeAll(hostViewType);
foreach (var hostView in hostViews)
{
var viewType = hostViewType.GetField("m_ActualView", BindingFlags.NonPublic | BindingFlags.Instance);
if(viewType.GetValue(hostView).GetType() != gameViewType) continue;
// 获取 m_OnGUI 字段
var onGUIField = hostViewType.GetField("m_OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
var onGUIDelegate = onGUIField.GetValue(hostView);
// 获取委托类型
var onGUIdelegateType = onGUIDelegate.GetType();
// 创建自定义委托
MethodInfo onGUIhandler = typeof(GameViewExtension).GetMethod(nameof(CustomOnGUI), BindingFlags.Static | BindingFlags.NonPublic);
if (onGUIhandler != null)
{
var customOnGUIDelegate = Delegate.CreateDelegate(onGUIdelegateType, onGUIhandler);
// // 将原始委托和自定义委托合并
var combinedDelegate = Delegate.Combine(onGUIDelegate as Delegate, customOnGUIDelegate);
// 将更新后的委托设置回 m_OnGUI 字段
onGUIField.SetValue(hostView, combinedDelegate);
}
}
}
1791

被折叠的 条评论
为什么被折叠?



