Search Unity

Popup Gui Help

Discussion in 'Immediate Mode GUI (IMGUI)' started by Bloodember, Apr 7, 2014.

  1. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Hi,

    I'm learning Unity and C# and need some assistance. I have a Gui Menu created and I only want it to show up when I click on the object? Then I have buttons on the Gui, how do get the button to run another script to create another Gui Menu?

    Thanks in advance, I'm hoping with this help I can start getting my game working.
     
    Last edited: Apr 7, 2014
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Hi,

    There are two part parts to this:
    1. Handle mouse clicks on an object, and
    2. Change menus when a button is clicked
    For the first part, you can use OnMouseEnter() and Input.GetMouseButtonDown(). There are many ways to do this, but a simple one is to create a selector class with a static variable that keeps track of what's under the mouse cursor:
    Code (csharp):
    1.  
    2. public class Selector : MonoBehaviour {
    3.  
    4.     public static Clickable target = null;
    5.  
    6.     void Update() {
    7.         const int LeftMouseButton = 0;
    8.         if (Input.GetMouseButtonDown(LeftMouseButton)) {
    9.             if (target != null) target.Click();
    10.         }
    11.     }
    12. }
    13.  
    Add Selector to an object in your scene. Then make sure your object has a collider and create a Clickable class that implements OnMouseEnter():
    Code (csharp):
    1.  
    2. public class Clickable : MonoBehaviour {
    3.  
    4.     void OnMouseEnter() {
    5.         Selector.target = this;
    6.     }
    7.  
    8.     void OnMouseExit() {
    9.         Selector.target = null;
    10.     }
    11.  
    12.     public virtual void Click() {}
    13. }
    14.  
    Add this to your object. You could modify Selector to give the user feedback that the mouse is hovering over a Clickable, such as adding a glow effect, or showing some GUI label.

    Implement Click() to actually open the menu, such as:
    Code (csharp):
    1.  
    2. public class ClickableMenu : Clickable {
    3.  
    4.     private bool showMenu = false;
    5.  
    6.     public override void Click() {
    7.         showMenu = true;
    8.     }
    9.  
    10.     void OnGUI() {
    11.         if (showMenu) {
    12.             // draw your menu
    13.         }
    14.     }
    15. }
    16.  
    If you want to position the menu over the object, use Camera.WorldToScreenPoint() to translate the object's world position into a GUI screen position.

    For the second part, you can modify ClickableMenu to keep track of which menu to show:
    Code (csharp):
    1.  
    2. public class ClickableMenu : Clickable {
    3.  
    4.     private enum Menus { None, Menu1,  Menu2, Menu3 }
    5.  
    6.     private Menus menu = Menus.None;
    7.  
    8.     public override void Click() {
    9.         if (menu == Menus. None) menu = Menus.Menu1;
    10.     }
    11.  
    12.     void OnGUI() {
    13.         switch (menu) {
    14.         case Menus.Menu1:
    15.             // draw menu 1
    16.             if (GUILayout.Button("Open Menu2")) menu = Menus.Menu2;
    17.             break;
    18.         case Menus.Menu2:
    19.             // draw menu 2
    20.             break;
    21.         }
    22.     }
    23. }
    24.  
     
  3. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Thanks for the help. If I have any other questions I'll post them here.
     
    Last edited: Apr 9, 2014
  4. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Ok I got the first part running. Now I don't understand the second. Do I replace my Gui.Buttton , with the Guilayout.button? Then how do I tell it to run the script for the menu that the button is suppose to bring up and also close the open one? I've tried a few different things and nothing worked.

    What I want to happen is when I press one of the buttons a new menu pops up and the old menu closes.

    edit: figured out how to close the old menu: Destroy(this); Works like a charm.
     
    Last edited: Apr 9, 2014
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    You can use GUI.Button or GUILayout.Button, whatever works best for you.

    The example code I posted assumed that all menus would be in the same script, and the active menu would be specified by the value of the variable menu. They way you're doing it -- in separate scripts -- is fine, too.
     
  6. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Actually, I didn't even think to do it all in one script. I'll try it with one script. I'll probably have more questions/
     
  7. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    Wouldn't it be easier to just enable/disabled the various menu script he has ?

    Here's a simple example with a working chaining workflow

    1/ a base class for the GUI script:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ChainingScript: MonoBehaviour
    6. {
    7.     public MonoBehaviour _parent;
    8.  
    9.     protected void ReturnToParent()
    10.     {
    11.         //call this.ReturnToParent() from derivated scripts when you want to close this one and re-activate the parent, wether it's a gui or a click script...
    12.  
    13.         this._parent.enable = true;
    14.         this.enabled = false;
    15.     }
    16. }
    17.  
    2/ That you can use this way :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class YourGUIScript: ChainingScript
    6. {
    7.     void OnGUI()
    8.     {
    9.         //put your GUI here
    10.  
    11.         //And when you're done here: (for example)
    12.         if (GUILayout.Button("close"))
    13.             this.ReturnToParent();
    14.     }
    15. }
    16.  

    3/ And a trigger for the clickable object:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ClickActivator : MonoBehaviour
    6. {
    7.     public ChainingScript _target;
    8.  
    9.     void OnMouseDown()
    10.     {
    11.         this._target._parent = this;
    12.  
    13.         //This way you can toggle a script on/off with your clicks
    14.         this._target.enabled = !this._target.enabled;
    15.  
    16.         /* Or another way to always turn on if you want to disable this feature (until the other reactivates this one for example) :
    17.         this._target.enabled = true;
    18.         this.enabled = false;
    19.         */
    20.     }
    21. }
    22.  
     
  8. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Thanks for the reply. I do have another question though. I want to be able to click anywhere on the background and close a menu, how would I do that?
     
  9. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    As the very first button drawn, create an invisible GUI.Button that fills the whole screen. When clicked, close the menu. To make the button invisible, use a GUI Style without a background, such as the default GUI.skin.label, and use a single blank character for the button text.
     
  10. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41

    Okay I'm trying to implement this and when I draw my menu and and check my code, it says warning unreachable code. Here's what I have:

    Code (csharp):
    1.  
    2. case Menus.Menu2:
    3.  
    4.             // draw menu 2
    5.             GUI.Box( new Rect(626, 215, 371, 453), "text" );
    6.            
    7.             someTextFieldebugStringing0 = GUI.TextField( new Rect(640, 244, 345, 52), someTextFieldebugStringing0, 28 , textfield );
    8.            
    9.             if ( GUI.Button( new Rect(711, 315, 198, 52), "text1" ) ) menu = Menus.Menu2a;
    10.             break;
    11.  
    12.         case Menus.Menu2a:
    13.                 //Required Variables:
    14.                 int someSelectionGridInteger0 = 0;
    15.                 //This array is the labels for each button in the selection grid
    16.                 string[] someStringArray0 = new string[4] { "x", "x", "x", "x" };
    17.                                
    18.                     GUI.Box( new Rect(205, 62, 485, 464), "text2" );
    19.                    
    20.                     someSelectionGridInteger0 = GUI.SelectionGrid( new Rect(247, 99, 395, 84), someSelectionGridInteger0, someStringArray0, 4 );
    21.                    
    22.             break;
    23.                
    24.             if ( GUI.Button( new Rect(711, 465, 198, 52), "S" ) )
    25.            
    26.             if ( GUI.Button( new Rect(711, 390, 198, 52), "T" ) )
    27.  
    28.             if ( GUI.Button ( new Rect (915, 630, 75, 32), "next") )
    29.            
    30. }
    31. }
    32. }
    33.  
    It's telling me line 23, the button "S", is unreachable(it doesn't show up, when I check). How do I get my other buttons to show up after I create the menu that each button will create?

    I appreciate the help you've been giving me.
     
    Last edited: Apr 11, 2014
  11. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    In case Menus.Menu2a, the break statement will cause execution to leave the switch statement, which means case Menus.Menu2a will never run the 3 GUI.Button statements below break. If you want to run these as part of Menus.Menu2a, put the break statement after the 3 GUI.Button statements.

    If, however, you want to run the 3 GUI.Button statements after the entire switch statement, you need to close the switch statement with a right brace after break:
    Code (csharp):
    1.  
    2.     case Menus.Menu2a:
    3.                 //Required Variables:
    4.                 int someSelectionGridInteger0 = 0;
    5.                 //This array is the labels for each button in the selection grid
    6.                 string[] someStringArray0 = new string[4] { "x", "x", "x", "x" };
    7.  
    8.                     GUI.Box( new Rect(205, 62, 485, 464), "text2" );
    9.  
    10.                     someSelectionGridInteger0 = GUI.SelectionGrid( new Rect(247, 99, 395, 84), someSelectionGridInteger0, someStringArray0, 4 );
    11.  
    12.             break;
    13.  
    14.         }  // <--- ***** Add this. *****
    15.  
    16.     if ( GUI.Button( new Rect(711, 465, 198, 52), "S" ) )
    17.  
    18.     if ( GUI.Button( new Rect(711, 390, 198, 52), "T" ) )
    19.  
    20.     if ( GUI.Button ( new Rect (915, 630, 75, 32), "next") )
    21. }
    22.  
     
  12. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Thanks for the reply. What I want happen is those three buttons are to be with the button that makes menu2a, when I put them before the break in the menu2 I get an error: CS0163: Control cannot fall through from one case label to another

    Here's my full code:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class MonitorMenus : Clickable {
    6.    
    7.     private enum Menus { None, Menu1,  Menu2, Menu3, Menu4, Menu2a, Menu2b, Menu2c }
    8.    
    9.     private Menus menu = Menus.None;
    10.    
    11.     public override void Click() {
    12.         if (menu == Menus. None) menu = Menus.Menu1;
    13.     }
    14.  
    15.  
    16.    
    17.     //Required Variables:
    18.     string someTextFieldebugStringing0 = "";
    19.  
    20.  
    21.     void OnGUI() {
    22.  
    23.         GUIStyle textfield = new GUIStyle(GUI.skin.textField);
    24.         textfield.alignment = TextAnchor.MiddleCenter;
    25.  
    26.         switch (menu) {
    27.                 case Menus.Menu1:
    28.  
    29.             //draw Menu 1
    30.                         GUI.Box (new Rect (660, 230, 302, 405), "C");
    31.  
    32.             //menu 2 button
    33.                         if (GUI.Button (new Rect (740, 308, 144, 55), "O"))
    34.                                 menu = Menus.Menu2;
    35.                         break;
    36.  
    37.  
    38.                 case Menus.Menu2:
    39.  
    40.             // draw menu 2
    41.                         GUI.Box (new Rect (626, 215, 371, 453), "N");
    42.            
    43.                         someTextFieldebugStringing0 = GUI.TextField (new Rect (640, 244, 345, 52), someTextFieldebugStringing0, 28, textfield);
    44.            
    45.                         if (GUI.Button (new Rect (711, 315, 198, 52), "G")) menu = Menus.Menu2a;
    46.  
    47.             if ( GUI.Button( new Rect(711, 465, 198, 52), "S2" ) )
    48.                
    49.                 if ( GUI.Button( new Rect(711, 390, 198, 52), "T" ) )
    50.                    
    51.                     if ( GUI.Button ( new Rect (915, 630, 75, 32), "next") )
    52.                         break;
    53.  
    54.                 case Menus.Menu2a:
    55.                 //Required Variables:
    56.                         int someSelectionGridInteger0 = 0;
    57.                 //This array is the labels for each button in the selection grid,
    58.                         string[] someStringArray0 = new string[4] { "x", "x", "x", "x" };
    59.                                
    60.                         GUI.Box (new Rect (205, 62, 485, 464), "Genre");
    61.                    
    62.                         someSelectionGridInteger0 = GUI.SelectionGrid (new Rect (247, 99, 395, 84), someSelectionGridInteger0, someStringArray0, 4);
    63.                    
    64.                         break;
    65.                
    66.                
    67.        
    68.  
    69.             //if ( GUI.Button( new Rect(325, 279, 144, 55), "S" )) menu = menus.Menu3;
    70.             //break;
    71.            
    72.            
    73.             //if ( GUI.Button( new Rect(325, 364, 144, 55), "G" )) menu = menus.Menu4;
    74.             //break;
    75.         }
    76.     }
    77. }
    78.  
    79.  
    As you can see in the code I have a two other buttons I have killed from menu1 for the same reason, I want those in menu1. The buttons S2, T, and next are to be in menu2. The code above gives that error code at line 46. Those buttons will also open a menu each, with other stuff in them, haven't gotten to them yet because of this problem.

    Thanks for the help.
     
  13. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Looks like you forgot the code after the if statement's condition. For example, instead of:
    Code (csharp):
    1. if ( GUI.Button( new Rect(325, 279, 144, 55), "S" ))
    use:
    Code (csharp):
    1. if ( GUI.Button( new Rect(325, 279, 144, 55), "S" )) menu = menus.Menu3;
     
  14. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    As soon as I add the buttons under the menu2b with the code it gives me the error: CS0163: Control cannot fall through from one case label to another from the switch (menu) { line. I don't know what I'm doing wrong.

    Ok, I started over, here is my new code and I still get the CS0163 error.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. //experimental monitor menu
    7. public class MenuMonitor : MonoBehaviour {
    8.  
    9.  
    10.     public class MonitorMenus : Clickable {
    11.        
    12.         private enum Menus { None, CMenu, N, S}
    13.        
    14.         private Menus menu = Menus.None;
    15.        
    16.         public override void Click() {
    17.             if (menu == Menus. None) menu = Menus.CMenu;
    18.  
    19.         }
    20.         //Required Variables:
    21.         string someTextFieldebugStringing0 = "";
    22.  
    23.         void OnGui() {
    24.  
    25.             GUIStyle textfield = new GUIStyle(GUI.skin.textField);
    26.             textfield.alignment = TextAnchor.MiddleCenter;
    27.  
    28.                         switch (menu) {
    29.                         case Menus.CMenu:
    30.                 //CMenu menu
    31.                                 GUI.Box (new Rect (660, 230, 302, 405), "CMenu");
    32.                
    33.                                 if (GUI.Button (new Rect (740, 308, 144, 55), "N"))
    34.                                         menu = Menus.N;
    35.                                 if (GUI.Button (new Rect (325, 279, 144, 55), "S"))
    36.                                         menu = Menus.S;
    37.                                 break;
    38.  
    39.                         case Menus.N:
    40.                 //N menu
    41.                                 GUI.Box (new Rect (626, 215, 371, 453), "New ");
    42.                
    43.                                 someTextFieldebugStringing0 = GUI.TextField (new Rect (640, 244, 345, 52), someTextFieldebugStringing0, 28, textfield);
    44.  
    45.                                 if (GUI.Button (new Rect (711, 315, 198, 52), "G"))
    46.                                 if (GUI.Button (new Rect (711, 390, 198, 52), "T"))  
    47.                                         break;
    48.  
    49.                         case Menus.S:
    50.                 //S menu
    51.                                 GUI.Box (new Rect (626, 215, 371, 453), "S");
    52.                
    53.                                 someTextFieldebugStringing0 = GUI.TextField (new Rect (640, 244, 345, 52), someTextFieldebugStringing0, 28, textfield);
    54.                
    55.                                 if (GUI.Button (new Rect (711, 315, 198, 52), "G"))
    56.                                 if (GUI.Button (new Rect (711, 390, 198, 52), "T"))  
    57.                                         break;
    58.  
    59.  
    60.                         }
    61.                 }
    62.     }
    63.  
     
    Last edited: Apr 12, 2014
  15. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Try this.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. //experimental monitor menu
    7. public class MenuMonitor : MonoBehaviour {
    8.  
    9.  
    10.     public class MonitorMenus : Clickable {
    11.        
    12.         private enum Menus { None, CMenu, N, S}
    13.        
    14.         private Menus menu = Menus.None;
    15.        
    16.         public override void Click() {
    17.             if (menu == Menus. None) menu = Menus.CMenu;
    18.         }
    19.  
    20.         //Required Variables:
    21.         string someTextFieldebugStringing0 = "";
    22.  
    23.         void OnGui() {
    24.  
    25.             GUIStyle textfield = new GUIStyle(GUI.skin.textField);
    26.             textfield.alignment = TextAnchor.MiddleCenter;
    27.  
    28.             switch (menu) {
    29.  
    30.             case Menus.CMenu:
    31.                 //CMenu menu
    32.                 GUI.Box (new Rect (660, 230, 302, 405), "CMenu");
    33.                 if (GUI.Button (new Rect (740, 308, 144, 55), "N")) menu = Menus.N;
    34.                 if (GUI.Button (new Rect (325, 279, 144, 55), "S")) menu = Menus.S;
    35.                 break;
    36.  
    37.             case Menus.N:
    38.                 //N menu
    39.                 GUI.Box (new Rect (626, 215, 371, 453), "New ");
    40.                 someTextFieldebugStringing0 = GUI.TextField (new Rect (640, 244, 345, 52), someTextFieldebugStringing0, 28, textfield);
    41.                 if (GUI.Button (new Rect (711, 315, 198, 52), "G")) menu = Menus.CMenu;
    42.                 if (GUI.Button (new Rect (711, 390, 198, 52), "T"))  menu = Menus.CMenu;
    43.                 break;
    44.  
    45.             case Menus.S:
    46.                 //S menu
    47.                 GUI.Box (new Rect (626, 215, 371, 453), "S");
    48.                 someTextFieldebugStringing0 = GUI.TextField (new Rect (640, 244, 345, 52), someTextFieldebugStringing0, 28, textfield);
    49.                 if (GUI.Button (new Rect (711, 315, 198, 52), "G")) menu = Menus.CMenu;
    50.                 if (GUI.Button (new Rect (711, 390, 198, 52), "T"))  menu = Menus.CMenu;
    51.                 break;
    52.  
    53.             }
    54.         }
    55.     }
    56. }
    Notice that on lines 40-41 and 48-49, I added menu = Menus.XXX at the end of the if statement.

    In your code, your lines 44-46 read as:
    Code (csharp):
    1.  
    2.     if (GUI.Button (new Rect (711, 315, 198, 52), "G")) {
    3.         if (GUI.Button (new Rect (711, 390, 198, 52), "T")) {
    4.             break;
    5.         }
    6.     }
    7.  
    which isn't what you want.

    Also, in my version above, I set the menu to Menus.CMenu because it's not clear what "G" and "T" are supposed to open. You may need to change this to the right menu.
     
  16. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    Thanks, didn't realize not having the buttons go anywhere yet would mess the code up. Now that the first error is gone, now it shows two more.

    1. rc.right != m_GfxWindow->GetWidth() || rc.bottom != m_GfxWindow->GetHeight()

    2. GUI Window tries to begin rendering while something else has not finished rendering! Either you have a recursive OnGUI rendering, or previous OnGUI did not clean up properly

    These have me stumped as well.

    EDIT: Got it, I fixed it. I had two public class designations at the top by accident.
     
    Last edited: Apr 12, 2014
  17. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    All working now?
     
  18. Bloodember

    Bloodember

    Joined:
    Mar 30, 2014
    Posts:
    41
    So far, still working on it though. The game I'm working on is menu heavy, so I'll most likely ask for more help. I also found the C# programming guide from Microsoft, that should help also.