WorldToScreenPoint in 3D appearing behind camera?

https://www.youtube.com/watch?v=8_qynfdoTAw

Hi, so I have the following wip script.

using UnityEngine;

public class EyeIcon : MonoBehaviour
{

    public Transform target;
    public RectTransform pointerRectTransform;

    private void Update()
    {
        bool visible = target.GetComponent<Renderer>().isVisible;
        Vector3 pos = Camera.main.WorldToScreenPoint(target.position);
        if (visible)
        {
            pointerRectTransform.position = new Vector3(pos.x, pos.y, 0);
        }
        else
        {
            pointerRectTransform.position = new Vector3(Mathf.Clamp(pos.x, 0,Screen.width - 100), Mathf.Clamp(pos.y, 0, Screen.height - 100), 0);
        }
    }
}

I want to move an icon over a target when it's visible and when it's not visible, move it to the nearest edge of the screen. However because I'm in 3D the worldtoscreenpoint seems to render the icon in the center of the screen when the target is behind the camera as the positional value is flipping back over to the start.

Any thoughts on how I should go about handling this situation? I know how to determine if the object is behind the camera, but not exactly what to do with the UI at that point to prevent it from rendering in the wrong location.

Thanks!

It all depends on what's the behavior you want when the target is behind the camera. A couple of ideas:

  • Make the arrow to simply disappear if the target is outside the camera view
  • Find the nearest edge on the screen and make the arrow point there

Here is what I came up with. It does feel a little janky still so would love some feedback, as it's definitely not going to the "Closest" edge now.

using UnityEngine;

public class EyeIcon : MonoBehaviour
{

    public Transform target;
    public RectTransform pointerRectTransform;
    public float border = 200;

    private void Update()
    {
        Vector3 pos = Camera.main.WorldToScreenPoint(target.position);
        Vector3 dir = target.transform.position - Camera.main.transform.position;
        float dot = Vector3.Dot(dir, Camera.main.transform.forward);
        bool visible = (pos.x > 0 && pos.x <= Screen.width) && (pos.y > 0 && pos.y <= Screen.height) && dot > 0;
        if (visible)
        {
            pointerRectTransform.position = new Vector3(pos.x, pos.y, 0);
        }
        else
        {
            if (dot < 0)
            {
                pos.x = -pos.x;
                pos.y = -pos.y;
            }
            pointerRectTransform.position = new Vector3(Mathf.Clamp(pos.x, border, Screen.width - border), Mathf.Clamp(pos.y, border, Screen.height - border), 0);
        }
    }
}

Bump

Probably helps to ask in the correct subforum: https://forum.unity.com/forums/ugui-textmesh-pro.60/

This is the UI Toolkit subforum.