How to draw GUI controls on top of each other

My game has a dropdown menu that I've implemented with a GUI.Toggle and a GUI.SelectionGrid. The toggle button determines whether or not the selection grid should be displayed.

The SelectionGrid appears over another (unrelated) group of GUI.Toggle buttons. Even though the toggle buttons are drawn first, they still steal the mouse focus when the mouse is over the selection grid.

Both components are drawn in the same OnGUI function. Actually, they're both drawn in the same function, which gets called from the OnGUI function in my script.

Here is a screenshot that demonstrates the problem:

alt text

The mouse is over the 'Foo' item in the selection grid, yet the toggle button enters its hover state and will become active if I click my mouse.

Here's the code that draws the selection grid, which is called AFTER the list of toggle buttons has been drawn:

var buttonRectangle   =  getDropdownButtonRect();
var choicesRectangle  =  getScrollBoxRect(buttonRectangle);

showMenuChoices = GUI.Toggle(buttonRectangle, showMenuChoices, buttonLabel, "dropdownButton");

if ( showMenuChoices ) {

    lastSelection = chosenMenuOffset;

    GUI.Label(choicesRectangle, dropdownBackground);
    chosenMenuOffset = GUI.SelectionGrid(choicesRectangle, chosenMenuOffset, menuNameList, 1, "dropdownMenu");

    if ( chosenMenuOffset != lastSelection ) { 
        resetButtons();
        showMenuChoices = false;
    }

    if ( isCloseDropdownClick() ) {
        showMenuChoices = false;
    }
}

Thanks in advance for any help...

I finally figured this out now that I've accepted the fact that you must draw GUI elements that overlap in separate scripts.

Once I realized that, there were a couple other things I needed to figure out:

  • Both scripts must declare a class with a name that matches the file name (even javascript)
  • The classes must extend MonoBehaviour
  • Both scripts need to declare their GUI.depth in their OnGUI function
  • GUI.depth is an integer
  • The script with the lowest depth will be drawn last and will capture the mouse input

filename: DrawMeFirst.js

class DrawMeFirst extends MonoBehaviour
{
    var bottomLayerNumber : int = 10;

    function OnGUI ()
    {
        GUI.depth = bottomLayerNumber;
        drawStuffUnderneathOtherStuff();
    }

    function drawStuffUnderneathOtherStuff ()
    {
        if ( GUI.Button(Rect(0, 0, 100, 100), "I am underneath") ) {
            Debug.Log("This will never get printed");
        }
    }

filename: DrawMeLast.js

class DrawMeLast extends MonoBehaviour
{
    var topLayerGetsMouseInput : int = 1;

    function OnGUI ()
    {
        GUI.depth = topLayerGetsMouseInput;
        drawControlsYouCanClick();
    }

    function drawControlsYouCanClick ()
    {
        if ( GUI.Button(Rect(0, 0, 100, 100), "I am on top") ) {
            Debug.Log("I will always get clicked");
        }
    }

You don’t actually have to separate your different layers into separate scripts. The real problem is just a bug in GUI.Button; it doesn’t properly honor GUIUtility.hotControl.

See more details, and a replacement button function, on this Unity Forum thread.

Simple

GUI.depth = 1;

//draw your front gui here

GUI.depth = 2;

//draw your back gui here

Of course you can add even more layers.

i tried this but it won't work where do you have to set your buttons or toggle's? do i have to set them in a function callt drawControlsYouCanClick or just under it? i tried everything can you make a simple sample with 2 buttons 1 greater then the other on top of each other?

i gave my scripts the right name and tried and tried but it won't happen. its always clicking the wrong one :(

thanks in advance,

It seems this is appropriate topic so I’ll leave it here. GUI.Button is not broken - but if you do something like this in your script “useGUILayout = false;” it may seems so.

I just spent 5 hours debugging this problem that appeared from “nowhere”.
Imagine two buttons written in 2 scripts overlapping each other.

Script1
 button1

Script2
 button2

To set which one will be on top of the other you only need to set execution order in the inspector. Let’s say the execution order is first Script1 then Script2. Now if you click on the button1 which is in foreground the button1 will execute.

Here comes the nasty “useGUILayout = false”. If you wrote that line of code, say in Script 1, the rendering order would be the same but instead of executing button1 you will end up executing button2!

In my case i have a clickable darkness (fullscreen background) and button submit (foreground) in one OnGUI method. And if you want to prevent darkness click when you click on submit button you may use workaround bellow.

void OnGUI()
{
	if (Event.current.type == EventType.Layout || Event.current.type == EventType.mouseDown || Event.current.type == EventType.mouseUp) 
	{
		if(GUI.Button(submitRect, "submit", submitStyle))
		{
			//do submit
		}
		if(GUI.Button(new Rect(0,0,screen.width, Screen.height), "", darknessStyle))
		{
			//back to preview page
		}
	}
	if (Event.current.type == EventType.Repaint)
	{
		if(GUI.Button(new Rect(0,0,screen.width, Screen.height), "", darknessStyle))
		{
			//back to preview page
		}
		if(GUI.Button(submitRect, "submit", submitStyle))
		{
			//do submit
		}
	}
}