Search Unity

That darn missing GUI.DrawLine()

Discussion in 'Immediate Mode GUI (IMGUI)' started by capnbishop, Oct 24, 2009.

  1. capnbishop

    capnbishop

    Joined:
    Oct 4, 2007
    Posts:
    50
    I've been struggling with the lack of a DrawLine function which is painfully vacant from the Unity GUI system (seriously, guys, what's the holdup?), and I know that a number of people have had trouble with this also. Well, thanks to some inspiration from a few posts in this forum, I finally figured out a fast, effective way of doing it, and posted the script on the Unify wiki.

    http://www.unifycommunity.com/wiki/index.php?title=DrawLine

    For ease's sake, here it is as well, with the example built in:

    Code (csharp):
    1. var width = 1.0;
    2. var color = Color.black;
    3.  
    4. function OnGUI () {
    5.     // Render a line from the center of the screen to the mouse position
    6.     DrawLine(Vector2(Screen.width/2, Screen.height/2), Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y), color, width);
    7. }
    8.  
    9. /****************************************************************************************************
    10. static function DrawLine(rect : Rect) : void
    11. static function DrawLine(rect : Rect, color : Color) : void
    12. static function DrawLine(rect : Rect, width : float) : void
    13. static function DrawLine(rect : Rect, color : Color, width : float) : void
    14. static function DrawLine(pointA : Vector2, pointB : Vector2) : void
    15. static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color) : void
    16. static function DrawLine(pointA : Vector2, pointB : Vector2, width : float) : void
    17. static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color, width : float) : void
    18.  
    19. Draws a GUI line on the screen.
    20.  
    21. DrawLine makes up for the severe lack of 2D line rendering in the Unity runtime GUI system.
    22. This function works by drawing a 1x1 texture filled with a color, which is then scaled
    23.  and rotated by altering the GUI matrix.  The matrix is restored afterwards.
    24. ****************************************************************************************************/
    25.  
    26. static var lastColor : Color;
    27. static var lineTex : Texture2D;
    28.  
    29. static function DrawLine(rect : Rect) { DrawLine(rect, GUI.contentColor, 1.0); }
    30. static function DrawLine(rect : Rect, color : Color) { DrawLine(rect, color, 1.0); }
    31. static function DrawLine(rect : Rect, width : float) { DrawLine(rect, GUI.contentColor, width); }
    32. static function DrawLine(rect : Rect, color : Color, width : float) { DrawLine(Vector2(rect.x, rect.y), Vector2(rect.x + rect.width, rect.y + rect.height), color, width); }
    33. static function DrawLine(pointA : Vector2, pointB : Vector2) { DrawLine(pointA, pointB, GUI.contentColor, 1.0); }
    34. static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color) { DrawLine(pointA, pointB, color, 1.0); }
    35. static function DrawLine(pointA : Vector2, pointB : Vector2, width : float) { DrawLine(pointA, pointB, GUI.contentColor, width); }
    36. static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color, width : float) {
    37.     // Save the current GUI matrix, since we're going to make changes to it.
    38.     var matrix = GUI.matrix;
    39.    
    40.     // Generate a single pixel texture with the designated color. This will be the color of the line.
    41.     // This looks more complex then it needs to be for optimization.
    42.     //  Instead of regenerating the texture every time, we only do so when the color has changed.
    43.     if (!lineTex) { lineTex = Texture2D(1, 1); }
    44.     if (color != lastColor) {
    45.         lineTex.SetPixel(0, 0, color);
    46.         lineTex.Apply();
    47.         lastColor = color;
    48.     }
    49.  
    50.     // Determine the angle of the line.
    51.     var angle = Vector3.Angle(pointB-pointA, Vector2.right);
    52.    
    53.     // Vector3.Angle always returns a positive number.
    54.     // If pointB is above pointA, then angle needs to be negative.
    55.     if (pointA.y > pointB.y) { angle = -angle; }
    56.    
    57.     // Use ScaleAroundPivot to adjust the size of the line.
    58.     // We could do this when we draw the texture, but by scaling it here we can use
    59.     //  non-integer values for the width and length (such as sub 1 pixel widths).
    60.     // Note that the pivot point is at +.5 from pointA.y, this is so that the width of the line
    61.     //  is centered on the origin at pointA.
    62.     GUIUtility.ScaleAroundPivot(Vector2((pointB-pointA).magnitude, width), Vector2(pointA.x, pointA.y + 0.5));
    63.    
    64.     // Set the rotation for the line.
    65.     //  The angle was calculated with pointA as the origin.
    66.     GUIUtility.RotateAroundPivot(angle, pointA);
    67.    
    68.     // Finally, draw the actual line.
    69.     // We're really only drawing a 1x1 texture from pointA.
    70.     // The matrix operations done with ScaleAroundPivot and RotateAroundPivot will make this
    71.     //  render with the proper width, length, and angle.
    72.     GUI.DrawTexture(Rect(pointA.x, pointA.y, 1, 1), lineTex);
    73.    
    74.     // We're done.  Restore the GUI matrix to whatever it was before.
    75.     GUI.matrix = matrix;
    76. }
     
  2. capnbishop

    capnbishop

    Joined:
    Oct 4, 2007
    Posts:
    50
    Well, looks like I just discovered an issue with this. Altering the GUI transform matrix works great the way that this script handles it (by saving and restoring the matrix at the beginning and end), in that it doesn't affect any other GUI element. However, it appears that this isn't quite so for a GUI rendering group. If you use this DrawLine function from within a GUI.BeginGroup, then when the line is rendered it is done so as if the group rect rotates with the line. This means that rending the line outside the bounds of the group rect is as if the rect is rotated, and looks damn odd.

    Looks like I'm not quite done.

    Hey, Unity Tech, how about you guys implement this function so we don't have to resort to workarounds!
     
  3. HiggyB

    HiggyB

    Unity Product Evangelist

    Joined:
    Dec 8, 2006
    Posts:
    6,183
    We understand that our GUI system isn't complete, far from it, and things like line drawing are a needed item. The only "hold up" is the simple fact that there's lots of other stuff to work on and it's always a balance of what's the most important work to get done at any time and hile needed, this hasn't rated high enough just yet. But again, that's not so say we don't recognize the need so look for improvements in the future!
     
  4. capnbishop

    capnbishop

    Joined:
    Oct 4, 2007
    Posts:
    50
    In the meantime, I don't suppose you have any idea how to go about tweaking the GUI transform matrix while obeying the original group rect bounds which were set before the rotation?
     
  5. matrix211v1

    matrix211v1

    Joined:
    Jan 20, 2009
    Posts:
    193
    It seems that evilness runs in this DrawLine code. I like the code, but it runs differently on a Mac and a PC. Mac it works fine, PC I have had to write offsets for things.
     
  6. capnbishop

    capnbishop

    Joined:
    Oct 4, 2007
    Posts:
    50
    Hmm, that's interesting. I'd like to see the code to see what you mean. I put that together on a Mac.

    Otherwise, I've pretty much abandoned this script. It pretty clearly doesn't work with GUI groups, and I can't get it to.
     
  7. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
  8. Tilds

    Tilds

    Joined:
    Mar 6, 2012
    Posts:
    1
    Is this not working anymore, or am I doing something wrong?

    If not, can somebody tell me if there is an easy fix?
     
  9. SevenBits

    SevenBits

    Joined:
    Dec 26, 2011
    Posts:
    1,953
    Why not make this an extension method for the GUI class? Then you can call GUI.DrawLine().
     
  10. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Extension methods don't work that way - they only work via an instance of a class/struct. That is to say, you can create something like:

    Code (csharp):
    1.  
    2. Color myColor = Color.white;
    3. myColor.MyExtensionMethod();
    4.  
    The extension method works based on that instance of Color. However, there is no way to make...

    Code (csharp):
    1.  
    2. Color.MyExtensionMethod();
    3.  
    AFAIK they feel that the benefit of having "static" extension methods (all EM are static... but you catch my drift) is so minor compared to the potential downfalls/obfuscation. I'd just stick it in a class like GUILine.Draw() and be done with it, adding it as GUI.DrawLine isn't an option.
     
  11. grimmy

    grimmy

    Joined:
    Feb 2, 2009
    Posts:
    409
    Yeah it's not working for me either. I'm using

    Code (csharp):
    1. var pointA = Vector2(Screen.width/2, Screen.height/2);
    2. var pointB = Event.current.mousePosition;
    ..but the end/starts are nowhere near either the mouse pointer (PointB) or the center of the screen(PointA)

    I noticed that the values for PointA and PointB are fixed when printed to the console but on screen the line's origin and end point both move all over the place when I move the mouse.

    Any idea?
     
    Last edited: Apr 20, 2012
  12. Zardoz

    Zardoz

    Joined:
    Apr 23, 2012
    Posts:
    3
    Grimmy :

    I'm having the a similar problem. Post if you find a solution?

    Edit: actually, it's not the same problem. if I use the example, everything works fine:
    Code (csharp):
    1. var pointA = Vector2(Screen.width/2, Screen.height/2);
    However, when I try to use the object's position as a screen position (or GUI - I'm not clear on which it should be actually), it draws some strange line that appears to be a segment along the mathematical line defined by the two points, but with variable endpoints.

    Code (csharp):
    1. var pointA = HandleUtility.WorldToGUIPoint(this.transform.position);
    or
    Code (csharp):
    1. var pointA = Camera.main.WorldToScreenPoint(transform.position);
     
    Last edited: Apr 23, 2012
  13. Flynn

    Flynn

    Joined:
    May 24, 2009
    Posts:
    311

    Hi, there! This problem also struck me, I think I have a solution:


    I found that the values going in can't have very extravagant decimal values, IE:


    Code (csharp):
    1. Drawing.DrawLine(posA, posB, 2);
    was not working. However, when I used:

    Code (csharp):
    1. Drawing.DrawLine(new Vector2((int)posA.x, (int)posA.y), new Vector2((int)posB.x, (int)posB.y), 2);
    I found that all the crazy jittering disappeared!
     
  14. PlasmaByte

    PlasmaByte

    Joined:
    Sep 18, 2012
    Posts:
    6
    This modified version works wonders for me and I've also included some 3D drawing commands:

    Code (csharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class Drawing
    5. {
    6.     public static Texture2D lineTex;
    7.    
    8.     public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB) { DrawLine(cam,pointA,pointB, GUI.contentColor, 1.0f); }
    9.     public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB, Color color) { DrawLine(cam,pointA,pointB, color, 1.0f); }
    10.     public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB, float width) { DrawLine(cam,pointA,pointB, GUI.contentColor, width); }
    11.     public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB, Color color, float width){
    12.         Vector2 p1 = Vector2.zero;
    13.         p1.x = cam.WorldToScreenPoint(pointA).x;
    14.         p1.y = cam.WorldToScreenPoint(pointA).y;
    15.         Vector2 p2 = Vector2.zero;
    16.         p2.x = cam.WorldToScreenPoint(pointB).x;
    17.         p2.y = cam.WorldToScreenPoint(pointB).y;
    18.         DrawLine(p1,p2,color,width);
    19.     }
    20.    
    21.     public static void DrawLine(Rect rect) { DrawLine(rect, GUI.contentColor, 1.0f); }
    22.     public static void DrawLine(Rect rect, Color color) { DrawLine(rect, color, 1.0f); }
    23.     public static void DrawLine(Rect rect, float width) { DrawLine(rect, GUI.contentColor, width); }
    24.     public static void DrawLine(Rect rect, Color color, float width) { DrawLine(new Vector2(rect.x, rect.y), new Vector2(rect.x + rect.width, rect.y + rect.height), color, width); }
    25.     public static void DrawLine(Vector2 pointA, Vector2 pointB) { DrawLine(pointA, pointB, GUI.contentColor, 1.0f); }
    26.     public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color) { DrawLine(pointA, pointB, color, 1.0f); }
    27.     public static void DrawLine(Vector2 pointA, Vector2 pointB, float width) { DrawLine(pointA, pointB, GUI.contentColor, width); }
    28.     public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width)
    29.     {
    30.         pointA.x = (int)pointA.x;   pointA.y = (int)pointA.y;
    31.         pointB.x = (int)pointB.x;   pointB.y = (int)pointB.y;
    32.        
    33.         if (!lineTex) { lineTex = new Texture2D(1, 1); }
    34.         Color savedColor = GUI.color;
    35.         GUI.color = color;
    36.        
    37.         Matrix4x4 matrixBackup = GUI.matrix;
    38.        
    39.         float angle = Mathf.Atan2(pointB.y-pointA.y, pointB.x-pointA.x)*180f/Mathf.PI;
    40.         float length = (pointA-pointB).magnitude;
    41.         GUIUtility.RotateAroundPivot(angle, pointA);
    42.         GUI.DrawTexture(new Rect(pointA.x, pointA.y, length, width), lineTex);
    43.        
    44.         GUI.matrix = matrixBackup;
    45.         GUI.color = savedColor;
    46.     }
    47. }
     
  15. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    If anyone's still interested in drawing lines with this technique, I just posted an update on that other thread with an optimized version of DrawLine. It's more than four times faster when drawing lots of lines.
     
  16. dear_cj

    dear_cj

    Joined:
    Jul 17, 2013
    Posts:
    2
  17. betaFlux

    betaFlux

    Joined:
    Jan 7, 2013
    Posts:
    112
    First, I am very thankful for your try to make the Drawing feature work with 3D vectors, but sadly those methods do not work properly. The lines are drawn at proper lengths, but not at the correct positions and angles. I've put this in a script on the main camera:

    void OnGUI()
    {
    Drawing.DrawLine(Camera.main, GameObject.Find("Cube1").transform.position, GameObject.Find("Cube2").transform.position, Color.red, 1);
    }

    EDIT: This is how it looks in game:

    $Issue DrawLine.jpg

    Did I forget anything here? Is it even supposed to work in 3D space?
     
    Last edited: Jan 26, 2014