2
\$\begingroup\$

I have a custom editor to which I want to be able to draw a basic line graph. I have much of the functionality down, but I have only been able to test as much via the debug. I am yet to figure out how to draw the actual lines to the inspector.

I want to be able to expand a horizontal layout, and draw a set of lines inside of it.

How do I draw lines to a custom inspector?


I have found plenty of online resources, in regards to custom drawing to the editor. However, everything I find pertains specifically to drawing in the scene view; nothing seems to work inside of an actual inspector.

The two general methods I have found involve using Handles.DrawLine and the GL library. Neither appear to work; however, I have only been able to test functionality with blind coordinates, so far. I can not find anything in regards to actually determining the local coordinates of the area on the inspector where I wish to draw. Perhaps I need to start with an initial "origin" position, Perhaps I need to start with a local area defined by a Rect. I simply don't know.


It has also been a while since I had to use OpenGL, directly. It is entirely possible I am simply doing it wrong, to begin with. Here is my current graph method:

private void DrawVelocityGraph(float[] velocityValues, float minimumVelocity, 
    float maximumVelocity)
{
    float velocityValue = velocityValues[0];
    float increment = 0;
    Vector3 lineStart = Vector3.zero;
    Vector3 lineEnd  = new Vector3(horizontalIncrement, velocityValue * verticalIncrement);

    GL.Begin(GL.LINES);

    for(int i = 1; i < velocityValues.Length; i++)
    {
        if(velocityValue == velocityValues[i] 
            && (velocityValue == minimumVelocity || velocityValue == maximumVelocity))
        {
            Handles.color = VelocityGrapherColours.outOfRangeColour;
        }
        else
        {
            velocityValue = velocityValues[i];
            Handles.color = VelocityGrapherColours.lineColour;
        }

        lineStart = lineEnd;
        lineEnd.x += increment;
        lineEnd.y = velocityValue * verticalIncrement;

        GL.Vertex(lineStart);
        GL.Vertex(lineEnd);
    }

    GL.End();
}
\$\endgroup\$
2
  • \$\begingroup\$ Could you use docs.unity3d.com/ScriptReference/… instead of creating your own? \$\endgroup\$
    – Savlon
    Commented May 19, 2017 at 6:38
  • \$\begingroup\$ @Savlon, from what I read, no. The line graph consists of straight lines, while CurveField manages curved lines; they would not be suitable for the task I have described. \$\endgroup\$
    – Gnemlock
    Commented May 19, 2017 at 6:44

2 Answers 2

8
\$\begingroup\$

I ended up asking over at Unity Answers, and I was quickly given a reliable answer from Bunny83, including a working example of a custom inspector with custom drawing logic. Ultimately, you can draw lines over a custom inspector using OpenGL, I was just doing it wrong. Of particular note, the answer I received made note of additional requirements I had missed:

  • I had to reserve a space to draw over, using GUILayoutUtility.GetRect().
  • I had to ensure I was only drawing during the EventType.Repaint event.
  • While not a requirement, I had to assign a specific material, using material.SetPass(). Not doing this can have undesirable results.
  • I had to use further common OpenGL logic, such as clearing a new area with GL.Clear(), and pushing and popping the current matrix with GL.PushMatrix() and GL.PopMatrix().

<summary>The material to use when drawing with OpenGL.</summary>
private Material material;

void OnEnable()
{
    // Find the "Hidden/Internal-Colored" shader, and cache it for use.
    material = new Material(Shader.Find("Hidden/Internal-Colored"));
}

public override void OnInspectorGUI()
{   
    // Begin to draw a horizontal layout, using the helpBox EditorStyle
    GUILayout.BeginHorizontal(EditorStyles.helpBox);

    // Reserve GUI space with a width from 10 to 10000, and a fixed height of 200, and 
    // cache it as a rectangle.
    Rect layoutRectangle = GUILayoutUtility.GetRect(10,10000,200,200);

    if(Event.current.type == EventType.Repaint)
    {
        // If we are currently in the Repaint event, begin to draw a clip of the size of 
        // previously reserved rectangle, and push the current matrix for drawing.
        GUI.BeginClip(layoutRectangle);
        GL.PushMatrix();

        // Clear the current render buffer, setting a new background colour, and set our
        // material for rendering.
        GL.Clear(true, false, Color.black);
        material.SetPass(0);

        // Start drawing in OpenGL Quads, to draw the background canvas. Set the
        // colour black as the current OpenGL drawing colour, and draw a quad covering
        // the dimensions of the layoutRectangle.
        GL.Begin(GL.QUADS);
        GL.Color(Color.black);
        GL.Vertex3(0, 0, 0);
        GL.Vertex3(layoutRectangle.width, 0, 0);
        GL.Vertex3(layoutRectangle.width, layoutRectangle.height, 0);
        GL.Vertex3(0, layoutRectangle.height, 0);
        GL.End();

        // Start drawing in OpenGL Lines, to draw the lines of the grid.
        GL.Begin(GL.LINES);

        // Store measurement values to determine the offset, for scrolling animation,
        // and the line count, for drawing the grid.
        int offset = (Time.frameCount * 2) % 50;
        int count = (int)(layoutRectangle.width / 10) + 20;

        for(int i = 0; i < count; i++)
        {
            // For every line being drawn in the grid, create a colour placeholder; if the
            // current index is divisible by 5, we are at a major segment line; set this
            // colour to a dark grey. If the current index is not divisible by 5, we are
            // at a minor segment line; set this colour to a lighter grey. Set the derived
            // colour as the current OpenGL drawing colour.
            Color lineColour = (i % 5 == 0 
                ? new Color(0.5f, 0.5f, 0.5f) : new Color(0.2f, 0.2f, 0.2f);
            GL.Color(lineColour);

            // Derive a new x co-ordinate from the initial index, converting it straight
            // into line positions, and move it back to adjust for the animation offset.
            float x = i * 10 - offset;

            if (x >= 0 && x < layoutRectangle.width)
            {
                // If the current derived x position is within the bounds of the
                // rectangle, draw another vertical line.
                GL.Vertex3(x, 0, 0);
                GL.Vertex3(x, layoutRectangle.height, 0);
            }

            if (i < layoutRectangle.height / 10)
            {
                // Convert the current index value into a y position, and if it is within
                // the bounds of the rectangle, draw another horizontal line.
                GL.Vertex3(0, i * 10, 0);
                GL.Vertex3(layoutRectangle.width, i * 10, 0);
            }
        }

        // End lines drawing.
        GL.End();

        // Pop the current matrix for rendering, and end the drawing clip.
        GL.PopMatrix();
        GUI.EndClip();
    }

    // End our horizontal 
    GUILayout.EndHorizontal();
}

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Why don't you just use the Handles.DrawAAPolyLine(), to draw custom lines on the inspector panel, like:

public override void OnInspectorGUI()
{
    // ...
    GUI.BeginClip(rect);
    Handles.color = Color.red;
    Handles.DrawAAPolyLine(
        Texture2D.whiteTexture,
        15,
        Vector3.zero,
        new Vector3(120, 91, 0),
        new Vector3(220, 91, 0),
        new Vector3(350, 20, 0));
    GUI.EndClip();
    // ...
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .