OnMouseEnter problem for a particular situation

So I’m making a right click menu in my inventory. I currently have it set up so the click options are buttons and I have a background behind the buttons.

What my goal is looks like this: Screenshot - 7601ce652041fedfd7d4c70ed6c8cc86 - Gyazo
(this is the dropdown menu with THIS background image behind it). (Screen capture - fcb178b9bb5fa1b036002ae4d7f37edc - Gyazo)
My goal is to set up my script so that when the mouse leaves the white “HoverBackground”, I call DropdownMenu.setActive(false);

Unfortunately, when I use my script with OnMouseEnter and Exit, the buttons get in the way of the “background” and never trigger because the raycast doesn’t reach through the buttons. I’ve tried a few things, including putting the script on the buttons, but of course with the way my buttons are set up, there is space between each one (I use a vertical layout group - even with spacing set to 0, there’s a slight space between each button). If I travel off of one button (and set the gameobject to false so it is no longer displayed) then I can never hover over any other options.

Another thing I tried is setting the background in front of the button (with a low alpha for testing purposes) and then on mouseclick I disable the background. Doing this allows me to disable the option when it is not hovered over, but it takes two mouseclicks every time you want to select an option - that’s not okay with me!

Please help! :cry:

Here’s my script. I left all of my comments on there - some of it might not make sense but it could give you a good idea of the different things I’ve tried.

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

public class OnMouseOverBackGround : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler//, IPointerClickHandler
{
    public GameObject rightClickDisplay;
    //public bool hovered;
    //LayerMask layerMask1;
    //LayerMask layerMask2;
    //LayerMask itemsAndGround;
    public void OnPointerEnter(PointerEventData eventData)
    {
        Debug.Log("Entered");
        //rightClickDisplay.SetActive(true);
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        Debug.Log("Exited");

        //if (eventData.pointerCurrentRaycast.gameObject.GetComponent<Text>() == null)
        //{
            rightClickDisplay.SetActive(false);
        //}
    }

    //public void OnPointerClick(PointerEventData eventData)
    //{
    //    gameObject.SetActive(false);
    //    //gameObject.GetComponent<Image>().raycastTarget = false;
    //}

    //void Start()
    //{
    //    //layerMask1 = 1 << LayerMask.NameToLayer("UI");
    //    //layerMask2 = 1 << LayerMask.NameToLayer("");
    //    //itemsAndGround = layerMask1 | layerMask2;
    //}
    void Update()
    {
        //hovered = true;
        //if (hovered == false)
        //{
        //    Debug.Log("Hovered is false");
        //    rightClickDisplay.SetActive(false);
        //}
        //RaycastHit[] hits;
        //hits = EventSystem.RaycastAll(Camera.main.ScreenPointToRay(Input.mousePosition), Mathf.Infinity, layerMask1);
        //Debug.Log(hits.Length);
        //for (int i = 0; i < hits.Length; i++)
        //{
        //    Transform hitTransform = hits[i].transform;

        //    if (hitTransform == rightClickDisplay.transform)
        //    {
        //        Debug.Log("Hovering over the background");
        //        hovered = true;
        //        return;
        //    }
        //}

        //PointerEventData pointer = new PointerEventData(EventSystem.current)
        //{
        //    position = Input.mousePosition
        //};

        //List<RaycastResult> raycastResults = new List<RaycastResult>();
        //EventSystem.current.RaycastAll(pointer, raycastResults);

        //foreach (var go in raycastResults)
        //{
        //    if(go.gameObject == rightClickDisplay)
        //    {
        //        hovered = true;
        //        break;
        //    }
        //    hovered = false;
        //}

        //if (hovered == false)
        //{
        //    Debug.Log("Hovered is false");
        //    rightClickDisplay.SetActive(false);
        //}
    }
}
1 Like

You might be able to use your “area in front”, if on-click you raycast; and if you find a button, you invoke its event.
I don’t know if you considered clicking = close, too? That’s pretty common for popup context menus. Doesn’t mean you have to do it because it’s common, but if you chose that path, it’s good and pretty easy :slight_smile:
I mean of course if you click outside the area, it closes the menu…

Just to clarify, you want to bubble the events through from the buttons to the object directly behind the buttons?

You can use a combination of EventSystem.RaycastAll and ExecuteEvents to achieve this.

Event bubbling is on my list of tutorials to make. If you ask nicely I might be persuaded to do it properly over the weekend.

Hey, thanks for the idea. I ended up doing something just like this - and yep, whether the person clicks an option on the menu or the mouse hovers off the menu it closes. I ended up making 2 identical objects (one without the script however) where one was in the background and one was in the foreground. The one in the foreground has an alpha set to 0 and uses this script, the one in the background just has what I want the background to look like behind the options in my inventory.
Here’s what it looks like overall: https://gyazo.com/efea4f179f3e6dc75793c606e63fb4b5
Here’s the finished code:

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

public class OnMouseOverBackGround : MonoBehaviour, IPointerExitHandler, IPointerClickHandler //, IPointerEnterHandler
{
    public GameObject rightClickDisplay;
    public Button clickedButton;
    //public void OnPointerEnter(PointerEventData eventData)
    //{
    //    Debug.Log("Entered");
    //    //rightClickDisplay.SetActive(true);
    //}

    public void OnPointerExit(PointerEventData eventData)
    {
        Debug.Log("Exited");
        rightClickDisplay.SetActive(false);
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        PointerEventData pointer = new PointerEventData(EventSystem.current)
        {
            position = Input.mousePosition
        };

        List<RaycastResult> raycastResults = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointer, raycastResults);
        List<GameObject> gameObjectsClicked = new List<GameObject>();

        foreach (var go in raycastResults)
        {
            gameObjectsClicked.Add(go.gameObject);
        }

        Button[] clickTypeOptions;
        clickTypeOptions = rightClickDisplay.GetComponentsInChildren<Button>();
        Slot s = GameObject.FindObjectOfType<DropdownMenu>().rightClickedSlot;

        foreach (var button in clickTypeOptions)
        {
            if(gameObjectsClicked.Contains(button.gameObject) && button.name != s.itemName)
            {
                Debug.Log("Clicked a button: "+ button.GetComponentInChildren<Text>().text);
                s.DropDownMenuClick(button.name);
                gameObject.SetActive(false);
               
            }
        }
    }
}
1 Like

I probably didn’t do it “properly” but I have an implementation that works and that I’m happy with now :slight_smile:

Cool, glad you got it working.
1 small note: I think maybe you don’t need to create the PointerEventData because it comes as a parameter in that method :slight_smile:

Haha, Yup. Guess my eyes glazed over after working on this problem for so long