how to draw debug text into scene?

I'd like to have the facility to draw 2d text above the heads of my game characters that shows any relevent info I need for testing/debugging.

What's the best way of achieving this? I dont see anything in the API.

thanks

If you need the text to be in the scene view, you can use the Handles class

void OnDrawGizmos() 
{
    Handles.Label(transform.position, "Text");
}

You could 'roll your own' by projecting the character's position onto the screen and draw some gui there? I.e. use Camera.WorldToScreenPoint() then in OnGUI:

void OnGUI()
{
  if (Application.isEditor)  // or check the app debug flag
  {
    GUI.Label(screenRect, "Debug text");
  }
}

Remember you may need to invert the Y coord as the origin is at top-left.

Thanks to mcmikecreations,

this solution from mcmikecreations works so far, but the GUI.Label lost it’s position while distancing the editor view camera.

Here is an optimized version that didn’t lost it’s position. Further you can set an x,y offset to the text.

Copy the whole code into your required script and call drawString() in OnDrawGizmos().
Text outside the scene view would be clipped for better performance.

		static public void drawString(string text, Vector3 worldPos, float oX = 0, float oY = 0, Color? colour = null) {

#if UNITY_EDITOR
			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;
			}

			UnityEditor.Handles.Label(TransformByPixel(worldPos, oX, oY), text);

			GUI.color = restoreColor;
			UnityEditor.Handles.EndGUI();
#endif
		}

		static Vector3 TransformByPixel(Vector3 position, float x, float y) {
			return TransformByPixel(position, new Vector3(x, y));
		}

		static Vector3 TransformByPixel(Vector3 position, Vector3 translateBy) {
			Camera cam = UnityEditor.SceneView.currentDrawingSceneView.camera;
			if (cam)
				return cam.ScreenToWorldPoint(cam.WorldToScreenPoint(position) + translateBy);
			else
				return position;
		}

Use this script from OnDrawGizmos():

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();
        }

It’s from GitHub.

Here’s my variation of the above. Allows you to call it like you would any other debug command and it will create a temporary scene object to display the handles.

public class Debug2Mono : MonoBehaviour
{
    public class Debug2String 
    {
        public Vector3 pos;
        public string text;
        public Color? color;
        public float eraseTime; 
    }

    public List<Debug2String> Strings = new List<Debug2String>();

    public void OnDrawGizmos()
    {
    foreach ( var stringpair in Strings )
    {
        GUIStyle style = new GUIStyle();
        Color color = stringpair.color.HasValue ? stringpair.color.Value : Color.green;
        style.normal.textColor = color;

#if UNITY_EDITOR            
        UnityEditor.Handles.color = color;
        UnityEditor.Handles.Label (stringpair.pos, stringpair.text, style);
#endif
    }
    }

    private static Debug2Mono m_instance; 
    public static Debug2Mono instance {
        get
        {
            if ( m_instance == null )
            {
                m_instance = GameObject.FindObjectOfType<Debug2Mono>();
                if ( m_instance == null ) 
                {
                    var go = new GameObject("DeleteMeLater");
                    m_instance = go.AddComponent<Debug2Mono>();
                }
            }
            return m_instance;
        }
    }

    public static void DrawText(Vector3 pos, string text, Color? color = null)
{
    instance.Strings.Add ( new Debug2Mono.Debug2String() { text = text, color = color, pos = pos, eraseTime = Time.time + duration,} );

    List<Debug2Mono.Debug2String> toBeRemoved = new List<Debug2Mono.Debug2String>();

    foreach ( var item in instance.Strings )
    {
        if ( item.eraseTime <= Time.time )
            toBeRemoved.Add(item);
    }

    foreach ( var rem in toBeRemoved )
        instance.Strings.Remove(rem);
}
}

In one case I had to represent a number from 0 - 5 so using correct number of wire-spheres did the trick to communicate the info. Like a gizmo-progress-bar kind of idea.

Honestly, one of the easiest/most foolproof ways is to add a TMPro.TextMeshPro to your prefab. You can position it relative to the object, it generates easy to read text, and it’s easy to change the color or other attributes to indicate additional data.