Search Unity

Question Blocking onPointerEnter for GameObjects behind current one

Discussion in 'UGUI & TextMesh Pro' started by rob6566, Jan 20, 2024.

  1. rob6566

    rob6566

    Joined:
    Feb 8, 2022
    Posts:
    3
    Hi,

    I'm building a card game, and having a problem where the onPointerEnter event is triggering for items behind the currently selected card.



    When you mouse over a card, it sets it as the last child of the canvas to bring it to the front.

    Any ideas on how I can block onPointerEnter for the cards behind the active one? I thought "Blocks Raycasts " might do it, but I haven't had any luck.

    Here's the components of a card:
     
  2. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    220
    Note: if that large card also has a UI component(image etc.) with raycast target enabled, the other cards wouldn't receive pointer events unless they are above the large card. Otherwise continue reading.

    You can either disable the
    .blocksRaycasts
    of every other canvasgroup and/or use a custom logic.(I use both in my project)

    This is called input bubbling if I am not mistaken, if you need to stop pointer enter etc. when your mouse is over a second UI object, you need to stop bubbling. More read here

    Stopping it is rather easy, just add this component to any UI object that'll count if pointer is over it or not. Then use the
    InputBlockingUICount
    to determine whether you need to do anything or not
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.EventSystems;
    4.  
    5. namespace YourNameSpace.UI
    6. {
    7.     /// <summary>
    8.     /// Blocks the input when mouse enters the UI of this object or its children
    9.     /// </summary>
    10.     public class InputBlockingUI : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
    11.     {
    12.         public void OnPointerEnter(PointerEventData eventData)
    13.         {
    14.             UIManager.Instance.InputBlockingUICount++;//you can also use a public static field/property inside this class
    15.         }
    16.  
    17.         public void OnPointerExit(PointerEventData eventData)
    18.         {
    19.             UIManager.Instance.InputBlockingUICount--;
    20.         }
    21.     }
    22. }
    23.  
    You can also use the
    InputBlockingUICount
    in your input detection to prevent left click etc. doing what it usually does.
     
    rob6566 likes this.
  3. rob6566

    rob6566

    Joined:
    Feb 8, 2022
    Posts:
    3
    Thanks @venediklee. I didn't end up trying the InputBlockingUICount solution, but updating all other cards worked. I always get an onPointerExit on one card before the onPointerEnter on the card below, so I think InputBlockingUICount would have stayed at 0-1.

    The large cards DO have image components with a raycast target, but the raycasts still seemed to get through. Disabling raycast target on other cards didn't do the trick either.

    What DID do the trick was disabling the 2D box collider on all other cards when you hover over one. The solution isn't pretty (seems inelegant/inefficient to be modifying everything else instead of the current card), but it worked.

    Here's the code I used:

    Code (CSharp):
    1. //In Card onPointerEnter
    2. gameManager.setAllCardBoxCollidersActive(false);
    3. attachedCard.setBoxColliderActive(true);
    4.  
    5. //In Card onPointerExit
    6. gameManager.setAllCardBoxCollidersActive(true);
    7.  
    8.  
    9. //In gameManager
    10. public void setAllCardBoxCollidersActive(bool active) {
    11.         foreach(int player in new List<int>{1,2}) {
    12.             List<Card> cardsInDeck=deckManager[player].getAllCardsInDeck();
    13.             foreach (Card cardInDeck in cardsInDeck) {
    14.                 cardInDeck.setBoxColliderActive(active);
    15.             }
    16.         }
    17.  
    18.         foreach (Card cardInPlay in combatManager.battleMap.getAllCards(true)) {
    19.             cardInPlay.setBoxColliderActive(active);
    20.         }
    21. }
    22.  
    23.  
    24. //In the card class
    25. public void setBoxColliderActive(bool active) {
    26.         BoxCollider2D collider = cardUI.GetComponent<BoxCollider2D>();
    27.         collider.enabled=active;
    28. }
     
    venediklee likes this.
  4. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    220
    :thinking: I tested it with an overlay UI in front of world space UI(no 2d collider on either) and it blocked the raycast behind correctly; I feel like your cards dont scale their box collider or something(especially because you get onpointerexit before a second onpointerenter call)

    Cheers mate
     
    rob6566 likes this.
  5. rob6566

    rob6566

    Joined:
    Feb 8, 2022
    Posts:
    3
    Ok, I've got a better solution. When moving the card to the last child of the canvas, the Z index was also changing, so it was further away from the camera. Setting the Z index to make it closer to the camera than the other cards has solved the problem.

    I removed the previous code, then added this:
    Code (CSharp):
    1. float zIndexBeforeTransform=cardTransform.localPosition.z;
    2.  
    3. /// .... code to set new parent ...
    4.  
    5. cardTransform.localPosition= new Vector3(cardTransform.localPosition.x, cardTransform.localPosition.y, zIndexBeforeTransform-20f);
    (note - the camera is positioned so that a negative Z index is closer to it)

    My previous code worked great most of the time, but occasionally it would fire an onPointerEnter with no OnPointerExit (at least I think this is what happened) and all cards would become non-interactable.

    I did test the box colliders, and they seemed to be sizing to the card correctly.

    Unity 2D layering does my head in! I originally played with Z indexes, but discovered that re-ordering in the heirarchy seemed to be the most effective method to move stuff to the top. It seemed in this case I needed to do both.

    Thanks for your help!
     
    venediklee likes this.
  6. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    220
    You could also add a canvas component to whatever you want to be on top and set its sort order to default + 1 upload_2024-1-21_13-35-54.png

    this second canvas would be a child of another canvas