Unity通过代码实现多个窗体排版

最近共工作要求,需要对Timeline面板编辑器编写一些辅助性工具。但是我们没有办法对Timeline编辑器进行重绘或修改功能(我是不知道),所以我打算通过新建一个EditorWindow,在新的窗口中实现辅助功能。
实际上我想要的工具效果为:
在这里插入图片描述
但实际上打开的效果是这样的:
在这里插入图片描述
让我感觉有点不爽,不够干净整洁,看着就感觉麻烦。就开始考虑Unity中是否存在这样的接口,可以让我们主动去实现自己想要的排版界面。
然后开始百度/Google 搜索,结果都是Layout的讲解(难道就没有人有这样需求吗?)
然后又开始API查找,代码查找。结果还真到找了一个API
在这里插入图片描述
这个API可以自己的窗口打开时,窗口与目标窗口标签贴合。类似于这样:
在这里插入图片描述
但是明显不是我想要的效果,咋整?继续找…直到看到这个:
在这里插入图片描述
通过这段代码,感觉到这应该是一个Unity创建窗口的一套流程
ContainerWindow 是窗体最外层框架
SplitView是一套切割框架
DockArea是窗体载体
EditorWindow就是实际的窗体

对不对呢?实践才是真理,模仿写一套试试。
这几个脚本,在Unity中都是 internal ,说白了就是,我们无法直接使用,那万能的反射就派上用场了。
首先,先将我们需要的几个类中的方法反射调用出来:
ContainerWindow 反射:

using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Reflection;

public static class EditorContainerWindow
{
    private static Type _containerWindowType;
    /// <summary>
    /// 类型
    /// </summary>
    public static Type ContainerWindowType
    {
        get
        {
            if (_containerWindowType==null)
                _containerWindowType =  typeof(EditorWindow).Assembly.GetType("UnityEditor.ContainerWindow");
            return _containerWindowType;
        }
    }
    /// <summary>
    /// 创建实例
    /// </summary>
    public static object CreateInstance()
    { 
        return ScriptableObject.CreateInstance(ContainerWindowType);
    }
    
    /// <summary>
    /// 设置RootView
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="value"></param>
    public static void SetRootView(object instance, object value)
    {
        FieldInfo finfo = ContainerWindowType.GetField("m_RootView", BindingFlags.Instance|BindingFlags.NonPublic);
        if (finfo!=null)
            finfo.SetValue(instance,value);
    }
    /// <summary>
    /// 设置坐标
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="position"></param>
    public static void SetPosition(object instance, Rect position)
    {
        PropertyInfo pInfo = ContainerWindowType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
        if (pInfo==null) return;
        pInfo.SetValue(instance,position);
    }
    /// <summary>
    /// 获取坐标
    /// </summary>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static Rect GetPosition(object instance)
    {
        PropertyInfo pInfo = ContainerWindowType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
        if (pInfo==null) return default(Rect);
        return (Rect)pInfo.GetValue(instance);
    }
    /// <summary>
    /// 显示
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="showMode"></param>
    /// <param name="loadPosition"></param>
    /// <param name="displayImmediately"></param>
    /// <param name="setFocus"></param>
    public static void Show(object instance, int showMode, bool loadPosition, bool displayImmediately, bool setFocus)
    {
        MethodInfo mInfo = ContainerWindowType.GetMethod("Show", BindingFlags.Public | BindingFlags.Instance, null,
            new Type[]
            {
                typeof(EditorWindow).Assembly.GetType("UnityEditor.ShowMode"), typeof(bool), typeof(bool), typeof(bool)
            }, null);
        if (mInfo==null) return;
        mInfo.Invoke(instance, new object[] {showMode, loadPosition, displayImmediately, setFocus});
    }
    
    /// <summary>
    /// 设置尺寸
    /// </summary>
    /// <param name="instance"></param>
    public static void OnResize(object instance)
    {
        MethodInfo mInfo = ContainerWindowType.GetMethod("OnResize", BindingFlags.Instance | BindingFlags.NonPublic);
        if (mInfo==null) return;
        mInfo.Invoke(instance, null);
    }
}

DockArea 反射:

using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
using Object = System.Object;

public static class EditorDockArea
    {
        private static Type _dockAreaType;
        /// <summary>
        /// 类型
        /// </summary>
        public static Type DockAreaType
        {
            get
            {
                if (_dockAreaType==null)
                    _dockAreaType =  typeof(EditorWindow).Assembly.GetType("UnityEditor.DockArea");
                return _dockAreaType;
            }
        }

        /// <summary>
        /// 创建实例
        /// </summary>
        /// <returns></returns>
        public static object CreateInstance()
        {
            return ScriptableObject.CreateInstance(DockAreaType);
        }

        /// <summary>
        /// 添加Tab
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="window"></param>
        /// <param name="sendPaneEvents"></param>
        public static void AddTab(object instance, EditorWindow window, bool sendPaneEvents = true)
        {
           MethodInfo mInfo = DockAreaType.GetMethod("AddTab", BindingFlags.Instance | BindingFlags.Public, null,
                new Type[] {typeof(EditorWindow), typeof(bool)}, null);
           if (mInfo==null) return;
           mInfo.Invoke(instance, new object[] {window, sendPaneEvents});
        }

        /// <summary>
        /// 设置坐标
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="position"></param>
        public static void SetPosition(object instance, Rect position)
        {
            PropertyInfo pInfo = DockAreaType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
            if (pInfo==null) return;
            pInfo.SetValue(instance,position);
        }
        
        /// <summary>
        /// 获取坐标
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        public static Rect GetPosition(object instance)
        {
            PropertyInfo pInfo = DockAreaType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
            if (pInfo==null) return default(Rect);
            return (Rect)pInfo.GetValue(instance);
        }
    }

EditorWindow反射:

using System;
using UnityEngine;
using UnityEditor;
using System.Reflection;

public static class EditorEditorWindow
{
    private static Type _editorWindowType;

    public static Type EdtiorWindowType
    {
        get
        {
            if (_editorWindowType == null) _editorWindowType = typeof(EditorWindow);
            return _editorWindowType;
        }
    }

    /// <summary>
    /// 匹配父物体尺寸
    /// </summary>
    /// <param name="instance"></param>
    public static void MakeParentsSettingsMatchMe(object instance)
    {
        MethodInfo mInfo = EdtiorWindowType.GetMethod("MakeParentsSettingsMatchMe", BindingFlags.Instance | BindingFlags.NonPublic);
        if (mInfo==null) return;
        mInfo.Invoke(instance, null);
    }
}

SplitView反射:

using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;

public static class EditorSplitView
{
    private static Type _splitViewType;

    public static Type SplitViewType
    {
        get
        {
            if (_splitViewType==null)
                _splitViewType = typeof(EditorWindow).Assembly.GetType("UnityEditor.SplitView");
            return _splitViewType;
        }
    }

    /// <summary>
    /// 创建实例
    /// </summary>
    /// <returns></returns>
    public static object CreateInstance()
    {
        return ScriptableObject.CreateInstance(SplitViewType);
    }
    /// <summary>
    /// 添加子视图
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="view"></param>
    public static void AddChild(object instance, object view)
    {
        MethodInfo mInfo = SplitViewType.GetMethod("AddChild", BindingFlags.Public | BindingFlags.Instance, null,
            new Type[] {typeof(EditorWindow).Assembly.GetType("UnityEditor.View")}, null);
        if (mInfo==null) return;
        mInfo.Invoke(instance, new object[] {view});
    }
    /// <summary>
    /// 设置坐标
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="position"></param>
    public static void SetPosition(object instance, Rect position)
    {
        PropertyInfo pInfo = SplitViewType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
        if (pInfo==null) return;
        pInfo.SetValue(instance,position);
    }
    /// <summary>
    /// 获取坐标
    /// </summary>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static Rect GetPosition(object instance)
    {
        PropertyInfo pInfo = SplitViewType.GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
        if (pInfo==null) return default(Rect);
        return (Rect)pInfo.GetValue(instance);
    }
    /// <summary>
    /// 设置是否为垂直布局
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="isVertical"></param>
    public static void SetVertical(object instance, bool isVertical)
    {
        FieldInfo fInfo = SplitViewType.GetField("vertical", BindingFlags.Public | BindingFlags.Instance);
        if (fInfo==null) return;
        fInfo.SetValue(instance,isVertical);
    }
}

3个窗口,其中Timeline编辑窗口,我们得使用反射获取Timeline的窗口类。

//工具窗口
using UnityEditor;
public class NTimeLineWindow:EditorWindow
{
    static void OpenWindow()
    {
        var window = GetWindow<NTimeLineWindow>();
        window.Show();
    }
}

//Menu菜单类窗口
using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
using System.Collections.Generic;

public class NTimeLineMenuWindow:EditorWindow
{
    private static Type _TimelineWindowType;
    /// <summary>
    /// 获取Timeline窗口类型
    /// </summary>
    private static Type TimelineWindowType
    {
        get
        {
            if (_TimelineWindowType == null)
            {
                Assembly a = null;
                foreach (System.Reflection.Assembly assembly in System.AppDomain.CurrentDomain.GetAssemblies())
                {
                    if (assembly.FullName.ToLower().Contains("unityeditor.timeline"))
                        a = assembly;
                    if (a != null)
                        _TimelineWindowType = a.GetType("UnityEditor.Timeline.TimelineWindow");
                }

            }
            return _TimelineWindowType;
        }
    }

	//显示Timeline
    public static void ShowTimeLine()
    {
        Type menuType = typeof(NTimeLineMenuWindow);
        Type toolType = typeof(NTimeLineWindow);
        //创建最外层容器
        object containerInstance = EditorContainerWindow.CreateInstance();
        //创建分屏容器
        object splitViewInstance = EditorSplitView.CreateInstance();
        //设置根容器
        EditorContainerWindow.SetRootView(containerInstance,splitViewInstance);
        //添加menu容器和工具容器
        object menuDockAreaInstance = EditorDockArea.CreateInstance();
        EditorDockArea.SetPosition(menuDockAreaInstance,new Rect(0,0,200,800));
        EditorWindow menuWindow = (EditorWindow) ScriptableObject.CreateInstance(menuType);
        //menuWindow.position = new Rect(0,0,150,800);
        EditorDockArea.AddTab(menuDockAreaInstance,menuWindow);
        EditorSplitView.AddChild(splitViewInstance,menuDockAreaInstance);
        
        //tool面板与timeline面板分割面板
        object tool_timelineSplitViewInstance = EditorSplitView.CreateInstance();
        EditorSplitView.SetPosition(tool_timelineSplitViewInstance,new Rect(0,0,600,800));
        //设置垂直状态
        EditorSplitView.SetVertical(tool_timelineSplitViewInstance,true);
        object toolDockArea = EditorDockArea.CreateInstance();
        EditorDockArea.SetPosition(toolDockArea,new Rect(0,0,600,200));
        EditorWindow toolWindow = (EditorWindow) ScriptableObject.CreateInstance(toolType);
        EditorDockArea.AddTab(toolDockArea,toolWindow);
        EditorSplitView.AddChild(tool_timelineSplitViewInstance,toolDockArea);
        
        //添加timeline窗体
        object timelineDockAreaInstance = EditorDockArea.CreateInstance();
        EditorDockArea.SetPosition(timelineDockAreaInstance,new Rect(0,0,600,600));
        EditorWindow timelineWindow = (EditorWindow) ScriptableObject.CreateInstance(TimelineWindowType);
        EditorDockArea.AddTab(timelineDockAreaInstance,timelineWindow);
        EditorSplitView.AddChild(tool_timelineSplitViewInstance,timelineDockAreaInstance);
        
        //添加tool_timeline切割窗体
        EditorSplitView.AddChild(splitViewInstance,tool_timelineSplitViewInstance);
        
        EditorEditorWindow.MakeParentsSettingsMatchMe(menuWindow);
        EditorEditorWindow.MakeParentsSettingsMatchMe(toolWindow);
        EditorEditorWindow.MakeParentsSettingsMatchMe(timelineWindow);

        EditorContainerWindow.SetPosition(containerInstance,new Rect(100,100,800,800));
        EditorSplitView.SetPosition(splitViewInstance,new Rect(0,0,800,800));
        EditorContainerWindow.Show(containerInstance,0,true,false,true);
        EditorContainerWindow.OnResize(containerInstance);
    }
    
    [MenuItem("Tools/Time Line Menu")]
    static void OpenWindow()
    {
        ShowTimeLine();
    }
}

根据我们想要的排版 所以实际上的嵌套关系应该是这样的
在这里插入图片描述
NTimeLineMenuWindow.ShowTimeLine()方法内依照上图方法进行嵌套,得到最终结果:

在这里插入图片描述
完美!!!
我不知道Unity有没有提供这样的接口,如果有的话还得麻烦告诉我一下(我也就不用这么麻烦了)…

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘建宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值