How Uppercut Games intelligently leveraged procedural level generation and handled optimization to develop City of Brass
We caught up with the three founders of the studio, and in this Q&A, the trio elaborates on how City of Brass contains BioShock DNA, discuss how they infused traps into procedurally generated levels, explain how they elegantly optimized the game to run smoothly on the Nintendo Switch, and more. Thanks for your time and congratulations on the recent Switch release! With members who worked on BioShock 1 and 2, what lessons from the beloved series did Uppercut Games bring to the development of City of Brass?
Art Director Andrew James: When we first thought about a game with a whip and a sword, we immediately began to draw inspiration from the core combat mechanic of Bioshock; the “One-Two Punch,” where players were encouraged to use electrobolt to stun enemies from range and then finish them off with the melee wrench. We thought this core mechanic suited a non-damaging tool like the whip well. We kept expanding on this core idea with the whip being your “multi-tool” that can manipulate enemies and the environment, trigger traps, grab things from afar, etc.
The way players interact with the Djinn (Genies) in City of Brass is also inspired by the many vending machines and stations in Bioshock. The Vending Genie is analogous to the Circus of Values (but without BioShock Writer Ken Levine’s haunting voice). The Healing Genie is like a heath station. The evil Genies also serve similar functions to the security system and security bots in Rapture.
Finally, as we were building systems to create completely procedural levels, we analyzed some of the Bioshock level construction, and turned what the level designers had done by hand into rules for the procedural system in an effort to create a more believable space that feels like it was built by a human.
City of Brass initially released in Early Access. What has Uppercut Games learned from that initial launch that you've incorporated into the latest version of the game?
James: For us, launching into Early Access was an invaluable experience, and greatly improved the final version of the game. We had a small dedicated community that gave us detailed feedback and suggestions, from features to include through to balancing the difficulty curve.
Design Director Ed Orman: One concrete result of feedback we got from Early Access is the Blessings & Burdens system. Blessings & Burdens are essentially mutators that you can turn on to tweak the level of challenge to what suits you best. By including these, we were able to make the game more broadly accessible, and address some of the concerns of our earliest players. For example, the “Leisurely” Blessing removes the time limit from the game, which was a common request.
After bringing City of Brass to the PS4, Xbox One, and PC in May of 2018, the game recently released on Nintendo Switch. How did you optimize for each platform to ensure a consistent, quality experience throughout?
James: We initially found that our levels were too big for console. We had too many actors and draw calls for the hardware to handle. Thankfully, we have a procedurally generated game, so we started by changing the level-generation code to bring the level sizes down. Next, we found that we had a heap of actors that you could not see, but were still ticking away in the background. By reducing the cull distance on these actors and turning things like particle systems and lights off completely at a set distance from the player, we were able to get back to a good base framerate.
The final piece of the puzzle was to go through and profile the cost of our lighting and post process settings. We decided we wanted to keep distance field ambient occlusion on as it helped ground our dynamically lit environments, so this required reducing the fidelity of some of the other lighting and post process settings to keep the performance where we needed it. Most of these settings can be changed at runtime via the console (you can check the host of r. variables in default scalability.ini to see all the different features you have to tune your performance.)
Technical Director Ryan Lancaster: When we first deployed City of Brass to Switch, we could see we were obviously GPU-bound. The Switch was having the same problem the Xbox One initially had, which was too many dynamic lights. Being a dynamically generated game, nothing in City of Brass has static lighting. On Xbox One, we had to go through all the light components in our Actors and set the “Max Distance Fade Range” so that all the lights faded out once they were too far away. For Switch, the dynamic shadows turned out to be too much, and we had to make the decision to turn off shadow casting on all lights except the sunlight.
After getting the GPU time under control, we were now CPU-bound and we simply had too many Actors ticking. We’d already put in place a system for distance culling our traps and AI, which had the most excess tick time, but bringing in the culling distance on those systems now allowed the player to see the AIs popping in. When an enemy dies in City of Brass, they dissolve back into the sand of the desert. To cover up the AIs popping into existance, we applied the death effect backwards so that the enemies appear to form out of the blowing sands.
The other main performance problem was the usual issue of bursts of activity (too many particles and physics objects) causing hitches. That was solved once we upgraded to 4.21 and got the dynamic resolution system configured.
City of Brass features vibrant graphics with great lighting effects. How did you go about achieving the game's visuals?
James: City of Brass was made with two full-time artists and one part-time concept artist and character modeler. We’re very proud of what we have achieved with such a small team!
As a small indie studio, we are always looking to reuse things developed from previous titles. One of these things was the dynamic Time-of-Day-lighting system we developed for Submerged. We slotted that system straight into City of Brass. The Time-of-Day system has a heap of custom curves that allows for everything from the lighting colors, fog, skybox, clouds, indirect lighting, and post process settings to be tuned for every hour of the day and night. As with our previous game, Submerged, we were not looking to simulate real-world settings, but rather create idealized hues and palettes for the lighting to complement the architecture and scenery.
Everything in the game is also textured using a small set of Substance Painter materials we shared in the studio. This allowed us to very rapidly create assets that all matched perfectly in surface detail, metalness, roughness, and hue.
All of the environment art then has its Substance textures plugged into material instances of a few master materials, allowing us to globally tweak settings that then propagate to almost every asset in the game.
What drew the studio to an Arabian theme for City of Brass?
Orman: As a kid, there were parts of the 1001 Arabian Nights stories that I had some exposure to via cartoons and movies: a very Western interpretation of the mythos. But when I actually went to read the book (one of my kid’s books, in fact) I found that it was a much richer fictional world than I’d imagined. The structure of the book is a bunch of nested stories-told-within-stories, and the City of Brass is this great tale of a city cursed by its own greed.
We’d already started on prototyping a first-person roguelite before I read the book, but once I read about the City of Brass, it instantly felt like the setting we should go for. And that decision opened the creative pathway towards having the whip as the core gameplay tool, using Genies and wishes - everything sort of fell into place.
While there are many challenging roguelike games, not many of them use the first-person perspective. What made the studio go with this perspective for City of Brass?
Orman: That’s a combination of factors. One is that we have a bunch of experience working in first person, on Tribes: Vengeance, SWAT 4, and the BioShock games. Since the beginning of our studio, we’ve allowed ourselves to try and make a lot of very different game types, but had not had the opportunity to return to those first-person roots. It kind of felt like we were letting that experience go to waste, so we got to thinking about what kind of FP game we’d like to make.
Second, I like roguelikes! I got pretty heavily into them a few years back, especially Spelunky and some of the other old-school 2D ones. And it didn’t take long before I started wondering what it would be like to see those kinds of games expressed in first person.
City of Brass has been praised for its combat, particular the whip mechanic, which provides different contextual actions that can stun enemies or pull them into traps and more. Can you explain how the studio approached combat design?
Orman: The foundation of our combat is that one-two-punch idea: that the optimum way to approach each combat is to first try to manipulate your enemy in some way before you set about damaging them.
Since we’d chosen the whip to be the player’s super-tool, it was natural for us to use it as a basis for those combat manipulations. The guideline was that the whip could put enemies into states, but by and large couldn’t directly damage them. From there, it was a matter of identifying what would constitute a whippable part of an enemy -- what made sense, what was easy to target -- and then thinking about how whipping the enemy in those parts would alter their state.
The simplest example is “whip an enemy in the face, and they get stunned for a moment,” but we rapidly expanded this to tripping, disarming, dragging, knocking back -- all of these states either buy you time to get your actual attack in, or let you cause damage to the enemy via some other environmental hazard, such as the many traps littered around the City.
Considering the traps in City of Brass not only hurt you but enemies as well, can you talk about how you incorporated procedural level generation into the game?
James: City of Brass generates a new level procedurally every time you play, so no two runs are ever the same.
Traps are a very interesting example of how, when making a procedurally generated game, “procedural” should not equal “random.” What we mean by this is that while in a lot of cases it is relatively quick to create systems that “randomly” pick places to spawn items or objects, or randomly generate a series of interconnecting tiles, you inherently have little to no control over the result. This is bad from a number of perspectives:
- Your game ends up being what the RNG gods decide, not what you want.
- It is very difficult to tune or balance a system containing many random sub systems.
- It usually looks “random” and breaks players’ suspension of disbelief. For instance, “Who would ever put a toilet in front of the fridge?”
- "Random” often translates to “Unfair” in players’ minds as our brains look for patterns in the world, in nature, and especially in games. A player instinctively wants to learn the rules, find the patterns and then use that new knowledge to get better.
When we first added the traps, they were just randomly placed and this created a very frustrating “unfair” feeling experience. As with nearly everything in City of Brass, we turned off random and instead wrote a series of rules about where traps can and cannot appear.
We found placing floor traps in front of doors (while initially counter intuitive) was actually a good place that players could learn “There are usually traps after doors, I need to be careful here.” Another example is that having wall traps set right around corners was very frustrating, as you have little chance to see them before going around the corner, so we exclude the walls next to corners for spawning these types of traps. We also try to spawn traps in the center of the player’s path where they are forced to navigate around them, or can use them to dispatch enemies using the whip.
Did the studio use Blueprints in any way?
James: Most of our game objects are Blueprints that are derived from code classes created by the programming team. This approach gives us the best of both worlds. If we need specific functionality or optimization, the programmers can add this to the base class. Artists and designers on the team then extend the individual derived Blueprints adding new components like emitters, lights, audio, animated meshes, or whatever other components we need. We also trigger events on these Blueprints from code, then use Blueprints scripting to generate the desired result in game.
We also have a library of custom components that can be added to a Blueprint to easily create new gameplay or interaction. For example, the “whip component” can be added to any Blueprints object to allow scripted interaction with the player’s Whip.
Does the studio have any favorite UE4 tools or features?
Lancaster: I’m excited for 4.22. City of Brass levels are made up of lots of small meshes and we’re hoping to get a nice performance boost from the auto-instancing at runtime feature. If the gains are big enough, we might be able to get some of our dynamic lights casting shadows again on Switch!
How, if at all, did the studio leverage the Unreal Engine Marketplace during development?
Lancaster: The system we based our level generation on was actually a plugin bought from the Unreal Engine Marketplace. “Dungeon Architect” gave us a massive head start very early on in the project. We heavily modified it over the course of the project, but not starting from scratch was a real win.
James: We purchased a number of assets from the Unreal Engine Marketplace during the development of City of Brass. Sometimes we didn’t use the Marketplace asset in the final game, but just got them to see how someone else has implemented a particular feature, or just use one sub component of the Marketplace Asset, whatever helps us get to the best result in the shortest amount of time. Especially with things like Materials and Particle Systems, it can be extremely useful to look through how someone achieved a particular result, and that can greatly reduce your own iteration time.
Can we expect future City of Brass updates?
Orman: Absolutely! We have a couple of things in the pipe right now.
First, we have updates going out to the consoles: Xbox One, PlayStation 4, and Nintendo Switch, which will bring the playable crossbow-wielding Revenant to those platforms for the first time. Our goal has always been to get all platforms to the same feature parity as quickly as possible, so that’s our highest priority.
After that, we have plans for yet another free expansion that focuses on increasing the variety for players even more - so stay tuned!
Thanks again for your time, where can players learn more about City of Brass and what Uppercut Games is working on?