Little Victories in Game Development - Editor selection groups
Making games is hard. It’s even harder when you work alone or in a team consisting of only a few people, especially when making your own tech. That’s why every little victory counts. In ‘Little Victories in Game Development’ we take a look at things that went really right; things that payed back their development cost many times over, and made our work much easier or more fun!
Today I’d like to talk a bit about level editors. Game developers use level editors to design levels and content for their games. The editor allows the developers to place objects, shape the environment, set up lighting, write behavior scripts, design AI encounters etc. For many people working with game development, the level editor is one of the the most important tools in their toolbox. It needs to be easy to use but powerful and flexible enough to cater to all kinds of workflows. The user experience most be as fluid as possible. Any friction leads to lost productivity and frustration. Frustration kills creativity.
Madrigal’s game engine, Basis, also has a level editor. I started writing it back in 2014, and since then there has been one pretty annoying issue with the user experience. Over the years I have tried to solve it in various, often complicated, ways. A few weeks back I finally solved it, and the solution was super easy. Let’s talk about it.
I assume most of you are familiar with the Unity game engine (If not, go check it out, it’s pretty neat). The Basis editor is similar to the Unity editor in many ways. You place objects into the scene, move them around in the object hierarchy and set properties on them. Also much like in Unity, if you want some sort of logic to run in a level, a simple way to achieve that is to write the code and wrap it in a game object which is placed in the level. That way you can adjust the properties of the system through the Game Object Inspector window (on the right side in the image above). The currently selected object’s properties show up in that window.
Basis embraces this “systems as objects” philosphy. In fact, many engine-level systems don’t have any special UI or settings windows in the editor. Instead they have a purely logical, transformless, object which can be added to the level and loaded/unloaded just like any other object. For example, in the image above the selected object is a nav mesh object. When selected, the object allows the user to adjust the properties of the nav mesh, rebuild it, draw it in the editor etc. You can have several nav meshes in a single level. Other objects, such as the ambience controller, are singleton-like objects in that you can only ever have one of them in a level. The editor will not allow a second one. The ambience controller keeps track of various level-wide settings, such as fog.
A nav mesh is used by the AI system to navigate the game world. I plan to cover navigation and nav meshes in the next part of our Blog series on game AI.
The problem
So what’s the problem? Imagine you are working on a large set piece made up of several objects, eg. a cliff rising from thick fog. You want to position the cliff pieces so that they look nice. You want to have some smaller rocks, trees and foliage on top of and around the cliff. But you also want to adjust the height-based fog so that you can see the cliff from afar. Suddenly you have a lot of different objects to poke at. Some of them are visible in the editor view, so you can just click on them to select them. But what about those purely logical “manager” objects? To adjust the fog you would need to find the ambience controller in the object hierarchy (which can be pretty long) and you loose the selection every time you select another object.
On the left is an image of the level objects window, with most of the hierarchy folders (called “layers”) collapsed. As you can see there is a lot of stuff here. Layers can be loaded and unloaded as needed. Some of them contain helpers to aid with level design, some are there to reproduce some obscure bug which occurred once and were left there in case they were needed. At the time of writing, the level in the image above has 7836 objects, and the number is growing.
My point is that navigating the list to find something like the ambience controller is very annoying if you have to do it often enough.
My first attempt at solving this issue was to introduce something called the “quick access” window. If you found you needed an object often enough you could add it to another window where you could find it easily because this window would contain a much smaller set of objects. This worked, but it was quite a lot of work to set up. If you deleted or renamed an object you needed to rename it in the quick access window too (+ a whole slew of other similar things to keep track of). Most of all though, it was annoying to use. You had to move your focus from whatever you were working on to another part of the program, find the object in the list, click it and resume your work. It also ate a pretty significant chuck of the screen real estate, ie. there were less room for other more important windows.
At some point I realized I needed to add a filtering textbox to the level objects window, which you can see in the image to the left. This wasn’t meant as a solution to the quick access problems. Sometimes you just need to find an object with a certain name. However, the filtering was handy enough that I figured I should remove the quick access window completely and prefer filtering instead. So I started typing in the name of the manager objects whenever I needed to select them, to filter out all other objects. I also adopted a standard where I would put all “manager objects” in the base layer of the level, which is always the top one. That way I could collapse all layers and reopen the first one, then find the object there. Not a very nice way of working.
The solution
A few weeks ago I sat down to play one of my favorite classic Real-time Strategy (RTS) games, Command & Conquer: Tiberian Sun. This game, like many other RTS games, has a simple unit selection group system where you can quickly assign numbers to a group of units. For example, select a group of soldiers, hold down Ctrl and press 1. Later if you need to select those units you simply press 1 and they become selected. If any of those soldiers die the game selects those that are still there. At any time, you can assign another group of units and overwrite the previous selection group with the new selection. Simple, elegant, powerful. No UI to keep synched with the data, no weird corner cases.
It took me about an hour to implement the selection groups in the editor and they have worked flawlessly ever since. I can keep focused on the level view, switch to any object without taking my eyes off whatever I am currently working on. Also, I can add temporary groups that make sense in the context of what I am currently working on. When I no longer need those groups I can simply override them with new ones. It’s great! Why did it take me this long to figure it out? I have been playing RTS games since the early 90s. This is nothing new.
I have come up with a “standard” for object selection groups. Group 1 is usually the main terrain object. Group 2 is the nav mesh I am currently most interested in. Group 3 is the ambience controller. Group 4 is the Script Relay, which keeps track of gameplay scripts in the single-player campaign of Project Slide. Groups 9 and 0 are for temporary groups and I keep overwriting them all the time. The groups are saved to the level editor document which means that I don’t have to set them more than once. If an object is renamed and no longer there they are simply skipped. I don’t even update the group. I just select the objects again and reassign it. Creating and updating groups is so easy and fast.
I realize that storing the groups in the level document only works if everyone working on the level agrees on the standard. For larger teams it might make sense to store the object selection groups in local per-user data. That way every user could have their own groups, however they like.
Conclusion
So what’s the lesson here? Probably that sometimes the simplest solutions are the best. The user experience was improved by removing, rather than adding. Also, you should play old RTS games. They are great!
Do any of the widely used game engines have a similar system? I don’t know, but I haven’t encountered any. Maybe there isn’t need for one unless you use objects as interfaces to systems, like Basis does. Let me know if you know of any.