【问题标题】:Unity - Dock an editor window above the game viewUnity - 在游戏视图上方停靠一个编辑器窗口
【发布时间】:2019-08-13 22:43:58
【问题描述】:

我想要实现的是通过脚本将编辑器窗口停靠在 Unity 编辑器中游戏视图的下方、顶部或左侧或右侧(为简单起见,我们只专注于停靠在游戏视图上方)。查看this 视频,了解我想要实现的目标。

编辑

由于问题被标记为离题,这里是一个代码示例。我遇到的问题是Dockmethod 中的dropInfo 为null,因此SplitViewPerformDrop 方法会导致null ref 错误。

public static class Docker
{

    private class _EditorWindow
    {
        private EditorWindow instance;
        private Type type;

        public _EditorWindow( EditorWindow instance )
        {
            this.instance = instance;
            type = instance.GetType();
        }

        public object m_Parent 
        {
            get 
            {
                var field = type.GetField( "m_Parent", BindingFlags.Instance | BindingFlags.NonPublic );
                return field.GetValue( instance );
            }
        }
    }

    private class _DockArea
    {
        private object instance;
        private Type type;

        public _DockArea( object instance ) 
        {
            this.instance = instance;
            type = instance.GetType();
        }

        public object window 
        {
            get 
            {
                var property = type.GetProperty( "window", BindingFlags.Instance | BindingFlags.Public );
                return property.GetValue( instance, null );
            }
        }

        public object s_OriginalDragSource 
        {
            set 
            {
                var field = type.GetField( "s_OriginalDragSource", BindingFlags.Static | BindingFlags.NonPublic );
                field.SetValue( null, value );
            }
        }
    }

    private class _ContainerWindow
    {
        private object instance;
        private Type type;

        public _ContainerWindow( object instance ) 
        {
            this.instance = instance;
            type = instance.GetType();
        }


        public object rootSplitView 
        {
            get 
            {
                var property = type.GetProperty( "rootSplitView", BindingFlags.Instance | BindingFlags.Public );
                return property.GetValue( instance, null );
            }
        }
    }

    private class _SplitView
    {
        private object instance;
        private Type type;

        public _SplitView( object instance ) 
        {
            this.instance = instance;
            type = instance.GetType();
        }

        public object DragOver( EditorWindow child, Vector2 screenPoint ) 
        {
            var method = type.GetMethod( "DragOver", BindingFlags.Instance | BindingFlags.Public );
            return method.Invoke( instance, new object[] { child, screenPoint } );
        }

        public void PerformDrop( EditorWindow child, object dropInfo, Vector2 screenPoint ) 
        {
            var method = type.GetMethod( "PerformDrop", BindingFlags.Instance | BindingFlags.Public );
            method.Invoke( instance, new object[] { child, dropInfo, screenPoint } );
        }
    }

    public enum DockPosition
    {
        Left,
        Top,
        Right,
        Bottom
    }

    /// <summary>
    /// Docks the second window to the first window at the given position
    /// </summary>
    public static void Dock( this EditorWindow wnd, EditorWindow other, DockPosition position ) 
    {
        var mousePosition = GetFakeMousePosition( wnd, position );

        var parent = new _EditorWindow( wnd );
        var child = new _EditorWindow( other );
        var dockArea = new _DockArea( parent.m_Parent );
        var containerWindow = new _ContainerWindow( dockArea.window );
        var splitView = new _SplitView( containerWindow.rootSplitView );
        var dropInfo = splitView.DragOver( other, mousePosition );
        dockArea.s_OriginalDragSource = child.m_Parent;
        splitView.PerformDrop( other, dropInfo, mousePosition );
    }

    private static Vector2 GetFakeMousePosition( EditorWindow wnd, DockPosition position ) 
    {
        Vector2 mousePosition = Vector2.zero;

        // The 20 is required to make the docking work.
        // Smaller values might not work when faking the mouse position.
        switch ( position ) 
        {
            case DockPosition.Left:
                mousePosition = new Vector2( 20, wnd.position.size.y / 2 );
                break;
            case DockPosition.Top:
                mousePosition = new Vector2( wnd.position.size.x / 2, 20 );
                break;
            case DockPosition.Right:
                mousePosition = new Vector2( wnd.position.size.x - 20, wnd.position.size.y / 2 );
                break;
            case DockPosition.Bottom:
                mousePosition = new Vector2( wnd.position.size.x / 2, wnd.position.size.y - 20 );
                break;
        }

        return new Vector2(wnd.position.x + mousePosition.x, wnd.position.y + mousePosition.y);
    }
}

public static class SomeStaticClass
{
    [MenuItem("DOCK TESTING/Dock Above")]
    public static void DockAbove()
    {
        SysType gameViewType = Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.GameView");
        EditorWindow baseWindow = EditorWindow.GetWindow(gameViewType);
        EditorWindow newWindow = (EditorWindow)ScriptableObject.CreateInstance(gameViewType);
        baseWindow.Dock(newWindow, Docker.DockPosition.Top);
    }
}

【问题讨论】:

    标签: c# unity3d unity-editor


    【解决方案1】:

    因此,为了将来参考,对于尝试此代码的其他人(找到 here),这就是我为它工作所做的工作。

    dropInfo 对象始终为 null 的原因有两个:

    首先使用固定值 20 来计算假鼠标位置,生成的位置是游戏视图的标签区域,因此无法将另一个窗口锚定到游戏视图(这只是发生尝试在游戏视图顶部进行选项卡时)。为了解决这个问题,我只是使用了一个不同的值,作为 offset 参数传递给 GetFakeMousePosition

    其次,使用编辑器窗口位置来计算假鼠标位置,总是给出奇怪的结果,即当我注意到窗口position 属性返回窗口相对于其父窗口的本地位置时,而不是窗口的屏幕位置,所以我从使用窗口位置更改为使用它的顶部父位置(Window->DockArea->SplitView)位置来计算假鼠标位置。

    在所有这些更改之后,GetFakeMousePosition 看起来像这样:

    private static Vector2 GetFakeMousePosition(_SplitView view, DockPosition position, float offset) 
    {
        Vector2 mousePosition = Vector2.zero;
    
        switch ( position ) 
        {
            case DockPosition.Left:
                mousePosition = new Vector2(offset, view.position.height / 2);
                break;
            case DockPosition.Top:
                mousePosition = new Vector2(view.position.width / 2, offset);
                break;
            case DockPosition.Right:
                mousePosition = new Vector2(view.position.width - offset, wnd.position.size.y / 2 );
                break;
            case DockPosition.Bottom:
                mousePosition = new Vector2(view.position.width / 2, view.position.height - offset);
                break;
        }
    
        return new Vector2(view.position.x + mousePosition.x, view.position.y + mousePosition.y);
    }
    

    _SplitView 类看起来像这样:

    private class _SplitView
    {
        private object instance;
        private Type type;
    
        public _SplitView( object instance ) 
        {
            this.instance = instance;
            type = instance.GetType();
        }
    
        public object DragOver( EditorWindow child, Vector2 screenPoint ) 
        {
            var method = type.GetMethod( "DragOver", BindingFlags.Instance | BindingFlags.Public );
            return method.Invoke( instance, new object[] { child, screenPoint } );
        }
    
        public void PerformDrop( EditorWindow child, object dropInfo, Vector2 screenPoint ) 
        {
            var method = type.GetMethod( "PerformDrop", BindingFlags.Instance | BindingFlags.Public );
            method.Invoke( instance, new object[] { child, dropInfo, screenPoint } );
        }
    
        public Rect position
        {
            get 
            {
                var property = type.GetProperty("screenPosition", BindingFlags.Instance | BindingFlags.Public);
                return property.GetValue(instance, null);
            }
        }
    }
    

    【讨论】:

    • 谢谢!这节省了我阅读 Unity 的 C# 源代码的时间!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    • 2011-04-27
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    相关资源
    最近更新 更多