TACStrike Level Editor

Published on Tuesday, 17 February 2015 11:59
Written by snake5

Since I've recently finished most of the editor work, I thought I could tell more about how it works as well as how it is designed. There's very few resources on this available, everyone just kind of tries to pick things up as they make it, so I thought it might be helpful to share my experience. Be aware though that some of the language used here will only be relevant to game developers that use C++, but I'll try to keep that to a minimum.

Everything begins with the most important thing - the data. The subject to edit. I knew I would like to keep it simple, but - how simple? From previous experience I thought I'd need some kind of blocks to make the basic layout and some other things (called "entities") to put everywhere. Let's start with the blocks.

Meet the block


Any block has 3 to 14 vertices and +2 surfaces (one for each edge, one for top, one for bottom). It also has top and bottom height. Even though it allows us to create walls in any direction, slopes can't be made just yet. For the slopes, each vertex has an extra Z coordinate for the top offset. It's mostly for graphics, though, since most of the physics is going to happen in 2D.

Surfaces describe how each side of the block will look (and perhaps feel as well) in the game. Each surface of the block has a texture and some parameters (offset, scale, angle) to specify how to map the texture to the surface.


The other things

As mentioned before, entities represent all non-block things in the game. Entities come in various shapes and sizes. A mesh entity would represent a pre-made 3D model, placed in the level in some specific way. A light entity would specify a light that illuminates surroundings at the specified position and color (and some other parameters). The data is different for each entity, the only uniting parameters are type and position - we do have to know where and what the entity is, after all.


To provide sufficient editing capabilities for entities, there are various user interface elements that can be added to the property list. On/off switches, numbers, vectors of two and three numbers, and even mesh/texture picking screens. To make all that work, we need a graphical user interface system that supports all required features.

User interface

The two main "schools" of user interface tell you: it's either immediate mode GUI (rendered and processed as you call a function) or regular GUI control (or window, or maybe DOM element, depending on who you listen to) tree with objects allocated and added to other objects. There is actually a way to squeeze through with the third way - one that combines a regular GUI structure with allocation-less (generally referred to as "allocated in-place") systems (UI controls can be inherited, added on the stack or included as class members) and some immediate mode trickery inside events to handle button clicks without having as many actual buttons.

However, that is not the only addition I could think of. To avoid the difficulties of working with a textbox or slider for numbers, I designed the number wheel - a control to increment/decrement the number as cursor is turned around the center of the wheel, at the speed specified by clicking on the right track.


Getting back to GUI control inheritance, just wanted to add that there are a few restrictions on the usage of such controls, biggest one being - copy construction / assignment does not work as expected, those features must by overridden properly (by manually copying property values and nothing else) or disabled.

To give some idea of the scale of the currently implemented GUI code, I can tell you it takes about 2000 lines of code and show this picture:


Given the size of the code as well as the number of defined classes, I can say that the user interface is sufficiently simple. Same thing could be said about the data. I have described it here in such detail that it could be implemented by many other developers easily. But the editor is not just data and user interface, surely it's not all we care about?

Simple... or is it?

There are many places for improvements in a typical game level editor. You have all the possible improvements of a modelling tool, a sculpting tool, image editing and so on. So it's important to know where to stop.

Actually, there's a lot more to cut without major losses way before that. Selection of multiple items, for example. Sure, it's somewhat convenient when you have to move many things at once, however the problems it creates usually does not outweigh the benefits. When I will encounter problems with my approach, I intend to create groups - objects that contain handles to other objects. These items could then be moved together, as well as targeted by many other features of the editor.

3D modelling is another such thing. As necessary as it might be to make the core layout more detailed, modelling cannot be used as it creates many issues. First of all, the walls have no volume that way. It might be useful for calculating if a bullet could penetrate some wall, for example. Second, you now have polygon triangulation issues, because polygons are easier to deal with in editing. For simplified volumes, it's the opposite - they can assume some things to avoid overcomplication of further calculations.

On the topic of blocks and further calculations, plane-based geometry description (a convex plane-based hull, or "plane-hull" as I'll call it here) is something I've opted to avoid as well. It would mean that every surface is a mathematical plane (vector+distance), not a polygonal face, based on vertices. Even though it aligns well with the needs of a CSG (constructive solid geometry) processing tool, it seems to be much easier to generate planes from a polygon mesh than it is to generate a mesh from planes. Here's why:

The short version of the "plane-hull -> mesh" algorithm is - create vertices at three-plane-intersection points (so essentially three nested loops with two kinds of intersection tests - plane/plane and plane/segment), for each plane - collect such vertices, sort them by angle, add this new polygon to the mesh. In practice, floating point math adds some issues to this approach as well. For "mesh -> plane-hull": for each polygon, calculate plane (cross product for normal, dot product for distance) and apply surface data from polygon. Less math, less loops, one tiny assumption (that all polygonal faces make perfect planes).

As for the entities... it's... interesting. Each entity type class is also the instance class and the user interface structure. That part's quite amazing. I can just insert either an instance or a type into another control and it's ready for editing. Cloning from either prototype or instance works exactly the same. There's also a system where it's necessary to register each entity only once and it takes care of creating UI buttons and provides an array where it's possible to find any entity prototype by name. The not-so-amazing bit is the scattered entity code (declaring a UI control at one spot, setting default value at another, serialization in two different places etc.), but that's something I'm willing to put up with, given that everything else that normally creates huge headaches is just extremely simple this time around.

War stories

This adventure of writing an editor has brought quite a few experiences of bug fixing to me. Forgetting to remove a deleted mesh pointer from cache, wild memory overwrites from double ownerships, created via copying. The last two are actually unique in that they show the same issues that forgetting the "rule of three" (C++ users should understand what I mean here) would show (double ownership), except that the rule was not in effect!

Not only bug fixing has taken some time. I estimate approximately 5 hours of walking around the room in an attempt to understand both my long-term and short-term goals, as well as how to design certain elements in the editor or its GUI. Planning is crucial to keeping momentum. Always Be Planning. This might sound extremely counter-intuitive to some, given that for planning, you have to step away from the code, stop writing it. But it pays off quite soon.

In ending...

I'd just like to say that the previous week has been extremely interesting, however this is just the beginning. It would help if you could vote for the game in Greenlight. As for me, I'm still working on the editor, but that's soon to be concluded, letting me get back to the game itself, as well as level design planning and putting it all together. I hope you're as excited as I am to see how it'll all look and play in the end.


Latest images