Unity mobile optimisation (iOS)

september 21, 2011 11:27am

At the start of the project, we've never had our hands on Unity before.
The engine looked capable, and very promising, now we are getting near the end of your project, we are glad we took that decision.
Ofcourse with learning something new, comes trouble. Our biggest issue was that we were making an iOS game, but developed it on PC first.
We had opted to do this, to get to learn the engine, thinking it would be "easy to come back and fix it for iOS".

After we had setup our first prototype, and got some core mechanics of our game up and running (which was a breeze with Unity, thanks for that to the brilliant people at Unity HQ), we wanted to test it on the device.
I remember very well when firing up the first time, you see the default Unity logo, and you get all excited about the fact that your game is running on the target device. I had my hopes up high. The game ran between 3-5 FPS... Needless to tell you how disappointed I was with myself.

That ment though, it was time to go through it all again, and see where I could optimze everything. The programmer in myself was very eager to start this journey!

My first optimisation was the most obvious, but as I dived into the project, using it to learn the engine as well, I overlooked some critical information.

Typecasting variables & storing references
I have been using JavaScript for this project, and that lead me to use undeclared type's of variables. Just make sure you know what you are going to use, be it int, float, boolean, GameObject or whatever. That said, I believe in Unity 3.4 they added a check for this (god, how I wished I had this a year ago).

After that, my code ran slightly better, I had a whopping 10-12FPS, not yet stable, but getting there. Some more reasearch into getting script to run faster lead me to storing references.

Doing a GetComponent multiple times per frame, turns out, makes you kill performance. With GetComponent, I dont mean the function only though, ofcourse it was not that easy ;) whenever you do something like gameObject.transform, that is actually getting a component as well!

I ended up caching all my gameObject, transform and script references where I needed them on Start of a script. that way you dont have to find the Component every time, and saves you a lot of performance. This performance saver totally reflected on the FPS, I was now running somewhere between 15-20FPS!

Ofcourse, someone as passionate and obsessed over gameplay performance, does not stop here, I wanted to make sure the game does not give the player a bad experience due to low framerate (especially because we are using physics, you dont want that to become choppy and jumpy).

Getting rid of OnGUI
So, as I was getting more settled with Unity, I dove into the profiler.
It showed me that all my scripts actually did not spike in performance (though, in hindsight, they were running quite high on /ms), However I had a random spike every so often on the Garbage collector!
After some googling and forum browsing, other people reported this too, and it was due to OnGUI calls. I took matters into my own hand, and decided to avoid OnGUI entirely in the game.

This leads to the use of GUITexture, but the disadvantage over this was that it adds an extra drawcall for every object drawn (this also happens with OnGUI)...
Obviously, during gameplay we want to minimize this as much as possible, so I went to look for an alternative.
I was mainly worried about performance, so I wanted the GUI to run off an atlas texture, so it would only give me a single extra draw call for ALL Gui.

There are several options here, you can go for EzGui, Sprite Manager and some others. I chose to go for a free option here, and use QuadUI (http://blog.equals-equals.com/quadui/). Quad UI came with a nice visual editor to generate buttons off an atlas texture, exactly what I needed! So, I used a new camera that is attached to the main camera, and renders another layer over it, only used for GUI items. All my GUI is now visible, with only a single extra drawcall :D

Drawcalls and LOD
Now I had learned all about drawcalls, I went on a new hunt, to try and shoot down as much drawcalls as possible that werent needed.
One of the first things we had set out to do was combine as much objects as possible into a single texture/material, that on itself already made a huge difference, however it was not enough to me, I also had to keep in mind iOS cant render more then a certain amount of polygons, so that had to be culled as well.
Unity did not come out yet with a default LOD code (It promises for Unity 3.5 though!), So I had to write this one myself.
After some tinkering I wrote a system that works perfectly for our needs, we can cull meshFilters or replace them with lower polygon versions.
this severely impacted the amount of drawcalls, and ofcourse our framerate. We went up to roughly 23-25FPS constantly, now we are getting there.

Instantiating is evil
The game itself is quite action packed, so we were creating tracers, muzzle flashes and other FX on screen by instantiating them and Destroying them.
This going off possibly multiple times every single frame, made the game drop heavily during action scenes.
Again resorting to forums, google, and my now best friend Unity IRC channel, I found a very interesting way to get around this, it was called a Object Pool.

A pool basically contains several objects that can be accessed and restored without the overhead costs of Instantiate/Destroy.
Basically they were Instantiated on Start, and then stored in an Array, instead of having to Destroy them, they are set inactive untill they are accessed again.
Now in theory this sounds like it should run properly. In reality, it made the combat from the moment stuff would be set active drop all the way down back to 3FPS...

That's not optimisation :(

However, instead of getting rid of the whole pool idea, I decided to sit down, and take apart this whole thing, to find out why in earth it makes the game perform that bad suddenly. I knew for fact the object pool was the culprit, because when it's not accessed it would climb back up to our previous gained 25FPS.
It took me well over a week, and lots of IRC discussions to find the culprit. Also, did I mention it would only occur on target hardware, in the Editor it ran fine.

Apparently, I was using Array's too much/often!

Array vs List (in JavaScript)
As I just learned, Arrays had an unpleasant surprise in store for me. The way I was storing my array's (as array inside an array, as multi-dimensional array), would make Unity think, the array has no typecast... Yeah, I hear you think, hey, we discussed that before! Indeed, we agreed to avoid that.
I had to convert all my Arrays to some type that would be known on beforehand.

So, again some searching, I came across List, which is an Array, but with a type that you have to declare on beforehand. YAY, that's exactly what I needed.
Updating my whole game object pool code to make use of this, I no longer had any framedrops during combat, hurray :D However, it seemed I only had a single FPS gained by doing so... running a steady 26FPS still aint no 30FPS, so I continued onwards with my optimisation pass.

Wait a minute though, I suddenly remembered that my piece of code I wrote for the waypoint system, was entirely build out of nested Arrays!
Creating a new class for waypoint nodes, and storing that properly into the List that now resembles the waypoint graph (instead of an Array ofcourse), I tested it on my device once again.

I couldnt believe it! It's finally running on 30FPS, and stable too, I've not seen it drops since then.

Final tid-bits for performance
Obviously, we are getting in more and new content, and I try to optimize everything I come across that might cause some trouble later.
One thing that comes to my mind on that is trying to avoid string comparisons.
If you somewhere are checking layers, please make sure you are comparing the .layer, and not the "layerName" ;)

That is all, took us a long while to get Unity's drawcalls and FPS stable, but it's all been worth it so far.

To illustrate it has been worth it, the game now runs so stable, we actually had space left over. Gave us room to add a detail shader, and a nice looking Lens Flare!

If you have any further question about optimisations, would like to share some of your own, or just want to say hi, you can find us on twitter via @RotorGames.
Also like us on facebook here or via the link on the bottom of the page.


Read more developer blogs by Rotor Games:

Comments disabled.

Latest News

Final Run available on the AppStore!

After 15 months of hard work we're extremely proud to finally have our ambitious project driving action/vehicular combat game Final Run online on the AppStore! IT'S ONLY $3,99! Check it out!

Final Run - Vehicle action game for iOS!
super simple blog script super simple RSS script