Let’s keep working on our RTS and add a main menu scene!
This article is also available on Medium.
Last week, we worked on the organisation of our project and we worked on our Unity scenes to prepare everything for a main menu, based on a multi-scene workflow.
Today, let’s start designing this main menu! We won’t wrap everything up in just 10 minutes, so we’ll do this in two parts. And to begin with, let’s create some UI to dynamically list the available maps and show up some details. Then, after the Christmas break, we’ll finish this by connecting the menu and the game scene and propagating all the necessary data!
Making a simple menu scene
The main menu will live in its own scene: the “MainMenu” Unity scene. This scene will mostly contain UI elements, but I’m also a real fan of the original Warcraft 3 menu (no, I won’t talk about the “reforged” edition… that rant would be too long!), so I’ll take this opportunity to have some 3D elements on the side too, as decor 😉
Note: in this tutorial, you can directly open this scene in the Unity editor and run it as standalone, without the rest of the multi-scene workflow. We’ll see next time how to integrate it in the rest of the project!
Here’s for example a basic screenshot of my menu scene:
Of course, you can create our own 3D decor and import your own 3D meshes, and you can also place the UI elements as you wish. And if you really have some time left on your hands, you can even think of animating some element somewhere (like, in my case, adding a flag to the tower or a fire in front of it for example)… 😉
The important thing for the rest of this tutorial is that my UI canvas contains:
- the buttons that are visible initially, on the left: the “new game” button will open the panel to create a new game, the “quit” button will exit the game and, very soon, we’ll add a “load game” button, too
- the “new game panel” is hidden at first, and it will be revealed when we click on the “new game” button; it’s similar to our settings panel in the game scene, with a clickable background overlay that can close it back, and the content in a “wooden-styled” panel in the middle:
But, you guessed it: we don’t want to populate this list of maps by hand! Instead, we want to generate these selectable items dynamically – and it’s the same for the details panel on the right, it has to be contextual, depending on the map that is currently selected on the left 🙂
Automatically listing the maps
First of all: in order to get my list of map captures via script, I’ll need to move them to the “Resources” folder. That’s why I’ve modified my minimap capture tool to have it store the map images in a “Resources/MapCaptures” directory:
My map metadata (stored as Scriptable Objects) is already in the “Resources” folder, so it will be easy to access 🙂
And this is done in a new C# class, the
MainMenuManager – you can place this on an empty “MANAGER” object in the “MainMenu” scene:
Note: also, I’ve added some C# regions to better group my functions and sort through the script if/when it gets longer 😉
_PopulateMapsList() method is called when the scene starts and it simply needs to go through the folder of maps metadata to create one item for each. The list will be contained in a scroll view – this way, if we have lots of maps to show, we’ll be able to scroll through them!
Also, those list items are instances of a UI prefab, “MenuScenePick”, that has the following hierarchy:
Note: I won’t be sharing all the details about these UI elements in the article, but if you want more info, be sure to check out the project on Github! 🚀
The script also assigns the data dynamically to get the proper map picture, name, size display and max number of players:
Finally, we have to make sure that those items are clickable and that we can select a map from the list to play on. We’ll do this by adding a callback function – and remember that, as we saw in a previous tutorial from this series, we need to “extract” this to another method because we’re creating those in a loop and we could have local variable issues:
For now, the
_SelectMap() doesn’t do much – it just sets some data… but let’s see how to improve it so that it also shows up the map details and allows us to set up the players!
Showing up more details on the map
When we click on an item on the left, we want to use the right part of the panel to show the exact size of the map, its map capture as a larger image than the list thumbnail and, most importantly, the list of players.
I’ve prepared the UI of the map details, so I won’t have to re-create it but simply re-update it when I pick another map:
For the players list, I want to dynamically create as many slots as the max number of players of the selected map and, just like before, I’ll rely on another UI prefab, “PlayerPicker”:
Those player slots have the following elements:
- a colour selector: this button will show/update the colour of the player for this game – I’ll re-use the player banner from our game UI
- a name input: this text field will show/update the name of the player for this game
- a toggle button: this allows us to enable or disable a player slot from the list – you may not want to play on a map for 4 players with 4 players every time!
These elements will be configured in my C# class, in another set of methods that should spawn and setup the players list, in addition to adding a callback to their various elements.
To begin with, let’s simply create and update the map info, the map image and the players list in the details sub-panel:
For the map info, I’m using Unity’s rich text feature that lets me add some specific tags to the text to apply effects, such as a custom font size (or bold/italic font, etc.).
We need to prepare some
PlayerData instance for these soon-to-be players, too – so let’s prepare some basic player colours, and also a simple constructor for the
PlayerData class. Let’s define our list of possible colours as a static variable in our
MenuManager – I’ll stick with Unity’s pre-defined palette but it’s a bit “harsh” on the eye, so feel free to spend a bit more time finding nice colours 😉
After instantiating my player list items, I simply have to set the various inputs with default values (I’ll leave aside the toggle for now, we’ll discuss it in a few seconds):
Now, I want to setup the different callbacks for my players list item elements: the colour button, the name input and the toggle on the right.
Changing the colour
To change the players’ colours, I’ll have little colour pickers that will be enabled by clicking on the colour icon of one list item. I’ll use my set of 8 possible colours and I’ll need to make sure that no two players have the same one.
Here is the sub-part of the hierarchy we’re interested in – as you can see, the pickers are initially hidden (the game object is disabled):
The “Col-0”, “Col-1″… elements are simple buttons that will each have one possible colour and assign it to the selected player:
The panel will stay hidden until you’ve clicked on the player’s colour input. And we’ll want to use the
_playerColors variable again to setup the colour pickers elements. Finally, we’ll hook these colour pickers to indeed change the player’s colour (both in the data and visually, in the UI), by assigning another callback function to each –
At this point, if we click on the colour flag of a player list item, we get a little palette on the left to pick a new colour from, and we can change the player’s colour 🙂
But remember we also want to insure that players each have a different colour! So I should disable the ones that are already taken to avoid any duplicates… I’ll do this by having another list of colours,
_availableColors, that is initially the same as the static list and then gets added or removed the colours depending on what the players currently have.
I’ve also added a bit of logic so that, if I toggle players on and off, their current colour is taken from or pushed to the
_availableColors list; and if you happen to toggle on a player which was previously assigned a colour that is now unavailable, this colour will automatically get replaced by the next available one!
(Yep – that’s a bit of a preview of the toggle feature we’ll add in the following section!)
Setting up the name
For the player names, it’s actually way easier – we’ll simply have our own callback on the “onValueChanged” event of the UI text field input:
Toggling players on and off
The last option I want for this list of players is the ability to toggle some on or off.
For now, we haven’t set a limit to how many players can be on a given map. So, if you decide to add a hundred spawnpoints, then you’ll be able to add up to 100 players when you create a new game!
First, that’s why I used another scroll view for this part of the interface. If I ever get more slots than what my screen can fit, those will simply be scrollable in this area.
Second, it means that we should allow the players to easily play smaller sessions on those maps, too. So, we want to make it quick to select how many players there are, and if some slots aren’t used this time.
To do this, I’ll add the notion of “active players” in my
MainMenuManager, and I’ll use my toggle on the right to turn these active flags on and off.
- we don’t want to ever toggle off the first two slots because those are for your own player data, and your first opponent – and you need at least one to play the game!
- we want these two first slots to be active by default
So let’s insure we only have interactable toggles starting with the third slot (if there is one), that the toggles call the right piece of logic and that the first players are toggled on:
At that point, we have a little toggle that indicates whether or not a player is active, and we’ve stored this info in our
There is, however, a small improvement we can add: when a player is inactive, we don’t want its colour and name to be editable: let’s block it with a UI element!
In my “PlayerPicker” prefab, I have prepared some “curtains” that can slide up and down on the colour and name inputs, like this:
These images will block any click that is made to the UI elements behind them so, if they completely cover my inputs, they’ll essentially disable the edition.
To bring them up or down, just for fun, we can use a little coroutine so that it happens in half a second, instead of popping in and out instantaneously:
Note: be careful – some of the hardcoded values in here come from my own UI elements, sizes and locations; so you might need to adapt them to your interface! 😉
Tadaa! We now have a nice toggle feature and we can choose how many players we want for this session 🙂
Today, we’ve started to work on our main menu scene and we’ve prepared all the UI elements we need for now. We’ve also made sure that most of it is auto-generated via script so that we can create as many maps as we want without worrying about updating this menu.
I’ll be taking a winter break next week but when the new year comes, we’ll see in part 2 of this sub-tutorial how to store the important data in our shared data source and start a new game from this menu!
In the meantime, I wish you a merry Christmas and New Year – see you next year! 😉