How to raycast onto a unity canvas ui image?

Is it possible to raycast onto a canvas Image component? My Image components have “Raycast Target” checked on.

I’m NOT raycasting from a mouse position, but rather based on a different game objects position (raycasting from a different rectTransform that lives within the same canvas).

My current canvas is in Render Mode ‘Screen Space - Camera’, but I could also do a world space canvas if needed.

FWIW -I want to avoid adding box colliders to each canvas element. Then there’s the whole z-depth issue for raycasting w/ multiple overlapping box colliders which becomes messy very quickly.

Yes, it is possible with EventSystem. current.RaycastAll(myCustomPointerData, resultList);.
See here.

Edit: you can also use a GraphicsRaycaster directly.

4 Likes

Thanks. Ended up doing the following to get it working:

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

public class MyVirtualCursor : MonoBehaviour
{

    [SerializeField]  GraphicRaycaster m_Raycaster;
    PointerEventData m_PointerEventData;
    [SerializeField] EventSystem m_EventSystem;
    [SerializeField] RectTransform canvasRect;

   
    void Update()
    {
       
            //Set up the new Pointer Event
            m_PointerEventData = new PointerEventData(m_EventSystem);
            //Set the Pointer Event Position to that of the game object
            m_PointerEventData.position = this.transform.localPosition;

            //Create a list of Raycast Results
            List<RaycastResult> results = new List<RaycastResult>();

            //Raycast using the Graphics Raycaster and mouse click position
            m_Raycaster.Raycast(m_PointerEventData, results);

            if(results.Count > 0) Debug.Log("Hit " + results[0].gameObject.name);

    }

}
17 Likes

Really works for u? i have all right but in console doesn’t show anything :(, i have been 3 days with this problem ;_;

Why can’t I do the same thing?

Try to replace m_PointerEventData.position = this.transform.localPosition; with m_PointerEventData.position = Input.mousePosition;, then it will try to get what you hit “at the mouse position”.
The original code tried to get hits where the attached transform is, which means, whatever it was “on top of”, I believe that’s why you guys were unable to get any results.

4 Likes

You’re a hero. I have been stuck trying to use the GraphicsRaycaster for over 24 hours now and that simple comment is all it took to get it working. You, my friend, are better than the NON-EXISTANT documentation on the eventsystem library.

5 Likes

i wanna navigate player if user touches the screen then raycasting from pointer and navigation stuffs. by i have an essue that then he touches buttons like pause button he moves around .

Raycast method has layermask parameter where you could specify which layers it should ignore. You can tell it to avoid hitting UI objects with that.

Same idea but simpler:

public static class RaycastUtilities
{
   public static bool PointerIsOverUI(Vector2 screenPos)
   {
      var hitObject = UIRaycast(ScreenPosToPointerData(screenPos));
      return hitObject != null && hitObject.layer == LayerMask.NameToLayer("UI");
   }

   static GameObject UIRaycast (PointerEventData pointerData)
   {
      var results = new List<RaycastResult>();
      EventSystem.current.RaycastAll(pointerData, results);

      return results.Count < 1 ? null : results[0].gameObject;
   }

   static PointerEventData ScreenPosToPointerData (Vector2 screenPos)
      => new(EventSystem.current){position = screenPos};
}
9 Likes

It’s working alright, modified it to work for me, however, I noticed that EventSystem.current.RaycastAll is quite slow and causes pretty big allocations when used many times in a frame.

2 Likes

Thanks a lot, it worked like a charm :smile:
I used this in an AR App and just had to pass a
“new Vector2(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y”
To the “PointerIsOverUI” to know if we have touched a UI element or not :smile:

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

// using this for mouse clicking on Disable UI to add popup for Mobile since there is no Mouse over functionality on touch screen
[RequireComponent(typeof(Button))]
public class UIAddToolTip : MonoBehaviour
{
      private Button button;
      public GameObject UI_MouseOverToolTip;

      private void Start()
     {
          button = This.GameObject.GetComponent<Button>();
     }

     private void Update()
     {
            if (button.IsInteractable() == false)
           {
                if (Input.GetButtonUp("Fire1"))
               {
                    bool isPointOVerThis = RaycastUtilities.PointerIsOverUI(Input.mousePosition, this.gameObject);

                    if (isPointOVerThis)
                    {
                        Debug.Log("Pointer is OVer disabled UI");
                        UI_MouseOverToolTip.SetActive(true);
                         //add code to populate the UI ToolTip with needed info
                     }
                }
           }
      }
}

//added a check to see if  this button was hit with raycast
public static class RaycastUtilities
{
    public static bool PointerIsOverUI(Vector2 screenPos, GameObject GO)
    {
        var hitObject = UIRaycast(ScreenPosToPointerData(screenPos));
        return hitObject != null && hitObject.layer == LayerMask.NameToLayer("UI") && hitObject == GO;
    }

    public static GameObject UIRaycast(PointerEventData pointerData)
    {
        var results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointerData, results);
        Debug.Log(results[0].gameObject.name + "Was raycast hit...");
        return results.Count < 1 ? null : results[0].gameObject;
    }

    static PointerEventData ScreenPosToPointerData(Vector2 screenPos)
       => new(EventSystem.current) { position = screenPos };
}

using this for mouse clicking on Disable UI to add popup for Mobile since there is no Mouse over functionality on touch screen

1 Like

If still doesn’t work, check your Canvas Group where it must enable interactable and blocks raycast
9490786--1335844--upload_2023-11-24_16-3-33.png

Think about why RaycastAll received the result in the argument.