That darn missing GUI.DrawLine()

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:

var width = 1.0;
var color = Color.black;

function OnGUI () {
    // Render a line from the center of the screen to the mouse position
    DrawLine(Vector2(Screen.width/2, Screen.height/2), Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y), color, width);
}

/****************************************************************************************************
static function DrawLine(rect : Rect) : void
static function DrawLine(rect : Rect, color : Color) : void
static function DrawLine(rect : Rect, width : float) : void
static function DrawLine(rect : Rect, color : Color, width : float) : void
static function DrawLine(pointA : Vector2, pointB : Vector2) : void
static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color) : void
static function DrawLine(pointA : Vector2, pointB : Vector2, width : float) : void
static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color, width : float) : void

Draws a GUI line on the screen.

DrawLine makes up for the severe lack of 2D line rendering in the Unity runtime GUI system.
This function works by drawing a 1x1 texture filled with a color, which is then scaled
 and rotated by altering the GUI matrix.  The matrix is restored afterwards.
****************************************************************************************************/

static var lastColor : Color;
static var lineTex : Texture2D;

static function DrawLine(rect : Rect) { DrawLine(rect, GUI.contentColor, 1.0); }
static function DrawLine(rect : Rect, color : Color) { DrawLine(rect, color, 1.0); }
static function DrawLine(rect : Rect, width : float) { DrawLine(rect, GUI.contentColor, width); }
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); }
static function DrawLine(pointA : Vector2, pointB : Vector2) { DrawLine(pointA, pointB, GUI.contentColor, 1.0); }
static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color) { DrawLine(pointA, pointB, color, 1.0); }
static function DrawLine(pointA : Vector2, pointB : Vector2, width : float) { DrawLine(pointA, pointB, GUI.contentColor, width); }
static function DrawLine(pointA : Vector2, pointB : Vector2, color : Color, width : float) {
    // Save the current GUI matrix, since we're going to make changes to it.
    var matrix = GUI.matrix;
    
    // Generate a single pixel texture with the designated color. This will be the color of the line.
    // This looks more complex then it needs to be for optimization.
    //  Instead of regenerating the texture every time, we only do so when the color has changed.
    if (!lineTex) { lineTex = Texture2D(1, 1); }
    if (color != lastColor) {
        lineTex.SetPixel(0, 0, color);
        lineTex.Apply();
        lastColor = color;
    }

    // Determine the angle of the line.
    var angle = Vector3.Angle(pointB-pointA, Vector2.right);
    
    // Vector3.Angle always returns a positive number.
    // If pointB is above pointA, then angle needs to be negative.
    if (pointA.y > pointB.y) { angle = -angle; }
    
    // Use ScaleAroundPivot to adjust the size of the line.
    // We could do this when we draw the texture, but by scaling it here we can use
    //  non-integer values for the width and length (such as sub 1 pixel widths).
    // Note that the pivot point is at +.5 from pointA.y, this is so that the width of the line
    //  is centered on the origin at pointA.
    GUIUtility.ScaleAroundPivot(Vector2((pointB-pointA).magnitude, width), Vector2(pointA.x, pointA.y + 0.5));
    
    // Set the rotation for the line.
    //  The angle was calculated with pointA as the origin.
    GUIUtility.RotateAroundPivot(angle, pointA);
    
    // Finally, draw the actual line.
    // We're really only drawing a 1x1 texture from pointA.
    // The matrix operations done with ScaleAroundPivot and RotateAroundPivot will make this
    //  render with the proper width, length, and angle.
    GUI.DrawTexture(Rect(pointA.x, pointA.y, 1, 1), lineTex);
    
    // We're done.  Restore the GUI matrix to whatever it was before.
    GUI.matrix = matrix;
}

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!

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!

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?

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.

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.

I was very surprised to find that this hadn’t been requested on the feedback site, but now I’ve made the request, so please go vote for it!
http://feedback.unity3d.com/forums/15792-unity/suggestions/1451375-gui-drawline

Is this not working anymore, or am I doing something wrong?

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

Why not make this an extension method for the GUI class? Then you can call GUI.DrawLine().

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:

Color myColor = Color.white;
myColor.MyExtensionMethod();

The extension method works based on that instance of Color. However, there is no way to make…

Color.MyExtensionMethod();

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.

Yeah it’s not working for me either. I’m using

var pointA = Vector2(Screen.width/2, Screen.height/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?

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:
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.

var pointA = HandleUtility.WorldToGUIPoint(this.transform.position);or

var pointA = Camera.main.WorldToScreenPoint(transform.position);

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:

Drawing.DrawLine(posA, posB, 2);

was not working. However, when I used:

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!

This modified version works wonders for me and I’ve also included some 3D drawing commands:

using System;
using UnityEngine;
 
public class Drawing
{ 
    public static Texture2D lineTex;
	
	public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB) { DrawLine(cam,pointA,pointB, GUI.contentColor, 1.0f); }
	public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB, Color color) { DrawLine(cam,pointA,pointB, color, 1.0f); }
	public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB, float width) { DrawLine(cam,pointA,pointB, GUI.contentColor, width); }
	public static void DrawLine(Camera cam, Vector3 pointA, Vector3 pointB, Color color, float width){
		Vector2 p1 = Vector2.zero;
		p1.x = cam.WorldToScreenPoint(pointA).x;
		p1.y = cam.WorldToScreenPoint(pointA).y;
		Vector2 p2 = Vector2.zero;
		p2.x = cam.WorldToScreenPoint(pointB).x;
		p2.y = cam.WorldToScreenPoint(pointB).y;
		DrawLine(p1,p2,color,width);
	}
	
    public static void DrawLine(Rect rect) { DrawLine(rect, GUI.contentColor, 1.0f); }
    public static void DrawLine(Rect rect, Color color) { DrawLine(rect, color, 1.0f); }
    public static void DrawLine(Rect rect, float width) { DrawLine(rect, GUI.contentColor, width); }
    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); }
    public static void DrawLine(Vector2 pointA, Vector2 pointB) { DrawLine(pointA, pointB, GUI.contentColor, 1.0f); }
    public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color) { DrawLine(pointA, pointB, color, 1.0f); }
    public static void DrawLine(Vector2 pointA, Vector2 pointB, float width) { DrawLine(pointA, pointB, GUI.contentColor, width); }
    public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width)
    {
		pointA.x = (int)pointA.x;	pointA.y = (int)pointA.y;
		pointB.x = (int)pointB.x;	pointB.y = (int)pointB.y;
		
		if (!lineTex) { lineTex = new Texture2D(1, 1); }
		Color savedColor = GUI.color;
        GUI.color = color;
		
		Matrix4x4 matrixBackup = GUI.matrix;
		
		float angle = Mathf.Atan2(pointB.y-pointA.y, pointB.x-pointA.x)*180f/Mathf.PI;
		float length = (pointA-pointB).magnitude;
        GUIUtility.RotateAroundPivot(angle, pointA);
        GUI.DrawTexture(new Rect(pointA.x, pointA.y, length, width), lineTex);
		
        GUI.matrix = matrixBackup;
        GUI.color = savedColor;
    }
}

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.

Hello guys

I have some troubles with this function on mobile build (In Unity IDE everything ok)


http://answers.unity3d.com/questions/602509/guidrawtexture-wrong-on-mobile-build.html

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:

1497853--84123--$Issue DrawLine.jpg

Did I forget anything here? Is it even supposed to work in 3D space?