How do you get the correct Screenspace position of an element when in Scaling with Screensize?

I have a visual Element that I’m trying to get the correct screen space position for. I’ve tried several “positions” and none are correct unless the Scale Mode in the UI Panel Settings is Constant Pixel Size.

I have done a test where I am doing a ray to the mouse position which is 100% accurate. (See code and screenshot).

The values I’m seeing are essentially this:

  • Mouse: (832.00, 448.37, 0.00)
  • WorldBound: (961.53, 486.47)
  • Position: (911.53, 486.47)
  • Layout: (959.70, 540.06)

Aspect Ratio is 16:9 and panel properties are:

Code is as follows:

var mousePosition = Input.mousePosition;

//Mouse
var rayPosition = CameraController.Instance.MainCamera.ScreenPointToRay(mousePosition);
Debug.DrawRay(rayPosition.origin, rayPosition.direction * 100f, Color.blue, 5f);

//WorldBound
rayPosition = CameraController.Instance.MainCamera.ScreenPointToRay(_crosshairElement.worldBound.center);
Debug.DrawRay(rayPosition.origin, rayPosition.direction * 100f, Color.red, 5f);

//Position
rayPosition = CameraController.Instance.MainCamera.ScreenPointToRay(_crosshairElement.transform.position);
Debug.DrawRay(rayPosition.origin, rayPosition.direction * 100f, Color.green, 5f);

//layout
rayPosition = CameraController.Instance.MainCamera.ScreenPointToRay(_crosshairElement.layout.center);
Debug.DrawRay(rayPosition.origin, rayPosition.direction * 100f, Color.yellow, 5f);

Hello Yecats!

We noticed this was a limitation and added the missing API to achieve what you’re aiming for. The new API is Panel.scaledPixelsPerPoint and is only available in Unity 6. Here’s a usage example:

using UnityEngine;
using UnityEngine.UIElements;

[RequireComponent(typeof(UIDocument))]
public class UISpriteSync : MonoBehaviour
{
    UIDocument uiDocument;
    PanelSettings panelSettings;
    IPanel uiPanel;
    VisualElement targetVE;
    public GameObject targetObject;
    public float factor;

    private void OnEnable()
    {
        uiDocument = GetComponent<UIDocument>();
        uiPanel = uiDocument.rootVisualElement.panel;
        panelSettings = uiDocument.panelSettings;

        targetVE = uiDocument.rootVisualElement.Q<VisualElement>("unity-dragger");
        targetVE.RegisterCallback<GeometryChangedEvent>(GeometryChange);
    }

    void GeometryChange(GeometryChangedEvent evt)
    {
        if (targetVE == null)
        {
            Debug.Log("targetVE is null");
            return;
        }

        if (targetObject == null)
        {
            Debug.Log("targetObject is null");
            return;
        }

        targetObject.transform.position = targetVE.LocalToWorld(targetVE.localBound).position * factor;
    }

    private void LateUpdate()
    {
        if (targetVE == null)
        {
            Debug.Log("targetVE is null");
            return;
        }

        if (targetObject == null)
        {
            Debug.Log("targetObject is null");
            return;
        }

        var localBounds = targetVE.contentRect.center;
        var worldBounds = targetVE.LocalToWorld(localBounds);
        var position = worldBounds;
        var ppp = uiPanel.scaledPixelsPerPoint;
        var factored = position * factor * ppp;

        factored.y = Screen.height - factored.y;

        factored = Camera.main.ScreenToWorldPoint(new Vector3(factored.x, factored.y, 10));

        targetObject.transform.position = factored;

    }
}

Let us know if that solves your issue.

Cheers,