Sponsored By

How we made a visually complex PC game run smoothly on mobile using Unreal Engine 4

"Epic of Kings" is a high quality mobile game developed by a small development team.The goal of this post is to share some of the techniques used to achieve smooth performance on mobile devices using Unreal Engine 4 built-in tools.

Puya Dadgar, Blogger

April 25, 2016

11 Min Read
Game Developer logo in a gray background | Game Developer

In early 2014, me and a developer friend have been presented with task of creating a high quality mobile game using Unreal Engine 4. We have been given the art assets of an existing PC game. The challenge was to create new mobile compatible gameplay while achieving graphic quality comparable to the last generation of consoles (PS3/XBOX360). After almost two years, we managed to release “Epic of Kings” on App Store. If you haven’t seen the game yet, here is our launch trailer that showcases mobile game footage:

The levels, meshes and textures that we had to work with were all made for a high quality PC game. The levels had around 800K polygons on average. In addition we had to add  various characters, complex particles, and post process effects to achieve what we were aiming for. The task seemed to be almost impossible considering our very small development team. Our target was to be able to run the game smoothly on iPhone5s, and Nvidia Shield tablet.

The goal of this post is to share some of the techniques helped us achieving smooth performance on mobile devices. These are mostly related to Unreal Engine 4 built-in tools. Additionally, there were other topics related to coding and software architecture affecting performance of our game that will not be addressed here.

First task was profiling different levels while we were working on various gameplays.

Unreal Engine Profiling tools

Unreal Engine 4 provides a variety of profiling tools. We started with by using “Stat Unit” command line on the device and profiled the GPU and CPU time anywhere in our levels. For more details we used "Stat Slow".



Then using Stat UnitGraph, we could observe which part of the level had a performance hit.

Show SceneRendering could break down which part was using more time.



and used Stat Anim to profile the animation performance.



If we suspected that a Post Process effect was causing a performance hit, we could easily enable/disable Post Processing using Show PostProcessing command line and then if that’s the case, investigate it further.

In addition, we used GPU vendors profiling tools such as Trepn Power Profiler (for Qualcomm Snapdragon processors), Tegra System Profiler (for NVIDIA GPUs), as well as Apple Time Profiler for additional profiling.

In early 2015, We managed to run the game using OpenGL ES 3.1 on Shield tablet. It was using UE4’s PC rendering path, and the render result was amazing. Although it required quite a bit of optimization to run smooth on a year old tablet.

Later in the process we decided to stick to OpenGL ES2 for rendering path on all devices, but enabled the dynamic shadow to achieve the highest graphic quality possible.

Profiling result, and optimization

After looking carefully at the result of profiling, we have found issues with draw calls (number of polygons being rendered), texture memory, particles and audios which we explain in the rest of this article how we dealt with them.   

Some post process effects were costly, and as Unreal documentation states correctly it would be better to stick to Temproal AA (Anti-Aliasing), and Bloom. Depth of Field, light shafts, film grain, color grading LUT, ambient cubemap usually are costly. Although eventually we managed to use more expensive but a better looking FX-AA.

Dynamic shadows are very costly, especially on older devices like iPhone5s, or iPad Air, there was a huge performance hit. Therefore, UE4 Device Profile took a major part on how settings for each device had to be configured. Specifically, disabling dynamic shadows for low-end devices, and use of Content Scale Factor helped us to reach the reasonable FPS on low-end devices.

Mesh Optimization using LOD

We used LOD for some of the meshes to get better performance. Creating Level of Detail for mesh and knowing when to switch between the meshes can be tricky. Especially when you are dealing with trees in a forest, the switch can be very noticeable. UE4 provides an easy workflow to change the meshes at runtime based on their size in screen space, so the meshes in far can be switched to meshes with lower qualities.

Camera Design

Lots of fans didn’t like the way we implemented our camera system since they couldn’t control it freely, and look at different areas of the level. But with so many high poly meshes that we had in our levels, constraining the camera movement was the only solution. Controlled camera that only moved on player’s path. We had no choice to restrict the free camera movement for better performance.

Hiding Objects in the level

As a last resort, we had to change the design of some levels, so that we could hide objects when they were not in line of sight, and unhide them when needed manually.

Also on some combat scenes where the camera zoomed or cut into the fight, we changed, hid or rearranged meshes in the background to render less polygon, and achieve smooth FPS for combat, which was the main goal. The lower frame rate could affect controls and game experience significantly, especially on touch-based devices. Just note that this mesh rearrangement or replacement during combats was done very carefully, so that users could never notice object popping. Mostly we did this during camera cuts, while switching to the combat. Since the combat camera FOV was different from the environment camera FOV, the changes would not be noticeable to the players.

Using Cull and Precomputed Visibility Volumes

Setting up Cull Volumes will hide/unhide objects based on their size/distance. This will automatically boost performance in the areas that everything can be seen on a straight line. As we get closer, smaller objects will get unhidden and player sees more detail. This was an important UE4 feature for us, in terms of gaining better performance. Since our main performance bottleneck was triangle count and we could hide unnecessary triangles easily that way.

Additionally, We were able to use precomputed visibility volumes on some levels to significantly boost the performance.

Particle Optimization

Based our experience, mobile devices tend not to be particle friendly. Having complex particles can potentially affect FPS significantly. But since particles can highly improve the game visuals, we wanted to be able to have lots of them in our combat scenes, environments and even in our cut-scenes.

We started by using UE4 Particle LOD system to set different resolutions for each particle. Particle emitters which are distant from camera are using a lower level of detail. They become lightweight in comparison to the closer particles. The closer ones are more detailed since they are using higher level of detail. UE4 can automatically switch between particles LOD setting based on the camera distance from the emitter.

During profiling we noticed significant frame drop when different particles with transparency overlap each other. Moving the Emitters physically in the levels so that transparent particles won’t overlap boosted performance significantly.

Texture Resolution

Many of the textures provided to us, were high resolution textures. UE4 texture LOD bias feature helped us reduce texture resolution without a need of using an external software.

Audio Optimization

Audio was an important part of our game. We had a goal of using various audios in combat scenes, mixing them together and randomly select different dialogues and sound effects to make exciting and unique environment for each combat. However, the problem was that mobile devices specifically Android devices had a very limited audio memory. This made Unreal engine drop some of the sounds during combat, since audio channels are limited on mobile devices. The most annoying issue was that many of the bold sounds like a beast scream or music were dropping during combat. To avoid these issues we had to use some UE4 built-in features.

First we controlled how many instances of an audio can be played concurrently. By setting that up, we limited playing unnecessary instances for each sound. We had a scene in which the beast was following the hero and he crushed through environment. There were so many audios in that scene including beast’s footsteps, hero’s footsteps, environment destruction, attacks, beast’s screams, music and so on. The hero’s footsteps sound was too lengthy and his sprint animations was very fast which meant when hero’s second footsteps sound occurred, the previous one or two footsteps sounds were still being played and fading out, so we would end up with three footsteps being played concurrently. This caused other important sounds to be dropped. UE4 feature to limit playing number of instances of an audio concurrently, avoided playing unnecessary concurrent sounds. The feature comes with every audio instance and titled with “Max Concurrent Resolution Rule” and “Max Concurrent Plays”. We set these values carefully for every sound to achieve better performance for audios.

The other thing that helped us with audio performance was changing many audios from stereo to mono. Originally, many of the audio files that were provided to us were stereo. They didn’t have to be stereo especially on a limited device like mobile phone or tablet. We saved lots of audio memory by converting some audios to mono.

Finally, we still had places in which some audios were getting dropped. We wanted to be sure that these dropped audios won’t be the important ones like music, conversations or screams. So, we classified our sounds using UE4 sound class system. In each sound class there is a feature named “Always Play”. This sets a higher priority for the sounds belonged to a specific sound class and if UE4 audio system wants to drop a sound due to the memory or channel limitation, it will not drop the sounds belonging to the sound class marked as always play.

Animation Optimization

Despite the meshes, textures and audios which have been made for a PC game originally, all the animations were designed and created specifically and carefully for Epic of Kings mobile game. We created almost 800 animations including combats and cut-scenes. The average bone count for characters was 70, which is a high value of bones for a character used in a mobile game.

Despite having lots of animations and high bone counts, the animations did not make much trouble for us since the character count was not that high in our scenes. However, we optimized the animations effectively to help the game run smoothly and using less memory.

UE4 compresses the animations using keyframe reduction algorithms at the time of import. This feature helped us a lot to save more memory, since many of the detailed bones like fingers and facial bones were not moving in combat animations. UE4 drop of unnecessary exported keyframes was quite helpful in terms of memory usage.

Furthermore to have less animation calculation, we set the “Enable Anim Update Rate Optimizations” to let the animations not to tick every frame when their owner Actor had a small screen size. Just note that this feature might cause some jitters or foot sliding on your characters. But it worked well for us in the most cases since it only got activated when the character had a small screen size and we had plenty of camera angles in which the enemy characters were small.

We also disabled the animation and pose update for the characters outside of the camera frustum to have less CPU calculations. This feature also comes with the skeletal mesh components of UE4.

One another aspect of reducing animation memory usage was using single-framed additive animations instead of full-body animations. UE4 anim graphs easily let us bring additive animations into each animation graph and blend them with other animations.

Finally, we used separated skeletons for player character in the different combat types. As an example, we had two types of levels: One in which the player fought with normal characters and moved through the level and the other which a boss pursued the player and player ran away from him. The player character was not sharing any animation between these two types of combat. So we used two skeletons for the player. One for the normal fights and the other one for pursuit scenes. Having two skeletons for different levels means that if we load one skeleton the animations assigned to that skeleton is loaded. So in a pursuit scene, we were not loading the animations used in the normal combats and we could save memory.

Conclusion

With a small but somewhat experienced team, we tried to create a game which could push the limits of mobile gaming both graphically and gameplay wise. We did not try to create a casual mobile game like most of the mobile game developers. We were facing the huge challenge of developing a high quality game using assets not suitable for mobile platforms with two full-time developers!

Unreal Engine 4 tuned out to be a huge asset for us in this journey. Maybe sharing this experience can be helpful for other indie developers who are trying to make high quality mobile games on a small development team.

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like