微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

模仿SceneView的自定义窗口

如何解决模仿SceneView的自定义窗口

我正在构建一个自定义窗口,并且试图重用Unity的Scene视图,以便能够直接从此特定窗口进行绘制。

我设法通过扩展UnityEditor.SceneView来重现正确的窗口,这就是我所拥有的:

enter image description here

这是代码

[EditorWindowTitle(title = "Shape Editor",useTypeNameAsIconName = false)]
public class strokeEditor : SceneView
{
    [MenuItem("Recognizer/Shape Editor")]
    public static void Init()
    {
        var w = Getwindow<strokeEditor>();
        w.in2DMode = true;

        EditorSceneManager.NewScene(NewScenesetup.EmptyScene,NewSceneMode.Single);
    }

    protected override void OnGUI()
    {
        using (new GUILayout.HorizontalScope())
        {
            GUILayout.Button("Add stroke");
            GUILayout.Button("Edit stroke");
            GUILayout.Button("Delete stroke");
        }

        base.OnGUI();
    }
}

有了这个,我可能快完成了。

这是正确的前进方法吗? 我感到有些不对劲,因为每当我使用EditorSceneManager.NewScene(NewScenesetup.EmptyScene,NewSceneMode.Single);时,都会在主场景视图中创建一个新场景。 (我希望主场景视图保持不变) 我还应该能够从场景视图中看到这些工具,例如:

enter image description here

有没有更好的方法来实现我想要的?

编辑1:

所有这些的最终用途是能够通过单击和拖动鼠标(如移动电话的手势)在窗口内绘制2D形状。由此,我将能够获得一些职位来满足我的一种算法...

解决方法

您可以使用新的GraphView。这为您提供了一些您想要“免费”的东西,主要是缩放和平移视图。由于ShaderGraph使用此功能,因此如果需要的话,应该更容易构造节点,选择节点并四处移动。

以下是自定义编辑器窗口的一个玩具示例,该窗口使您可以编辑可编写脚本的对象中的点列表:

enter image description here


Shape.cs
-具有点列表的简单脚本对象。

[CreateAssetMenu(menuName = "Test/ShapeObject")]
public class Shape : ScriptableObject
{
    public List<Vector2> PointList = new List<Vector2>();
}

ShapeEditorWindow.cs
-具有工具栏和graphview的编辑器窗口,用于打开Shape类型的可编写脚本的对象。

using UnityEngine;
using UnityEditor;
using UnityEditor.UIElements;

public class ShapeEditorWindow : EditorWindow
{
    private ShapeEditorGraphView _shapeEditorGraphView;
    private Shape _shape;

    [UnityEditor.Callbacks.OnOpenAsset(1)]
    private static bool Callback(int instanceID,int line)
    {
        var shape = EditorUtility.InstanceIDToObject(instanceID) as Shape;
        if (shape != null)
        {
            OpenWindow(shape);
            return true;
        }
        return false; // we did not handle the open
    }

    private static void OpenWindow(Shape shape)
    {
        var window = GetWindow<ShapeEditorWindow>();
        window.titleContent = new GUIContent("Shape Editor");
        window._shape = shape;
        window.rootVisualElement.Clear();
        window.CreateGraphView();
        window.CreateToolbar();
    }
    
    private void CreateToolbar()
    {
        var toolbar = new Toolbar();        
        var clearBtn = new ToolbarButton(()=>_shape.PointList.Clear()); ;
        clearBtn.text = "Clear";  
        var undoBtn = new ToolbarButton(() =>_shape.PointList.RemoveAt(_shape.PointList.Count-1)); 
        undoBtn.text = "Undo";
        toolbar.Add(clearBtn);
        toolbar.Add(new ToolbarSpacer());
        toolbar.Add(undoBtn);
        rootVisualElement.Add(toolbar);
    }

    private void CreateGraphView()
    {       
        _shapeEditorGraphView = new ShapeEditorGraphView(_shape);
        _shapeEditorGraphView.name = "Shape Editor Graph";
        rootVisualElement.Add(_shapeEditorGraphView);
    }
}

ShapeEditorGraphView.cs
-具有缩放,网格,平移(使用ContentDragger)和形状编辑器的graphview。

using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;

public class ShapeEditorGraphView : GraphView
{
    const float _pixelsPerUnit = 100f;
    const bool _invertYPosition = true;
    public ShapeEditorGraphView(Shape shape){        
        styleSheets.Add(Resources.Load<StyleSheet>("ShapeEditorGraph"));
        this.StretchToParentSize();
        
        SetupZoom(ContentZoomer.DefaultMinScale,ContentZoomer.DefaultMaxScale);        
        Add(new GridBackground());

        //pan with Alt-LeftMouseButton drag/ MidleMouseButton drag
        this.AddManipulator(new ContentDragger());

        //other things that might interest you
        //this.AddManipulator(new SelectionDragger());
        //this.AddManipulator(new RectangleSelector());
        //this.AddManipulator(new ClickSelector());
        
        this.AddManipulator(new ShapeManipulator(shape));
        
        contentViewContainer.BringToFront();
        contentViewContainer.Add(new Label { name = "origin",text = "(0,0)" });

        //set the origin to the center of the window
        this.schedule.Execute(() =>
        {
            contentViewContainer.transform.position = parent.worldBound.size / 2f;
        });
    }    
    
    public Vector2 WorldtoScreenSpace(Vector2 pos)
    {
        var position = pos * _pixelsPerUnit - contentViewContainer.layout.position;
        if (_invertYPosition) position.y = -position.y; 
        return contentViewContainer.transform.matrix.MultiplyPoint3x4(position);        
    }

    public Vector2 ScreenToWorldSpace(Vector2 pos)
    {             
        Vector2 position = contentViewContainer.transform.matrix.inverse.MultiplyPoint3x4(pos);
        if (_invertYPosition) position.y = -position.y;        
        return (position + contentViewContainer.layout.position) / _pixelsPerUnit;
    }
}

不幸的是,网格背景和网格线的颜色相同,因此,要查看网格线,我们必须编写样式表并设置GridBackground属性。该文件必须位于编辑器/资源中,并加载有styleSheets.Add(Resources.Load<StyleSheet>("ShapeEditorGraph"));

Editor / Resources / ShapeEditorGraph.uss

GridBackground {
    --grid-background-color: rgba(32,32,1);
    --line-color: rgba(255,255,.1);
    --thick-line-color: rgba(255,.3);    
    --spacing: 100;
}

ShapeManipulator.cs
-绘制和编辑形状。这类似于RectangleSelector

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;

public class ShapeManipulator : MouseManipulator
{ 
    private Shape _shape;
    private ShapeDraw _shapeDraw;
    
    public ShapeManipulator(Shape shape)
    {
        activators.Add(new ManipulatorActivationFilter { button = MouseButton.LeftMouse });
        _shape = shape;        
        _shapeDraw = new ShapeDraw { points = shape.PointList };
    }
    protected override void RegisterCallbacksOnTarget()
    {
        target.Add(_shapeDraw);
        target.Add(new Label { name = "mousePosition",0)" });
        target.RegisterCallback<MouseDownEvent>(MouseDown);
        target.RegisterCallback<MouseMoveEvent>(MouseMove);
        target.RegisterCallback<MouseCaptureOutEvent>(MouseOut);
        target.RegisterCallback<MouseUpEvent>(MouseUp);
    }

    protected override void UnregisterCallbacksFromTarget()
    {
        target.UnregisterCallback<MouseDownEvent>(MouseDown);
        target.UnregisterCallback<MouseUpEvent>(MouseUp);
        target.UnregisterCallback<MouseMoveEvent>(MouseMove);
        target.UnregisterCallback<MouseCaptureOutEvent>(MouseOut);
    }

    private void MouseOut(MouseCaptureOutEvent evt) => _shapeDraw.drawSegment = false;

    private void MouseMove(MouseMoveEvent evt)
    {
        var t = target as ShapeEditorGraphView;
        var mouseLabel = target.Q("mousePosition") as Label;
        mouseLabel.transform.position = evt.localMousePosition + Vector2.up * 20;
        mouseLabel.text = t.ScreenToWorldSpace(evt.localMousePosition).ToString();

        //if left mouse is pressed 
        if ((evt.pressedButtons & 1) != 1) return;
        _shapeDraw.end = t.ScreenToWorldSpace(evt.localMousePosition);
        _shapeDraw.MarkDirtyRepaint();
    }

    private void MouseUp(MouseUpEvent evt)
    {
        if (!CanStopManipulation(evt)) return;        
        target.ReleaseMouse();         
        if (!_shapeDraw.drawSegment) return;   
        
        if (_shape.PointList.Count == 0) _shape.PointList.Add(_shapeDraw.start);

        var t = target as ShapeEditorGraphView;
        _shape.PointList.Add(t.ScreenToWorldSpace(evt.localMousePosition));
        _shapeDraw.drawSegment = false;
       
        _shapeDraw.MarkDirtyRepaint();
    }

    private void MouseDown(MouseDownEvent evt)
    {
        if (!CanStartManipulation(evt)) return;       
        target.CaptureMouse();   
        
        _shapeDraw.drawSegment = true;
        var t = target as ShapeEditorGraphView;

        if (_shape.PointList.Count != 0) _shapeDraw.start = _shape.PointList.Last();
        else _shapeDraw.start = t.ScreenToWorldSpace(evt.localMousePosition);

        _shapeDraw.end = t.ScreenToWorldSpace(evt.localMousePosition);
        _shapeDraw.MarkDirtyRepaint();
    }
    private class ShapeDraw : ImmediateModeElement
    {
        public List<Vector2> points { get; set; } = new List<Vector2>();
        public Vector2 start { get; set; }
        public Vector2 end { get; set; }
        public bool drawSegment { get; set; }
        protected override void ImmediateRepaint()
        {
            var lineColor = new Color(1.0f,0.6f,0.0f,1.0f);
            var t = parent as ShapeEditorGraphView;            
            //Draw shape        
            for (int i = 0; i < points.Count - 1; i++)
            {
                var p1 = t.WorldtoScreenSpace(points[i]);
                var p2 = t.WorldtoScreenSpace(points[i + 1]);
                GL.Begin(GL.LINES);
                GL.Color(lineColor);
                GL.Vertex(p1);
                GL.Vertex(p2);
                GL.End();
            }

            if (!drawSegment) return;

            //Draw current segment
            GL.Begin(GL.LINES);
            GL.Color(lineColor);
            GL.Vertex(t.WorldtoScreenSpace(start));
            GL.Vertex(t.WorldtoScreenSpace(end));
            GL.End();
        }
    }
}

这只是示例代码。目的是使某些东西可以工作并绘制到屏幕上。

,

过去,我已经解决了类似的问题。当我想扩展SceneView时,我已经使用Gizmos并绘制了回调以将自己的控件添加到场景视图中,但是我怀疑您可能会想要更多的自由。

我要做的另一件事是创建一个“编辑器预览场景”,向其中添加摄影机,并将该摄影机渲染到我的自定义EditorWindow中。这是很多工作,但是一旦完成,我就完全可以自定义编辑器体验了。

enter image description here

从Unity的SceneView继承可能很危险,因为我希望它会经常更改,以至于您可能很难使自己的东西在多个版本上工作。当Unity的代码不希望任何人从SceneView继承代码时,您可能还会发现自己在打破东西。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。