How do I make my menu slide into the screen?

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?

Use a coroutine. Something like:

private bool isMenuOpen = false;
private float menuOffset;

void Update() {
    if (Input.GetKeyDown(KeyCode.M)) {
        isMenuOpen = !isMenuOpen;
        if (isMenuOpen) StartCoroutine(Slide());
    }
}

void OnGUI() {
    if (isMenuOpen) {
        // Draw your menu here, but offset it by menuOffset:
        Rect rect = new Rect(0, menuOffset, 100, 100);
    }
}

IEnumerator Slide() {
    for (int i = -10; i < 0; i++) {
        menuOffset = 10 * i;
        yield return new WaitForSeconds(0.1f);
    }
    menuOffset = 0;
}
1 Like

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

private float menuOffset = -100f;

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.

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?

Simple, use GUI.BeginGroup

GUI.BeginGroup use a Rect to draw GUI elements.

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

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.

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.

I have a GUI group with several GUI boxes in them, and I want to slide them in to the screen from different directions

Repeat, then you need different GUI.BeginGroup.

Luck.

I’m sorry but could you clarify that? What do I need to change about my GUI.BeginGroup?

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:

private float nextTimeKeypressAllowed = 0;

void Update() {
    if (Input.GetKeyDown(KeyCode.M) && (Time.realtimeSinceStartup >= nextTimeKeypressAllowed)) {
        nextTimeKeypressAllowed = Time.realtimeSinceStartup + 1.0f; // 1 second between keypresses
        isMenuOpen = !isMenuOpen;
        if (isMenuOpen) StartCoroutine(Slide());
    }
}

this is great…can you help me…instead of pressing M…i want to press a button and the label will apper?

If you mean a gamepad button, try this:

void Update() {
  if (Input.GetButtonDown("Fire1")) { // Fire1 button opens/closes menu.
  isMenuOpen = !isMenuOpen;
  if (isMenuOpen) StartCoroutine(Slide());
  }
}

If you mean a GUI button, delete Update() and try this:

void OnGUI() {
  if (GUILayout.Button("Menu")) { // "Menu" button opens/closes menu.
    isMenuOpen = !isMenuOpen;
    if (isMenuOpen) StartCoroutine(Slide());
  }
  if (isMenuOpen) {
    // Draw your menu here, but offset it by menuOffset:
    Rect rect = new Rect(0, menuOffset, 100, 100);
  }
}

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

Happy to help!

Wow man you are very patient.

1 Like

i have another question sir…if i click the button it will slide up not disappear on the screen…how?

You need another coroutine to slide back up:

void OnGUI() {
  if (GUILayout.Button("Menu")) { // "Menu" button opens/closes menu.
    if (IsMenuOpen) {
      StartCoroutine(SlideUp());
    } else {
      StartCoroutine(SlideDown());
    }
  }
  if (isMenuOpen) {
    // Draw your menu here, but offset it by menuOffset:
    Rect rect = new Rect(0, menuOffset, 100, 100);
    // (your GUI code here)
  }
}

IEnumerator SlideUp() {
  for (int i = 0; i < 10; i++) {
  menuOffset = -10 * i;
  yield return new WaitForSeconds(0.1f);
  }
  menuOffset = -100;
  isMenuOpen = false;
}

IEnumerator SlideDown() {
  isMenuOpen = true;
  for (int i = -10; i < 0; i++) {
  menuOffset = 10 * i;
  yield return new WaitForSeconds(0.1f);
  }
  menuOffset = 0;
}

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.