This month I’ve been straying well outside my comfort zone to work on an in-game menu allowing players to change various quality settings during runtime. Unity offers a standard, built-in menu that appears when a game starts up but it hides several settings under “Quality Levels”, has a rather confusing input editor and requires the player to restart the game in order to make any changes. It’s also not customizable to any great extent and doesn’t really create the best first impression, so I thought I’d try and make my own.
All the menu elements are 3d objects as opposed to uGUI buttons, I’m using Playmaker for all the scripting and Easy Save 2 for saving/loading data and, as I haven’t completely nailed down how I’m handling input yet, the setup responds only to mouse and keyboard input, hopefully controller and touch wont be too tricky to add in later on…
These are all the settings I currently have working; toggle-fullscreen, screen width and height, anti-aliasing level and vertical-sync. (Playmaker actions for accessing them are here and here). Settings Menu Update, Settings Menu and Resolutions are empty objects at 0,0,0, everything else is a menu button. (You can ignore everything from Main Camera 60 down to Field_Guide in the hierarchy) At the moment all the buttons are just Text Meshes, it made them easy to edit and I could just set the font to bold and back to indicate button state, I’ll be changing most of them to icons at some stage but it should be pretty straight forward to swap them out for any kind of set up.
As well as being able to accept new settings, the menu should indicate what the current settings are, if this is the first time the game has been run there also need to be some defaults already in place, it also should have a temporary method of storing choices before hitting the Apply button, this should only become active when the player has made changes. Here it is in action:
The Saving and loading of values is handled by the Settings IO FSM on Settings Menu Update:
On starting, IO checks to see if there is a settings file already available, if there is it loads the relevant values, updates the local variables and the actual quality settings themselves. If there is no file available it creates one out of a set of default values, then loads/updates. The red state on the left is where updated values from the various menu buttons are dealt with. The variables should mostly be self explanatory, the setting for screen size takes an integer but at various time’s we’ll need to find the aspect ratio as a float so we’re storing Screen Width / Height as both.
There are quite a few things being updated simultaneously and from various different places, Check Settings File Exists broadcasts an IO Busy event, the individual FSMs on buttons are paused and wait for the IO Idle event we’ll send when we’re done, this should help make sure events are firing in the order we want. We then check for a playerSettings file using the Easy Save action Exists.
If no file exists we need to set some defaults, getting the Screen Width and Screen Height here will take the current screen dimensions as floats, this should give you the native resolution providing you have that set to default in your build settings. We convert and store them as integers as well.
Save Defaults and Save New Settings are series of save actions to store relevant settings in the newly created playerSettings file. Save Defaults only runs if the initial check doesn’t find a settings file, it uses pre-set values from the state rather than stored variables, by default I have fullscreen set to true, anti-aliasing set to zero, v-sync set to every v-blank, resolution set to the native width/height we got in the previous state. After a settings file is created we use Save New Settings instead, this will overwrite values in the file with the values from our local variables, this state is only entered via the Apply New Settings IO global event. Load Settings is then the equivalent load actions from the same file and Apply Settings uses the quality settings actions to apply these values to their respective property. Finally, Idle broadcasts an IO Idle event and waits for further input…