How to 'click through' a gui object to select another gui object underneath it?

I am trying to make an interface where the player can pick up items from the ground, and then organize their inventory via “inventory slots”. Simple stuff, you’d think. (No different than most games with inventory slots in your “bag”/“backpack” and item icons).

So the player clicks an item in game, and it disappears in the game world and appears as a GUI Icon following the mouse position.

However, this is when the entire system messes up. To make sure I don’t “Click To Move” when I click the GUI, I have to make sure that the mouse is not hovering over a GUI object.

if (Input.GetMouseButton (0) && EventSystem.current.IsPointerOverGameObject() == false

However, this means that whenever the player picks up an item, and the GUI Icon appears at the mouse (and follows the mouse), then player can no longer move.

Furthermore, the player can no longer click any GUI buttons. The mouse always seems to click only the GUI object which follows the player’s mouse. I’ve tried just not allowing player movement when holding an item in the mouse (I’m just trying to prototype this quickly), but this still doesn’t allow me to click any other GUI objects with the component if the player is “holding something” (GUI Icon following mouse, always in front of all gui button objects)

I’ve done my research, but am right now stumped and would prefer not to have to figure this out for hours if someone else knows a good solution.

I’ve read that with other GUI solutions (ex. NGUI) one could simply raycast and then check the names of all the GUI objects hit. I find no such functionality in Unity’s new GUI. Any raycasting I do, ignores the GUI and simply raycasts in the 3D world. (Perhaps I am doing the raycasting wrong?)

The GUI Icon which follows the mouse, is just an Image (Script) component. If I hide the “Image (Script)” component, it allows me to click on any and everything. Obviously though, I cannot do this, as that is the entire point of the GUI Icon.

Events don’t bubble on the Canvas UI. You’re stuck doing good old raycasting.

You can raycast with this:

pointer.position = Input.mousePosition;
var raycastResults = new List();
EventSystem.current.RaycastAll(pointer, raycastResults);

Thanks @zarawesome

Here is a method I use to determine if the pointer is over the player’s interface, which is a different set of GUI objects than the item icon which follows the mouse.

I do not consider the GUI Icon (a child of what I call the “MouseHand”) as part of the player’s interface, even though it is part of the Canvas UI. In this way, if there is a GUI Icon following the mouse around because the player picked up an item in game, when clicking on the Canvas UI elements, it knows if it is the player’s interface or the “MouseHand” GUI Icon.

In this way, if the player clicks on the player interface, they don’t “Click To Move”. However, if they are holding an item (GUI Icon following the player’s mouse) and they click the ground, it WILL move them.

Works perfectly, and I’m sure the rest that I need to do will come easy now. Thanks again! :slight_smile:
This should actually work perfectly in my current code to tell what slot is being manipulated by the player just by using the raycast hitGOB.name

bool IsPointerOnGUI()
   {
     PointerEventData pointer = new PointerEventData(EventSystem.current);
     pointer.position = Input.mousePosition;
     List<RaycastResult> hits = new List<RaycastResult>();
     EventSystem.current.RaycastAll (pointer, hits); //Raycast into GUI

     foreach(RaycastResult hit in hits) //iterate through each hit from raycast
     {
       GameObject hitGOB = hit.go; //get the gameobject of what was hit.
       Debug.Log ("hitGOB: " + hitGOB.name);

       if(hitGOB.transform.parent.name != "MouseHand") //If the hit gameobject was NOT in the MouseHand.
       {
         Debug.Log ("Not on mousehand! Returning true...");
         return true;
       }
     }

     return false;
   }

A few things to note, the Unity documentation for the new UI looks as though you need to type, on line 11, hitGOB = hit.gameObject.

Nowhere does it mention that it is actually “.go”. This was a bit confusing, and I had to use trial and error (assuming “go” was it, as intellisense seemed to indicate it was “.go” and not “.gameObject”.

edit: Seems that “.go” was in beta, and replaced with “.gameObject” during release, and I was using an older version.

Also if you hit the link at the top, it leads to a dead page

Description
A hit result from a BaseRaycastModule.

The new UI is a bit confusing since the documentation is unfinished and (unless I misunderstand documentation) incorrect.
This link helped a lot too: (4.6 UI) How to detect mouse over on button? - Questions & Answers - Unity Discussions

Two suggestions.

  • Use a physics raycaster and the event system to allow interaction with the world. This brings all of your clicks into the same system. The UI can then consume clicks as needed.
  • Use a canvas group to control the blocks raycast property of each UI element

Thanks for the tips, but before I read your post, I had already used

EventSystem.current.RaycastAll(pointer, raycastResults);

and I was able to create a fully working inventory system, with swappable items and everything- without conflicting with player movement / world interaction. I’m done for the most part.

I just did the raycasting and checked if the gameObject name matched.

Something like

foreach(Transform child in GlobalVariables.GUIcanvas.transform) //Scan through all children in the Canvas, to find the BackPack and MouseHand objects.
         {
           if (child.gameObject.name == "MouseHand") //Find the MouseHand gameobject
           {
             GUIicon_mouseHand = child.gameObject;
           }
           if (child.gameObject.name == "BackPack") //Find the BackPack gameobject
           {
             foreach(Transform child2 in child) //Scan through all children of the BackPack to find the specific BackPackSlot_#
             {
               if (child2.gameObject.name == "BackPackSlot_" + strBackpacknumber //Find the correct backpackslot_# gameobject
               {
                 GUIicon_backpackslot = child2.gameObject;
               }
             }

           }
         }

I then used a lot of swapping gameobject parents to UI objects in the Hierarchy, which also helped to keep the draw order correct.

foreach(Transform child in GUIicon_mouseHand.transform) //Scan the MouseHand gameobject's children, which should only be one.
           {
             child.parent = GUIicon_backpackslot.transform; //Place this one gameobject in the hierarchy Canvas->BackPack->BackPackSlot_#->this
             child.GetComponent<GUI_ItemIcon>().isInMouseHand = false;
             child.transform.localPosition = new Vector3(0,0,0);
           }

Not sure how efficient it is, but it works :slight_smile:

Not so efficient, but to keep my code more readable, I kept the PlayerInput_Movement and PlayerInput_GUI in two separate scripts.