Mirroring Technology of Towerborne

 

Background

Towerborne is a side-scrolling action RPG brawler, built in Unreal Engine 5, that is heavily inspired by classic sprite based games such as Golden Axe, Streets of Rage, and Teenage Mutant Ninja Turtles.  In order to capture the feel of those older sprite based  games using 3d characters inside the Unreal Engine, we had to make all our 3d character mirror from left to right, instead of having full 360 rotation.  This created several unforeseen technical challenges that had to be overcome in order to execute on the vision set forth by the game's retro inspired design.

Technical Hurdles

To accomplish a mirrored character, would exist in one of two states, “facing right”, which is the default behavior.  Or “facing left”, which is when a character is considered to be in a mirrored state.   If a character was in the mirrored state, we would scale the entire character by -1 along the X axis, thus flipping a character into a visually mirrored state.  This created several bugs out of the gate.  Things such as surface normals, cloth, VFX, attachments, and complex materials did function correctly with inverse scaling.  In order to correct this, complex systems were built to correct the bad behavior of each of the aforementioned systems when  negatively scaled.  Fixing most mirroring bugs was a straightforward process, except for VFX, which kept running into unforeseen bugs with negative  scaling.  

An important aspect to understand about VFX assets that is different from all other Unreal assets is that they can’t be quantified into a single archetype.  A VFX asset can be point clouds, static meshes, skeletal meshes, fully animated blueprint actors, it can even be all of these things at the same time.   Each of these object types have very different properties from one object to the next.  In order to achieve the desired result, all of these properties need to mirror at the same time. However, executing on that premise over time became very expensive to support.   After several months of sunk engineering costs trying to find a “one size fits all” mirroring solution for VFX, it was clear we needed to pivot. No matter what we did, there  was always a new edge case that would emerge that would break the whole system, forcing very expensive code re-writes.

Re-Thinking Mirroring

After numerous mirroring setbacks something drastic had to be done.  Our previous attempts to code our way out of the problem were unable to deliver on what we  were asking it to do.  The breakthrough came from  changing the definition of  what it means to “mirror” a VFX.  I had the idea that instead of trying to mirror effects, we remove VFX completely from the mirroring system(VFX would no longer be allowed to scale negatively).  Instead I was going to re-imagine the way we thought about mirroring VFX holistically. The idea was simple, we don’t mirror VFX, we fake mirroring within the content itself. The goal being to remove the expensive engineering solution and offload the heavy lifting of mirroring content onto artists themselves.  How was this done?  Bear with me as I lay out an example using some technical jargon.

Imagine a particle emitter shooting particles with some velocity down the X axis.   If you had an effect with a velocity of 100 on your X axis, if you multiply just the velocity on X by -1, the result will be -100.  If someone was viewing that effect from the side, the effect would have appeared to mirror because it switched direction, but behind the scenes only a single value was changed.  What this means is  you can achieve the look of a mirrored effect by targeting specific values to invert, instead of dealing with the chaos of fully inverting the scale of an entire  unreal asset.

This simple concept, when paired with Niagara’s inherent ability to allow a modification of low lying modules, allowed me to introduce artist controlled mirroring logic. This gave artists finite control over how an effect would invert, giving the  illusion of mirroring without actually flipping the effects scale.  This elegant solution allowed us to drastically reduce the amount of engineering needed to support mirroring while gaining significant performance gains.   Moreover it allowed artists to have ultimate control over how content would mirror.  Making it easier to maintain, predictable during runtime, and cheaper to fix.

Final Implementation

When I implemented the final mirroring solution I wanted it to be easy for any artist who came onto the project to understand how mirroring works and how to build their content with it.  To make it as easy as possible, I made it so setting up mirroring only involved two steps.  1) Click a checkbox to turn it on. 2) Drag and drop a single “BF_Mirror” parameter onto a float field named “BF_Mirror”. 3) Save and walk away.   Below is an example of what the mirroring logic looked like from the perspective of a user.

Mirroring Workflow Sample

Before any mirroring could work, a user would have to create a float parameter called “BF_Mirror”.  This parameter is the only place where the mirroring system running outside of Unreal Engine’s Niagara would talk to the mirroring system inside the VFX.  If a VFX needed to be spawned in a mirrored state, this parameter would be set to -1 by the external mirroring system..  If it was spawning in a non mirrored state, it would default to 1

Inside of a Niagara effect, any module that has mirroring logic inside would have this checkbox available.

Once enabled, a float field named “BF_Mirror” will appear in the module.

Finally a user would drag and drop the user float parameter named “BF_Mirror” into the “BF_Mirror” float field on their Niagara module, and the setup is complete.

The example below shows the finished setup.

Some modules required some extra control for users when dealing with certain types of operations.  Mesh transform vectors  or UV’s, for example, might need more finite control over which axis mirroring should take place.  For those modules only, a user would encounter an expanded set of controls with the same principle of only having to turn on mirroring only for what a user would care about.

Final Thoughts

Mirroring VFX in Towerborne was a major success for the project.  There aren’t many times when a feature is extremely buggy and a solution comes from outside of engineering.  In the case of mirroring, we successfully pivoted from an engineering driven solution to a content driven solution.   By thinking outside the box we were able to figure out a user driven, content focused, mirroring solution that got rid of the need for ongoing engineering support.  All while empowering artists and designers to own how content mirrors without the need for outside departments.  This saved time, money, resources, and ultimately delivered a more performant version of mirroring to the end user.