DOOM-Style LEVEL EDITOR

For the past year, while stuck at home, my quarantine project has been developing a standalone Doom-style level editor in Unity, using the new ECS architecture. This may seem like a random project, so let me provide a little bit of history and more context by starting at the beginning.

chapter 1: doom souls

Back in 2015, my coworker Pat and I were discussing game ideas, and we thought it might be fun to do a game in the style of Doom and with progression like Dark Souls. Classic shooters like Doom and Dark Souls actually share a lot of common design themes: 

  • Winding, non-linear levels that loop back on themselves.

  • Having to manage limited resources in order to progress.

  • Dark theming and sometimes intense levels of difficulty. 

There have been about a million indie clones of Dark Souls (although, there weren’t as many when we started our project), but they have all stuck to the same basic formula of either 3rd person or 2D side scrolling melee-based combat. We thought it might be interesting to apply the same Dark Souls level design principles to a first person shooter -- think, a giant labyrinthian Doom level where you are moving from bonfire to bonfire, with limited healing and ammo in between, leveling up your weapons and gear in order to progress. Pat suggested we might be able to use the Doom engine to accomplish this, since it is open source, though its features proved to be limited. Instead, we decided to develop a Doom-style level editor in Unity, since we were already using it at work together and were familiar with the platform. I then got started by figuring out the best way to accomplish this.

A few basics to get started… 

For those who are unfamiliar, Doom is actually a 2D game basically extruded upwards to give the illusion of being 3D. Every Doom map could be played top-down, as there are no overlapping floors (bridges, underground tunnels, etc.). Each map is composed of a series of two dimensional polygons, called Sectors, and each Sector represents a room with a floor height, a ceiling height, and an arbitrary amount of walls.

Doom E1M1: Each shape defined by white lines is a sector. Fully opaque white lines define the boundaries of the world, while more transparent lines are the borders between sectors.

Doom E1M1: Each shape defined by white lines is a sector. Fully opaque white lines define the boundaries of the world, while more transparent lines are the borders between sectors.

On edges that are the border between two sectors, there can be up to two walls: a bottom wall and a top wall. Knowing when two sectors border one another is a crucial part of generating the level geometry correctly.

Example screenshot of Doom with different sectors colorized and bottom and top walls labeled.

Example screenshot of Doom with different sectors colorized and bottom and top walls labeled.

Off to work

I started by looking at how current Doom level editors work, the most popular being Doom Builder. Since the map is 2D and two sectors can’t overlap, when the level designer draws one sector over the other, you have to resolve it somehow. Doom Builder handles this by generating additional sectors whenever two sectors overlap, as shown here:

1. Two sectors 2. A sector being drawn that overlaps both 3-4. Resulting sectors, notice that 3 sectors are generated.

1. Two sectors 2. A sector being drawn that overlaps both 3-4. Resulting sectors, notice that 3 sectors are generated.

The algorithms to produce this type of result are generally known as “Shape Clipping”. After some research, I was able to find a C# library that performed Shape Clipping, called ClipperLib.

In my spare time, over the course of a few months, I implemented the level editor as a tool designed to be used inside the Unity editor scene view. I won’t go into the nitty gritty details of how this was all implemented, but I will mention a few key details that will be relevant later…

Implementation:

  • Each sector’s vertices were defined by an int2 (x and y), so there were no floating point values. There were 32 possible level editor units per Unity unit. I decided on this direction, because it mirrors how the original Doom works (although I believe they may use shorts instead of ints, because of memory limitations at the time development). It was also convenient to be able to compare two vertices directly without having to rely on a floating point tolerance, and they were easily hashable. This also was the input taken by the clipping library.

  • The clipping plugin worked exactly as intended, but I ran into several issues for our use case. The first and primary issue was that the Doom builder output (shown above) wasn’t something that it could produce in a single pass. Clipping libraries are designed to perform the boolean operations, as shown here:

BooleanOperations.png

In order to get the desired results after a shape was drawn, I was forced to do two different clipping operations: one cutting all existing shapes out of the newly drawn shape (shape 2 in the screenshot above), and one to find the intersections of all existing shapes with the newly drawn one (shapes 1 and 3 in the screenshot above). The second issue was that it didn’t handle points on edges well. When drawing one shape inside another with points on each edge, you would want it to split the original shape. Instead, it would designate the drawn shape as a hole inside of the other, leading to issues with triangulation and navmesh generation.

Example input and difference between desired output and actual.

Example input and difference between desired output and actual.

Finally, when shapes were output from the clipping operation, there wasn’t an easy way to link them to the input. There was information associated with each sector (floor and ceiling height, assigned materials, etc.). This resulted in having to perform a relatively expensive process of looping through all of the input sectors and output sectors, while attempting to match up the input with the output. Certain actions, like having the vertices defined as integers, helped with this, but it was still less than ideal. 

Pat’s feedback on actually using the tool was mixed. Having the sectors clip when drawn was somewhat limiting to him as a designer, since once you drew a sector over an existing one, the existing one was permanently modified. There was undo/redo functionality, but it was still a fairly destructive form of editing. His words were something along the lines of, “It’s best to carefully plan levels for this tool before you actually start making them.”

Despite this, Pat managed to make some pretty cool looking stuff:

DoomSoulsLevel2.jpg
DoomSoulsLevel3.jpg
Top-Down view of level editing and original tools.

Top-Down view of level editing and original tools.

Unfortunately, as happens with many side projects, things slowly petered out. We both became increasingly busy working on our latest title for GameResort, Tank Party, and put all our effort into making that game the best it could possibly be.


Chapter 2: Down with the sickness

Fast forward to early April 2020, and quarantine was officially in full swing. Though Pat had left GameResort, we kept in touch and were tooling around with the idea of revisiting the Doom level editor. Since then, we had shipped Tank Party together and had a few ideas for how our Doom-inspired game could be different. 

Tank Party is a multiplayer game for iOS and Android, inspired by Agar.IO and Slither.IO. The basic premise is that players are dropped into a server with up to 63 other players and have to travel around the map, destroying AI bots and collecting resources to level up tanks and climb the in-game leaderboard. There’s more to it, including squads, control points and lots of great map design and points of interest; if you’re curious you can read more about it here

Identifying Opportunity

While Tank Party hadn’t been as successful as we had hoped, we still believed in the core concept. We also thought that it might have more of a life on PC as a first person shooter with human characters, rather than on mobile as a top-down twin-stick shooter with tanks. We still wanted to use a Doom style level editor to do this for a few reasons:

  • If fully realized, the Doom level editor could allow for extremely rapid iteration on levels. Since the goal for this game was to have large maps with lots of points of interest, this would be crucial. By restricting the map to two dimensions, Pat would be able to draw and edit sectors quickly, allowing for rapid design iteration in comparison to using BSP brushes or something similar. 

  • By designing the map entirely in-editor, we would eliminate the possibility of “holes” in the map geometry for players to get stuck in or take advantage of. By making art assets purely decorative instead of having them define the play space, we could keep the play space clean. All floors are entirely horizontal and all walls are entirely vertical, so there is no ambiguity about which slopes are walkable.

  • Making the map purely 2D was not actually that much of a limitation. Many of the best multiplayer FPS games rarely have overlapping stories or floors in their maps. Typically, designing maps that overlap can lead to blind corners (coming up a flight of stairs and having to look directly behind you), or audio confusion (hearing footsteps near you in a multi-story building and not knowing if they are above, below, or on the same level as you).

Dust 2 from CS:GO. The red marked area is the only part of the map that actually has multiple stories.

Dust 2 from CS:GO. The red marked area is the only part of the map that actually has multiple stories.

  • It would create a unique look for the game and seemed like it would be fun to work on.

Starting Over

We considered dusting off the old level editor and going from there, but I decided to build a new one for several reasons:

  • As mentioned before, working in the previous level editor was very destructive. Inability to freely draw and position sectors was limiting to the creative process.

  • Pat wanted functionality to duplicate and rotate sectors. Both of these actions would be difficult to perfect while using the previous system; the first, because once sectors were drawn they were sort of “attached” to the surrounding sectors, and the second, because sector points were represented as integers and rotating a sector would result in rounding errors, ruining the original shape.

  • I wanted to make a standalone level editor that could ship with the game to encourage modding and custom maps.

  • Although the code base for the original level editor wasn’t bad, my experience had grown considerably since then and I wanted a fresh start. I was also very interested in using Unity’s ECS architecture, since I thought it would be a natural fit for this sort of thing.

And so, with that, I set out to make a new level editor…

View a video of the new level editor at the top of this webpage. This is a work in progress, and I will discuss more about the development process in the coming weeks. Check back for additional updates.

Previous
Previous

Stupid Zombies 3

Next
Next

Totes Tubular