DM-Tokyotrainwreck is basically the first time I’ve worked in 3D or level design since I doofed around with the Doom editor back in 1996. I’m pretty happy with the results, and was surprised how easy it was to get up and running in UnrealEd. I attribute a lot of that to the fact that Epic has amazing free documentation, and the supportive modding community that is always willing to lend a helping hand or give advice. I was super lucky in that I had the help of my friend Rob who did custom modeling and shaders galore, and my other friend Satoko who did some really immaculate texture work. Near the end I had my friend Kay record some announcer dialogue for me in Japanese and paint some posters, my friend Nick from Go Studios contributed extra signage, and finally my friend Alan from Petit Panda Extreme did an interactive soundtrack. Here is a video I cut together.
It wasn’t done in time for the Make Something Unreal contest, but we did manage to submit an earlier version of it to the IGDA Level Design Contest and won 3rd place.
Finished level vs. Original reference shot
CONCEPTION & TEST
I always went home through Shinjuku Station’s Oedo Line, which is one of the deepest subway stations in Japan. Whenever I’d go home I’d have to go through 4 sets of stairs and elevators. Some parts of the station were newer than others, so there were always interesting transitions from the older, gritty 1980’s architecture and surfacing into a cleaner, late-90’s look. I thought that this would be a quick and dirty level to crank out, but once we started creating our own content, development stretched into about a year of on-again, off-again work in our spare time.
To start, I took a few reference photos, got some maps of the real-life station and started building in BSP. After a day or two I had managed to build out the base Shinjuku subway line. I threw in some pathnodes and weapons and started playing.
No surprise, it sucked! Real-life layouts again proved to be uninteresting for deathmatch. Disappointed, I started punching out walls and honeycombing in tunnels, taking a lot of ideas from Hourences’s tutorials. The general rule of thumb is that each room should have at least two exits and never be a “trap.” I added in some one-way transitions (falling through ceilings) and decided the defining part of the level would be a train that collapsed through a ceiling, which could be used for traversal.
Another requirement was player-activated traps and environmental hazards. I love Unreal Tournament maps like DM-Pressure where you could trap someone else in a room and make them explode. There are three main hazard areas:
- A maintainence room where you can trap someone, a countdown starts, and then a train drops on their head.
- High-speed trains running on the lower platforms (with shock rifles nearby so players can “bump” enemies onto the tracks)
- A lower tunnel the player has to quickly snake through to grab a weapon. A train is almost always running through here, so its proximity is telegraphed by a traffic light and an animated static light that represents its headlights (on the other trains this is done with a lightfunction).
I am also a big fan of underground structures like the Tokyo G-Cans drainage system, but this was executed so well in Mirror’s Edge that I figured I’d try something else. I wound up making a big wind tunnel that lifts you up to more weapons with a GravityVolume.
I did a lot of testing early on; I would cook a PS3 version and bring it to friends’ houses for them to try. I noticed the most common problem was people going up the down escalators or vice-versa, so Rob added a glowing arrow at the base of every escalator indicating which way to go.
The biggest problem was communicating how to use switches. In UT3 there is no Use button. You could jam your body against a button Quake-style, but that seemed sloppy and imprecise, especially if you want to press the button with perfect timing. I wound up putting a big pink button on the ceiling that you could jump to activate. After a few tests it was obvious that nobody knew how to activate the switches, so my artist created a two-frame billboard animation of a jumping man (she had been playing a lot of Portal, I guess). After that everyone immediately knew what to do.
I started out using Epic assets as placeholder, then would rough in proxy custom meshes with BSP. Rob would take the proxies’ measurements then crank them out as static meshes in 3D Studio. (I always found it was a great motivator to tell Rob, “You know, my BSP modeling is pretty good as-is, we should probably just leave that as BSP.” This would always drive him crazy and he’d crank out an actual mesh to replace it in less than 24 hours.)
I know a lot of people don’t do lighting until the level is done, but I found that dropping in proxy lights as I worked totally helped in detailing the level and figuring out how to transition between zones.
I also had an enormous problem. The stock UT3 lighting system is pretty flat and there’s no ambient occlusion or radiosity, so you have to do a lot of fudging. I was able to fake radiosity by just dumping in a ton of small point lights where lights would bounce, but shadows remained a problem. Objects on flat BSP surfaces looked like they were just floating and not part of the scene:
You don’t notice this in games like Gears because they have really high-frequency texturing, but in our map it’s mostly simple, smooth surfaces, so I had to find a solution.
I wound up creating three decals. One is a circular gradient, the other is a rectangular blob, and the other is a flat gradient. I went through the map and started slapping these down on the ground to fake shadowing. I also threw down some dirt and grunge on the most-traveled areas of the floor.
Aha! Much better. Objects look like they’re more rooted to the ground. Additionally, the gradient decal let me fake hard edges in some areas, kind of a throwback to when everyone would do shadows in geometry like in N64 Goldeneye:
Performance didn’t take too large a hit either — make sure that the decals are only affecting the surfaces they need to, though. If a decal volume clips through the ground and affects geo underneath it, that’s extra draws and it will start bogarting your performance very very quickly.
Nowadays Unreal has Lightmass which supports radiosity, ambient occlusion and cool dominant direction lights, so you probably don’t have to do dumb stuff like this anymore. You can play with it for free if you download the UDK from Epic.
The level was originally built on a low-powered laptop and targeted towards the PS3, but it still went out of control. Lighting was taking upwards of 15 minutes per build and the engine was chugging more than I’d prefer. Fortunately I did two things that dramatically cut down my level size and brought performance back to 30fps on the PS3. Here were the two major optimizations I did.
- Selective Lighting: Vertex vs. Lightmap
All static meshes in Unreal default to vertex lighting. However, if your mesh has a second UV channel you can use lightmaps instead. Lightmaps are super awesome for two reasons:
- Lightmaps can stream out of the texture cache while vertex lighting data sits in RAM hogging memory.
- Lightmaps can save HUGE amounts of space if applied to high-poly decorative meshes.
For example, let’s say I use a total of 8 girders at 842 tris each. If I use vertex lighting, my lightmap size is a hefty 111kb. But if I change it to use lightmaps at 32×32 pixels each, that total is cut down by a factor of ten to about 11k! And it looks almost exactly the same.
Of course, in some areas vertex lighting might look better than lightmaps, or you might want to bump up the lightmap resolution if a shadow’s falling across geo in a weird way. I wound up going through the map piece by piece, and adjusting on a per-object basis. Girders up near the ceiling where light wasn’t reaching could get knocked down to 8×8-pixel lightmaps with no loss in quality and a huge reduction in the memory footprint. My lightmap size plummeted from 40mb down to 12mb.
Note that if you’re using lower-polygon meshes, sometimes vertex lighting can be better. It’s all about balance. Here is an Excel sheet from Epic that shows you when it’s cheaper to use a lightmap than vertex lighting.
- Manual Culling vs. Occlusion Culling
When Unreal draws a scene, it uses crazy Tim Sweeney Voodoo Math to figure out if the object in front of you is blocking your view of anything further down the road. Multiplied by several thousand objects per scene, this can get kind of slow. Instead, you can tell the engine “If this object is X units away, just don’t draw it” by setting each individual object’s CullDistance under the rendering tab. I went through the map, manually setting CullDistance on every individual object as I moved through the editor, figuring out how far I could move away from an object before it wouldn’t be visible from any angle. This made my performance skyrocket, especially on PS3. I also manually set CullDistance for particle systems too, because I think they always render no matter where you are in the level. (You can also use a CullDistanceVolume as well, but it can create weird popping effects and it isn’t so hot for dense indoor levels.)
SOUND, MUSIC, AND POLISH
Near the end of the project I read this tutorial on implementing interactive music and asked my friend Alan to make a few tracks. I think they added a lot to the overall atmosphere, plus they’re low-key enough to not be annoying after 20 minutes of looping. I also wrote a bunch of dialogue for station announcements and had it translated into Japanese, and got my friend to record the lines. There’s a chunk of Kismet that randomly fires every minute and triggers one of 10 announcements. (Note that the jingle that plays before each announcement is the Unreal Tournament 3 theme, but in a major key.)
To make the station feel like a real place while not infringing on any train companies’ copyrights, we decided to basically create a “fiction” to unify the level’s look and feel. We fired up Yahoo Japan Maps and laid out two new, theoretical subway lines. We gave each of them a name and a color code, then we created color and B&W corporate logos for both the train company (ISR) and the construction zaibatsu that built the station (Sanmaru Heavy Industries, a play on san-roku-maru, the Japanese name for the Xbox 360). Because of this, all the in-station signage is accurate, and locations referred to in the station’s billboards correspond with “Points of Interest” on the Exits & Facilities guide.
Also, if you zoom in on the LCD monitors, you’ll notice that the train station’s software is running on Apple IIgs BASIC. Yes, I am a huge nerd.
Last-minute change: Video!
A few days before release I realized that you can put your own prerendered Bink videos into the engine and they hardly take any CPU time. Fortunately, my friend Vanessa was in town, so I grabbed all the lights in my apartment, put her in front of a camera, and she tousled her hair around. After about an hour in After Effects I made this video loop.
STUPID STUFF I DID
I went into this from zero, so I made a lot of dumb mistakes. Here’s what I learned:
- Don’t be stupid, don’t detail in BSP
This goes without saying, but I’m persistent enough (and so crappy at Maya) that I will totally go out of my way to do extraneous detailing in BSP. This results in nasty subdivisions of level geo that you just don’t want. That’s not to say that you should never ever use BSP — Mirror’s Edge does all of its interiors in BSP:
Left: Mirror’s Edge BSP only. Right: Mirror’s Edge with static meshes added.
Of course, they’re just doing simple surfaces, and you should too.
- Check your sightlines
There are one or two totally ridiculous long sightlines in the map that could have been fixed back when I was BSPing everything out. These make for bad performance and annoying gameplay when someone’s got a sniper rifle.
- Don’t expect bots to do anything smart, ever
Making traps is easy but making bots actually use them is hard. I wound up scripting behavior exclusively through Kismet, which resulted in this mess of spaghetti-script:
Barf. It almost works okay because it keeps checking a lot of variables (is the weapon I want there? is the door already open? has someone else activated this switch? is someone else in the vicinity of the trap and likely to enter it so I can kill them?), and the break cases kind of work because “Move to” commands can be interrupted. It’s still a mess, and it breaks so horribly in Team Deathmatch that I’m totally embarrassed.
In the end, I’m happy with the way it turned out. You can grab it here if you have a copy of Unreal Tournament 3 and want to give it a shot.