World Space to Screen Space UI

It’s not actually that complicated but this is a simple technique for achieving the effect of 2D UI tracking world space positions. If you’re new to Unity then this tutorial may be helpful to you.

The Unity3D UI offers great flexibility, allowing easy setup of multiple canvases in screen space and world space. World space UI works exceptionally well for things like interactive computer screens, but may not always be exactly what you want due to scaling/orientation. In the above case, I needed a way to clearly mark all the planets in the system, so I render them all in the main screen space UI and use the setup as I will describe below.

Step 1) The parent rect transform should extend to all edges of the screen, otherwise this will not work.

Step 2) The UI object should be configured as such:

The important details to note are the anchor is in the lower left corner and the pivot is set to 0, 0. This ensures that the origin of the UI element will always track to the exact point. If you want it off-centered then you can modify the children of the main tracking UI element.

3) Now in your update function you’ll want to find the world position of your target and then set the UI RectTransform’s anchored position. Here’s the example from my code (where I’m iterating over a list of available planets).

Vector3 viewPos = Camera.main.WorldToViewportPoint(planets[i].transform.position);
planetLabels[i].anchoredPosition = new Vector2(viewPos.x * canvasSize.x, viewPos.y * canvasSize.y);

planetLabels[i].gameObject.SetActive(viewPos.z > 0);

The viewPos will always be a normalized value between 0 and 1. So when you apply that to the anchored position of the UI you’ll need to multiply it by the canvas’s size. The size won’t always match the screen’s dimensions, so to find the canvas size you should use Canvas.pixelRect.width and Canvas.pixelRect.height – referencing the Canvas object in the UI element’s upper hierarchy.

It’s also very important to note that if the viewPos.z is less than 0 it means that the object is behind the camera, so unless you take this into account for your handling (in my case I disable the game object), the UI will show up in a mirrored position if it is behind the camera.

That’s all there is to it. In the gif above I also modify the alpha of the parent CanvasGroup to fade in and out the elements. As always, let me know if you have any questions @GhostTimeGames on twitter.