I’d like to draw a rect in screen pixel coordinates in OnDrawGizmos in a MonoBehaviour
public class Test : MonoBehaviour
{
void OnDrawGizmos()
{
Rect rect = new Rect(10, 10, 150, -100);
UnityEditor.Handles.DrawSolidRectangleWithOutline(rect, Color.black, Color.white);
}
}
Those values in rect end up being world space coords oriented toward the screen relative to the Transform of the GameObject this script is attached to.
I’d like to just draw in pixels on the screen where 0, 0 is the top left of the screen. Do I need to get out the camera and compute those positions relative to the current GameObject’s transform or is there an easier way?
Ideally I’d like to be able to use things like GUI.Label it’s not working
Unity’s gizmos can’t draw in screen space but you can draw on the camera.NearClipPlane. This will give the impression of drawing on the screen. You just have to take the camera projection type and canvas scale into account. Further explanation and more content can be found at my blog: Unity: Screen Space Gizmos – Anja's Website
using UnityEngine;
public static class ScreenGizmos
{
private const float offset = 0.001f;
/// <summary>
/// Draws a line in screen space between the
/// <paramref name="startPixelPos"/> and the
/// <paramref name="endPixelPos"/>.
/// </summary>
public static void DrawLine(
Canvas canvas,
Camera camera,
Vector3 startPixelPos,
Vector3 endPixelPos)
{
if (camera == null || canvas == null)
return;
Vector3 startWorld = PixelToCameraClipPlane(
camera,
canvas,
startPixelPos);
Vector3 endWorld = PixelToCameraClipPlane(
camera,
canvas,
endPixelPos);
Gizmos.DrawLine(startWorld, endWorld);
}
/// <summary>
/// Converts the <paramref name="screenPos"/> to world space
/// near the <paramref name="camera"/> near clip plane. The
/// z component of the <paramref name="screenPos"/>
/// will be overriden.
/// </summary>
private static Vector3 PixelToCameraClipPlane(
Camera camera,
Canvas canvas,
Vector3 screenPos)
{
// The canvas scale factor affects the
// screen position of all UI elements.
screenPos *= canvas.scaleFactor;
// The z-position defines the distance to the camera
// when using Camera.ScreenToWorldPoint.
screenPos.z = camera.nearClipPlane + offset;
return camera.ScreenToWorldPoint(screenPos);
}
}
So this is NOT the solution I was looking for. The solution I wanted was some way to just tell Unity I want to draw in 2D and start using the GUI, GUILayout functions but when I tried to use them I got errors. Maybe someone has an example?
In lieu of that here’s some code to compute the correct world coordinates for the desired screen coordinates
Vector3 ScreenToWorld(float x, float y) {
Camera camera = Camera.current;
Vector3 s = camera.WorldToScreenPoint(transform.position);
return camera.ScreenToWorldPoint(new Vector3(x, camera.pixelHeight - y, s.z));
}
Rect ScreenRect(int x, int y, int w, int h) {
Vector3 tl = ScreenToWorld(x, y);
Vector3 br = ScreenToWorld(x + w, y + h);
return new Rect(tl.x, tl.y, br.x - tl.x, br.y - tl.y);
}
So with that this seems to work
public class Test : MonoBehaviour
{
void OnDrawGizmos()
{
Rect rect = ScreenRect(10, 10, 150, 100);
UnityEditor.Handles.DrawSolidRectangleWithOutline(rect, Color.black, Color.white);
}
...