This is the trailer for the soft-launch of Speed Freeks, a Warhammer 40k mobile lane runner for iOS. This is the first mobile game I’ve worked on after many years of PC and console games.
I’ve learned a lot about working on mobile games as a vfx and tech artist on this project. The game is very vfx heavy and learning to optimize the rendering was challenging but a lot of fun. I plan on 10x more orky destruction after soft-launch.
The effects are clearly what sells the game and makes it worthwhile looking into!
Any tips you would want to share that you discovered while working on a such a heavy effects mobile game?
The biggest thing for me was optimization. I am responsible for optimizing all of the rendering in the game, not just the fx. I wrote all of the shaders, optimize the vehicle models, set up cameras and layers, write scripts and implement every technique I know to keep the GPU at 30 fps or better.
List of tips and tricks using Unity on mobile:
Profile on device: you should always be profiling on device through XCode for iOS devices. This is the most efficient and productive way to target the things that are eating up GPU time.
Forward rendering allowed me to use 4 vertex lights plus one pixel direct light and it was fast. This allows one player vehicle, usually 4 or 5 enemy vehicles on screen, full environments with lightmaps, spec and normals and lots of vfx running on an iPhone 5s at around 20ms on GPU. Not maxing out the GPU allows the game to feel smoother and lowers the heat of the device.
iPad mini 2 is the hardest to optimize for because it’s 2048 x 1536 screen is over 4x bigger than an iPhone 5s. I’ve got it running at around 29 ms on GPU without any downscaling but it was a real challenge.
Batching is great for small effects. Sprites with simplified custom shaders can batch very effectively. Frame debugging is a good way to see what is batching correctly.
Shaderforge is a great prototyping tool but I’ve written all of the shaders manually. Almost all of the shaders are a single pass and made for forward rendering.
Scripting is important for swapping shaders at different distances, calculating stuff once then passing to the shaders, disabling objects and vfx that are far away, etc. For example, enemy vehicles have 3 shader LOD levels that I swap at different distances with a simple script.
Check the materials in your game and how they are making use of your shaders. If you find that a slider is always being left at 1, then you can get rid of it and save a multiply instruction in the shader.
We have a huge vista to draw and you can see far, far away due to the camera angle. I made two cameras: one for distant objects and one for near objects. The near object camera with a shorter draw distance improves the accuracy of things like shadow calculations.
Think about the future growth of the game. If you think that features are going to be added and those features are going to impact the GPU, then be prepared with solutions sooner rather than when that day comes. If you have some spare time, create a branch of your game in your version control system and try and create that future condition. For example, can you have 20 enemy vehicles on the screen and what does that do to the GPU?
The simplest techniques are the best techniques for mobile. But if you optimize the hell out of a game, you can create some space for a few fancy things.
Lots more I could write. I actually really enjoy optimizing, I find it relaxing.
The vfx in the game were done with every technique you can think of. I mix sprites, shaders, particles and meshes in whatever combination is needed. For example, there is a beam weapon in the game that I made that is done almost entirely through scripting with some additive shaders. There were also tons of different explosions and weapon fx, stuff like this:
Here’s something I made for smoke that I ended up not using. This script used too much CPU time and I ended up going back to a looping smoke sprite.
This last thing is maybe kind of boring but I thought about it a lot. Sometimes you will have seams in your game where two sections meet. For this game the sections can be mixed and matched so the edges cannot be pre-blended because there are thousands of combinations and there is always a slight discontinuity. I made a realtime blender that can be placed anywhere in the game and it will automatically blend out what is underneath.
Wow, what a great breakdown and loads of tips I never would have thought of!
Optimizing the game performance is something I’ve not looked into as much as I would like and this certainly gave me some insight how to think!
Thanks for sharing!