Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Drawing a rectangle around an object on screen

Discussion in 'General Graphics' started by Michael_Hutton, Sep 2, 2023.

  1. Michael_Hutton

    Michael_Hutton

    Joined:
    May 4, 2020
    Posts:
    9
    Hi all,

    I am trying to achieve an effect where a window will pop up on screen next to an object with it's information when the mouse is hovering over a gameobject.

    The gameobject has a collider with IsTrigger = true. So onMouseXXX works.

    It is easy to get the position of the object on screen but I am struggling to calculate the size of the object in screen co-ordinates and especially when the camera moves.

    My code so far is:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3.  
    4. public class SelectionScript : MonoBehaviour
    5. {
    6.     private bool IsMouseOver;
    7.     private Rect WindowRect = new(0, 0, 100, 100);
    8.     private int WindowID;
    9.     private string WindowName;
    10.     Renderer _renderer;
    11.  
    12.     private void Start()
    13.     {
    14.         WindowID = GetHashCode();
    15.         WindowName = gameObject.name;
    16.         _renderer = GetComponent<Renderer>();
    17.     }
    18.  
    19.     private void OnGUI()
    20.     {
    21.         if (!IsMouseOver) return;
    22.  
    23.         Rect rect = new Rect();
    24.         rect.position = WorldToGuiPoint(gameObject.transform.position);
    25.         // Ok, fine so far but how do I find the size o fthe object in screen space co-ordinates?
    26.         // I know I can use renderer.bounds. min and max but this doesn't seem to take perspective into consideration..
    27.         rect.width = 100;
    28.         rect.height = 100;
    29.         Utils.DrawScreenRectBorder(rect, 2, new Color(0.0f, 0.0f, 0.0f)); // This is a seperate script but works.
    30.         // I would like the onGUI window to pop up on the top Right of the object and not obscure it (if it does
    31.         // this can lead to a flickering effect if the cursor is over the object but also in the window as the
    32.         // onMouseOver toggles on/off eavery other frame)
    33.         WindowRect = GUILayout.Window(WindowID, WindowRect, WindowFunc, WindowName);
    34.     }
    35.  
    36.     void WindowFunc(int id)
    37.     {
    38.         GUILayout.Label("Hello World");
    39.     }
    40.  
    41.     private void OnMouseOver()
    42.     {
    43.         IsMouseOver = true;
    44.     }
    45.  
    46.     private void OnMouseExit()
    47.     {
    48.         IsMouseOver = false;
    49.     }
    50.  
    51.     public Vector2 WorldToGuiPoint(Vector3 GOposition)
    52.     {
    53.         Vector2 guiPosition = Camera.main.WorldToScreenPoint(GOposition);
    54.         guiPosition.y = Screen.height - guiPosition.y;
    55.         return guiPosition;
    56.     }
    57.  
    58.     // Not used but for investigation purposes!
    59.     public void OnDrawGizmos()
    60.     {
    61.         Renderer r = GetComponent<Renderer>();
    62.         if (r == null)
    63.             return;
    64.         Gizmos.matrix = Matrix4x4.identity;
    65.         Gizmos.color = Color.blue;
    66.         Gizmos.DrawWireCube(r.bounds.center, r.bounds.extents * 2);
    67.     }
    68.  
    69. }
    Has anyone solved this problem or have any good ideas? Thank you in advance.

    Michael
     
  2. Michael_Hutton

    Michael_Hutton

    Joined:
    May 4, 2020
    Posts:
    9
    [Solved] (In a way I like anyway...!) Others may find it useful. :)

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class SelectionScript : MonoBehaviour
    5. {
    6.  
    7.     public bool IsSelected;
    8.     private bool IsMouseOver;
    9.  
    10.     private Rect WindowRect;
    11.     private int WindowID;
    12.     private string WindowName;
    13.     Renderer _renderer;
    14.     Color rectColour = Color.green;
    15.     Vector2[] points;
    16.  
    17.     private void Start()
    18.     {
    19.         WindowID = GetHashCode();
    20.         WindowName = gameObject.name;
    21.         _renderer = GetComponent<Renderer>();
    22.         points = new Vector2[8];
    23.     }
    24.  
    25.     private void OnGUI()
    26.     {
    27.         if (!IsSelected) if (!IsMouseOver) return;
    28.  
    29.         Rect rect = Rect.zero;
    30.         rect.position = WorldToGuiPoint(_renderer.bounds.center);
    31.  
    32.         float x = _renderer.bounds.extents.x;
    33.         float y = _renderer.bounds.extents.y;
    34.         float z = _renderer.bounds.extents.z;
    35.  
    36.         points[0] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(x, y, z));
    37.         points[1] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(x, -y, z));
    38.         points[2] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(x, y, -z));
    39.         points[3] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(x, -y, -z));
    40.         points[4] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(-x, y, z));
    41.         points[5] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(-x, -y, z));
    42.         points[6] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(-x, y, -z));
    43.         points[7] = WorldToGuiPoint(_renderer.bounds.center + new Vector3(-x, -y, -z));
    44.  
    45.         foreach (Vector2 point in points)
    46.         {
    47.             rect = GrowRectangleToInlcudePoint(rect, point);
    48.         }
    49.  
    50.         Utils.DrawScreenRectBorder(rect, 2, rectColour);
    51.  
    52.         WindowRect.position = new Vector2(rect.xMax, rect.yMin - WindowRect.height);
    53.         WindowRect = GUILayout.Window(WindowID, WindowRect, WindowFunc, WindowName, GUILayout.MinWidth(80));
    54.     }
    55.  
    56.     void WindowFunc(int id)
    57.     {
    58.         GUILayout.Label("Hello World", GUILayout.MinWidth(100));
    59.     }
    60.  
    61.     private void OnMouseEnter()
    62.     {
    63.         IsMouseOver = true;
    64.     }
    65.  
    66.     private void OnMouseExit()
    67.     {
    68.         IsMouseOver = false;
    69.     }
    70.  
    71.     private void OnMouseDown()
    72.     {
    73.         IsSelected = !IsSelected;
    74.         if (IsSelected)
    75.         {
    76.             rectColour = Color.red;
    77.         }
    78.         else
    79.         {
    80.             rectColour = Color.green;
    81.         }
    82.     }
    83.  
    84.     public Vector2 WorldToGuiPoint(Vector3 pos)
    85.     {
    86.         Vector2 guiPosition = Camera.main.WorldToScreenPoint(pos);
    87.         guiPosition.y = Screen.height - guiPosition.y;
    88.         return guiPosition;
    89.     }
    90.  
    91.  
    92.     Rect GrowRectangleToInlcudePoint(Rect rect, Vector2 p)
    93.     {
    94.         if (!rect.Contains(p))
    95.         {
    96.             if (p.x < rect.xMin)
    97.                 rect.xMin = p.x;
    98.             else if (p.x > rect.xMax)
    99.                 rect.xMax = p.x;
    100.  
    101.             if (p.y < rect.yMin)
    102.                 rect.yMin = p.y;
    103.             else if (p.y > rect.yMax)
    104.                 rect.yMax = p.y;
    105.         }
    106.         return rect;
    107.     }
    108.  
    109.     // Not used but for investigation purposes!
    110.     //public void OnDrawGizmos()
    111.     //{
    112.     //    Renderer r = GetComponent<Renderer>();
    113.     //    if (r == null)
    114.     //        return;
    115.     //    Gizmos.matrix = Matrix4x4.identity;
    116.     //    Gizmos.color = Color.blue;
    117.     //    Gizmos.DrawWireCube(r.bounds.center, r.bounds.extents * 2);
    118.     //    Gizmos.DrawSphere(r.bounds.center, 0.1f);
    119.     //    float x = r.bounds.extents.x;
    120.     //    float y = r.bounds.extents.y;
    121.     //    float z = r.bounds.extents.z;
    122.     //    Gizmos.color = Color.yellow;
    123.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(x, y, z), 0.1f);
    124.     //    Gizmos.color = Color.blue;
    125.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(x, -y, z), 0.1f);
    126.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(x, y, -z), 0.1f);
    127.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(x, -y, -z), 0.1f);
    128.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(-x, y, z), 0.1f);
    129.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(-x, -y, z), 0.1f);
    130.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(-x, y, -z), 0.1f);
    131.     //    Gizmos.DrawSphere(r.bounds.center + new Vector3(-x, -y, -z), 0.1f);
    132.     //}
    133.  
    134. }
     
  3. Michael_Hutton

    Michael_Hutton

    Joined:
    May 4, 2020
    Posts:
    9
    Oh, and Utils is...(which I've lifted form somewhere but I can't remember who coded it originally - sorry)

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public static class Utils
    4. {
    5.     public static Rect GetScreenRect(Vector3 screenPosition1, Vector3 screenPosition2)
    6.     {
    7.         // Move origin from bottom left to top left
    8.         screenPosition1.y = Screen.height - screenPosition1.y;
    9.         screenPosition2.y = Screen.height - screenPosition2.y;
    10.         // Calculate corners
    11.         var topLeft = Vector3.Min(screenPosition1, screenPosition2);
    12.         var bottomRight = Vector3.Max(screenPosition1, screenPosition2);
    13.         // Create Rect
    14.         return Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
    15.     }
    16.  
    17.     public static Bounds GetViewportBounds(Camera camera, Vector3 screenPosition1, Vector3 screenPosition2)
    18.     {
    19.         var v1 = Camera.main.ScreenToViewportPoint(screenPosition1);
    20.         var v2 = Camera.main.ScreenToViewportPoint(screenPosition2);
    21.         var min = Vector3.Min(v1, v2);
    22.         var max = Vector3.Max(v1, v2);
    23.  
    24.         min.z = camera.nearClipPlane;
    25.         max.z = camera.farClipPlane;
    26.  
    27.         var bounds = new Bounds();
    28.  
    29.         bounds.SetMinMax(min, max);
    30.  
    31.         return bounds;
    32.     }
    33.  
    34.     public static void DrawScreenRect(Rect rect, Color color)
    35.     {
    36.         GUI.color = color;
    37.         GUI.DrawTexture(rect, Texture2D.whiteTexture);
    38.         GUI.color = Color.white;
    39.     }
    40.  
    41.     public static void DrawScreenRectBorder(Rect rect, int thickness, Color color)
    42.     {
    43.         Utils.DrawScreenRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), color);
    44.         Utils.DrawScreenRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), color);
    45.         Utils.DrawScreenRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), color);
    46.         Utils.DrawScreenRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), color);
    47.     }
    48. }