Home > Software design >  Unity: screen space vs window space vs monitor space?
Unity: screen space vs window space vs monitor space?

Time:09-22

I am trying to take a Ui object's screen space position and translate that to what I am calling 'monitor space'.

As far as I can tell, screen space, in Unity, is relative to the applications' window. That is, even if the app is not full screen, and moved around on your monitor, 0,0 will still be the lower left of the app window.

I need to translate one of those screen space values into the actual position within the user's monitor. This is especially important when considering that the user might have multiple monitors.

I am not finding anything to get this done, though.

I am hoping to find a platform agnostic solution, but if it must be Windows-only than I can make that work as well.

Any help on this would be greatly appreciated.

Thank you

CodePudding user response:

Alright first off - sorry for the late response just got back and was able to type up an answer.

From what I have found, this solution does not work in the editor and produces odd results on Mac with retina display. In the editor, the Screen and Display spaces appear to be exactly the same. There is probably a solution to fix this but I did not look into the specifics. As for Mac, for whatever reason, the internal resolution outputted is always half the actual resolution. I am not sure if this is just a retina display bug with Unity or a general Mac bug. I tested and ran this test script on both a Windows computer and Mac with a retina display. I have yet to test it on any mobile platform.

I do not know exactly what you would like to achieve with the values you wish to find, so I set up a demo scene displays the values instead of using them.

Here is the demo script:

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;

public class TestScript : MonoBehaviour
{
    [SerializeField] private RectTransform rect = null;
    [SerializeField] private List<Text> text = new List<Text>();
    [SerializeField] private Canvas parentCanvas = null;
    [SerializeField] private Camera mainCam = null;

    private void Start()
    {
        // determine the canvas mode of our UI object
        if (parentCanvas == null)
            parentCanvas = GetComponentInParent<Canvas>();

        // only need a camera in the case of camera space canvas
        if (parentCanvas.renderMode == RenderMode.ScreenSpaceCamera && mainCam == null)
            mainCam = Camera.main;

        // generate initial data points
        GenerateData();
    }

    /// <summary>
    /// Onclick of our button to test generating data when the object moves
    /// </summary>
    public void GenerateData()
    {
        // the anchored position is relative to screen space if the canvas is an overlay - if not, it will need to be converted to screen space based on our camera
        Vector3 screenPos = parentCanvas.renderMode == RenderMode.ScreenSpaceCamera ? mainCam.WorldToScreenPoint(transform.position) : rect.transform.position;

        // our object relative to screen position
        text[0].text = "Screen Pos: "   screenPos;

        // the dimensions of our screen (The current window that is rendering our game)
        text[1].text = "Screen dimensions: "   Screen.width   " "   Screen.height;

        // find our width / height normalized relative to the screen space dimensions
        float x = Mathf.Clamp01(screenPos.x / Screen.width);
        float y = Mathf.Clamp01(screenPos.y / Screen.height);

        // our normalized screen positions
        text[2].text = "Normalized Screen Pos: "   x   " "   y;

        // grab the dimensions of the main renderer - the current monitor our game is rendered on
#if UNITY_STANDALONE_OSX
        text[3].text = "Display dimensions: "   (Display.main.systemWidth * 2f)   " "   (Display.main.systemHeight * 2f);

        // now find the coordinates our the UI object transcribed from screen space normalized coordinates to our monitor / resolution coordinates
        text[4].text = "Display relative pos: "   (Display.main.systemWidth * x * 2f)   " "   (Display.main.systemHeight * y * 2f);
#else
        text[3].text = "Display dimensions: "   Display.main.systemWidth   " "   Display.main.systemHeight;

        // now find the coordinates our the UI object transcribed from screen space normalized coordinates to our monitor / resolution coordinates
        text[4].text = "Display relative pos: "   (Display.main.systemWidth * x)   " "   (Display.main.systemHeight * y);
#endif
    }

    /// <summary>
    /// Just for debugging - can be deleted
    /// </summary>
    private void Update()
    {
        if (Input.GetKey(KeyCode.A))
        {
            rect.anchoredPosition  = new Vector2(-10f, 0f);
        }

        if (Input.GetKey(KeyCode.W))
        {
            rect.anchoredPosition  = new Vector2(0f, 10f);
        }

        if (Input.GetKey(KeyCode.S))
        {
            rect.anchoredPosition  = new Vector2(0f, -10f);
        }

        if (Input.GetKey(KeyCode.D))
        {
            rect.anchoredPosition  = new Vector2(10f, 0f);
        }
    }
}

I accounted for the parent canvas being either Overlay or Camera mode and put in a check for an OSX build to adjust to the proper screen dimensions.

Here is a gif of the build on OSX. I set the window to be 1680x1050 and my computer's current resolution is 2880x1800. I had also test it on Windows but did not record it as the example looks nearly identical. Example

Let me know if you have more questions about the implementation or if there are issues with other platforms I did not test.

Edit: Just realized you want the screen space coordinate relative to the monitor space. I will correct the snippet in a little bit - in a meeting right now.

Edit2: After a bit more looking, it will not be easy to get the exact coordinates without the window being centered or getting the standalone window's position. I do not believe there is an easy way to get this information without a dll, so here is a enter image description here

Note: while Unity Screenspace is 0,0 at the bottom left in normal display pixels 0,0 is actually rather top-left. So you might need to invert these.

  • Related