How to get width of RectTransform? (in screen coordinates, for position relative to, say, mouse input)

Here is what I have:

  • Unity 4.6 (Beta 20)
  • Scene with Panel UI object.
  • Panel’s anchors are set to its four corners.
  • Panel’s pivot point is the center (0.5, 0.5).
  • Wrote code to position the panel relative to the mouse as the mouse moves around.

Here is the code that is setting the position of the panel:

public GameObject goToolTipPanel; // Set in Unity IDE.
// Update is called once per frame
void Update() 
  {
    if (blToolTipOn)
    {
      RectTransform rt = goToolTipPanel.GetComponent<RectTransform>();      
      rt.position 
        = new Vector3(
            Input.mousePosition.x - rt.rect.width/2, // <--- WIDTH IS STRANGE HERE?!
            Input.mousePosition.y,
            goToolTipPanel.transform.position.z);    
    }	
  }

I expected the above code to show the panel entirely to the left of the mouse cursor (with the right edge of the panel at the mouse cursor’s position). For example:

33508-example.png

This isn’t the case. Instead, the panel is significantly to the left of the mouse cursor (such that there is a large gap between the right edge of the panel and the cursor). For example:

33509-example2.png

In the Unity IDE, the panel’s width is shown in the rect transform to be “198”. A print statement in the code also shows that rt.rect.width is “198”; however moving the panel’s position to the left by 198/2 does not do what I expected … it moves it too far. I seem to need to move it by around “60” to actually move the panel as expected.

Can someone please explain what I’m doing wrong? Why is moving the position to the left by half of the size of the rect transform NOT moving the panel half its size to the left in screen coordinates?

How can I determine the actual screen coordinate width of the panel and re-position it relative to the mouse cursor?

Multiply the width of the RectTransform by the Canvas.scaleFactor to get the correct width you can use for the position.

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(RectTransform))]
public class TestScript : MonoBehaviour
{
    public bool blToolTipOn;
    public Camera camera;
    public Canvas canvas;

    // Update is called once per frame
    void Update()
    {
        if (blToolTipOn)
        {

            RectTransform rt = GetComponent<RectTransform>();
            Vector3 input = Input.mousePosition; 
            input.x -= ((rt.rect.width * canvas.scaleFactor) / 2); // The important part!

            // In my case, I needed to do this aswell, you probable don't need this in your setup and can just set rt.position with the input instead
            Vector3 output;
            RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, input, camera, out output);
            rt.position = output;
        }
    }
}

EDIT: 30-NOV-2016

As per comments, this are two other ways to do it. I think better ways as they handle a lot of edge cases better. The resolution reference is now called canvas scaler, which is a much better name for it as it tells you what it does better. It scales the canvas :wink: And because it scales the canvas, the thing you really have to do is take note that RectTransform.rect it’s size is its local size, to get the world size, you’ve got to scale it properly. Which is what I do in the following code.

using UnityEngine;

public class Example : MonoBehaviour
{
	public bool blToolTipOn;
	public bool setpivot; // if true is sets the pivot, if false not
	public Canvas canvas;
	
	void Update()
	{
		if (blToolTipOn)
		{
			RectTransform rt = GetComponent<RectTransform>(); // The recttransform to drag
			RectTransform plane = canvas.GetComponent<RectTransform>(); // On which plane we drag the object
			Vector3 input = Input.mousePosition; // In screen space

			Camera cam;
			switch (canvas.renderMode)
			{
				case RenderMode.ScreenSpaceOverlay:
					cam = null;
					break;
				case RenderMode.ScreenSpaceCamera:
				case RenderMode.WorldSpace:
					cam = canvas.worldCamera;
					break;
				default:
					throw new System.NotImplementedException();
			}
			Vector3 output;

			if (RectTransformUtility.ScreenPointToWorldPointInRectangle(plane, input, cam, out output))
			{
				if (setpivot)
				{
					rt.pivot = new Vector3(1f, 0.5f); // set the pivot to the right center before setting the position

					// Example for left side
					//rt.pivot = new Vector3(0f, 0.5f);
				}
				else
				{
					// example for right side
					output += rt.TransformVector(-(rt.rect.width * (1f - rt.pivot.x)), 0, 0); // offset our position by the amount of pixels between the pivot and the right side

					// Example for left side
					//output += rt.TransformVector(rt.rect.width * rt.pivot.x, 0, 0);
				}

				rt.position = output;
			}

		}
	}
}