The Technical Art of Ascending Inferno

Making Of / 27 October 2024

Hey everyone! Two years ago I had the opportunity to join Oppolyon Studios to work on their project Ascending inferno. From day one, I could tell that this project would not only push my technical and creative boundaries but would also allow me to collaborate with a team of passionate and talented developers.

In this post I’ll be taking you behind the scenes of my contributions to Ascending Inferno; diving into some of the challenges and features I had the opportunity to work on and showcasing some of my proudest visual effects. But before we jump into the details, let me give you a quick overview of Ascending Inferno.

Ascending Inferno is a 2.5d rage-platformer where you play as Dani, a soccer player, tragically you will die and be sent to the bottom of hell where, fortunately you encounter your brother Vincent. Unfortunately, however Vincent has taken on the form of a ball and needs your help to be kicked up the nine levels of Dante's inferno.

You can find the game here!


The game is set across ten distinct levels: The Earthly Realm (which serves as the tutorial), followed by Limbo, Lust, Gluttony, Greed, Wrath, Heresy, Violence, Fraud, and Treachery. Below, I’ll take you through some of the mechanics, visual effects, optimizations, and world-building elements that helped bring these environments to life.

Contents

  • Visual Effects For Game Mechanics
  • Visual Effect Overlays
  • Vincent Camera - Improving Gameplay
  • Optimising The Project
  • Shaders
  • Project Juice
  • World Building Blueprints
  • Set Dressing and Level Construction Challenges
  • Turntables: Crafting Immersive Character Customisation
  • Character Customiser: Adding Wings, Hats and Trails
  • Automatic Scalability Options
  • Conclusions

Visual Effects For Game Mechanics

To begin, let’s take a closer look at the particle effects and how they play a critical role in world-building and conveying information to the player. In this section, I’ll break down some of the core game mechanics, their visual effects, and the layers we added to achieve the result. I’ll also touch on some of the decorative effects that brought our world to life.

Each of our ten levels features a unique gimmick that players must understand to progress, with one exemption: Limbo. The tutorial is designed to quickly teach players how to control Dani and the soccer ball, guiding them through movement, manipulating the ball, and how the ball affects the world. It was important that we introduced the mechanics progressively as to not overwhelm them with new information and ensure smooth progression. We chose to first introduce a door – something familiar, but which required players to fully understand before they could continue forward.

Once the tutorial is completed, players are thrust into Hell, where the true challenge begins. At this point, there are few areas that feel truly safe, leaving falling between levels as a constant threat. In designing Limbo, we decided that the level itself should be challenging enough without the need for an additional gimmick. Instead, we focused on large, simple jumps and big falls. We also used Limbo as an opportunity to introduce shortcuts—secret routes that would let skilled players bypass some of the harder sections. While these shortcuts were subtle, they played an important role in the player's exploration and learning experience.

Moving on to the third level, Lust, we introduced the player’s first real obstacle: a demon whose hypnotic dance draws Vincent in before kicking him away. To bring this mechanic to life, I created three distinct visual effects: a swirling vortex around the dancer, a screen-space overlay to enhance the hypnotic effect, and a subtle blue swirl around Vincent. These effects worked together to clearly signal the mechanic’s function to the player, adding both visual clarity and thematic consistency to the level.

Next up is Gluttony. This level presented some design challenges, and we went through several iterations before settling on the final mechanic. Initially, we created sticky platforms that were supposed to make it harder to move Vincent, but this inadvertently helped players more than hindered them. To raise the difficulty, we switched to “hot” platforms. These would cause Vincent to bounce uncontrollably, eventually ejecting him if the player didn’t regain control. It was a chaotic mechanic that fit the gluttonous excess of the level perfectly.

The fifth level, Greed, was another one that took a few attempts to get right. Initially, we wanted to punish players who took too many shortcuts, but this design proved problematic as our intended shortcuts took too long to access. By this point, the level’s layout was already locked in, so we pivoted to a wind mechanic. Now, strong gusts of wind push Vincent left or right while inside the wind volume, adding an additional layer of difficulty to the level.

For Wrath, we were torn between two ideas; sinking platforms and hands that pull you through the ground. Both ideas drew inspiration from Dantes Inferno, but both presented their own design challenges. Ultimately, we decided to go with the hands. This is one of the mechanics I’m most proud of, the first iteration I made ran below 20 frames per second with only 8 sets of hands running. After a couple attempts, I was able to offload all the calculations onto the GPU through the shader, then using Unreal Engine’s foliage brush we could have them instanced anywhere. Suddenly we went from approximately 50 hands all the way up to 80,000 before we started seeing framerate issues.

Our next level Heresy is right before the checkpoint, so we wanted it to be a bit of a challenge but not completely unforgiving. We ended up making this one the moving platforms biome. Aside from a few steam and smoke visual effects there weren’t too many particles to be made here, so instead most of my time was focused on the trains.

Once players make it past the checkpoint in between Heresy and Violence it becomes impossible to fall back to the previous levels, this means that we could drastically increase the difficulty of the mechanics without them being too punishing. For Violence we wanted something that could throw Vincent off screen with a large surrounding space to ensure disaster. I was tasked with creating a “lightning” and “sphere” effect, which I interpreted as a plasma ball. Sparks would gather around the outer edge of the sphere and discharge toward the centre, creating a satisfying explosion that propels Vincent off into danger.

Fraud was all about creating the feeling of being out of place, as an impostor. To reinforce this, we designed a stage that our player would run along with a grand audience behind them, watching all their movements. Our mechanic for this one was going to be false platforms driven by what was and was not in light. After some playtesting we found it just wasn’t fun or easy to convey, so instead the platforms now glitch before popping out of visibility.

We also wanted Fraud to have a fake ending—a grand, open, celebratory moment where players think they’ve beaten the game. Little do they know, they are still in Fraud, and there’s more to come. This presented a significant technical challenge, as the entire level above had to remain loaded and visible while the player navigated the fake finale. To solve this we set all the materials in Treachery to invisible, then as the player hits a trigger we fade in the entire level and cover it all with another skybox.


Finally, we reach Treachery, the last level of Ascending Inferno. From the beginning, we knew that this level would be modelled after the frozen lake described in Dante’s Inferno. The key mechanic here was the icy platforms, which were slick and unpredictable, creating one last difficult challenge before the final ascent.

Visual Effect Overlays

In addition to the core particle effects, we added an extra layer of visual polish to Ascending Inferno through screen space overlays. These overlays played an important role in providing key information to the player without breaking immersion. One of the most prominent examples is the title cards that appear when the player enters a new level. For these, our talented pixel artist Fauzi created a series of flipbook textures, and I took on the task of integrating them into the game. I developed the blueprint and shader system that would trigger these title cards, ensuring they appeared smoothly and consistently across each level.

Another notable overlay effect I worked on was what we called "the supernatural effect." This was triggered whenever the player discovered one of the game’s secret rooms. The effect prominently featured the demon’s emblem glitching across the screen before settling in the centre and fading out. I approached this effect like a camera shake, introducing randomness and distortion into the visual to give it an unsettling, otherworldly quality. This was achieved by offsetting the texture by random values, applying a glitch texture to distort the UVs, and adding some chromatic aberration in the shader. These small details gave the effect a chaotic, supernatural feel that fit perfectly with the tone of the game. The supernatural effect was designed to unsettle the player, giving the discovery of secret rooms an eerie, unpredictable feel. By using random distortion and chromatic aberration, I wanted to create a moment that felt visually jarring, amplifying the tension and mystery that players would associate with these hidden areas.

Vincent Camera - Improving Gameplay

One of the core challenges in Ascending Inferno is navigating Vincent—the ball—up through the layers of hell. While getting the player to the top is relatively straightforward, taking no more than 15 minutes of continuous running, the true difficulty lies in managing Vincent’s journey. Given how easily Vincent can fall or be kicked off course, it became essential to provide the player with a way to keep track of him. To address this, we implemented a secondary camera that pops up in a small window whenever Vincent goes off-screen, showing his position as he falls, waits, or gets knocked around.

However, this feature came with a major challenge: rendering a second camera while maintaining good performance. Initially, we thought this feature might be too demanding, especially for lower-spec systems, and considered abandoning it. But after revisiting the problem, I made several optimisations to the camera system. By reducing the secondary camera’s framerate to 24fps and lowering some of its rendering quality, we made it more efficient without compromising the player experience. Our programmer, Ben, also played a pivotal role by developing a chunking system to deactivate unnecessary game objects in the scene. Together, these optimisations allowed us to reintroduce the Vincent camera without seeing any significant performance drop, even on the lowest-spec machines. This was a huge win for our team and really added to the player's connection with Vincent, ensuring they could always keep an eye on him during tricky segments.

Optimising The Project

When I first joined the team, Ascending Inferno was running at a sluggish 20fps, even on relatively high-end computers. It was immediately clear that there were multiple bottlenecks impacting the GPU, CPU, and memory usage, all of which needed to be addressed to improve performance. The first issue I tackled was the game’s texture budget, which was filling up rapidly, even with relatively few assets on screen.

The problem stemmed from the workflow our team was using, which, due to a limited understanding of optimisation, involved creating models in MagicaVoxel, transferring them to Blender to reduce polycount and bake textures from vertex data, and then importing them into Unreal Engine. This process left a lot of wasted texture space, and downscaling from 4K textures was nearly impossible. Additionally, each asset had its own unique shader rather than using material instances, which meant we weren’t taking advantage of shader batching, leading to unnecessary overhead.

To fix this, I revamped our material workflow. We didn’t have an immediate solution for how to remake the models, but I knew we could simplify our materials to reduce both texture usage and shader complexity. I implemented a 2D lookup table to sample the colours based on the voxel UVs. With this system in place, we were able to recolour objects simply by swapping the lookup table, drastically reducing texture sizes and increasing efficiency.

Once the memory issue was under control, I shifted my focus to the CPU and GPU bottlenecks. The CPU problem was relatively easy to identify but incredibly complex to solve. Unreal Engine is optimised for games built on two primary axes (X and Y), but our game had a unique structure: it was 60,000 units tall but only a single chunk wide. Because of this, Unreal’s built-in chunking system wasn’t functioning properly, and the game was keeping objects in memory and processing them even when they were far out of view.

At first, we tried disabling frustum culling, which yielded a slight performance improvement since our game uses forced perspective, but it wasn’t enough. Working closely with Ben, our programmer, we devised a custom chunking system. This allowed us to dynamically load and unload portions of the level as the player progressed, significantly reducing CPU overhead.

With the CPU issue addressed, I moved on to tackling the GPU bottlenecks. Although Unreal Engine 5.0 had introduced powerful new tools like Nanite and Lumen, we quickly discovered that our forced-perspective voxel art style limited our ability to take full advantage of them. Nanite’s clustering wasn’t compatible with our models, and as a result, Lumen’s performance also suffered without Nanite-enabled assets.

Digging deeper, I identified three key issues affecting the GPU:

  1. Material Batching: As mentioned earlier, our use of single-use materials instead of instances was preventing efficient shader batching.
  2. Lighting: Our scenes were overloaded with overlapping lights, creating hotspots and driving up the complexity in lighting calculations.
  3. Geometry Density: Our voxel models were highly detailed with numerous hard edges, making many of the polygons smaller than a single pixel. This sub-pixel anti-aliasing was inflating the cost of rendering the scene, especially on large, dense meshes.

Unfortunately, by the time we discovered these issues, many of the assets for the first three levels had already been created and integrated, and reworking them wasn’t feasible given our production timeline. However, we found a compromise: we simplified the geometry on larger, flat surfaces, replacing complex meshes with simple cubes. We then used normal maps, displacement, and parallax occlusion mapping to preserve the visual detail, all while reducing the rendering cost.

In the end, these optimisations drastically improved performance across the board, making the game playable on lower-end hardware without sacrificing the artistic integrity or visual appeal of the levels. While we couldn’t leverage all of Unreal Engine’s new features, the custom solutions we developed allowed us to bring Ascending Inferno to life, without compromising on performance.

Shaders

In my pursuit to optimize the rendering pipeline for Ascending Inferno, I developed several shaders, each with the following considerations in mind:

  1. Adjustability: Could the shader be adapted for many variations?
  2. Modularity: Could it be used for tiling assets or modular pieces?
  3. Performance: How efficient was it for rendering?
  4. Scalability: Could we simplify it for lower-end platforms?
  5. Transparency: Could we avoid using transparency to optimize performance?

While not every shader could meet all these constraints (such as glass shaders, which require transparency), each was crafted to strike the best balance for the game’s unique needs.

The first shader I developed was meant to optimize prop rendering, particularly for the large number of assets already in development. Initially, it was a basic shader with an albedo colour chart and world-space tri-planar projected roughness, but over time it evolved to include several crucial features.

One major improvement was shifting the UV space from world-space tri-planar to local-space tri-planar, which allowed the roughness to respect object rotation, thus preserving the correct voxel sizes. Although I encountered a bug where scaling distorted the UVs, it remained mostly unnoticeable in the final build.

To provide more control over how objects appeared, I introduced metalness and rust sliders, which allowed artists to adjust the wear and aging effects on props. This flexibility gave the assets a much broader visual range without needing to recreate textures or models. Additionally, to solve visibility issues (when players passed behind large props), I added a screen-space dithered mask cut-out. This feature allowed certain props to become transparent or semi-transparent, improving gameplay clarity without compromising immersion.

Tri-planar

This shader was critical in reducing GPU rendering costs. We needed a simple model that could be used across multiple levels while still looking visually detailed. The solution was a tri-planar shader, which used surface deformation methods to maintain the detailed look of objects without requiring expensive geometry. I also introduced object scale UVs to add further versatility, though this feature was eventually scrapped due to a recurring compilation error when applied to skeletal meshes.

Like the prop shader, this shader supported metalness, rust, and dithered opacity, which helped keep props and walls visually consistent throughout the game. The goal was to ensure that all elements appeared cohesive, even as we shifted the underlying technical complexity for optimization.

Sprites

The sprite shader was an entirely different challenge. Unlike the modular shaders for props and walls, this shader was focused on single-use textures. Initially, the shader supported a flipbook texture, an emission texture, and a few parameters to control timing. However, as the game’s design evolved, so too did the shader's complexity. For example, some textures had 6 frames and non-power-of-two dimensions, so I had to implement cropping to remove unnecessary blank space from the flipbook. New levels also demanded different visual effects, such as rim lighting for Lust and desaturation of blue and green channels for Violence.

Despite the increasing complexity, the shader maintained a consistent rendering cost, which was a relief. However, the numerous static switches led to longer compilation times towards the end of the project. Although we could have further reduced compilation complexity, the time investment wasn't justified given the project’s deadline. Initial builds took a couple of hours to compile, but subsequent builds reduced this time to around 30 minutes.

Glass

Glass presented a unique challenge in terms of both performance and appearance. Transparency is notoriously expensive, and in some areas of the game, we had a significant amount of glass. In our fixed perspective, glass often didn’t look convincing, so I stylized it with two key components:

  1. A white speckled noise texture, which helped ground the glass in the world.
  2. A diagonal streak effect, like what you’d see in cartoon games, adding a subtle shine and vibrancy to the otherwise flat material.

This approach allowed us to maintain the game’s visual style while optimizing the rendering cost of glass objects.

Mirror

A more specific shader developed late in production was the mirror material for Fraud. A standard low-roughness, metallic material didn’t quite fit our world. Like the glass shader, it felt like it was missing something. I reused the glass streak effect but skipped the speckled noise, instead introducing roughness variation across the surface to give the mirrors more life.

To complete the effect, we also made sure the mirrors had something to reflect, avoiding the dull appearance of reflecting an empty void.

Virtual production stage

One of the game's biggest technical hurdles was how to render an entire level while hiding it behind an artificial boundary, maintaining a large open area. I was tasked with creating a virtual production stage, inspired by hexagonal LCD panels.

The shader I developed used an alpha mask that gradually revealed more of the stage in a broken, jittering state. To achieve this, I designed the colour reveal to mimic the behaviour of real RGB pixels, increasing and decreasing their values according to the underlying texture.

Project Juice

With Ascending Inferno now running at a stable 60 frames per second, and shaders finalized, I shifted focus towards adding smaller details and interactive elements that could deepen the player's immersion. The goal was to fill the levels with objects that players could interact with or that would interact with them, creating a world that felt alive and responsive. This part of development focused on adding easter eggs, player-triggered interactions, and various “juice” elements to enhance the overall experience.

Lust: The Staring Eyes

One of the first interactive elements I developed was for Lust, a level filled with hundreds of eerie eyes scattered throughout the environment. These eyes were meant to act like cameras, always watching the player no matter where they moved.

Initially, I implemented this using a blueprint setup, which resulted in each of the 400-500 eye cameras being constantly active. Unfortunately, this tanked the frame rate, dropping it as low as 10 frames per second. To resolve this, I rewrote the blueprint so that the eyes would only activate when the player entered a specific trigger area. While this improved performance, a side effect of chunked level loading caused the eyes to lose track of the player after each load, resulting in periodic lag spikes.

After multiple attempts, I settled on a simpler solution—moving the eye movement to the shader. I had hoped to create a fully functional 3D rotation that would track the player’s movements but ran into limitations in my knowledge of vertex shader math. Instead, I made the eyes sweep back and forth on a set trajectory. Although I initially saw this as a compromise, playtesting revealed it was more engaging: more players noticed the sweeping motion than had noticed the original player-tracking version, so in the end, it was a happy accident.

Fraud: Virtual Production Cameras

Next, I worked on interactive cameras for the virtual production set in Fraud. These cameras, unlike the ones in Lust, were large filming cameras placed on tracks. Since there were only 2-4 cameras per scene, I didn’t have to worry about performance issues. I recycled much of the code from the Lust eyes blueprint and adapted it for this new purpose. With fewer cameras, even with chunked loading, performance remained stable.

Dynamic Lighting: Gradients Based on Player Movement

With Ben’s assistance, I worked on dynamic lighting that could shift based on the player's position. This effect was inspired by the team’s desire for lighting to reflect the emotional and visual tone of each level dynamically.

Using the player’s location as a reference, I sampled a colour curve to change the directional and skylights in real-time. Later, I expanded this feature to modify exponential height fog, allowing us to change its colour, density, and refraction based on the same system. This adaptive lighting helped keep the game visually fresh, adding subtle atmosphere shifts as players traversed each level.

Gluttony: The TV Easter Egg

In Gluttony, I was tasked with creating a working television set for the diner level. Initially, I experimented with smaller TVs scattered around the scene but found they lacked impact. Instead, I placed a single large TV that players could interact with.

Inspired by the chaotic nature of the level, I added a mechanic where kicking Vincent (the character) into the TV would cycle through channels until the television broke. To add a fun easter egg, I inserted a hidden video of my cat. If players hit the broken TV enough times, they would be rewarded with a short clip of my cat on-screen, adding a personal touch to the game.

Gluttony: Conveyor Belts and Trash Physics

While working on Gluttony, I noticed that the conveyor belts running through the lower levels needed more "juice." I experimented with allowing the player to kick objects off the belts. However, due to the way the belts were set up, it was tricky to get the interaction to feel natural.

Eventually, I found that adding a small collider to detect player momentum and applying it to the trash objects worked, but the interaction remained difficult to control. Although it wasn't perfect, the mechanic added a nice touch of interactivity to the level, making it feel more dynamic.

Interactive Platforms: Shimmy Without Collision

To make some of the platforms react to player inputs, we decided to add a shimmying effect when players jumped on them. However, the platforms couldn’t alter their collision or physically move the player or ball, so the effect had to be purely visual. This turned out to be more challenging than simply rotating an object, so I developed a solution using a shader.

The shader rotated the model based on a pivot point using a timeline defined in an external blueprint. While this method worked well and looked great, setting it up for each platform was time-consuming, so it wasn’t as widely implemented as I would have liked by the project’s end.

Violence: The Fighting Game Easter Egg

In Violence, Chris (our pixel artist) and I collaborated on a fun easter egg inspired by discussions about how video games allegedly cause violence. We thought it would be a nice touch to add a pixel-art fighting game in the background of this level.

Chris created pixel art sprites of the fighters, while I handled the shaders for the health bars and the infinite timer. I also scripted a blueprint that would toggle through the various fight states. Initially, the fighters were meant to be Chris and myself, but we later decided to reference all the developers in Ascending Inferno by cycling our names in the fight roster. This led to amusing moments during playtesting, as we’d stop and watch the characters fight to see who was winning that day. It became a fun, light-hearted feature that consistently drew laughs from my fellow developers.

World Building Blueprints

While the “juice” added flair and personality to Ascending Inferno, the backbone of the immersive experience was built on the carefully constructed world-building blueprints. These blueprints ensured that the game world could be created efficiently while maintaining the desired level of detail and atmosphere. Initially, several blueprints were already in place when I joined the project, but as development continued, it became apparent that additional, more specialized blueprints were necessary to meet the creative and technical demands.

Spline Follower Blueprint: Trains, Rats, and More

The first significant blueprint I worked on was a spline follower. This was pivotal for creating animated world elements like planes flying in the sky, rats scurrying along the ground, and trains moving along the tracks in Heresy.

The spline follower allowed us to lay out a path for these elements to follow smoothly. However, as we used this blueprint across various levels, one limitation became clear, there was no way to instance meshes along the same spline. This resulted in us duplicating the spline and manually offsetting objects, which became tedious, especially in Heresy, where both the tracks and the trains needed to follow the same path. Next time I work on a similar feature, I plan to include the ability to instance meshes directly along the spline, saving both time and effort.

Dynamic Lighting Blueprint: Adjusting for Quality Settings

Another critical blueprint I created was for dynamic lighting adjustments. This was born out of an issue we encountered while switching between high and low-quality settings. Our game relied heavily on Lumen (Unreal Engine’s global illumination system) and emissive materials to create the proper lighting ambiance. However, when switching to lower quality settings, the lighting in certain areas would fall flat, leading to a lack of depth.

To address this, I built a small blueprint that took the current quality settings into account. The blueprint would assess whether the emissive materials alone were sufficient to light the area or if additional light sources should be enabled. This was a fascinating technical challenge, and it’s something I hope we can further streamline in future projects to avoid needing such workarounds.

Clothesline Blueprint: Decorating Greed’s Underground

In Greed, we wanted to create a slums-like underground environment filled with clotheslines to help bring the space to life. Hand-placing each clothesline in Unreal Engine would have been a time-consuming task and building them as single-purpose in Blender would lack the flexibility we needed.

To solve this, I created a dynamic clothesline blueprint that allowed us to quickly scale and adjust the lines and the number of clothes hanging from them. This blueprint allowed the artists to scatter dozens of clotheslines throughout the underground area, giving the level a lived-in, chaotic look without requiring manual placement of each item.

Skybox and Layer Transitions: Seamless Vertical Biomes

One of the major visual challenges we faced was the vertical nature of the game. Each biome in Ascending Inferno needed to feel distinct and self-contained, but we also wanted to create the illusion of a continuous world.

To accomplish this, we opted for a fake skybox system that would wrap around and between each biome. The challenge, however, was making sure the transition between biomes appeared smooth. Without an object to block the view, the sky would continue into the camera plane, creating a jarring visual. To solve this, I used a custom depth pass to create cut-outs in the skybox, allowing for clean transitions between biomes. This maintained the illusion of a large, seamless world while allowing players to move vertically between different levels.

Parallax Backgrounds and Large Artwork: Solving Clipping Issues

The last major blueprint I worked on tackled a significant issue we faced with our parallax background art. For many levels, we split the background artwork into several layers to create a parallax effect, where objects at different distances would move at varying speeds as the player moved. These background layers were often hundreds of units tall and wide, sometimes clipping with skyboxes or other levels.

The most noticeable issue occurred in Lust, where a giant demon and a disco ball were part of the background art. As players progressed closer to Gluttony, the level geometry and meshes began covering the background art, breaking the illusion that the demon and disco ball were far away in the distance.

To fix this, I used a clever technique involving the camera position and screen-space UVs. Instead of displaying the demon and disco ball as part of the background layers, I projected them onto the skybox itself. This projection was paired with a slow offset, mimicking the parallax movement we initially desired. This solution allowed the demon and disco ball to remain visible and convincing, regardless of the player's location, without the risk of clipping through the environment. On the other hand, while this technique worked well, it also appeared in the main menu flythrough cameras meaning those had to be curated around the skybox limitation.

Set Dressing & Level Construction Challenges

The set dressing and level construction process for Ascending Inferno was highly collaborative, with each artist contributing to the creation and refinement of the game world. While many contributions were a joint effort and hard to define individually, there were certain aspects of the process that are clearly identifiable as my own work.

Setting the Mood: Initial Look Development

In the early stages of each level’s development sprint, my primary focus was on establishing the general feel and look of the level. This included spending several days working on the blockout, filling it with generic assets such as rocks, walls, windows, and doors. During this time, I also focused on material and texture creation to drive the look-development process and give the levels a clear aesthetic foundation.

While I worked on these foundational elements, the other 3D artists, Ricky and Lachlan, focused on creating unique props to further enrich the environment. These props, ranging from scattered debris to interactable objects, would later fill out the levels and provide them with detail and personality.

Alongside this, I also began the first pass on lighting for each scene. This initial lighting setup mainly involved directional and skylights, as it allowed us to get a rough idea of how the lighting would interact with the environment and assets as the level progressed.

Lighting for Atmosphere: Establishing Distinct Biomes

As the levels became more fleshed out and moved toward their final form, I turned my attention to lighting the levels to create a cohesive atmosphere. My goal was to use distinct colour palettes to reflect the themes and emotional tones of each biome. For example, the clearest colour choices were:

  • Red for Anger
  • Blue for Sadness
  • Yellow for Hunger

These colours not only added to the emotional weight of each area but also helped create a strong visual identity for each biome.

Some biomes, such as Treachery, which featured ice and frozen landscapes, were naturally suited for cool whites, blues, and turquoises. By using lighting that evoked coldness and isolation, we further emphasized the thematic qualities of treachery, drawing players into the oppressive atmosphere of the level.

Secret Rooms and Emblems: Visual Cues for Players

During the development of secret rooms, we faced the challenge of helping players identify and remember which rooms belonged to which biome. To solve this, we developed a set of emblems to serve as visual indicators. Each emblem corresponded to a specific demon and biome, and their colours were matched with the lighting of their respective level.

This colour-coding system provided a clear visual connection between the secret rooms and their respective biomes, making it easier for players to recognize rooms they had missed. These emblems also reinforced the overall mood of each biome, as they were designed to blend harmoniously with the lighting and overall aesthetic.

Set Dressing Challenges: Evolving Environments

One of the main challenges in set dressing was the evolution of the levels over time. As props were created and the levels matured, it often became necessary to revisit earlier set dressing decisions. What seemed like a simple task of placing objects initially became more complex as new assets, textures, and lighting changes were introduced.

For example, there were instances where:

  • Textures created early on clashed with newly developed assets, requiring re-texturing or re-mapping.
  • The addition of new props meant that certain objects had to be rearranged or removed to maintain a consistent look and avoid cluttering the scene.
  • Lighting changes sometimes drastically altered the feel of a level, making certain set dressing elements feel out of place or misaligned with the new atmosphere.

This iterative process meant that set dressing was rarely a “one-and-done” task, but rather something that needed to be continually refined as the game world evolved. The team had to balance creative freedom with the constraints of level design, ensuring that each level felt immersive while still serving the overall gameplay and progression.

Turntables: Crafting Immersive Character Customisation

As we neared the final stages of production, one of the key features we focused on was creating a visually engaging character customizer. Initially, the plan was simple: the player would be teleported to a generic room, customize their character, and then return to the game. However, this approach felt somewhat detached from the game's immersive experience.

I was later assigned the exciting task of designing unique customization environments for each level. Rather than taking the player out of the world, this new approach allowed them to stay connected with their surroundings by transporting them to a customization room that reflected the style and theme of the current level. This added a layer of immersion to the character creation process, making it feel more like a natural part of the game world rather than an isolated feature.

Each customization room was carefully designed with its own distinct visual identity, aligning with the tone, mood, and aesthetic of its respective level. This was a fun and challenging aspect of development, as it required pulling in familiar elements from throughout the level to ensure continuity, while also incorporating visual effects that elevated the overall experience.

If you're interested in seeing these unique rooms, you can check them out [here]!

Character Customiser: Adding Wings, Hats and Trails

After finalizing the customizer rooms, I moved on to creating some fun and thematic cosmetics for Vincent. These included a variety of wings, hats, and trails that players could unlock by achieving certain milestones, such as completing level-specific achievements. The goal was to give players a visual reward tied to their progression, adding a layer of personalization to their character.

Each set of cosmetics was inspired by the levels themselves, reflecting the themes and aesthetics of the world Vincent was exploring. While many of the levels had clear visual motifs to draw from, a few proved to be more challenging in terms of translating their essence into cosmetics—most notably, Greed and Violence. Finding ways to visually represent those abstract ideas in a cohesive and appealing way took some creative experimentation.

If you would like to see them in more detail, go [here]!

Automatic Scalability Options

The final feature I’d like to discuss is the automatic quality scaling system I developed. Rather than forcing players to manually adjust their settings, we wanted an option that would automatically optimize performance based on the players hardware capabilities.

The system is initiated when the game detects that its running below our minimum target framerate (50fps). When it detects poor framerate a UI element is presented to the player to initiate the automatic quality settings, where if accepted, will cycle through various settings such as texture quality, shadows, global illumination and shader complexity. It will gradually lower each setting until the game achieves a sustainable framerate.

Importantly, I made the design choice to handle this inside the main menu, before the player has an opportunity to play the game. This ensures that players are never distracted or disrupted by performance drops during critical moments as the adjustments can take place before the game even starts. Given our menus are full 3D scenes reflecting that of the game, it gives us a close approximation of the expected framerate while in gameplay.


In this way, the player enters the game world with optimal settings, without the need to tinker or experience frustrating performance drops. We can’t expect the player to know what each individual setting will do, so taking an automatic approach simplifies it a lot for them and creates a better game experience. You can check out how it works in the video below!

Conclusions

Looking back on the journey of working on this project, I can see how each piece I contributed has played a crucial role in shaping the final product. I’ve had the opportunity to collaborate with incredibly talented artists, designers, programmers and musicians, and in the process I’ve both taught and learned so much. Working on Ascending Inferno was both challenging, yet incredibly rewarding, allowing me to further myself as a technical artist.

One of the most fulfilling aspects was seeing how all these elements came together to create a cohesive experience for the player. Whether it was crafting the visual effects that enhanced gameplay or working through the intricacies of world-building, every step helped build a world that players could get lost in. And even though some contributions, like the screen-space overlays, may go unnoticed by the casual player, knowing how much they add to the overall immersion made every hurdle worth it.

As this project draws to a close, I find myself reflecting not just on the technical accomplishments, but also on the creative collaborations that made everything possible. Each shader tweak, lighting adjustment, and prop placement has been a step toward realizing the team's vision — and it’s something I’m incredibly proud to have been a part of.

Moving forward, I’m excited to see how players interact with the world we’ve built and where my next projects will take me. Every game is a new opportunity to learn, create, and push the boundaries of what's possible. I can’t wait for the next adventure, and I hope you’ll be there to experience it with me.


The Making of Me, Myself & Die (part 2)

Making Of / 02 December 2020

To read the first part click here. You can also find the game on the Google Store and Itch.io

The brief I was given for our character initially was much like the Borg from Star Trek. A block of metal with light shining through the cracks, however we wanted minimal detail. I made a cube made from smaller cubes with the intent of having them animated to bring life to our character. This proved problematic for a few reasons, our first being it had way too many bones and our second issue being time limited was a lot of work to animate. After a few playtests our designers found players wanted the player speed to be faster, leaving less than 3/10ths of a second for an animation to play. This simply wasn't possible for animating meaning it was time to go back to concepting. I made a few sketches and got the best bits from each to make our second character, but we felt it had lost a lot of its liveliness. I went online and found how others had made their cubes and came across one particularly nice stylised stone looking cube. I got the approval of my team to work on it from here and made my own cube. In the end I wasn't happy with this one as it still wasn't as lively as our first and there is something off about it. However I was under a strict deadline for this character so we had to deal with what we had and continue bringing the rest of the game up. 

As part of the character brief was to have light coming from its centre I used a mask for its emission and had it pulsing from another animated material. Our dead character uses the same animation but with a new emission colour for its three states.  

I looked into a few ways to make decals within Unity such as how Unreal does it, mesh decals and finally sprites. I chose to use sprites as it was less performance impacting than the other two options and it saved us a bunch of time not having to make custom meshes. Using a shader I made we can choose how worn the sprites look, 3 colours based on a mask and where emission comes from on these sprites. All that  was left was making the sprites themselves. I looked into some hieroglyphs and made some rough silhouettes to be placed around the scenes. As part of this there were 12 unique glyphs and two tile-able ones to give variance among our 40 levels. Our tiling sprites use Unity's tiling sprite settings to allow us to scale these to any size. With this in combination of the other 12 images we were able to decorate the scenes in many unique ways. 

Our game makes use of post processing in a few ways. I looked into post processing and found that bloom can make scenes look "hot" as it has a radiance around the emissive parts. This not only made our levels look hot but also our emissive objects look like they were glowing properly. To draw our players eye to the centre of the screen I added a vignette. The final use of our post processing was for colour correction and checking our values. By converting it to greyscale we were able to see how easy it is to distinguish the scene from mechanics. 

When I was first implementing our post processing stack nothing was rendering. When using the lightweight render pipeline there are a few extra settings to enable before the stack will start rendering.

The lighting of our scene is quite simple, it uses one directional light which is baked into the scene. At the start of production we were going to use real time lighting to save on file size but after some research into ambient occlusion decided to bake it into the scene. When I first baked the lighting I expected the scene to start mostly the same but with some shading in the corners. This is not what happened, but instead we had a much better result. The scene became a lot more desaturated and "dry" feeling as well as having the ambient occlusion we wanted. Although there were a few errors these were easily fixed by enabling "generate lightmap UVs," increasing the Texel density for the lightmaps and finally increasing the padding between shells. This made our scenes look really nice at the expense of 12mb per scene, way too much for a mobile game aimed at less than 100mb. Enabling compression left banding across the scene which was also undesirable but I found that using crunch compression removed this at the expense of overall quality. As we are targeting mobile phones we figured it would be hard to see the pixel sized artefacts and went with it. Now we had got the lightmaps to 1mb per scene which was still a bit too much for us. Knowing that our camera never got closer or further from our scene I disabled mipmaps for our textures, reducing the file size to just over 200kb which was a much more manageable file size.

Although our lightmaps were compressed we still had a very large game for such a simple project. Our total download size was over 90mb and although this was in our budget, we thought we could do better. I went through each of our assets and changed their import settings to have no animations, rig or imported materials. I also removed blend shapes, visibility, cameras and lights from the mesh imports. From here the last setting we changed on the meshes was the compression type. 

After changing the models I addressed the textures. When we were making them we decided to start at a higher resolution so we could down-sample them later. Doing many of the same settings as the lightmaps I set our textures to have no mipmaps and high quality compression. As many of our textures were quite simple I was able to down-sample most of them to 128x128 textures. This reduced the size of the game a bit but we were still sitting around 80mb, there was something still keeping our file size up. After looking around our project a bit I discovered that we were using sprites for our level selection screen. When we were taking the screenshots we didn't take them at a power of two number meaning they couldn't be compressed in Unity. After using David's screenshot tool to retake the images at 2048x2048 I applied the same compression settings as before. After these settings were applied I managed to take our game from 90mb down to 30mb.

Our team was made up of 6 people, our project lead and designer Jason Marsh, Level designers Mateusz Solle and Dean Subtrizky, programming by David Flintoft and art by Sie McEachern and myself. Our team was assisted by two audio engineers Lachlan Willacy and Yan Parums.

You can find the game on the Google Store and Itch.io

The Making of Me, Myself & Die (part 1)

Making Of / 28 November 2020

Me, Myself & Die is an isometric puzzle game for android. You are a mystic alien cube making your way through an Egyptian tomb. Orchestrate your death to reach new areas.

You can find the game on the Google Store and Itch.io

To get the feeling of our game right we started off concepting. Looking at real world places we initially looked toward Mayan temples and its grey brick walls, foliage and water. We knew we were on the right track but some of our mechanics had some sci-fi and futuristic elements to them. We decided to swap over to the pyramids of Egypt as there is a common association of aliens building the pyramids. Using a basic level block out I drew over the meshes to get an idea of unique pieces we would need, decorative pieces and finally some of our mechanics. While doing this I was looking at our colour pallet to try and make our mechanics stand out while still feeling as part of the environment. 

As we were targeting a mobile audience we thought it was best to use the Lightweight Render Pipeline in Unity. The Lightweight Render Pipeline is a stripped version of Unity's standard render pipeline, giving only what the user wants and nothing more. As part of this it comes with Shader-graph, a visual coding to create our own materials. In our early stages I did a lot of research into how we can use shader graph to our advantage. We chose early on to use only diffuse textures as our game was quite simple and having roughness and normal maps would detract from that. 

In my research I also discovered a way to tile object textures by their scale. This shader meant we could use one mesh to build entire levels at the sacrifice of details. We thought this would be a good compromise as it meant the artists could focus on other more intricate models without bottlenecking our designers and their work. 

The tiling shader works by getting the object scale and converts the three dimensions down to two for our UVs. We use the normal vector of the object as a mask for how it tiles, this means the shader will work well for cubes and not really much else, but in our case its not a problem. Another thing we wanted from our shader was the control of how lighting influenced the scene. As our lighting contained one directional light we were able to use a simple bit of code to get its direction, attenuation (falloff) and colour. From here we converted it to cell shading and had a few sliders to control both the shadow falloff and highlight amount. Our final controls in our tiling shader allow for both a world space and local space gradient. We wanted to have a gradient covering our scene but to do it locally would mean a lot of custom meshes, on the other hand however if we had it all world space then some would have little to no gradient which was also undesirable. We had a switch to flip between each dependent on the material used which proved quite handy to add some depth to our scene.

Our second shader was for our water. Using the same maths as the tiling shader, I plugged this into the UVs of a Voronoi noise node. I also used time for the angle offset variable to have the material animate like water. from here it was a matter of adding it to our lighting and giving it some colour.

The third shader I worked on was one for our sprites. Using the same lighting we were able to have sprites act as if they were part of the scene, however there was still an issue with them blending with the surroundings. I used some noise to break up the uniformity of the sprites and make them look worn down and old. Another feature of this shader was that we wanted some hieroglyphs to glow. As we were already using the alpha channel of the diffuse as our transparency we needed to use a second texture. This texture is broken into individual channels where we can choose the colour of certain elements in engine as well as mask where we want emission and its colour.

Our particle systems didn't require any special shaders but we made one anyway so that we could have a little more control over the inputs. We used this shader to assist both the repulsion beam and laser. For our repulsion beam it had the transparency we needed and wasn't affected by lighting like our other shaders. For our laser I used simple noise and time to distort the UV's and give a semi-straight line some waviness. The last issue we had was with our repulsion beam. We are using a cylinder mesh for where the lines render, but the transparency means we see the back faces, thus rendering nothing. The final step was going to the unlit master node and allowing two sided material. Using the blue channel of the normal vector in view space we can figure out whether we see the back or front of a face. I used this as a mask to differentiate the inside and outside lines and give them variance. 


Our game makes use of a fully modular kit. Our walls are made from 4 variants of mesh, a basic flat wall that can be scaled to any size, a window piece and two walls with insets. I made our modular kit in mind of our tiling shader to allow our designers to quickly build scenes without needing to worry about anything other than the puzzle. The issue with this is that if we want anything like chips and cracks in the walls we need to come up with a new solution. Sie our other artist made 7 variants of cracks that we could lay over the  walls as mesh decals. Using an unlit material and colour matching to our background we could cut away pieces of wall. Our other cracks have the same material as our walls which allows them to blend in and receive lighting in a similar manner. Our final modular pieces were our leaves. Sie created some mesh variants for them and in engine created some prefabs. From here I placed these over our levels using the same technique as the cracks. 

Our game has a few mechanics that have very unique properties to them. Our laser needed to be scalable but the mesh could not be larger than one unit. Using a line renderer we were able to scale the length of the laser but this was unique scene to scene. Our programmer David used a ray cast to determine the length of the laser at runtime. From here was a few particles to add some variance and feel more impactful. 

The repulsion beam was much like the laser but this time we needed a cylindrical mesh. With the pivot point at the start of the beam we scaled the repulsion beam to its desired length. The final touch was adding small flares at the end of the beam to give it directionality and feel a little more part of the world. The flares use an animated material rotating at a constant speed relative to the repulsion beam.

For the longest time I struggled to make our end platform and static hazard look good in both our scene as well as on a mobile device. There was issues with theming and screen resolution but in the end I'm happy with how it turned out. From my initial concept we wanted either a fire or spikes as our static hazard. As a team we decided spikes was the way to go, but we quickly found out that these were hard to read on the screen regardless of colours or silhouette due to our working resolution. From here we started concepting some more ideas and stumbled across an icon that looked like a sun. I mocked it up in photoshop to see how it would look and was happy with the outcome. From here I made my own concepts for the sprite and finally placed it in engine. 

Having the same visibility issues for our end and start platform we used the same technique as the static hazard. Sie worked on a few iterations and got the final product in engine. 


To continue reading click here and to find the game on the Google Store and Itch.io.