Search Unity

Unity UI Canvas Group not blocking raycasts even when option is enabled

Discussion in 'UGUI & TextMesh Pro' started by VileGoo, Sep 19, 2018.

  1. VileGoo

    VileGoo

    Joined:
    Apr 29, 2017
    Posts:
    220
    I created an empty game object and placed all my UI elements needed for my inventory inside of it and attached a Canvas Group component to that empty so I can easily hide/reveal it in script without disabling it. One of the options in the Canvas Group is 'Block Raycasts,' this gets enabled in my script when the UI is shown, but it won't block raycasts! I'm still able to click on objects through the UI and I can't figure out why. Help would be appreciated!

    Code (CSharp):
    1.         if (Input.GetKeyDown(KeyCode.Tab))
    2.         {
    3.             if (!inventoryOpen)
    4.             {
    5.                 inventoryOpen = true;
    6.                 inventoryUI.alpha = 1.0f;
    7.                 inventoryUI.blocksRaycasts = true;
    8.             }
    9.             else
    10.             {
    11.                 inventoryOpen = false;
    12.                 inventoryUI.alpha = 0.0f;
    13.                 inventoryUI.blocksRaycasts = false;
    14.             }
    15.         }

    Code (CSharp):
    1.        Ray ray = cam.ScreenPointToRay(Input.mousePosition);
    2.         if (Input.GetMouseButtonDown(0)) // Left click
    3.         {
    4.             if (Physics.Raycast(ray, out hit, interactDistance))
    5.             {
    6.                 Vector3 rayDirection = playerPos.position - hit.point;
    7.  
    8.                 InteractableObject interactableObject = hit.collider.GetComponent<InteractableObject>();
    9.  
    10.                 if (interactableObject != null) // If the player clicked on an object that has an interactableObject component on it, interact with it
    11.                 {
    12.                     InteractWithObject(interactableObject);
    13.                 }
    14.             }
    15.         }
     
    Ziplock9000 likes this.
  2. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,408
    i think it doesnt block the usual Physics.Raycasts,
    probably this would work
    https://docs.unity3d.com/ScriptReference/EventSystems.PhysicsRaycaster.html

    in 5.6 i was testing for ui elements with these, before doing raycast next
    Code (CSharp):
    1. if (eventSystem.IsPointerOverGameObject()) return;
    2. if (eventSystem.currentSelectedGameObject != null) return;
    *tested in 2020.2.2f1 still works
     
    Last edited: Feb 12, 2021
    LEduardZ likes this.
  3. VileGoo

    VileGoo

    Joined:
    Apr 29, 2017
    Posts:
    220
    Ah! I'll just add another condition to my interaction script to check if the inventory is not open, that should do the trick!
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Note that a CanvasGroup will never block raycasts on its own; you still need some component that acts as a raycast target. That flag can be used to make that object and all its children stop blocking raycasts when they would normally block them, but it doesn't cause them to be blocked if they normally wouldn't be.

    If you want to create a "shield" that blocks clicks from hitting things behind it, try using an Image with 100% transparency.
     
    Gibbonfiend likes this.
  5. masonhenry

    masonhenry

    Joined:
    Mar 2, 2019
    Posts:
    1
    Try putting a Graphic Raycaster on your canvas group.
     
  6. smiller3650

    smiller3650

    Joined:
    Oct 9, 2019
    Posts:
    3
    this worked
     
    MCLiving88 likes this.
  7. OrderOfOut

    OrderOfOut

    Joined:
    Sep 21, 2018
    Posts:
    37
    None of these solutions worked for me in 2018.4 — I had to hack my own method with a Boolean set and cleared on enables and disable of the canvas, and have the game objects directly underneath check for it before registering clicks.

    I don’t understand why this hasn’t been fixed yet — people should not have to bend over backwards to hack solutions like these together to stop canvas clicks from going through to physics objects behind them — it’s a no-brainer that something like that should never happen in a professional development environment. Please update the canvas system, as it’s a hot mess overall.

    I don’t mean to be rude, but our company is a pro paying customer and the number of issues we’ve had with trying to reconcile the Canvas system and the game objects has gotten out of control.
     
    Last edited: Apr 13, 2020
  8. DanVioletSagmiller

    DanVioletSagmiller

    Joined:
    Aug 26, 2010
    Posts:
    204
    I have not been able to get any of these solutions working in 2019.4. It seems as though this functionality has changed (or broken) Now, what I have to do is run two separate ray casts. A raycast from the canvas's GraphicsRaycaster to detect UI and then a separate raycast from Physics for the 3D world. It feels excessive, like north of 20 lines of code in a shortcut to get this separation to work in the code, when it used to be 1 line of code.
     
    TorchFireGames likes this.
  9. TITAN_MIK

    TITAN_MIK

    Joined:
    Oct 16, 2016
    Posts:
    2
    None of the above solutions worked for me in 2019.4.0f1 unfortunately. I added a dummy button component and set it to non interactive. That worked for me.
     
    jessicastudiofreiluft likes this.
  10. DomenickD3

    DomenickD3

    Joined:
    Jan 9, 2018
    Posts:
    3
    Can also confirm that none of the above methods worked in 2019.4.0.f1

    I had a game object with the following components:
    Canvas
    Canvas Group
    GraphicsRayCaster

    I couldn't get the above configuration of components to work with any variation of settings. This feels like a bug.

    Adding a transparent Image as with "Raycast Target" set to True worked for me.

    fyi, I am using PointerEventData argument in OnEndDrag; e.g. eventData.pointerCurrentRaycast;
     
  11. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    I found a bug in this (2019.4 LTS - the current live version of Unity):

    1. GraphicsRaycaster ignores all meshes, it only uses the Physics Colliders on objects (which is wrong, by definition - but it's unlikely Unity will ever fix this particular bug)
    2. In addition: GraphicsRaycaster is evaluated at the end of the frame, and if you disable or remove your Collider during the frame, while processing e.g. Unity's OnMouseDown event ... Unity will incorrectly re-run the raycast using the mid-frame-updated data, and fail to block the raycasts.

    I very much doubt Unity will fix any of this, but I'll try and make a small repro and submit a bug for it.
     
  12. TimmyTheTerrible

    TimmyTheTerrible

    Joined:
    Feb 18, 2017
    Posts:
    186
    You could put a check in your ray cast function to make sure no game object is highlighted by the event system. I think its stored in a static variable in EventSystem.SelectedGameObject.
     
  13. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    Any updates on this?

    Mobile is a disaster trying to block UI raycasts through UI elements. It only works for touch down.. not up or dragging.
     
    GreenCubeGames likes this.
  14. fwalker

    fwalker

    Joined:
    Feb 5, 2013
    Posts:
    255
    Having the same problem on 2019.4.1f :( Not fun !
     
    TorchFireGames and Ne0mega like this.
  15. mwlazlo885

    mwlazlo885

    Joined:
    May 17, 2018
    Posts:
    7
    2020.1.8f - same story
     
  16. TorchFireGames

    TorchFireGames

    Joined:
    Mar 9, 2017
    Posts:
    7
    Still running into this same issue with 2019.4.18. Can we get an update from Unity on a bug fix for this or at the very least a workaround? I've been trying to get this simple drag / drop working for days, it really shouldn't be this difficult.
     
  17. Brother_77

    Brother_77

    Joined:
    Feb 8, 2019
    Posts:
    233
    Unity's documentation https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/class-CanvasGroup.html
    states that
    • Making one or more UI elements not block mouse events by placing a Canvas Group component on the element or one of its parents and setting its Block Raycasts property to false

    My setup is an Image gameobject as parent with a button as child using Event Trigger system with OnPointerDown and the behaviour is the opposite:

    If Block Raycasts is ticked(true?) then it allows the raycasting and if unticked then I can't click on the button.
     
    Last edited: Feb 1, 2021
    Maphos0024 and Ziplock9000 like this.
  18. Hypertectonic

    Hypertectonic

    Joined:
    Dec 16, 2016
    Posts:
    75
    After a lot of frustation I have come to the conclusion that for a raycast to be properly blocked by a screenspace canvas and not hit the gameobjects in the scene, you need:

    1) A UI element that can block raycasts, like an image or a textmesh, set to block.

    For screenspace UI, it doesn't matter if your blocking objects are in the UI layer and that layer is excluded by the raycast layermask (presumably, because the screenspace is not the worldspace where physics happens), so what you also need is:

    2) Check
    EventSystem.current.IsPointerOverGameObject()
    in the code that throws the raycast. This is part of the UnityEngine.EventSystems namespace.

    Because Unity loves to confuse us, note that the horribly named IsPointerOverGameObject means that the pointer is over a UI element object and not just any GameObject, a behavior which is not documented anywhere I looked and had to deduce through experiment.

    So this should work:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class MouseClicker : MonoBehaviour
    5. {
    6.     [SerializeField] float maxRayDistance = 10;
    7.     [SerializeField] LayerMask collidableLayers;
    8.  
    9.     void Update()
    10.     {
    11.         if (Input.GetMouseButtonDown(0)) // 0 is left click
    12.         {
    13.             Ray ray = screenCamera.ScreenPointToRay(Input.mousePosition);
    14.             if (Physics.Raycast(ray, out RaycastHit hit, maxRayDistance, collidableLayers) && !EventSystem.current.IsPointerOverGameObject())
    15.             {
    16.                 // code here only executes if the raycast hit an object in a valid layer and is NOT over a UI object
    17.             }
    18.         }
    19.     }
    20. }
    It's a pain that Unity doesn't have some clear explanation of the chaos that is its GUI system, it drives me crazy.

    I'm thinking of creating some methods that wrap Unity's Raycast to have this check done so I don't have to remember this all the time...
     
    Last edited: Mar 3, 2021
  19. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    It's OK - the team has moved on now, they've created a whole new UI system with a fresh set of major bugs for us to enjoy! :)

    (but being serious: there are still at least a few Unity staff fixing bugs in the current UI, even if not many - I've logged bugs in the past 3-4 months that have been accepted and fixed in latest Unity. And it appears to be a different team that's making UIToolkit)
     
    DanVioletSagmiller likes this.
  20. dansopanso

    dansopanso

    Joined:
    Jun 14, 2018
    Posts:
    11

    Thank you so much! I ran into this problem in 2020.3.4 and I only had to add a Graphics Raycaster to my UI Image and then add the code you provided.

    I wonder what's the use for "Blocking Objects" and Blocking Masks, if changing these options does not actually block anything o_O
     
    Hypertectonic likes this.
  21. jsmilton05

    jsmilton05

    Joined:
    Jan 26, 2017
    Posts:
    2
    Struggled for hours on this. Thank you so much.
     
  22. MorrisCode

    MorrisCode

    Joined:
    Nov 28, 2021
    Posts:
    1
    I found a fairly elegant (at least I think so) solution to this.

    I set a bool value named "HUD_showing", which I set to true when I pop up my inventory screen, and set to false when I close it. Then, on my interactable objects, I check if HUD_showing is true before handling the clicks.

    Code (CSharp):
    1. public class HUD : MonoBehaviour
    2. {
    3.     protected Vector3 hidden = new Vector3(0f, 0f, 0f);
    4.     protected Vector3 shown = new Vector3(1f, 1f, 1f);
    5.     public static bool HUD_showing;
    6.  
    7.     void Start()
    8.     {
    9.         // Hide the HUD at first
    10.         hideMe();
    11.     }
    12.  
    13.     void Update()
    14.     {
    15.         if(Input.GetKeyDown(KeyCode.Escape))
    16.         {
    17.             hideMe();
    18.         }
    19.     }
    20.  
    21.     protected virtual void hideMe()
    22.     {
    23.         transform.localScale = hidden;
    24.         inMenu(false);
    25.     }
    26.  
    27.     protected virtual void showMe()
    28.     {
    29.         transform.localScale = shown;
    30.         inMenu(true);
    31.     }
    32.  
    33.     protected virtual void inMenu(bool value)
    34.     {
    35.         Cursor.visible = value;
    36.         FPController.inMenu = value;
    37.         HUD_showing = value;
    38.     }
    39. }
    40.  
    41.  
    Code (CSharp):
    1. public class Interactable : MonoBehaviour
    2. {
    3.     public virtual void OnLeftClick()
    4.     {
    5.         if (!HUD.HUD_showing)
    6.         {
    7.             Debug.Log("You left-clicked on " + this.tag);
    8.         }
    9.     }
    10.  
    11.     public virtual void OnRightClick()
    12.     {
    13.         if (!HUD.HUD_showing)
    14.         {
    15.             Debug.Log("You right-clicked on " + this.tag);
    16.         }
    17.     }
    18. }
     
  23. IsouEU

    IsouEU

    Joined:
    Aug 4, 2018
    Posts:
    12
    not even this worked for me, what pisses me off about unity is the wast time trying to solve a problem that shouldn't exist... who the hell wants to click on an object through a UI element ??????
     
  24. IsouEU

    IsouEU

    Joined:
    Aug 4, 2018
    Posts:
    12
    And what about if you waving a UI element triged by the character position, and it destroy if you move far, how to ? you may only accept that unity is not able to make professional games ?
     
  25. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    A few weeks ago I completely rewrote most of UnityEngine.EventSystems.

    Suddenly - many long-term bugs have vanished! Unity input works correctly! Edge-cases have gone! FPS setups work out-of-the-box! etc.

    TL;DR: Unity's EventSystems code, and the New Input System, were never properly designed or planned, they were just hacked together with lots of incompatible code that creates bugs because it wasn't intended to work together. The team(s) responsible decided not to clean or fix it, just left it in a half-working state and moved on. It only takes a few days to replace most/all of the Pointer stuff - you can see the source code from Unity, use that as a base and then just delete big chunks and write it properly. I now have faster running, more extensible, easier to work with, less buggy, pointers + input, and I'm now using it on all my projects. I highly recommend doing this.
     
    mgear likes this.
  26. Bmco

    Bmco

    Joined:
    Mar 11, 2020
    Posts:
    54
    I seem to come back to this problem and this thread every year or so. My current solution:

    1. In your game manager, have a static bool called ignorePhysicsRaycast
    2. Check this bool in any script that does raycasting
    3. Put an OnPointerEnter/Exit script in your canvas which toggles the bool when the cursor enters/exits.

    This seems annoying, but pretty clean. I forgive Unity for now...for PlasticSCM and it's current state, that's a different story.
     
    grentle likes this.
  27. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Unity has a bad habit of implementing their Enter/Exit logic so that you can get a "Exit" from one script after you get the "Enter" from another.

    (I have logged bugs against this. Most recently with UIToolkit where the Unity team wrote back that it would be too much effort to fix it, and they prefer to leave it broken. TL;DR: do not trust OnPointerEnter/Exit)

    You really need to do as per my post above: re-write EventSystems to "not be broken-by-design".

    EDIT: also ... having read through the source code ... it's obviously not fit for purpose on this stuff, it's bound to go wrong sooner or later. How it handles this state stuff is terrible.
     
  28. Bmco

    Bmco

    Joined:
    Mar 11, 2020
    Posts:
    54
    As with most things in Unity, there's 3 ways to do it, and no matter which path you take, it bites you later! Maybe I'll be back in a year to yell at my past self.
     
  29. laserbean00001

    laserbean00001

    Joined:
    Aug 23, 2020
    Posts:
    4
    I found out that if you're trying to block a raycast with anything on the gui, you need to use the Camera Render mode and not Screen Overlay. Also it seems that i need a box collider to actually block raycasts.
     
    AntoineProvost likes this.
  30. mprahladka

    mprahladka

    Joined:
    Jan 1, 2022
    Posts:
    4
    Am bogged down by this still in 2021.3.5f1

    I have a 4 player project where I am adding a bunch of cards to empty gameobject and depending on whose turn it is other three game objects will not be clickable / draggable.

    1> Canvasgroup plain doesn't work. I tried setting it up on parent, on each child, with and without graphic raycaster but nothing seems to stop the click through.
    2> Tried putting an larger transparent image with block raycasts and setting is as the last child in the Parent object, clicks still go through that and hit the cards underneath.
    Code (CSharp):
    1. GameObject.Find("PlayerArea1").transform.Find("Image").SetAsLastSibling();
    3> Finally went the <Selectable> way, using the code like this :
    Code (CSharp):
    1.    void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
    2.     {
    3.         var TG = GameObject.FindObjectOfType(typeof(GameSM)) as GameSM;
    4.         var DD = GameObject.FindObjectOfType(typeof(DragDrop)) as DragDrop;
    5.         switch (TG.GetCurrentState().name)
    6.         {
    7.             case "Player1 Turn":
    8.                 if (eventData.pointerCurrentRaycast.gameObject.transform.parent.name == "PlayerArea1") { DD.isDraggable = true; return; }
    9.                 else { DD.isDraggable = false; return; }
    10.             case "Player2 Turn":
    11.                 if (eventData.pointerCurrentRaycast.gameObject.transform.parent.name == "PlayerArea2") { DD.isDraggable = true; return; }
    12.                 else { DD.isDraggable = false; return; }
    13.             case "Player3 Turn":
    14.                 if (eventData.pointerCurrentRaycast.gameObject.transform.parent.name == "PlayerArea3") { DD.isDraggable = true; return; }
    15.                 else { DD.isDraggable = false; return; }
    16.             case "Player4 Turn":
    17.                 if (eventData.pointerCurrentRaycast.gameObject.transform.parent.name == "PlayerArea4") { DD.isDraggable = true; return; }
    18.                 else { DD.isDraggable = false; return; }
    19.             case "DealCards": return;
    20.         }
    21.     }
    22.  
    I can step through the code and see that the function is returning from BeginDrag() handler when isDraggable is false but it still allows me to drag and drop everything. Is there any update to this issue on how to get this working? possible workaround? I am working in Screen Space -> Camera (has a Physics Raycaster attached to it) and not in overlay.
     
  31. LEduardZ

    LEduardZ

    Joined:
    Jun 29, 2016
    Posts:
    2
    Hello friends! Hope this may help:

    Code (CSharp):
    1. using UnityEngine.EventSystems;
    2. //...
    3. if (Physics.Raycast(ray, out hitInfo, 10, yourRaycastLayer) //raycasting
    4. {
    5.     if (EventSystem.current.IsPointerOverGameObject())
    6.         //"disable" raycast - pointer over UI. Not performing after-raycast Physics functions
    7.     else
    8.         //raycast physics - enabled post-ray cast functions
    9. }
    Constantly keep checking this while raycast hits in order to switch between UI and Physics behaviors :)
     
  32. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    No. Read the thread. Do what was explained 1.5 years ago. Don't invent your own broken code that - as explained at length above - is incorrect/insufficient. Especially if you don't know what you're talking about: don't post random stuff you copy/pasted from somewhere else.
     
  33. fefox74

    fefox74

    Joined:
    Nov 5, 2021
    Posts:
    2
    i love you, that worked
     
  34. p3k07

    p3k07

    Joined:
    Mar 30, 2014
    Posts:
    10
    Block raycasts does not equal block input. The exact opposite in fact. Go Unity devs...........
    Just to make the labelling less confusing:
    Code (CSharp):
    1. using UnityEditor;
    2.  
    3. using UnityEngine;
    4.  
    5. [CustomEditor(typeof(CanvasGroup))]
    6. public class CanvasGroupEditor : Editor
    7. {
    8.     public override void OnInspectorGUI()
    9.     {
    10.         CanvasGroup canvasGroup = (CanvasGroup) target;
    11.  
    12.         float alpha = EditorGUILayout.Slider("Alpha", canvasGroup.alpha, 0, 1);
    13.         if (alpha != canvasGroup.alpha)
    14.         {
    15.             Undo.RecordObject(canvasGroup, "Toggle alpha");
    16.             canvasGroup.alpha = alpha;
    17.         }
    18.  
    19.         bool interactable = EditorGUILayout.Toggle("Interactable", canvasGroup.interactable);
    20.         if (interactable != canvasGroup.interactable)
    21.         {
    22.             Undo.RecordObject(canvasGroup, "Toggle interactable");
    23.             canvasGroup.blocksRaycasts = interactable;
    24.         }
    25.  
    26.         bool blockRaycasts = EditorGUILayout.Toggle("Recieve Event Input", canvasGroup.blocksRaycasts);
    27.         if (blockRaycasts != canvasGroup.blocksRaycasts)
    28.         {
    29.             Undo.RecordObject(canvasGroup, "Toggle Recieve Event Input");
    30.             canvasGroup.blocksRaycasts = blockRaycasts;
    31.         }
    32.  
    33.         bool ignoreParent = EditorGUILayout.Toggle("Ignore Parent Groups", canvasGroup.ignoreParentGroups);
    34.         if (ignoreParent != canvasGroup.ignoreParentGroups)
    35.         {
    36.             Undo.RecordObject(canvasGroup, "Toggle Ignore Parent Groups");
    37.             canvasGroup.ignoreParentGroups = ignoreParent;
    38.         }
    39.     }
    40. }