Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

How do I make my menu slide into the screen?

Discussion in 'Immediate Mode GUI (IMGUI)' started by Bridgeman, Aug 8, 2014.

  1. Bridgeman

    Bridgeman

    Joined:
    Apr 13, 2014
    Posts:
    33
    So I have a GUI menu with a GUI style, which opens and closes when "M" is pressed. But now, instead of it just appearing when opened, I want it to slide into the screen from out of the top. How can I do that?
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    Use a coroutine. Something like:

    Code (csharp):
    1. private bool isMenuOpen = false;
    2. private float menuOffset;
    3.  
    4. void Update() {
    5.     if (Input.GetKeyDown(KeyCode.M)) {
    6.         isMenuOpen = !isMenuOpen;
    7.         if (isMenuOpen) StartCoroutine(Slide());
    8.     }
    9. }
    10.  
    11. void OnGUI() {
    12.     if (isMenuOpen) {
    13.         // Draw your menu here, but offset it by menuOffset:
    14.         Rect rect = new Rect(0, menuOffset, 100, 100);
    15.     }
    16. }
    17.  
    18. IEnumerator Slide() {
    19.     for (int i = -10; i < 0; i++) {
    20.         menuOffset = 10 * i;
    21.         yield return new WaitForSeconds(0.1f);
    22.     }
    23.     menuOffset = 0;
    24. }
     
    Last edited: Aug 9, 2014
    Bloodberet likes this.
  3. Bridgeman

    Bridgeman

    Joined:
    Apr 13, 2014
    Posts:
    33
    Well, it slides. But when I press M the menu first just appears on the screen and then slides upwards instead of downwards, and then it resets to its original position on the screen. Kinda buggy.

    And it doesn't close when I press M again

    Can you explain what exactly that stuff in the IENumerator does? I pretty much get everything else, but that part confuses me
     
  4. GregMeach

    GregMeach

    Joined:
    Dec 5, 2012
    Posts:
    249
    private float menuOffset = -100f;
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    Sorry, I just fixed the logic in Slide()'s "for" loop, and also fixed Update() so pressing M again closes the menu. It hides the menu immediately. Once you get the hang of coroutines, you can modify your code to make it slide back up if you want.

    Slide() is the coroutine. You can kind of think of it running in a separate, parallel execution thread. (Check out the tutorial linked in my previous post for the real explanation.) Coroutines use cooperative multitasking, which means they promise to temporarily release control on a regular basis.

    That's what the yield statement does. Inside the "for" loop, it releases control for 0.1 seconds. In each iteration of the loop, it sets a float named menuOffset, gradually changing it from -100 all the way up to 0. This is the offset of the top of the menu.

    When the "for" loop starts, menuOffset is -100, which means the top of the menu is 100 pixels above the top of the screen.

    When the "for" loop is done, menuOffset is 0, which means the top of the menu is at the top of the screen.
     
  6. Bridgeman

    Bridgeman

    Joined:
    Apr 13, 2014
    Posts:
    33
    Cool, now it works. I have a couple of more questions if you don't mind.

    First, when "M" is pressed in rapid succession multiple times, it gets really trippy. Is there a fix for that?

    Second, how do I make it slide in other directions? Left, right, and up?

    Third, I was wondering if it's possible to put a Time.timeScale = 0 in here somewhere. Obviously I want to pause the game when the menu is open, but it won't slide if I put it in just like that.

    And finally, can I change the sliding speed?
     
    Last edited: Aug 9, 2014
  7. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    Simple, use GUI.BeginGroup

    GUI.BeginGroup use a Rect to draw GUI elements.

    Modify Rect position of GUI.BeginGroup to slide, change speed, etc...
     
  8. Bridgeman

    Bridgeman

    Joined:
    Apr 13, 2014
    Posts:
    33
    How does that do anything? A group is exactly the same as a box, except it's a group of GUI elements. Besides, I want to have different segments of my menu sliding into the screen from different directions.
     
  9. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    You're confused between GUI.BeginGroup and GUI.Box. Read documentation with code example. GUI.Box is a gui element, and GUI.BeginGroup is a window element(no window title).

    GUI.BeginGroup is similar to GUI.Window, you can group gui elements, but no window title.

    If you have different segments of menu, you need different GUI.BeginGroup.
     
  10. Bridgeman

    Bridgeman

    Joined:
    Apr 13, 2014
    Posts:
    33
    I have a GUI group with several GUI boxes in them, and I want to slide them in to the screen from different directions
     
  11. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    Repeat, then you need different GUI.BeginGroup.

    Luck.
     
  12. Bridgeman

    Bridgeman

    Joined:
    Apr 13, 2014
    Posts:
    33
    I'm sorry but could you clarify that? What do I need to change about my GUI.BeginGroup?
     
  13. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    Or, using the coroutine approach, you can change menuOffset to menuYOffset, and add a menuXOffset.

    In OnGUI, offset your entire GUI by (menuXOffset, menuYOffset).

    In the "for" loop -- which you could replace with a while loop depending on what you want to do -- update both menuXOffset and menuYOffset. For example, to slide right from the left edge of the screen, start with menuXOffset = -100 (or whatever is the width of your menu) and increase to MenuOffset = 0. If you want it to run independent of the current timescale, base it on Time.realtimeSinceStartup.

    You could also look into the free iTween, which will handle this kind of offsets-over-time for you automatically.

    For the rapid M keypress issue, you could build in a timer:

    Code (csharp):
    1. private float nextTimeKeypressAllowed = 0;
    2.  
    3. void Update() {
    4.     if (Input.GetKeyDown(KeyCode.M) && (Time.realtimeSinceStartup >= nextTimeKeypressAllowed)) {
    5.         nextTimeKeypressAllowed = Time.realtimeSinceStartup + 1.0f; // 1 second between keypresses
    6.         isMenuOpen = !isMenuOpen;
    7.         if (isMenuOpen) StartCoroutine(Slide());
    8.     }
    9. }
     
  14. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    this is great..can you help me..instead of pressing M..i want to press a button and the label will apper?
     
  15. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    If you mean a gamepad button, try this:

    Code (csharp):
    1. void Update() {
    2.   if (Input.GetButtonDown("Fire1")) { // Fire1 button opens/closes menu.
    3.   isMenuOpen = !isMenuOpen;
    4.   if (isMenuOpen) StartCoroutine(Slide());
    5.   }
    6. }
    If you mean a GUI button, delete Update() and try this:

    Code (csharp):
    1. void OnGUI() {
    2.   if (GUILayout.Button("Menu")) { // "Menu" button opens/closes menu.
    3.     isMenuOpen = !isMenuOpen;
    4.     if (isMenuOpen) StartCoroutine(Slide());
    5.   }
    6.   if (isMenuOpen) {
    7.     // Draw your menu here, but offset it by menuOffset:
    8.     Rect rect = new Rect(0, menuOffset, 100, 100);
    9.   }
    10. }
     
  16. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    this is perfect..thanks for the help bro...i change it to gui.button and i change also the rect..thanks for the help bro
     
  17. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    Happy to help!
     
  18. Aiursrage2k

    Aiursrage2k

    Joined:
    Nov 1, 2009
    Posts:
    4,835
    Wow man you are very patient.
     
    AmazinJacks likes this.
  19. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    i have another question sir..if i click the button it will slide up not disappear on the screen..how?
     
  20. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    You need another coroutine to slide back up:

    Code (csharp):
    1. void OnGUI() {
    2.   if (GUILayout.Button("Menu")) { // "Menu" button opens/closes menu.
    3.     if (IsMenuOpen) {
    4.       StartCoroutine(SlideUp());
    5.     } else {
    6.       StartCoroutine(SlideDown());
    7.     }
    8.   }
    9.   if (isMenuOpen) {
    10.     // Draw your menu here, but offset it by menuOffset:
    11.     Rect rect = new Rect(0, menuOffset, 100, 100);
    12.     // (your GUI code here)
    13.   }
    14. }
    15.  
    16. IEnumerator SlideUp() {
    17.   for (int i = 0; i < 10; i++) {
    18.   menuOffset = -10 * i;
    19.   yield return new WaitForSeconds(0.1f);
    20.   }
    21.   menuOffset = -100;
    22.   isMenuOpen = false;
    23. }
    24.  
    25. IEnumerator SlideDown() {
    26.   isMenuOpen = true;
    27.   for (int i = -10; i < 0; i++) {
    28.   menuOffset = 10 * i;
    29.   yield return new WaitForSeconds(0.1f);
    30.   }
    31.   menuOffset = 0;
    32. }
    Notes:
    • isMenuOpen is set inside the coroutines.
    • You could combine the SlideUp() and SlideDown() coroutines into one coroutine. I used two separate coroutines because I think it makes a clearer example.
     
  21. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    thanks sir..youre so great :)
    sir do you know how to use Sqlite to unity?
     
    Last edited: Aug 31, 2014
  22. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    sir do you know how to use sqlite to unity?
    because i had an error like this- Error building player:Extracting referenced ddl
    it works when i play..but when i build and run it there's an error
     

    Attached Files:

  23. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    Sorry, no. Try posting in a new thread so people familiar with Sqlite will notice it.
     
  24. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    can you help me about this code..i want to have a button permanent next and preview..a slideshow..
    public class HorizontalTransitionGUI : MonoBehaviour
    {
    //A 4x4 Matrix
    private Matrix4x4 trsMatrix;
    //A three dimension vector that will translate GUI coordinate system
    private Vector3 positionVec;
    //Two booleans to determine which of the GUI buttons have been pressed
    private bool next = false;
    private bool back = false;

    // Use this for initialization
    void Start()
    {
    //Initialize the matrix
    trsMatrix = Matrix4x4.identity;
    //Initialize the Vector
    positionVec = Vector3.zero;
    }

    // Update is called once per frame
    void Update()
    {
    //If the 'next' boolean is true
    if(next)
    {
    //Interpolate the current vector x component until it has the same as value the screen width
    positionVec.x = Mathf.SmoothStep(positionVec.x, Screen.width,Time.deltaTime*10);
    /*Make 'trsMatrix' a matrix that translates, rotates and scales the GUI.
    The position is set to positionVec, the Quaternion is set to identity
    and the scale is set to one.*/
    trsMatrix.SetTRS(positionVec , Quaternion.identity, Vector3.one);
    }
    else if(back) //If 'back is true'
    {
    //Interpolate the current vector x component until it reaches zero
    positionVec.x = Mathf.SmoothStep(positionVec.x, 0,Time.deltaTime*10);
    //Make 'trsMatrix' a matrix that translates, rotates and scales the GUI.
    trsMatrix.SetTRS(positionVec , Quaternion.identity, Vector3.one);
    }

    }

    void OnGUI()
    {
    //The GUI matrix must changed to the trsMatrix
    GUI.matrix = trsMatrix;

    //If the button labeled 'Next' is pressed
    if(GUI.Button(new Rect(Screen.width - 400, 315, 100, 30),"Next"))
    {
    next = true;
    back = false;
    }

    //The TextArea that appears on the first screen.
    GUI.TextArea(new Rect(300,200,Screen.width-600,100), "Click on the 'Next' button to change the Text Area.");

    //If the button labeled 'Back' is pressed
    if(GUI.Button(new Rect(-Screen.width + 300, 315, 100, 30),"Back"))
    {
    next = false;
    back = true;
    }

    //The TextArea that appears on the second screen
    GUI.TextArea(new Rect(-Screen.width + 300,200,Screen.width-600,100), "Click on the 'Back' button to return to the previous Text Area.");

    //To reset to GUI matrix, just make it equal to a 4x4 identity matrix
    GUI.matrix = Matrix4x4.identity;


    }
    }
     
  25. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    This would be best as a new thread. Try posting it as a new thread. Before you post, please read the Using code tags properly posting. It will help people to understand your code more easily.
     
  26. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    good day sir..do you know the coroutine ..for example i have a button in scene 1..and if i click that it will load scene 2..and a sliding box will appear from top..like the code before..but automatically if i the button in the 1st scene a box will appear from top in scene 2..thank you sir..
     
  27. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
    Maybe start the coroutine in a Start() method in a script in scene 2.

    If you only want it to run if the button in scene 1 was clicked, define a static bool variable in a script. If the button is clicked, set the variable true. The Start() method can check the static variable. If it's true, start the coroutine.
     
  28. Rbert

    Rbert

    Joined:
    Jul 13, 2014
    Posts:
    28
    sir can you convert this into javascript
    Code (CSharp):
    1. void OnGUI() {
    2.   if (GUILayout.Button("Menu")) { // "Menu" button opens/closes menu.
    3.     if (IsMenuOpen) {
    4.       StartCoroutine(SlideUp());
    5.     } else {
    6.       StartCoroutine(SlideDown());
    7.     }
    8.   }
    9.   if (isMenuOpen) {
    10.     // Draw your menu here, but offset it by menuOffset:
    11.     Rect rect = new Rect(0, menuOffset, 100, 100);
    12.     // (your GUI code here)
    13.   }
    14. }
    15. IEnumerator SlideUp() {
    16.   for (int i = 0; i < 10; i++) {
    17.   menuOffset = -10 * i;
    18.   yield return new WaitForSeconds(0.1f);
    19.   }
    20.   menuOffset = -100;
    21.   isMenuOpen = false;
    22. }
    23. IEnumerator SlideDown() {
    24.   isMenuOpen = true;
    25.   for (int i = -10; i < 0; i++) {
    26.   menuOffset = 10 * i;
    27.   yield return new WaitForSeconds(0.1f);
    28.   }
    29.   menuOffset = 0;
    30. }
     
  29. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,560
  30. Krijn_P

    Krijn_P

    Joined:
    Feb 12, 2018
    Posts:
    2
    I know this thread is quite old but for anyone who stumbles onto it with the same question here is my approach. I noticed TonyLi's approach was quite choppy in the movement. I solved it by using the mathf.lerp function to make it smooth:

    Code (CSharp):
    1. public class MenuController : MonoBehaviour
    2. {
    3.     public float transitionTime = 0.20f;
    4.  
    5.     private RectTransform rectTransform;
    6.  
    7.     [HideInInspector]
    8.     public bool isMenuOpen = false;
    9.  
    10.     private float toggleTime = 0;
    11.     private float toggleStartPosition;
    12.     private float menuOffsetOpen = 0;
    13.     private float menuOffsetClosed;
    14.     private float targetOffset;
    15.  
    16.     void OnGUI() {
    17.         rectTransform.offsetMax = new Vector2(0,Mathf.Lerp(toggleStartPosition,targetOffset,(Time.unscaledTime-toggleTime)/transitionTime));
    18.         rectTransform.offsetMin = new Vector2(0,rectTransform.offsetMax.y);
    19.     }
    20.    
    21.     void Start(){
    22.         rectTransform = GetComponent<RectTransform>();
    23.         menuOffsetClosed = rectTransform.rect.height;
    24.     }
    25.  
    26.     void Update()
    27.     {
    28.         menuOffsetClosed = rectTransform.rect.height;
    29.         if(Input.GetKeyDown("escape")){
    30.             toggleTime = Time.unscaledTime;
    31.             toggleStartPosition = rectTransform.offsetMax.y;
    32.             isMenuOpen = !isMenuOpen;
    33.             if (isMenuOpen){targetOffset = menuOffsetOpen;} else {targetOffset=menuOffsetClosed;}
    34.         }
    35.     }
    36. }
    37.  
    It checks how much time passes and interpolates between the two target states smoothly that way.