Unity 自定义NodeEditor 开发插件基础

Unity 自定义NodeEditor 开发插件基础_第1张图片

using System.Collections;

using System.Collections.Generic;
using UnityEngine;


using UnityEditor;
using System.IO;
using System.Reflection;
using System;


public class NodeEditor : EditorWindow
{
    static NodeEditor _window;
    public static float zoomDelta = 0.01f;
    public static float minZoom = 1f;
    public static float maxZoom = 8f;
    public static float panSpeed = 1.2f;
    // To keep track of zooming.
    private Vector2 _zoomAdjustment;
    private Vector2 _zoom = Vector2.one;
    public Vector2 panOffset = Vector2.zero;


    public void Pan(Vector2 delta)
    {
        panOffset += delta * ZoomScale * panSpeed;
    }




    public void Zoom(float zoomDirection)
    {
        float scale = (zoomDirection < 0f) ? (1f - zoomDelta) : (1f + zoomDelta);


        _zoom *= scale;


        float cap = Mathf.Clamp(_zoom.x, minZoom, maxZoom);
        _zoom.Set(cap, cap);
    }


    public float ZoomScale
    {
        get { return _zoom.x; }
        set
        {
            float z = Mathf.Clamp(value, minZoom, maxZoom);
            _zoom.Set(z, z);
        }
    }


    ///
    /// The size of the window.
    ///

    public Rect Size
    {
        get { return new Rect(Vector2.zero, position.size); }
    }
    [MenuItem("Scene/NodeEditor  #")]
    static void Init()
    {
        if (_window == null)
        {
            _window = EditorWindow.GetWindow(typeof(NodeEditor)) as NodeEditor;


            GUIScaleUtility.CheckInit();
           
        }




    }
 
    private static string getFullPath(string root, string targetFolderName)
    {
        string[] dirs = Directory.GetDirectories(root, targetFolderName, SearchOption.AllDirectories);


        // Return first occurance containing targetFolderName.
        if (dirs.Length != 0)
        {
            return dirs[0];
        }


        // Could not find anything.
        return "";
    }
    public static string GetTextureFolderPath()
    {
        string fullpath = Application.dataPath;// getFullPath(Application.dataPath, "");


        if (!string.IsNullOrEmpty(fullpath))
        {


            // Return the texture folder path relative to Unity's Asset folder.
            int index = fullpath.IndexOf("Assets");
            string localPath = fullpath.Substring(index);


            return localPath;
        }


        Debug.LogError("Could not find folder: " + "");
        return "";
    }


    Texture2D gridTex;
  Texture2D  _gridTex
    {
        get
        {
            if(gridTex==null)
            {
                string path = GetTextureFolderPath().Dir("Grid.png");


              
                gridTex = AssetDatabase.LoadAssetAtPath(path);
              //  Debug.Log(path);
                if(gridTex==null)
                {
                    Debug.Log("gridTex is null");
                    
                }
            }
            return gridTex;
        }
    }


    Texture2D circleTex;


    Texture2D _circleTex
    {
        get
        {
            if (circleTex == null)
            {
                string path = GetTextureFolderPath().Dir("Circle.png");




                circleTex = AssetDatabase.LoadAssetAtPath(path);
                //  Debug.Log(path);
                if (circleTex == null)
                {
                    Debug.Log("circleTex is null");
                }
            }
            return circleTex;
        }
    }
    private void OnGUI()
    {
        if (_window)
        {
            if (Event.current.type == EventType.Repaint)
            {
                if (bDrawGuide)
                { DrawGrid(); }
             
            }
            Rect graphRect = _window.Size;
            var center = graphRect.size / 2f;


            _zoomAdjustment = GUIScaleUtility.BeginScale(ref graphRect, center, ZoomScale, false);


            drawGridOverlay();




            GUIScaleUtility.EndScale();


            if (Event.current.type == EventType.ScrollWheel)
            {
                Zoom(Event.current.delta.y);
                Repaint();


            }
            if (Event.current.type == EventType.MouseDrag)
            {
                Pan(Event.current.delta);
                Repaint();


            }
            EditorGUILayout.BeginHorizontal("Toolbar");




            if (DropdownButton("Settings", kToolbarButtonWidth + 10f))
            {
                createSettingsMenu();
            }
            if (DropButton("ReCenter", kToolbarButtonWidth + 10f))
            {
                ReCenterGrid();
            }
            EditorGUILayout.EndHorizontal();
        }
    }
    public void ToggleDrawGuide()
    {
        bDrawGuide = !bDrawGuide;
    }
    public void ReCenterGrid()
    {
        panOffset = Vector2.zero;


    }
    public const float kToolbarHeight = 20f;
    public const float kToolbarButtonWidth = 50f;
    private   void createSettingsMenu()
    {
        var menu = new GenericMenu();


        menu.AddItem(new GUIContent("Show Guide"), bDrawGuide, ToggleDrawGuide);


        menu.DropDown(new Rect(0, kToolbarHeight, 0f, 0f));
    }
 
    public bool DropButton(string name, float width)
    {


        return GUILayout.Button(name, EditorStyles.miniButton, GUILayout.Width(width));
    }
    public bool DropdownButton(string name, float width)
    {
         
        return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width));
    }
    ///
    /// The rect bounds defining the recticle at the grid center.
    ///

    public static readonly Rect kReticleRect = new Rect(0, 0, 8, 8);


    ///
    /// Enables and disables drawing the guide to the grid center.
    ///

    public bool bDrawGuide = false;


    public Color guideColor =Color.gray;


    private void DrawGrid()
    {
        var size = _window.Size.size;
        var center = size / 2f;


        float zoom = ZoomScale;


        // Offset from origin in tile units
        float xOffset = -(center.x * zoom + panOffset.x) / _gridTex.width;
        float yOffset = ((center.y - size.y) * zoom + panOffset.y) / _gridTex.height;


        Vector2 tileOffset = new Vector2(xOffset, yOffset);


        // Amount of tiles
        float tileAmountX = Mathf.Round(size.x * zoom) / _gridTex.width;
        float tileAmountY = Mathf.Round(size.y * zoom) / _gridTex.height;


        Vector2 tileAmount = new Vector2(tileAmountX, tileAmountY);


        // Draw tiled background


        GUI.DrawTextureWithTexCoords(_window.Size, _gridTex, new Rect(tileOffset, tileAmount));
    }


    // Handles drawing things over the grid such as axes.
    private void drawGridOverlay()
    {
        drawAxes();
       // drawGridCenter();


        if (bDrawGuide)
        {
            //drawGuide();
            //_window.Repaint();
        }
    }
    ///
    /// Converts the graph position to screen space.
    /// This only works for geometry inside the GUIScaleUtility.BeginScale()
    ///

    ///
    ///
    public Vector2 GraphToScreenSpace(Vector2 graphPos)
    {
        return graphPos + _zoomAdjustment + panOffset;
    }
    ///
    /// Draws a GUI texture with a tint.
    ///

    ///
    ///
    ///
    public static void DrawTintTexture(Rect r, Texture t, Color c)
    {
        var guiColor = GUI.color;
        GUI.color = c;


        GUI.DrawTexture(r, t);
        GUI.color = guiColor;
    }
    private void drawGridCenter()
    {
        var rect = kReticleRect;


        rect.size *= ZoomScale;
        rect.center = Vector2.zero;
        rect.position = GraphToScreenSpace(rect.position);


        DrawTintTexture(rect, _circleTex, Color.gray);
    }


    private void drawAxes()
    {
        // Draw axes. Make sure to scale based on zoom.
        Vector2 up = Vector2.up * _window.Size.height * ZoomScale;
        Vector2 right = Vector2.right * _window.Size.width * ZoomScale;
        Vector2 down = -up;
        Vector2 left = -right;


        // Make sure the axes follow the pan.
        up.y -= panOffset.y;
        down.y -= panOffset.y;
        right.x -= panOffset.x;
        left.x -= panOffset.x;


        up = GraphToScreenSpace(up);
        down = GraphToScreenSpace(down);
        right = GraphToScreenSpace(right);
        left = GraphToScreenSpace(left);


     
        DrawLine(right, left, Color.white);
        DrawLine(up, down, Color.white);
    }


    ///
    /// Shows where the center of the grid is.
    ///

    private void drawGuide()
    {
        Vector2 gridCenter = GraphToScreenSpace(Vector2.zero);
        DrawLine(gridCenter, Event.current.mousePosition, guideColor);
    }




    ///
    /// Draws a bezier between the two end points in screen space.
    ///

    public static void DrawBezier(Vector2 start, Vector2 end, Color color)
    {
        Vector2 endToStart = (end - start);
        float dirFactor = Mathf.Clamp(endToStart.magnitude, 20f, 80f);


        endToStart.Normalize();
        Vector2 project = Vector3.Project(endToStart, Vector3.right);


        Vector2 startTan = start + project * dirFactor;
        Vector2 endTan = end - project * dirFactor;


        UnityEditor.Handles.DrawBezier(start, end, startTan, endTan, color, null, 3f);
    }


    ///
    /// Draws a line between the two end points.
    ///

    ///
    ///
    public static void DrawLine(Vector2 start, Vector2 end, Color color)
    {
        var handleColor = Handles.color;
        Handles.color = color;


        Handles.DrawLine(start, end);
        Handles.color = handleColor;
    }










}
public static class StringExtensions
{


    ///
    /// Merges the parent and child paths with the '/' character.
    ///

    ///
    ///
    ///
    public static string Dir(this string parentDir, string childDir)
    {
        return parentDir + '/' + childDir;
    }


    ///
    /// Appends the extension to the file name with '.'
    ///

    ///
    ///
    ///
    public static string Ext(this string file, string extension)
    {
        return file + '.' + extension;
    }
}
public static class GUIScaleUtility
{
    // General
    private static bool compabilityMode;
    private static bool initiated;


    // Delegates to the reflected methods
    private static Func GetTopRectDelegate;
    private static Func topmostRectDelegate;


    // Delegate accessors
    public static Rect getTopRect { get { return (Rect)GetTopRectDelegate.Invoke(); } }


    // Rect stack for manipulating groups
    public static List currentRectStack { get; private set; }
    private static List> rectStackGroups;


    // Matrices stack
    private static List GUIMatrices;
    private static List adjustedGUILayout;


    #region Init


    public static void CheckInit()
    {
        if (!initiated)
            Init();
    }


    public static void Init()
    {
        // Fetch rect acessors using Reflection
        Assembly UnityEngine = Assembly.GetAssembly(typeof(UnityEngine.GUI));
        Type GUIClipType = UnityEngine.GetType("UnityEngine.GUIClip", true);


        PropertyInfo topmostRect = GUIClipType.GetProperty("topmostRect", BindingFlags.Static | BindingFlags.Public);
        MethodInfo GetTopRect = GUIClipType.GetMethod("GetTopRect", BindingFlags.Static | BindingFlags.NonPublic);
        MethodInfo ClipRect = GUIClipType.GetMethod("Clip", BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder, new Type[] { typeof(Rect) }, new ParameterModifier[] { });


        if (GUIClipType == null || topmostRect == null || GetTopRect == null || ClipRect == null)
        {
            Debug.LogWarning("GUIScaleUtility cannot run on this system! Compability mode enabled. For you that means you're not able to use the Node Editor inside more than one group:( Please PM me (Seneral @UnityForums) so I can figure out what causes this! Thanks!");
            Debug.LogWarning((GUIClipType == null ? "GUIClipType is Null, " : "") + (topmostRect == null ? "topmostRect is Null, " : "") + (GetTopRect == null ? "GetTopRect is Null, " : "") + (ClipRect == null ? "ClipRect is Null, " : ""));
            compabilityMode = true;
            initiated = true;
            return;
        }


        // Create simple acessor delegates
        GetTopRectDelegate = (Func)Delegate.CreateDelegate(typeof(Func), GetTopRect);
        topmostRectDelegate = (Func)Delegate.CreateDelegate(typeof(Func), topmostRect.GetGetMethod());


        if (GetTopRectDelegate == null || topmostRectDelegate == null)
        {
            Debug.LogWarning("GUIScaleUtility cannot run on this system! Compability mode enabled. For you that means you're not able to use the Node Editor inside more than one group:( Please PM me (Seneral @UnityForums) so I can figure out what causes this! Thanks!");
            Debug.LogWarning((GUIClipType == null ? "GUIClipType is Null, " : "") + (topmostRect == null ? "topmostRect is Null, " : "") + (GetTopRect == null ? "GetTopRect is Null, " : "") + (ClipRect == null ? "ClipRect is Null, " : ""));
            compabilityMode = true;
            initiated = true;
            return;
        }


        // As we can call Begin/Ends inside another, we need to save their states hierarchial in Lists (not Stack, as we need to iterate over them!):
        currentRectStack = new List();
        rectStackGroups = new List>();
        GUIMatrices = new List();
        adjustedGUILayout = new List();


        initiated = true;
    }


    #endregion


    #region Scale Area


    ///
    /// Begins a scaled local area. 
    /// Returns vector to offset GUI controls with to account for zooming to the pivot. 
    /// Using adjustGUILayout does that automatically for GUILayout rects. Theoretically can be nested!
    ///

    public static Vector2 BeginScale(ref Rect rect, Vector2 zoomPivot, float zoom, bool adjustGUILayout)
    {
        Rect screenRect;
        if (compabilityMode)
        {


            // In compability mode, we will assume only one top group and do everything manually, not using reflected calls (-> practically blind)
            GUI.EndGroup();
            screenRect = rect;
        }


        else
        {


            // If it's supported, we take the completely generic way using reflected calls
            GUIScaleUtility.BeginNoClip();
            screenRect = GUIScaleUtility.GUIToScaledSpace(rect);
        }


        rect = Scale(screenRect, screenRect.position + zoomPivot, new Vector2(zoom, zoom));


        // Now continue drawing using the new clipping group
        GUI.BeginGroup(rect);
        rect.position = Vector2.zero; // Adjust because we entered the new group


        // Because I currently found no way to actually scale to a custom pivot rather than (0, 0),
        // we'll make use of a cheat and just offset it accordingly to let it appear as if it would scroll to the center
        // Note, due to that, controls not adjusted are still scaled to (0, 0)
        Vector2 zoomPosAdjust = rect.center - screenRect.size / 2 + zoomPivot;


        // For GUILayout, we can make this adjustment here if desired
        adjustedGUILayout.Add(adjustGUILayout);
        if (adjustGUILayout)
        {
            GUILayout.BeginHorizontal();
            GUILayout.Space(rect.center.x - screenRect.size.x + zoomPivot.x);
            GUILayout.BeginVertical();
            GUILayout.Space(rect.center.y - screenRect.size.y + zoomPivot.y);
        }


        // Take a matrix backup to restore back later on
        GUIMatrices.Add(GUI.matrix);


        // Scale GUI.matrix. After that we have the correct clipping group again.
        GUIUtility.ScaleAroundPivot(new Vector2(1 / zoom, 1 / zoom), zoomPosAdjust);


        return zoomPosAdjust;
    }


    ///
    /// Ends a scale region previously opened with BeginScale
    ///

    public static void EndScale()
    {
        // Set last matrix and clipping group
        if (GUIMatrices.Count == 0 || adjustedGUILayout.Count == 0)
            throw new UnityException("GUIScaleUtility: You are ending more scale regions than you are beginning!");


        GUI.matrix = GUIMatrices[GUIMatrices.Count - 1];
        GUIMatrices.RemoveAt(GUIMatrices.Count - 1);


        // End GUILayout zoomPosAdjustment
        if (adjustedGUILayout[adjustedGUILayout.Count - 1])
        {


            GUILayout.EndVertical();
            GUILayout.EndHorizontal();
        }
        adjustedGUILayout.RemoveAt(adjustedGUILayout.Count - 1);


        // End the scaled group
        GUI.EndGroup();


        if (compabilityMode)
        {


            // In compability mode, we don't know the previous group rect, but as we cannot use top groups there either way, we restore the screen group
            GUI.BeginClip(new Rect(0, 23, Screen.width, Screen.height - 23));
        }


        else
        {


            // Else, restore the clips (groups)
            GUIScaleUtility.RestoreClips();
        }
    }


    #endregion


    #region Clips Hierarchy


    ///
    /// Begins a field without groups. They should be restored using RestoreClips. Can be nested!
    ///

    public static void BeginNoClip()
    {
        // Record and close all clips one by one, from bottom to top, until we hit the 'origin'
        List rectStackGroup = new List();
        Rect topMostClip = getTopRect;
        while (topMostClip != new Rect(-10000, -10000, 40000, 40000))
        {
            rectStackGroup.Add(topMostClip);
            GUI.EndClip();
            topMostClip = getTopRect;
        }
        // Store the clips appropriately
        rectStackGroup.Reverse();
        rectStackGroups.Add(rectStackGroup);
        currentRectStack.AddRange(rectStackGroup);
    }


    ///
    /// Restores the clips removed in BeginNoClip or MoveClipsUp
    ///

    public static void RestoreClips()
    {
        if (rectStackGroups.Count == 0)
        {
            Debug.LogError("GUIClipHierarchy: BeginNoClip/MoveClipsUp - RestoreClips count not balanced!");
            return;
        }


        // Read and restore clips one by one, from top to bottom
        List rectStackGroup = rectStackGroups[rectStackGroups.Count - 1];
        for (int clipCnt = 0; clipCnt < rectStackGroup.Count; clipCnt++)
        {
            GUI.BeginClip(rectStackGroup[clipCnt]);
            currentRectStack.RemoveAt(currentRectStack.Count - 1);
        }
        rectStackGroups.RemoveAt(rectStackGroups.Count - 1);
    }


    #endregion


    #region Space Transformations


    ///
    /// Scales the rect around the pivot with scale
    ///

    public static Rect Scale(Rect rect, Vector2 pivot, Vector2 scale)
    {
        rect.position = Vector2.Scale(rect.position - pivot, scale) + pivot;
        rect.size = Vector2.Scale(rect.size, scale);
        return rect;
    }


    public static Vector2 GUIToScaledSpace(Vector2 guiPosition)
    {
        if (rectStackGroups == null || rectStackGroups.Count == 0)
            return guiPosition;
        // Iterate through the clips and add positions ontop
        List rectStackGroup = rectStackGroups[rectStackGroups.Count - 1];
        for (int clipCnt = 0; clipCnt < rectStackGroup.Count; clipCnt++)
            guiPosition += rectStackGroup[clipCnt].position;
        return guiPosition;
    }


    ///
    /// Transforms the rect to the new space aquired with BeginNoClip or MoveClipsUp.
    /// DOES NOT scale the rect, only offsets it!
    /// It's way faster to call GUIToScreenSpace before modifying the space though!
    ///

    public static Rect GUIToScaledSpace(Rect guiRect)
    {
        if (rectStackGroups == null || rectStackGroups.Count == 0)
            return guiRect;
        guiRect.position = GUIToScaledSpace(guiRect.position);
        return guiRect;
    }


    #endregion
}

你可能感兴趣的:(Unity3d技巧,VR)