Infinite Roller Tutorial Six – Menu

Welcome to Edge of Code’s series of Infinite Roller tutorials. If you’d prefer a video tutorial, you can find one here.

In the previous tutorial, we added random gaps to the terrain, made the character die when it fell through them, and stopped the character’s ability to jump multiple times. Today we’re going to create a menu using Unity’s UI system.

Open your Infinite Roller project and the scene, then right click in the hierarchy, go to UI (User Interface), and click on Canvas. This creates two new objects – a Canvas, and an EventSystem gameobject. If you click on the Canvas in the hierarchy, you should be able to see an empty rectangle in the Scene view (you might need to zoom out, or with the Canvas selected, move the mouse over the Scene view and press ‘f’ to find it). This is basically a holding area for everything you want to be in your UI; any other UI objects you create in this scene will be child objects of the Canvas. In game, the UI automatically appears over the top of the rest of the game. This can be changed so that a UI is within your game, but we won’t go into that in this tutorial. The EventSystem gameobject is there to handle what happens when you press a button placed on the canvas.

Next, right click in the hierarchy, go to UI and click Panel. This adds a child Panel gameobject to the Canvas and allows you to change the colour of the background of the menu or add an image if you want to. UI objects have a RectTransform rather than a Transform, and I’ll talk more about that when we change it later in the tutorial. We’re just going to change the colour – with the Panel selected in the hierarchy, click on Color under the Image component in the inspector. Pick a colour in the picker that pops up. I’ve made mine purple.

You can always see what the menu will look like in the Game window. At the moment it’s just a plain purple overlay. We’ll add some buttons now – click on the Panel, then right click in the hierarchy, go to UI and click Button. A Button gameobject that is the child of the Panel should be created, and you’ll be able to see a white button in the Scene and Game windows. You can change the size of the button by dragging the corner circles in the Scene view, or move it by dragging the Button object itself, or you can do either of these things by altering values in the Button’s RectTransform in the inspector.

If you change the size of the Game window by dragging the bar next to the Project window, you can see that at the moment, the button stays the same size no matter what size the game is. We’re going to change this so that the button increases/decreases in size proportionately to the Game window size. To do this, you can change the setting in the RectTransform’s Anchor presets (accessed by clicking on the anchor image). I suggest playing with these settings to see what they do. However, we’re going to do this slightly differently – delete the Button gameobject and with Panel selected, right click, then click on Create Empty. When a UI gameobject is selected in the hierarchy, this option creates a new empty UI gameobject with only a RectTransform component. Click on Add Component and type “layout” into the box. Click on Vertical Layout Group. The buttons will be children of the object. The Vertical Layout Group controls its children’s spacing and alignment. Rename the gameobject to Layout, then right click on it and create a new Button. You should get a square button in the middle of the Canvas.

Click on Layout, then click on the Anchor image in the RectTransform and select the bottom right image.

Unity tutorial menu layout

This stretches the Layout with the size of the game. Now change its Left and Right values to 75 each. This will stretch the Button – we’re using the Layout gameobject to size all of its child buttons. To see this better, add another button (Right click on Layout, go to UI and click on Button). Both Button objects are equal in size and positioned in a vertical grid. Each Button’s RectTransform is greyed out because the Layout object is controlling them.

Click on Layout again. You can also change the Top and Bottom values in RectTransform to change the height of the buttons. I’m going to decrease both slightly to 120 for now, but when we add a title in later, I’ll change them again.

I want the buttons to have a larger gap between them. To do this, make sure Layout is selected, then change the Spacing value to 10 in the inspector.

Click on the triangle next to the first Button in the hierarchy and then on its child Text gameobject. This lets you change what the button says as well as its font. Change what’s written in the text box in the inspector to ‘Play Infinite Roller’ and click the Best Fit check box. This reduces the size of the text when the button is smaller to a lower limit of Min Size, or increases it up to the Max Size value if it’s larger. Change Max Size to 30. Click again on the gameobject in the hierarchy, twice, and rename it to Play.

On the child Text gameobject of the second Button, change the text to say Quit (also rename the gameobject to Quit), then tick the Best Fit box so that it matches the first button.

Next look at the Play gameobject’s Button component – we’re going to change the colour of the background of the button. Click on Normal Color and pick a colour. I’ve chosen a turquoise-ish colour. Before you close the colour picker, copy the code from the Hex Color box and paste it into a Notepad file – we’ll want to use the same colour for the Quit button.

Unity tutorial menu button colour

Next, click on Highlighted Color. This is the colour the button will change to when the mouse hovers over it. I’m going to choose a different shade of my background colour – a slightly brighter turquoise – by pasting the Normal Color into the Hex Color box and moving the picker upwards. Copy the new Hex Colour and paste it into your Notepad file. Do the same for the Pressed Color. Again paste the new Hex Color into your Notepad file.

Go to the other Button gameobject and click twice to rename it Quit. Change its colours by pasting each of the Hex Colors into the appropriate places.

Save the scene and press play. Hovering and clicking on the buttons should change their colours. Stop the game. Later in the tutorial, we’ll make the buttons actually do something, but first we’re going to add a title and a couple of lines of instructions on playing the game. Select the Layout gameobject in the hierarchy, right click, go to UI and click on Text. This adds a new Text gameobject to the controlling Layout. At the moment, it’s underneath the buttons. To move it, we just need to change its position in the hierarchy. Click on the Text gameobject in the hierarchy then drag and drop it to above the buttons (but make sure it’s still a child of the Layout object). Click on it twice and rename it Title. Change the text in the inspector to say Infinite Roller. Click on the centre alignment option and click Best Fit. Change the Min Size to 20 and Max Size to 60 – since it’s the title, we want to to be larger than the button text. Change the Color option to black to make it stand out.

Let’s add the instructions next – add another Text gameobject to the Layout, but this time drag it below the Title object and above the buttons. Rename the gameobject to Info. Change the text to say something like “Don’t fall through the gaps! Press space bar to jump and twice to double jump.”

Select centre in the Alignment section. Tick the Best Fit box, change Max Size to 30, and change the Color to black. Now the Scene view shows the buttons looking squashed. Click on the Layout gameobject and change Top and Bottom in RectTransform to 50.

Now we’ll make the buttons work. In the Scripts folder in the Project window, create a new C# script called MenuManager. Open it and delete the existing Start and Update methods. Above the class definition, add

#if UNITY_EDITOR
using UnityEditor;
#endif

This lets us use classes in the UnityEditor namespace if we’re playing the game from Unity. Add a new method

public void QuitGame()
{
	#if UNITY_EDITOR
	EditorApplication.isPlaying = false;	
	#else
	Application.ExternalEval("window.close()"); 
	#endif
}

The method is public as we’ll make the Quit button call it when it’s clicked. If we’re playing the game from Unity, this sets the EditorApplication’s isPlaying variable to false, exiting play mode. Otherwise, we use the Application class’s ExternalEval method to close the game window by passing in “window.close()”. This is what will be used once we export our game for users. Create a new class variable

private Canvas menuCanvas;

We need a reference to the Canvas component of the Canvas gameobject as we need to disable it when we play the game. We’ll add the MenuManager script to the Canvas in a moment. Add an Awake method

void Awake()
{
	menuCanvas = GetComponent<Canvas>();		
	
	//set the timescale to zero so that it's initially paused.
	Time.timeScale = 0;
}

so that the menuCanvas variable is initialised and the game starts off paused. Add another new method

public void PlayGame()
{	
}

Inside this method, we’ll disable the canvas and unpause the game – add

//disable the canvas
menuCanvas.enabled = false;

//unpause the game
Time.timeScale = 1;

Save the script and go back to Unity. Select the Canvas gameobject in the hierarchy and drag the ManuManager script onto it to add it as a component. Click on the Play button gameobject in the hierarchy and click the plus (+) button under OnClick(). Drag the Canvas gameobject into the slot that appears. Now find and select the PlayGame method we just created in the menu to the right.

Unity tutorial menu button

Do the same for the Quit button, but select the Quit method instead. Save the scene and press play to check both buttons work as expected.

Open the GameController script. In the last tutorial, we added code to restart the game if we press R. When the character falls off the ground and dies, the menu should come back up and the game should properly restart when the play button is pressed. Add a reference to the MenuManager class

public MenuManager menuManager;

Save it, go to Unity, select the GameController and drag the Canvas gameobject (that the MenuManager is attached to) into the slot. Go back to the GameController class. Delete the Input.GetKeyDown if statement in the Update method, then replace the line Time.timeScale = 0; in the if statement checking the character’s height with

menuManager.ToggleMenu();

We’ll create the ToggleGame method now – save the script and go back to MenuManager. Add a new public method

public void ToggleMenu()
{
	//enable/disable the canvas
	menuCanvas.enabled = !menuCanvas.enabled;

	//pause/unpause the game
	Time.timeScale = Time.timeScale == 0 ? 1 : 0;
}

The line menuCanvas.enabled = !menuCanvas.enabled; enables the menuCanvas if it was previously disabled, or disables it if it was previously enabled. The ! means not here. In this case, we’re setting the boolean menuCanvas.enabled to be what it is not: true if it’s false, or false if it’s true.

The Time.timeScale = Time.timeScale == 0 ? 1 : 0; line is shorthand for

if (Time.timeScale == 0)
{
	Time.timeScale = 1;
}
else
{
	Time.timeScale = 0;
}

And is called the conditional/ternary operator. To make this slightly clearer, consider

private int GetVal(int number)
{
	int val = number == 0 ? 1 : number;
	return val;
}

If number == 0 evaluates as true (if number is equal to zero), val is set to 1. Otherwise, val is set to number. Similarly, if Time.timeScale == 0 evalates to true (i.e. If Time.timeScale is zero), Time.timeScale is set to 1, otherwise it’s set to 0. The ToggleMenu method now does the same as the PlayGame method. To make it clear what the button is doing, I want to keep the PlayGame method, so delete what’s inside and call the ToggleMenu method so that we have

public void PlayGame()
{
	ToggleMenu();
}

Go back to GameController. Call RestartGame() after the call to menuManager.ToggleMenu() in the Update method so that

void Update()
{
	if (character.position.y < GAME_OVER_HEIGHT)
	{
		menuManager.ToggleMenu(); 
		RestartGame();
	}
}

Make sure both scripts are saved, then go back to Unity and play the game. You should now be able to start the game by pressing play, and when you die, you should be brought back to the menu. At the moment though, if you try to jump, the menu will be brought back up. To fix this, stop the game and click on the Play gameobject, then under the Button component, change the Navigation drop down menu from Automatic to None. Do the same for the Quit button. With automatic navigation selected, pressing space bar is equivalent to clicking on the button. Even though the menu can’t be seen, we only disabled the Canvas rather than the gameobject itself. Hence the menu is still there, and pressing space selects the play button, which toggles the menu.

Something that would be useful here is a message telling you that you died, and perhaps what your score was and whether it was a highscore or not. We’ll add that later.

Make a second canvas gameobject (right click in the hierarchy, go to UI and select Canvas). Rename it HUD, and rename the other canvas gamsobject MainMenu. Add a new Text object to it (make sure the HUD gameobject is selected when you do this so that it’s added to the correct canvas). Rename it Score and change the Text box contents to Score: 0. Change the Font Style to Bold, tick Best Fit and change Max Size to 40. Click the centre button for the alignment. Click the anchor image in the RectTransform and select the centre top icon to anchor it to the top of the screen, then change the Pos Y variable in the RectTransform to -25 to position it.

Click on the HUD gameobject and add another new Text object. Rename this one HighScore and change its Text box contents to HighScore: 0. Change the Font Style to Bold, tick Best Fit and change Max Size to 40. Click the centre button for the alignment. Click the anchor image in the RectTransform and select the centre bottom icon to anchor it to the bottom of the screen, then change Pos Y to 23.

We’ll add scripts to actually calculate and display the score and highscore in a future tutorial. Go to the MenuManager script and and add a reference to the HUD canvas:

public Canvas hudCanvas; 

Save the script, click on the Canvas gameobject and drag the HUD gameobject into the slot. Go back to the MenuManager script to the ToggleMenu method and add

//toggle the hud canvas
hudCanvas.enabled = !hudCanvas.enabled;

We want this to be false to start with so that it appears while the game is being played and is disabled while the menu is visible. To do this we could disable it in the hierarchy, but I’m going to add a line to the Awake method because it will be useful for us to see both the menu and the HUD in the Scene view:

hudCanvas.enabled = false;

Save and go back to Unity to test this works properly. Next, go to the GameController script and create a new variable

//ref to the info text 
public Text info;

Save the script, go back to Unity and select the GameController in the hierarchy. Drag the info gameobject under Canvas onto the slot and go back to the GameController. First, cache the initial text – add a string

string initialInfoText;

And in Awake, add

//cache the initial text
initialInfoText = info.text;

After ToggleMenu in the Update method, add the line

SetGameInfoText();

And create a new method

private void SetGameInfoText()
{
}

For now, just add the line

info.text = initialInfoText + "\n\nYour score is SCORE. This is or is not a new highscore";

inside the method. Each \n means we’re adding a new line. In a future tutorial, we’ll make this display the actual score and display different text depending on whether this is a new highscore or not.

Save the script, then check this works in Unity. Save the scene and the project.

In this tutorial, we’ve created a menu system to start or quit the game, and started to create a HUD. Next time, we’ll add some rules to randomise the terrain height so that our game is more interesting.

Remember you can download the files for this tutorial on the Downloads page. See you next time!

PreviousTutorialButtonNextTutorialButton

Leave a Reply

Your email address will not be published. Required fields are marked *