Skip to content

Instantly share code, notes, and snippets.

@Arakade
Created January 3, 2015 16:54
Show Gist options
  • Save Arakade/9dd844c2f9c10e97e3d0 to your computer and use it in GitHub Desktop.
Save Arakade/9dd844c2f9c10e97e3d0 to your computer and use it in GitHub Desktop.
Call from OnDrawGizmos() to draw text at Unity3D glocal position in Editor
static void drawString(string text, Vector3 worldPos, Color? colour = null) {
UnityEditor.Handles.BeginGUI();
if (colour.HasValue) GUI.color = colour.Value;
var view = UnityEditor.SceneView.currentDrawingSceneView;
Vector3 screenPos = view.camera.WorldToScreenPoint(worldPos);
Vector2 size = GUI.skin.label.CalcSize(new GUIContent(text));
GUI.Label(new Rect(screenPos.x - (size.x / 2), -screenPos.y + view.position.height + 4, size.x, size.y), text);
UnityEditor.Handles.EndGUI();
}
@Domiii
Copy link

Domiii commented Jul 6, 2015

It works quite well!
However the "height fudge factor" (where you add + 4) seems to depend on the scene. Have not quite wrapped my head around it yet.
In my scene where the text is several hundred units high, the fudge factor is about 25.

@sashanicolas
Copy link

I passed a color and it made the whole scene view that color. I just removed it, and it satisfied what I intended. Well done! Thanks

@mythstified
Copy link

Hey I used this code and it works great! Except, I was thinking of visualizing coordinates so I did the following:

void OnDrawGizmos()
{
    int xSize = 10, ySize = 10, zSize = 10;
    for (int z = 0; z < zSize; z++)
    {
        for (int y = 0; y < ySize; y++)
        {
            for (int x = 0; x < xSize; x++)
            {
                drawString(x + ", " + y + ", " + z, new Vector3(x, y, z), Color.black);
            }
        }
    }
}

Try this out.. Problem is it works nicely when you're in the proper spot, but if you keep diving into it, suddenly everything gets trippy and weird. What's happening? I suspect it's something to do with the WorldToScreenPoint section

Edit: I fixed it! Check it out:

static void drawString(string text, Vector3 worldPos, Color? colour = null)
{        
    UnityEditor.Handles.BeginGUI();
    if (colour.HasValue) GUI.color = colour.Value;
    var view = UnityEditor.SceneView.currentDrawingSceneView;
    Vector3 screenPos = view.camera.WorldToScreenPoint(worldPos);

    if (screenPos.y < 0 || screenPos.y > Screen.height || screenPos.x < 0 || screenPos.x > Screen.width || screenPos.z < 0)
    {
        UnityEditor.Handles.EndGUI();
        return;
    }

    Vector2 size = GUI.skin.label.CalcSize(new GUIContent(text));
    GUI.Label(new Rect(screenPos.x - (size.x / 2), -screenPos.y + view.position.height + 4, size.x, size.y), text);
    UnityEditor.Handles.EndGUI();
}

@rogeratplumbee
Copy link

I fixed the colour stuff. You need to restore the GUI colour after drawing.

static public void drawString(string text, Vector3 worldPos, Color? colour = null) {
            UnityEditor.Handles.BeginGUI();

            var restoreColor = GUI.color;

            if (colour.HasValue) GUI.color = colour.Value;
            var view = UnityEditor.SceneView.currentDrawingSceneView;
            Vector3 screenPos = view.camera.WorldToScreenPoint(worldPos);

            if (screenPos.y < 0 || screenPos.y > Screen.height || screenPos.x < 0 || screenPos.x > Screen.width || screenPos.z < 0)
            {
                GUI.color = restoreColor;
                UnityEditor.Handles.EndGUI();
                return;
            }

            Vector2 size = GUI.skin.label.CalcSize(new GUIContent(text));
            GUI.Label(new Rect(screenPos.x - (size.x / 2), -screenPos.y + view.position.height + 4, size.x, size.y), text);
            GUI.color = restoreColor;
            UnityEditor.Handles.EndGUI();
        }

@jspillers
Copy link

I see this is a few years old, but just wanted to say thank you for this very useful bit of code and that it is still working great in 2018.3.8f1!

@nukadelic
Copy link

You can use handles inside the gizmo draw function instead like so :

void OnDrawGizmos()
{
   #if UNITY_EDITOR
   UnityEditor.Handles.color = Color.white;
   UnityEditor.Handles.Label( pointA.position, "Point A" );
   #endif
}

it also has overloads for texture and style ( font size can be changed )

@qdx
Copy link

qdx commented Nov 22, 2020

@nukadelic works like a charm, thanks!

@andigamesandmusic
Copy link

andigamesandmusic commented Jan 27, 2022

Thanks all for the snippet and tweaks! Based on the GUI version above, I further improved by using GUIStyle to specify color and font size without having to use the color restore logic. I also added pixel-precise positioning with anchor support (e.g. Vector2.zero = center, Vector2.one = anchor to bottom-left corner, -Vector2.one = anchor to top-right corner, etc.). The fudge factor issue is fixed by using camera.pixelHeight instead of view.position.height.

static public void drawString(string text, Vector3 worldPosition, Color textColor, Vector2 anchor, float textSize = 15f)
{
#if UNITY_EDITOR
    var view = UnityEditor.SceneView.currentDrawingSceneView;
    if (!view)
        return;
    Vector3 screenPosition = view.camera.WorldToScreenPoint(worldPosition);
    if (screenPosition.y < 0 || screenPosition.y > view.camera.pixelHeight || screenPosition.x < 0 || screenPosition.x > view.camera.pixelWidth || screenPosition.z < 0)
        return;
    var pixelRatio = UnityEditor.HandleUtility.GUIPointToScreenPixelCoordinate(Vector2.right).x - UnityEditor.HandleUtility.GUIPointToScreenPixelCoordinate(Vector2.zero).x;
    UnityEditor.Handles.BeginGUI();
    var style = new GUIStyle(GUI.skin.label)
    {
        fontSize = (int)textSize,
        normal = new GUIStyleState() { textColor = textColor }
    };
    Vector2 size = style.CalcSize(new GUIContent(text)) * pixelRatio;
    var alignedPosition =
        ((Vector2)screenPosition +
        size * ((anchor + Vector2.left + Vector2.up) / 2f)) * (Vector2.right + Vector2.down) +
        Vector2.up * view.camera.pixelHeight;
    GUI.Label(new Rect(alignedPosition / pixelRatio, size / pixelRatio), text, style);
    UnityEditor.Handles.EndGUI();
#endif
}

@joaoh82
Copy link

joaoh82 commented Aug 30, 2022

@nukadelic great solution! Simple! Thanks!

@MartijnPW
Copy link

Thanks a lot, works great!

For those who wonder what this pixelRatio is about: it depends on the display UI scaling (e.g. 1.5 for the '150%' setting in windows)

Instead of computing it, you can actually just get it using
var pixelRatio = EditorGUIUtility.pixelsPerPoint;
(see https://docs.unity3d.com/ScriptReference/EditorGUIUtility-pixelsPerPoint.html)

@Kaegun
Copy link

Kaegun commented Jan 15, 2023

Very neat, exactly what I was looking for!

@BurakKaragol
Copy link

BurakKaragol commented Apr 14, 2023

I passed a color and it made the whole scene view that color. I just removed it, and it satisfied what I intended. Well done! Thanks

I solved the issue color issue and also fixed the scene zoom text slipping

public static void DrawString(string text, Vector3 worldPos, Color? colour = null)
{
UnityEditor.Handles.BeginGUI();
Color defaultColor = GUI.color;
if (colour.HasValue) GUI.color = colour.Value;
var view = UnityEditor.SceneView.currentDrawingSceneView;
Vector3 screenPos = view.camera.WorldToScreenPoint(worldPos);
Vector2 size = GUI.skin.label.CalcSize(new GUIContent(text));
GUI.Label(new Rect(screenPos.x - (size.x / 2), -screenPos.y + view.position.height - size.y * 2, size.x, size.y), text);
GUI.color = defaultColor;
UnityEditor.Handles.EndGUI();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment