Posted On: 2021-09-06
Today's post is a brief story about how using a custom Editor Window in Unity helped me solve a long standing problem in my code. This won't cover how to create custom Editor Windows (that is covered in plenty of places elsewhere online.) Rather, this post will focus on why a custom Editor Window was the right tool for the particular problem I was facing. So, naturally, I'll need to start with a bit of background about my problem first.
Unity is designed to make it easy to create static environments and set-pieces - spaces that are designed to look a particular way, no matter what the player does. This is great for games that need it - allowing them much better performance and visuals than would otherwise be possible - but it does make the workflow for the more dynamic portions of the game feel a bit more awkward to use. This is particularly pronounced for games that are purely dynamic - where every object can be manipulated or moved. In such cases, the developer often uses a completely different approach to creating and populating the objects in their world.
I've written previously about how my game populates its world, but I'll briefly summarize the important parts. Whenever an area loads, the game tells Unity to first load an empty (static) scene, and then create each individual object. The properties of each object are loaded from the player's save file, so anything that moves or changes in any way will (mostly) automatically persist those changes across saves. When the player first starts the game, however, they don't have a save file, so it's something of a special case. For that situation, I maintain a single save file - the "template" - which represents the exact starting state of every object in the world.
Maintaining the template has been a bit awkward for me. Although I could use Unity's scene system to author the game's various areas, I still couldn't rely on them for loading the content, so I had to somehow convert them from Unity's static-centric scenes into a form compatible with the template. My first approach - which I've used for the past two years or so - was to create scripts that scan the scene for every object, and then forcibly save it into the template file. I set the scripts to run first thing whenever I played a scene in-editor (since I would test any changes in-editor before testing them in the game proper.) Importantly, I carefully made sure the script never ran inside the game proper (the template must not change during gameplay.)
Over two years of use, however, I began to see some unfortunate consequences to this choice:
Recently, I had the opportunity to revise how I approached the problem. While making other, unrelated changes to the template system, I decided to take a bit of time to tackle the awkardness of the template update workflow. Going into it, I knew I had three key constraints:
From these constraints, I determined my best approach would be some kind of in-editor button that would run the template update. It quickly became clear that having a dedicated window (including the button) would be best for the second constraint, as that gave me the flexibility for more robust validation (ie. prevent updating the template when unsaved scene changes exist [as a reminder to commit/revert any temporary changes]). Fortunately, the third constraint was met without much effort/thought: it was trivial to call the template update script from the new editor window (and discontinue calling it when the scene started.)
Using an editor window substantially expanded what was possible, and it became a natural place to maintain not just the current area's template, but all the areas. Before long I had added a grid detailing every area in the template file, which characters they contained, and convenient buttons to quickly update or delete the area*. Additionally, opening the window clearly expressed intent to update the templates, so I used that to lock the template file (thereby reducing the number of transient errors.) Lastly, since it works without having to run anything, there are opportunities to use it in conjuction with various automation tools as well (ie. automated tests to validate the scenes in Unity match the areas in the template file.)
Having now used a custom editor window to solve my template management problem, I wonder why I didn't use that approach to begin with. The custom editor is faster, more user-friendly, and more powerful than my original solution. Perhaps most importantly, however, the process of updating the template is explicitly something that is only relevant to the editor - it simply makes more sense having it be a part of that than trying to (metaphorically) shoe-horn it into a runtime script.
Looking back, the best explanation I can think of is that I simply had too much going on when I made the first implementation. I was building out other parts of the save system at the time, and managing templates was a background concern by comparison. While it's possible I'd considered and discarded the idea due to my inexperience with customizing the editor, it's far more likely that I simply didn't give it enough thought for even that. As this experience has taught me, customizing the editor is a valuable tool for solving problems - one that I'd do well to remember going forward.