Very Noob Question About Pause Menu Script.

Hello,

I’m hoping this is the right place to ask this. I’m pretty new to writing any sort of code (I’ve only ever taken one Java based class on modeling physical systems for my Physics degree, so far) and I’ve only been playing around with Unity for a few days.

I have been watching several video tutorials on youtube and was (roughly) following a tutorial by Brackeys about making a pause menu, but I can’t seem to get my script to work the way I want it to. What am I doing wrong?

I’ve used [SerializeField] to create a GameObject called pauseMenuUI and dragged a simple panel with Resume, Options and Quit buttons in the Inspector window, but nothing happens when I press escape. I would really appreciate it if someone could point out what I’m doing wrong as I’ve been playing around with it for a little while and I’m confused as to why it doesn’t work.

tldr: This script doesn’t work and I don’t know why:

using UnityEngine;
public class PauseMenuScript : MonoBehaviour
{
    [SerializeField] private GameObject pauseMenuUI;
    private bool GameIsPaused = false;
    private void Start()
    {
        pauseMenuUI.SetActive(false);
    }
    private void Update()
    {
        if (Input.GetKeyDown("escape") && GameIsPaused == false)
        {
            PauseGame();
        }
        if (Input.GetKeyDown("escape") && GameIsPaused == true)
        {
            ResumeGame();
        }
    }
    private void PauseGame()
    {
        pauseMenuUI.SetActive(true);
        Time.timeScale = 0f;
        GameIsPaused = true;
    }
    public void ResumeGame()
    {
        pauseMenuUI.SetActive(false);
        Time.timeScale = 1f;
        GameIsPaused = false;
    }
    public void QuitGame()
    {
        Debug.Log("QUIT!");
        Application.Quit();
    }
}

Hi and welcome! :slight_smile:

I just watched the video for reference and noticed you changed a couple of things. For example, why do you write “escape”, instead of KeyCode.Escape? While not wrong (to my knowledge) this just seems like a more inconvenient and more error-prone way to handle this. The way you set up your Update() method is also slightly different from the video in general, but should work the same. One thing to note is that it is bad practice to compare bools to true or false. So instead of “GameIsPaused == true” you can just write “GameIsPaused”, since true == true is the same as true. The same goes for bool == false, in which case you can just write “!GameIsPaused” (read: “not GameIsPaused”).

But none of the above should change the functionality. And quite frankly i dont see anything wrong with the script.
Is the script attached to something? Else it wont get called. In the video Brackeys attached it to the Canvas object. Is your scene structured similar to his? Does the GameObject you dragged into “pauseMenuUi” actually containing the Ui, and is the Ui actually visible if set to true (as in, not empty/transparent)?

As a rule of thumb, if you want to follow a tutorial, do everything exactly as shown there. Especially if you are not confident in programming (or Unity) yet. You can change things step by step after the tutorial is done and works for you, since you then have an easy reference for “if i change this it doesnt work anymore”, which helps you figure out how to fix it.

And while Brackeys is a good tutorial channel, i can definitely recommend the gamedev tutorial series on Unity and C# by Sebastian Lague. He explains both, the basics of C#, as well as the basics of Unity, gives tons of little exercises and examples and is generally just really good at explaining things in a logical order. I said “basics”, but he really covers a lot of ground! If you are new to Unity and programming in general, then that should prove extremely helpful to you.
Sebastian Lague Gamedev Series #1

1 Like

I appreciate your detailed response. I did change a few things from the original tutorial. I used “escape” rather than KeyCode.Escape because I had seen that used with Input.GetKeyDown in an unrelated tutorial and it was simply fewer keys to type. I’ll use KeyCode in the future if that works better.

I have tried attaching the script first to my Canvas object I created, but then moved it to be on the player because I wasn’t sure if it would run if it was attached to an object that isn’t active?

The UI GameObject I dragged into “pauseMenuUI” is visible as I added the private void start line because the UI GameObject was being loaded when I ran the game in Unity, but without setting the timescale to 0 in order to pause the game (I could still move and such) like it was some sort of overlay, then escape would actually “pause” it (setting the timescale to 0) and pressing it again would close it and “unpause” but after that escape wouldn’t do anything else.

I experienced this problem with the earliest version of my script, which (as I recall) mirrored the script Brackeys wrote almost exactly. I didn’t start to deviate much from his script until I realized it wasn’t working correctly for me the way I had it written.

I had originally written just GameIsPaused and !GameIsPaused but changed them because I thought maybe that was my problem.

I changed “escape” to KeyCode.Escape and reverted the changes to GameIsPaused as you suggested, but the script still doesn’t work.

For reference, with the above changes my script now reads:

using UnityEngine;

public class PauseMenuScript : MonoBehaviour
{
    [SerializeField] private GameObject pauseMenuUI;
    private bool GameIsPaused = false;

    private void Start()
    {
        pauseMenuUI.SetActive(false);
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape) && GameIsPaused)
        {
            PauseGame();
        }
        if (Input.GetKeyDown(KeyCode.Escape) && !GameIsPaused)
        {
            ResumeGame();
        }
    }
    private void PauseGame()
    {
        pauseMenuUI.SetActive(true);
        Time.timeScale = 0f;
        GameIsPaused = true;
    }
    public void ResumeGame()
    {
        pauseMenuUI.SetActive(false);
        Time.timeScale = 1f;
        GameIsPaused = false;
    }

    public void QuitGame()
    {
        Debug.Log("QUIT!");
        Application.Quit();
    }

}

I will definitely check out Sebastian Lague’s series you suggested, thank you!

Also, a question I had when writing this script. If I were to use an else statement, rather than a second if statment, i.e. replace

if (Input.GetKeyDown(KeyCode.Escape) && !GameIsPaused)
{
ResumeGame();
}

with

else
{
ResumeGame();
}

would I theoretically achieve the same result? Or would it resume the game during the next frame that escape was not pressed?

No, the whole point of an object being inactive, is that its components dont run. In the video however, the Canvas was never set to be deactivated. Deactivated is its child component, which contains all of the UI elements, including the slightly black-transparent panel. Putting the script on the player is a bit weird (design-wise), but it should technically work the same.

If i correctly understood you, then the UI was visible from the beginning (without pausing the game), pressing ESC once paused the game, then pressing it again unpaused it and closed the UI? If so, that makes sense. The UI is active by default, so you see it. Pressing ESC then activates it, which does nothing, and pauses the game. Pressing it again unpauses the game and deactivates the UI, which makes it invisible now.
Setting the UI inactive in Start() is not wrong by any means, but you could also just manually disable your UI parent gameobject in the hierarchy, making it off by default. Both approached are valid, i just wanted to make sure you understand the above “bug”.

With that exact change it would immediaely resume once the key was not pressed anymore. That is why Brackeys in the video made it two separate if-statements. If the ESC key is pressed, then it is checked if the game is paused. If so, resume, else pause. Hope this makes sense.
It comes down to: false && anything = false. So as soon as you let go of the ESC button, it enters the else{} part. By nesting the two if-statements like in the Brackeys video, the whole thing only gets looked at if ESC is pressed, thus the only important thing is GameIsPaused, which can only be true or false, making if-else a very clean solution.

I’ll take a look at your script right now…
… and through the magic of only posting this comment after i’m done, here is my solution to your problem:

You confused the correct boolean state for pausing and resuming - or rather it was probably just a hard to notice mistake. If GameIsPaused, you pause the game. If !GameIsPaused, you Resume(). Since GameIsPaused starts with false, it cannot ever become true. Instead, you want to pause the game if !GameIsPaused, and resume the game if GameIsPaused.
This technically fixes your bug, but when you test it you will see the same result. This is the case because now both cases will trigger after each other. If !GameIsPaused, you pause the game, making GameIsPaused true, which means we directly enter the other if statement and resume the game again - effectvely doing nothing.

In this case you only want to do one or the other, or in other words you actually need an if-else statement. You can either make two nested if-statements like in Brackeys video, or you can make a conditional else-if, looking like this:

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape) && GameIsPaused)
        {
            Debug.Log("Resume");
            ResumeGame();
        }else if (Input.GetKeyDown(KeyCode.Escape) && !GameIsPaused)
        {
            Debug.Log("Pause");
            PauseGame();
        }
    }

I personally find the two nested if-statements like in Brackeys video a bit more clean-looking, but both are fine. The above change fixes your problem for me. You can also remove the Debug.Log calls, but on that note: Debug.Log() is awesome for debugging purposes. You can print some messages when you enter certain functions, or if-statements, or you could print certain values to get a better idea of what’s happening. By simply printing “Resume” and “Pause” to the console in their respective calls, it was pretty easy to see what was going wrong. Try testing it (in your code) before copying the solution, maybe you can arrive at the conclusions on your own. Learning to work with Debug.Log() to find problems is definitely a very helpful skill.

Also, a very small advice: variables should be written in camelCase, so GameIsPaused should rather be gameIsPaused. I know this comes from the Brackeys video, but dont pick up bad habits hehe. Write camelCase by default, use UpperCamelCase for method, property or class names. There are some other naming conventions, but those are the most important to write code that is easily readable for other programmers in C#.

Hope this helps :slight_smile:

Please provide the output of your Debug.Log statements (and put more in!)

Whom is this directed towards? OP had no Debug.Log statements, and I just kept them in my example to show OP how you can find a problem. Thus i’m not quite sure what your comment is about.

Exactly! We need the output.

Is it really “bad practice”? It is a few unnecessary keystrokes, but is easier to understand for someone just learning to code when everything is like learning a foreign language.

I did it too at the beginning, but it is simply unnecessary, additional effort to write, creates longer lines, adds (unnoticably more) computations and is thus bad practice by any definition of bad practice, i’d say. However, while i believe it is the right thing to tell people about these bad habits or bad practices so they can get rid of it, especially if the only reason they do it is that they dont know better, of course everybody is free to decide for themselves if they want to change that or not :slight_smile:

1 Like

Thanks so much for taking the time to write such a detailed response! It works perfectly, and I feel I’ve learned a good bit from your explanation. I actually just realize earlier today that I had watched some of Sebastian Lague’s “Coding Adventures” videos a few days ago. I have very much enjoyed them, so far, and I’ll watch his Game Development series over the next few days.

And thank you for pointing out the convention of writing variables in camelCase. To be honest, despite seeing the it numerous times, I had never heard of the practice until now. Shows you how new I am, I guess. I have been having a lot of fun learning what I have so far, though, and I feel myself slowly become more capable.

Anyway, thanks again for taking the time to find my problem and, even better, giving me such detailed explanations and suggestions.

1 Like