Building the Blade Ballet Quality Settings: An adventure in JSON, IMGUI, and Reflection!

Hello! This is Neil, a Co-Founder and Programmer here at DreamSail ( Also the Game Design Lead for Blade Ballet). I’ve been meaning to write a post forever on the cool technical stuff we do to get Blade Ballet stuff working, but I’ve failed tremendously on actually putting pen to paper. But now as we approach our release date (August 9th!!!) I regained the spark and with some encouragement I’ve finally brought some interesting stuff for you all to check out. 

A quick note before I get into it, I’m going to write this with developers in mind, but this isn’t meant to be a tutorial. This is really just a broad exploration of some of the concepts I used to build a feature in Blade Ballet. That being said, If you want clarification or more info (I can talk all day) then let me know in the comments. Or equally if I’m wrong about something or you have any suggestions, definitely let me know that as well! I can also be reached on twitter by @NoNameGhost.

So without further ado, let me tell you all about my latest work on Blade Ballet: Building the Quality Settings. Now that might not seem too exciting, and to be honest it’s probably not, but what excites me is some of the things I did to complete the task, and the implications it could have on our future development work.

The Problem

This quality settings menu was a task we pulled up to address something that we had neglected for too long: low-end computers. Since the game was intended for both PC and PS4, we developed with the PS4 as a performance standard. The assumption was that as long as the game ran well and looked nice on console, PC’s would not have any problems running the game either. This was fine for our development machines, but down the line when we started having people play beta versions externally, we realized that people with low-end PCs suffered from worse performance. PCs span a wide spectrum, and with so many different combinations of hardware it’s impossible to ensure that the graphics of our game will work nicely with everything. So, drawing upon the experience left to us by almost every other PC game in existence, we decided to design/expose some graphical quality controls so people could get the best experience for their performance level.

The Easy Way?

So Unity actually tries to streamline this process by automatically generating default quality settings when you make a new project. These quality settings are then selected by the user in Unity’s neat little game launcher (See Above). On a large scale project like Blade Ballet, however,  Unity’s quality settings are a bit too generic to be effective alone. Playing the game in the “Fastest” quality wouldn’t improve our game’s performance enough.The real problem was our Custom Shaders, Renderers, and other game content, which Unity’s Quality settings had no method of properly optimizing/disabling. An example would be the Camera Effects, which are scripts running each frame that apply post-process effects like Bloom, Color Correction, and Tilt-Shifting. The overhead for these effects are pretty trivial for high-end graphics cards, but they eat frames that cannot be afforded on lower end machines. All these problems, and the fact that these quality presets would not expose individual settings (AA, Aniso, etc…) to the player, made the Unity Quality setting presets a no-go, meaning I would have to do some actual work.

The less easy but still surprisingly straightforward way.

My solution would consist of mixing the engine quality settings with our own game specific settings, and exposing all of those settings to the player in-game. So I started off by creating a “QualityManager” class which would store these settings and inform the rest of the game about them. Whenever it detects that a setting has been changed, it will update the engine quality settings AND fire an event that other quality-dependent components in the code could listen to.  I then worked with our art lead on identifying which specific quality settings we wanted to enable, resulting in a whiteboard full of settings that we could tweak. 

With that in hand I finally get to the meat of this story. I needed to accomplish three things from this point: 

  1. Creating a way to store these settings so player’s preferences are saved.
  2. Creating a simple way to debug/test the effectiveness of the settings.
  3. Building the actual in-game UI.

JSON is life.

Saving data can be done in many ways, but when dealing with player controlled settings it’s usually nice to have things saved as a config file. This means that the game’s settings are written to a human readable file that can be edited outside of the game, and are then interpreted by the game during runtime. So ideally the format is something easily used by both humans and computers. At DreamSail we prefer to use JSON. It stands for “JavaScript Object Notation” and is a great type of text-based object description. It’s useful for representing object structures in a similar way to how any OOP language constructs them, but is still easy enough to read and edit. This is how I would be represented in JSON:

It's cats all the way down

It's cats all the way down

As you can see, I can store multiple data types in “code format” style instead of just raw text. The above example includes strings, numbers, and bools. The curly brackets are the encapsulation for an object, with each line being a different “field” that exists on the object. Square brackets are used to define arrays of a type. (JS allows for arbitrary types in an array, but C# generally expects a single type, so beware)

The trick, however, is that JSON is written in a manner similar to code, which means it needs to be formatted properly. If you forget a comma or a closing bracket, the interpreter will likely fail to read. Many programmers would prefer something that looks like this:

blah4 = cats

This format, or something similar, provides the advantage of being harder for a user to mess up by being line based and forgoing extraneous formatting. But that means it lacks the same features as a widely supported standard like JSON and will probably require a custom interpreter to be written. The choice is very project and team specific.

In our case, I chose JSON because we already use it in other places within the project (Our game modes and their settings are all defined in JSON). Plus it’s nice to work with JSONFX, a JSON serialization plugin, since it allows me to do some cool things in regards to Serializing/Deserializing JSON data. Primarily, it allows me to treat data classes as “Templates” for the JSON that I’m reading/writing. It does this with code reflection (Which I will talk about in a bit) and a lot of logic for writing/reading most standard C#/Unity types.

My “QualityConfig” class is an example:

Beep beep boop

Beep beep boop

This class is instantiated by “QualityManager” and represents all the settings that Blade Ballet cares about related to graphics quality. It also serves as a template for the JSON file that we save with the user’s settings. When I serialize the class with JSONFX, I get this:

boop boop beep

boop boop beep

Exactly what I wrote in code! JSONFX is smart enough to know how a type should be represented in text, and then using the template it knows exactly how to convert a text value in the JSON back into the original type. (You can also make custom “read/write” functions in case the default settings are not enough).

And the code that does this is pretty simple.

Reading

Reading

For both reading and writing we do pretty standard file system checks to ensure that the directories/files we want to work with exist. For reading, we then get a StreamReader to read all of the JSON file’s text into a string, and then use JSONFX to deserialize it into our Config instance. For Writing, we set some options (Pretty Print makes it look nice) and then do the exact opposite, “stringifying” the class and then writing the output text to the config file.Just add some error handling and validation and there’s all the file code you need. 

Writing

Writing

A small thing to think about before moving on, however, is exactly where the file should be stored. Unity provides a string at “Application.dataPath” that represents the data folder of Unity project. On Windows, this is the “_data” folder that appears next to the game’s .exe file. In the editor it’s the “Assets” folder. Each platform has a specific path it uses. In many cases this is a fine place to leave game files, especially if the game is meant to be portable. But in our case this was a problem because our PC version is being released on Steam. Whenever the Steam client updates the game files, any loose files in the game’s directory might be deleted or corrupted, meaning that the player’s config files would be reset on the update. The answer here is to save the file based on the “Application.persistantDataPath” string. On Windows this points to the “hidden” app-data directory which won’t be touched by Steam during an update

Debugging UI and Reflection

The inspector

The inspector

The second objective I had was to figure out a good way to debug the Quality Manager which could also help our Art Lead inspect the game and see the effects of our quality changes. My initial thought was to simply have him use the inspector.

One of Unity’s greatest features is its ability to serialize stuff in the inspector and generate the controls to edit each field. This makes debugging things in the editor really easy over having to work with hardcoded stuff. However, I had a few issues with this answer. Firstly, at the time I had all my quality settings set as properties (JSONFX was not working with fields due to a setting I missed) and the inspector does not display properties. Second, it’s only available in the editor, so it doesn’t help if we’re running a standalone build. Also if I wanted to have buttons for saving/loading I would need to write a whole custom inspector for QualityManager, which didn’t seem worth it with the other two points. So, mostly propelled by my programmer curiosity, I looked for a way to view/edit the quality settings without being tethered to the editor. So I made a “DebugQualitySettings” component that would show an IMGUI with a bunch of controls for tweaking the settings on the fly.

IMGUI, You're GUI, We're all GUI

IMGUI stands for “Immediate Mode GUI”, and looks like this in Unity:

It’s designed to be data-driven, basic UI for debugging programs. The overhead is generally pretty limited too. Instead of setting up a file with UI elements or designing it in an editor, IMGUIs are actually created and drawn each frame in a function (hence “Immediate”). So calling a hypothetical function like “GUI.Box(0,0,100,100)” would draw a 100x100 box at 0,0. Calling that in an update function would make the box appear every frame. If the function stops being called, the box will not persist. It does not cache any objects or data, only drawing/using what is called in a function that frame. (This is opposed to “Retained UI”, which Unity also has, that uses persistent data that is read/drawn by the renderer.) 

Unity does IMGUI stuff in the OnGUI() function, which is a method that any Monobehaviour script can use (Just like Start, Awake, or Update). It runs every frame and is the only place you should (can?) call IMGUI functions. Here’s what the OnGUI() function does in my script:

GUI-le's theme goes with everything

GUI-le's theme goes with everything

Not much actually. The important bit of code is the GUILayout.Window() function. The “rect” Rect is a rect defined in screenspace, and the Window function uses and updates that rect to make a resizing, draggable window in the game view. The 3rd argument is actually another function on the script, which is where all the Window’s GUI controls are defined and drawn.

This function was huge… this screenshot can’t even fit it.

The code above was my first attempt at this debug UI. Every update, it would grab a few specific fields and draw out some IMGUI controls. Notice that the value of each field is being assigned to by the actual GUI function, which is because each frame the GUI function will return the potentially modified value. It seems a little strange, but it works.

And this is what it produced:

So functionally it seemed alright, but there were some issues I couldn’t get over. For one it was tedious to write all this stuff by hand. Every time I wanted to add a field from the config I needed to copy paste a conditional from the previous field and change all the values and controls to work with it. It was also ugly, and the sliders didn’t work for things like AA, where the ints needed to be discrete values (0,2,4,8) and not just some arbitrary float between 0,8. It was also just too messy and long, I needed too many fields too many conditionals to get anything done.

I was getting ready to scrap the idea, but stopped when I looked at the inspector itself. The inspector is great because it just reads whatever object you give it and serializes it into a control. I got curious to seeing if there was a way to do that with my dumb debugging tool. And after a bit of research, I realized that the answer I was looking for was Reflection.

Stop and Reflect

So I had mentioned reflection in the JSON section, but I’ll clarify what it’s about here. Reflection is the idea of having code look at itself and its components. It’s weird if you think about it for too long, but it’s actually tremendously useful in making tools and debugging. In my case I use it to have my code look at an object (In this case the “QualityConfig” instance) and show all its values. Reflecting a type produces arrays of field/property names and their values.

This is actually exactly how JSONFX does its thing. It reflects the template class and iterates through all the fields it holds. Then on each field it attempts to filter the type down to a writable value to put in the JSON text. So my code would need to do something similar. Here’s how it looks now (This is still the “WindowUpdate” function)

BAM! This is probably my favorite bit of code from this task. It gets all the fields in QualityConfig and iterates through them. For each field it makes a label and then, through some conditional, it will generate a control for it. I use GUILayout, the formatted version of Unity’s GUI class, to keep things nice and neat. With all that, here is how the new IMGUI looks:

It brings a tear to my eye. Now ANYTHING I add to QualityConfig will automatically show up on this “inspector”. The most I would have to write is a new condition in this function to account for a type I don’t have. 

Now, granted, this is not the neatest or most efficient code. I could definitely improve that. There are also probably better solutions in Unity considering that they already have a whole Serialization system and a full-featured inspector in editor. In any case, I’m happy with this solution as it stands. It’s easy to use, and since it’s so simple to add a field, it is extremely scalable. Plus this can easily be expanded to be a general tool for ANY object. Changing the “Target” variable to a “Game Object” would allow me to replicate a rudimentary version of the inspector in the game view. Even in a standalone build! (In our case this would also be fantastic for PS4 debugging!)

Wrapping Up

With the data saving and debugging tools complete, it was finally time to actually do the thing I was supposed to be doing. The Quality Settings UI itself is just a Unity UI prefab that appears on the Main Menu. This is actually the “Retained UI”  that I mentioned earlier. These UI elements are actually Unity game objects that store information like Size and Position and are drawn in-pipeline just like any sprite or model in Unity. This makes them easier to work with for artists and designers, since it isn’t all defined in a single script.

As for scripting this UI, I wrote some small “control” components that would be managed by a “QualitySettingsUI” class. The controls consist of a button that fires an “on click” event, and some UI representation of the data it relates to. The “QualitySettingsUI” is set up to from the “QualityConfig” when creating the controls, and update it as the controls are toggled. This, with some of our UI framework code, produces the following:

And that’s it! When the user backs out of the menu, the QualityConfig instance is updated and saved to JSON. 

Final Thoughts

Thank you for taking the time to read my weird, rambling dev blog! I hope that I somewhat enlightened you to some of the magic you can do with JSON, IMGUI, and Reflection in Unity. My favorite thing about programming is learning new tools and concepts, and it’s equally fun to share and discuss the things I learn with other developers. If you have any questions, concerns, or feedback, please let me know in the comments or on twitter @NoNameGhost. I can also be contacted via email at nsveri@dreamsailgames.com.

If you’d like to learn more about the tools and concepts above, here are a couple of links:

I wish I had a good link for Reflection, but it’s such a vast topic and I only scratched the surface of the capabilities. I personally got by with a lot of Stack Overflow/Unity Answers browsing.

I hope to be writing more of these in the future, so stay tuned!