- Home /
understand how "Raycast Target" works on UI elements
I am trying to under how "Raycast Target" option works. I think the it works is
if this is checked, then my UI element will consume the mouse/touch events and any 3D object behind it will not get the mouse/touch event.
If Unchecked, this will pass the mouse event to 3D object behind it.
but his is not how it is behaving.
I have a UI text on canvas and a 3D cube gameobject behind it.I have added following script on UI text
public class CaptureUIObjectEvent : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("In UI click event");
}
}
I have added following script on 3D object
public class Capture3DObjectEvent : MonoBehaviour
{
void OnMouseDown()
{
Debug.Log("In 3D click event");
}
}
When "Raycast Target is checked" : Both the UI text and 3D object is capturing the event. So both the logs are printed. The UI text is not blocking the click event. I think this is not the correct behavior. Also the 3D object event log is printed first. I think Unity has multiple default raycaster in scene for UI (graphics raycaster) and 3D (Physics raycaster) objects. Is it so? Is this behavior expected then and does this mean my 3D object will get the mouse click always?
When "Raycast Target is Unchecked" : Only 3D object is getting clicked. This is expected I think.
I have to make the CaptureUIObjectEvent derive from IPointerClickHandler to make it capture any event but for 3D object I dont have to use that. I thibk this is also because of different raycaster used by UI and 3D objects. Is it so?
Following is my scene
Answer by hexagonius · Jun 16, 2017 at 07:47 AM
The UI is solely reacting to the EventSystem. Put two UI elements on top of each other and the desired effect occurs.
OnMouseDown is extra and is basically called on all objects it occurs on. It doesn't make use of the extra layer of order the EventSystem uses.
You can however use both raycasters at once, Graphics and Physics, and use EventSystem events only. Just be aware the distance to the camera, which is what raycast results are sorted by.
Answer by jonc113 · Jan 02, 2019 at 07:39 AM
I have found that multiple raycasters can steal a click from each other. I had a raycaster in a completely different scene interfere with a button click. To solve this problem, I wrote this Editor utility - which can run while the game is playing - that finds ALL loaded scenes, then gets ALL raycasters in those scenes and checks them all (I am probably using more namespaces than necessary)
One caveat, I am using DontDestroyonLoad, which creates an "unnamed" scene. My EventSystem is in that group so I use it's "scene" to find the DontDestroy on Load items - you may need to alter that:
using UnityEditor ;
using UnityEngine ;
using UnityEngine.UI ;
using System.Collections.Generic ;
using System.Linq;
using System.IO ;
using UnityEngine.EventSystems ;
using UnityEngine.SceneManagement ;
public class AllHits : EditorWindow {
GraphicRaycaster callMeRay = null ;
PointerEventData pointerEventData ;
static EventSystem eventSystem ;
static string rayNames = "" ;
static List<GraphicRaycaster> callMeRays = new List<GraphicRaycaster>();
[MenuItem("Tools/Find Graphic Objects under Mouse in Game Scene")]
public static void ShowWindow() {
GetAllRayCasters() ;
EditorWindow.GetWindow(typeof (AllHits)); }
void OnGUI() {
GUILayout.Label("RayCasters found: "+rayNames.Substring(0, rayNames.Length-2)) ;
this.Repaint();
return ; }
public static void GetAllRayCasters() {
eventSystem = GameObject.Find("EventSystem").GetComponent<EventSystem>() ;
// get all scenes, then find all GraphicRaycasters in those scenes:
// Objects in DontDestroyonLoad are not in a "dummy" scene, which - in my case - includes the EventSystem, if this isn't your case, just use any object that is part of DontDestroyonLoad
for (int iScene = -1 ; iScene < SceneManager.sceneCount ; ++iScene) {
Scene thisScene ;
if (iScene < 0) thisScene = eventSystem.gameObject.scene ; // MUST USE DontDestroy GO
else thisScene = SceneManager.GetSceneAt(iScene);
Debug.Log("getting raycasters in scene: "+thisScene.name) ;
List<GameObject> allGOs = new List<GameObject>();
GameObject[] rootGOs = thisScene.GetRootGameObjects();
for (int i = 0; i < rootGOs.Length; i++) GetAllChildren(rootGOs[i].transform, allGOs);
// Find the raycasters:
foreach (GameObject GO in allGOs) {
GraphicRaycaster callMeRay = GO.GetComponent<GraphicRaycaster>();
if (callMeRay != null) {
rayNames += callMeRay.gameObject.name+", " ;
callMeRays.Add(callMeRay) ; } } } }
public static void GetAllChildren(Transform current, List<GameObject> arrayToFill) {
arrayToFill.Add(current.gameObject);
for (int i = 0; i < current.childCount; i++)
GetAllChildren(current.GetChild(i), arrayToFill); }
void Update() {
//Check if the left Mouse button is clicked
if (Input.GetMouseButtonDown(0)) {
//Set up the new Pointer Event
pointerEventData = new PointerEventData(eventSystem);
//Set the Pointer Event Position to that of the mouse position
pointerEventData.position = Input.mousePosition;
for (int i = 0; i < callMeRays.Count; i++) {
//Create a list of Raycast Results
List<RaycastResult> results = new List<RaycastResult>();
GraphicRaycaster callMeRay = callMeRays[i] ;
string rayName = callMeRay.gameObject.name ;
//Raycast using the Graphics Raycaster and mouse click position
callMeRay.Raycast(pointerEventData, results);
// Debug.Log(results.Count+" results found for "+rayName) ;
//For every result returned, output the name of the GameObject on the Canvas hit by the Ray
foreach (RaycastResult result in results) Debug.Log(rayName+" Hit " + result.gameObject.name); } } } }
Hope this is of help to someone...
Your answer
Follow this Question
Related Questions
IsPointOverGameObject() not working as intended!!! 2 Answers
Unity UI - Select with Raycast (Screenspace - Camera) 0 Answers
Holding down UI Button and shooting raycast from touch position doesn't work simultaneously:(( 0 Answers
Unity 4.6 UI - Ignore raycast -1 Answers
How do you perform a Graphic Raycast? 3 Answers