This article is going to focus on how to get your Unity game running as fast as possible on mobile devices, specifically iPhone but you can carry over techniques to Android as well. This is something I find a lot of people have issues with, their game running at terrible frame rates and not understanding why or what they can do about it! iPhone’s hardware isn’t that beefy which makes optimization much more important! Squeezing visual fidelity without suffering game play is the challenge.
Take a load off Culling… I got this! (Backface Culling)
Since this example is on a 2.5D side-scroller we have 3d models that we could let backface culling take care of reducing unseen polys but why? Let’s just not model what isn’t seen and save texture resolution at the same time! This could be used for several uses such as buildings, walls or any background pieces where you never see the back. Most importantly this saves a lot of that sweet never-have-enough texture space! I wouldn’t really recommend this method in a scenario where you can walk around in 3D space and manipulate objects
Hold my calls! (Draw Calls)
This is one of the leading causes of terrible frame rate-icitis aside from poly limits being exceeded. A draw call is issued from the GPU to render a texture to display on screen which also affects the CPU depending on how often this happens. When making a game for your desktop you can get away with a lot. Not for mobile, this is where having a really low amount of draw calls means everything! If you’re curious where your game sits use the stats button on the top right corner of your Game window.
But James! How do I reduce my draw calls? They are much too high! I’m glad you asked!
That GUI could be to blame. If you’re making your GUI with multiple textures such as a 200×75 play button (1 draw call) with another 200×75 texture for a option button, WRONG! If you’re making one texture for each prop, WRONG AGAIN!
You’ll want to bunch everything into a Texture Atlas. There is no reason to have more than 1 texture for your GUI elements. Using a Sprite manager is the best way to get your GUI working in 1 draw call. Otherwise a play, options, quit button would take at least 3 draw calls already. You can lump props into 1024×1024 or 2048×2048 (if hardware permits) texture atlases . So you can have several props take 1 draw call. You can still have animated buttons on an atlas you would just include all the frames and reference their x,y,height,width locations. You can even color these assets and create different effects for them.
GUITexture/OnGUI has never been iPhone friendly so if you can I suggest building your own from the ground up otherwise you’re going to have a bad time. There are so many other alternatives out there that offer better results. For example, we’re using a a second camera with a separate layer that only shows GUI elements (cubes) orthographically with the texture atlas material .
Avoid using multi-texture materials. This means spec, bump, normal maps, etc. You can include most of these effects in the diffuse maps, but face it…it’s mobile, people know what they’re getting into!
Compress them textures!
Let’s make it easier for the hardware, It’s been through enough! You’ll want to compress all textures to PVRTC. To do this simply go to the texture (not material!) and in the inspector Texture Type > Advanced. Then change Format (at bottom) to RGBA Compressed PVRTC 4 bits (or use RGB if it doesn’t require alpha). Compressing textures use significantly less memory bandwidth, here is a chart to show the difference.
Compression Memory consumption (bytes/pixel)
RGB Compressed PVRTC 2 bits 0.25 bpp
RGBA Compressed PVRTC 2 bits 0.25 bpp
RGB Compressed PVRTC 4 bits 0.5 bpp
RGBA Compressed PVRTC 4 bits 0.5 bpp
RGB 16bit 2 bpp
RGB 24bit 3 bpp
Alpha 8bit 1 bpp
RGBA 16bit 2 bpp
RGBA 32bit 4 bpp
So you can see from RGBA 32bit to RGBA Compressed PVRTC 2 bits there is a HUGE difference. 3.75 bpp to be exact! If you have a texture that is 960×640 the RGBA 32 bit would be 2457600 bpp or RGBA Compressed PVRTC 2 bits would be 153600 bpp. That is a difference of 2304000 bpp. There is a big difference for this compression it’s essentially 16x less!
What about quality? Surely you will have to compromise here! yes and no..Here is an example of the compression methods
As you can see there is a slight quality loss but if we zoom out…
From my experience though, PVRTC is usually not the best solution for UI and in some cases even as textures. This is something you’ll want to experiment with and basically means faster load times on iOS devices.
Useful things to consider:
Our programmer has informed me how terrible unity’s garbage collector is and that you should never destroy assets as it takes a huge performance hit. He implemented a clever way to get around this. Basically when something runs off screen it disables all its properties and throws it into a queue with one of his many managers and waits to be reused as a new asset and re-enables all the properties again. So we use this for enemies, we never kill or instantiate while the game is running, instantiating objects happens at the start of the game while it loads and waits to be called.
Scroll that texture!
Making a platformer? Have 1 background texture? scroll that baby! No need to have the same texture swapping 2 of the same assets to loop it. have multiple? Create a background texture atlas then so you can parallax it!
This is hardware specific, it seems the iPhone 3GS is capable of around 10k-15k tris, this would lead me to believe that the iPhone 4 would provide a better performance around 20k-30k. Anything newer should be capable of much more than this and you’re more lax, but ideally you’ll want to aim the lowest possible if you are trying to reach older devices. You can go over these mind you but it depends on a lot of factors on how well you optimized it such as draw calls and texture sizes. The lower never being recommend and even lower is a plus! The less strain on the hardware the better battery life you’re going to offer he player and nobody wants to play a game that drains their battery when they are on the go.
Avoid this whenever possible as they can be more expensive to calculate. This includes fog, particles, textures. This isn’t to say don’t use them but if you’re have a performance crisis you may want to investigate these options first. Alpha in textures are hard to avoid especially since most of the time it saves you on your polycount…just try to be clever.