The wonderful world of Unity editor tools is one of the most complex and irritating worlds I’ve ever had the pleasure to explore. Alas, it is one that I have to explore because I want to make it very easy to build levels for Exon. I want it to be as easy as farting out Warcraft III maps was, back in the day. Once I’ve built the engine and all the relevant bits, I want to shut Visual Studio down and never write another line of code for the rest of my life.
To get even close to that ideal, I have to go through a whole WORLD of pain — and the realisation that maybe, just maybe, one bloke in his bedroom can’t hold a candle to one of the greatest engineering marvels of the videogame world. Whatever happens here, nobody will be building the next DotA inside Exon.
Exon World Editor
The ultimate problem is that Unity is not a great level design tool. It swings between providing too much control and control in all the wrong places. It has a nasty focus on searching for things by name rather than picking them off the screen, except everything has the same name because you’ve pasted sixteen of them into the scene. It has no built-in controls for scattering decorations at all and the terrain system can’t do cliffs.
The thing is that all of its tools are very useful when doing complex and gritty things, like adding new units and buildings, interlocking all the pieces that make up a single “thing”. I just don’t want all of those precise low-level controls when I’m trying to lay out and decorate a level. I don’t need to know that a gate is composed of a wall block, unpathed frame, three panels and a pathing blocker — I just want to know that it is a gate.
Luckily, Unity does expose a whole world of systems you can hook into to start pushing it in the right direction. I’ve been working around these for quite some time — I’ve written simple extensions that allow you to do basic tasks, like, say, instantly killing a unit during the game, or turn off the fog of war. I’ve written more complex windows, like the conversation editor and the burgeoning trigger editor. These things, however, are all quite disparate, and all nestled into the full-fledged Unity editor with all its myriad distractions.
I realised that what I wanted was effectively a different “mode” — a mechanism whereby I can turn off everything in the editor in favour of a small, concise set of level building tools. It turns out, there is a way to do this. It just requires a huge plate of spaghetti, because Unity editor hooks are all over the place.
It begins with a Custom Editor Tool, which sits alongside the most fundamental controls — “move”, “rotate”, “scale” and so on. I detect when the user (uh, me) switches to this tool, and this is what starts reshuffling the entire Unity editor into the Exon World Editor.
First off, I maximise the scene view to clear away all the traditional editor gubbins. There are still toolbars and other bits around the edge but they can be ignored. Up comes my floating Tool Palette on the right instead, which contains controls for terrain editing and doodad placement.
The terrain editor is a straight port of the controls for my Terragne system that I built a while ago. The Custom Editor Tool offers mechanisms for raycasting into the scene view, which powers any terrain operations by finding out which tile you’re trying to click on. Originally, the Terragne controls were built in a custom inspector, so you could only edit the terragne that was selected — now, since it is in a free-floating window, you can edit any terragne at any time and it will Just Work (this will be useful in future when I have little caves and side areas that use different tilesets in the same level).
(I have still not implemented Undo properly for terragne modifications — although undo does reset the underlying data, which is packed into a 2D array, it doesn’t reset the generated mesh, so you have to dirty it by performing another operation or doing a full rebuild. Still not quite sure how to tackle this — try to shove the full mesh chunks into the Undo recorder too? The performance is a bit hideous right now so maybe I’m not going there.)
Doodad placement is fun because it requires several more systems working in tandem. Because we have basic raycasting, actually placing a doodad on the ground on click is simple enough, but this leaves you dropping decorations blind. Warcraft III‘s world editor gives you a shadow of what you are about to place, the rotation and scale already randomised, so you can ensure you click in just the right place to make your landscape lovely.
One of the really annoying things about doing editor extensions in Unity is that different hooks in different places have wildly differnet capabilities. The Handles system, for example, can only draw primitive shapes like cubes, while the Gizmos system can draw wireframe meshes. However, if you call either system from the wrong place, they just won’t work.
To get a shadow of a doodad to assist placement, a wireframe mesh seems like the closest we’re going to get… Which requires an actual MonoBehaviour on an object floating somewhere in the scene, so we can use its OnDrawGizmos method to draw that wireframe. But this needs to talk to the Custom Editor Tool to synch up with the raycaster that’s determining where the doodad will go, and it needs to talk to the Tool Palette (an EditorWindow) to know what the currently selected doodad is (and its randomised values).
Getting these things to talk to each other is… interesting. I have opted for a static glue class in the middle that manages all (uh… most…) of the intercommunication between these disparate parts.
That just leaves me with actually populating the doodad list. This is actually a pain in the ass because doodads are by their nature very simple objects — mostly just a mesh renderer and a collider, they don’t have any of my custom behaviours attached. This makes them hard to detect, because pretty much every prefab in my project has a mesh renderer and a collider.
I’ve pretty much gone for using folders and have to hope for the best. All doodads are stored in a folder called Doodads and anything inside that can be placed by the doodad system. If I want to place something that’s not in the Doodad folder, or if I put something that’s not a doodad in the Doodads folder, then… sucks to be me.
The one thing I’m kind of swithering about is how to mark doodads which should snap to the grid versus those that shouldn’t. Rocks and trees should be endlessly variable and free-floating, sure, but wall sections and road tiles need to line up precisely so they must be snapped to a grid and stick to a scale of 1. I’m currently using tags — things tagged as Doodad are unsnapped and randomly scaled, while anything else is snapped and unscaled. This annoys me because the grid-snapped objects are still technically doodads, and I can see myself forgetting how this works “correcting” these objects in future…
Sometimes doodads are destructibles, though, which require a bit more effort. I want to be able to scatter crates with wild abandon the same way I’d scatter trees and bushes, but crates need to have their contents assigned so there’s sweet sweet loot to find.
In Warcraft III, that was done by double-clicking a destructible and fiddling with its properties, so I just built that exact same thing. Double-clicking a doodad brings up a small, modal editor window where you can change its name and modify its primary class if it has one (i.e. the one that is part of my ecosystem, not the random mesh renderers and whatnot). Double-clicking is a bit annoying because the movement handle tends to get in the way, so I might bin that (but I’d have to implement drag ‘n’ drop to replace it and that’s a can of worms I’d rather not open, thank you very much).
So there you have it — I’ve neatly turned Unity into a special-purpose level editor just for my game.
… Or at least, its outdoor areas that use terragne…
… So I guess all this goes in the bin when I start doing floating space platforms and large vehicle levels…