Posted on 07 Jan 2014

In-game Menu in Unity

I wrote before about beginning to use Unity® for some fun projects in my spare time. After getting my feet wet with Unity a bit I started to think about what it is that, so far, seems like something required for each project I make. I guess you could consider this the first of a series of blog posts that will introduce concepts and show how I fit them together to become a "project template" of sorts.

As with most things in programming there are numerous ways you could implement this functionality. One way an in-game menu could be implemented is as a scene. You could create a new scene in Unity, set it up with your menu layout, and when you want to display the scene call one of the Application.LoadLevel* methods. If you don't load it with one of the LoadLevelAdditive* methods you would of course have to deal with the current game state and be prepared to reload that state when the game is resumed. If you do load the scene additively you need to handle freeing the resources of the menu's scene once the game is resumed. These are perfectly valid ways to do it, however I am not going to implement the in-game menu system in this manner.

Setting up the InGameMenu class

The way I implement an in-game menu is much simpler than that, and gets the exact effect I am looking for in an in-game menu. When I open the menu I want the following criteria met:

  • GUI appears
  • Game is paused
  • The game is "faded" in the background.

The last two of those aren't necessarily required or even desired in all situations, but don't worry they are easily removed if needed. The first thing I did was create a new C# script named InGameMenu and added the following code:

using UnityEngine;

public class InGameMenu : MonoBehaviour {
    // Reference to menu background prefab set in the inspector.
    public GameObject menuBackground;

    // Reference to the background object created when the menu is opened.
    GameObject menuBG;
    // Reference to the main camera to help orient the menu's background quad.
    Transform mainCamera;

    void OnEnable() {
        mainCamera = GameObject.FindGameObjectWithTag("MainCamera").transform;
        menuBG = (GameObject)Instantiate(menuBackground);

        // The background quad is a prefab set up to be oriented so that it takes up
        // the whole screen in relation to the camera's position so we set it's
        // parent to the mainCamera we grabbed above.
        menuBG.transform.parent = mainCamera;
        // By default the transform has us oriented in global space, but the position 
        // is meant to be local so we change that here.
        menuBG.transform.localPosition = menuBG.transform.position;

        // Pause the game.
        Time.timeScale = 0f;
        Debug.Log("PAUSE");
    }

    void OnDisable() {
        // We no longer need the quad.
        Destroy(menuBG);
        // Unpause the game.
        Time.timeScale = 1f;
        Debug.Log("RESUME");
    }

    void OnGUI() {
        // You can use whatever you want for the menu UI, this is a simple example solution
        // though.
        if(GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 35, 100, 30),
                      "Resume")) {
            this.enabled = false;
        }
        if(GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2, 100, 30),
                      "Quit Game")) {
            // Just doing this check to show the button was pressed since Application.Quit()
            // doesn't have an effect in the editor.
            if(Application.isEditor)
                Debug.Log("'Quit Game' button clicked!");
            Application.Quit();
        }
    }
}

Hopefully the comments in the code clearly explain what's going on.

Monitoring the Input

Now we need something to monitor input so we can toggle the menu! It may not be obvious from the above code, so you might be tempted to add in an Update method to the class to check for input, however this won't work. When this script is attached to a game object in Unity, you are going to set it as diabled by default. If it is disabled then Unity won't be monitoring its' Update method. That said, I created this very simple game manager class for demonstraction purposes:

using UnityEngine;

// This is about as basic of a "game manager" class as it gets.
public class GameManager : MonoBehaviour {
    // Reference to the InGameMenu script.
    InGameMenu igm;

    void Awake() {
        // Assumes this script is on the same object as the InGameMenu script.
        igm = gameObject.GetComponent<InGameMenu>();
    }

    void Update() {
        // Monitor for 'm' being pressed.
        if(Input.GetKeyDown(KeyCode.M)) {
            // Toggle the menu.
            igm.enabled = !igm.enabled;
        }
    }
}

That's the code, now what do I do with it?!

I am going to be brief in this section as I assume you are familiar with Unity. If any of this doesn't make sense feel free to leave a comment asking for help, or go check out the Unity documentation.

  1. Open Unity and create a new project with no imports.
  2. In the project window you are going to create 2 C# scripts named InGameMenu and GameManager.
  3. Open those scripts in your IDE and add the code we just went over into their respective files.
  4. Add a GameObject to the hierarchy and attach those 2 scripts to it.
  5. Create a Quad.
    • Reset its' transform.
    • Set the xPos = 0, yPos = 0, zPos = 5
    • Set the xScale = 100, yScale = 100, and zScale = 1
  6. Create a Material.
    • Set the shader to Transparent/Diffuse.
    • Set the color to somewhere around (58, 58, 58, 190).
    • Attach this material to the quad.
  7. Create a prefab out of the quad, and delete it from the hierarchy.
  8. Select the GameObject.
    • Disable the InGameMenu script.
    • Set the quad prefab you just created as the "Menu Background" object on the InGameMenu script.

Now run the game, and while keeping an eye on the Console hit the 'm' key. It will toggle on the in-game menu giving you 2 buttons to choose from. In the Console you will see it prints "PAUSE". If you then hit 'm' or click the "Resume" button it will toggle off the in-game menu, and print "RESUME" in the Console.

There you have it, a simple in-game menu than can easily be extended to be as fancy as you'd like. You can check out the repository for this project here, and it is also listed on my projects page.