Optimising with Unity for iOS
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.
I‚Äôve found it‚Äôs often the unexpected that takes time. Cuban customs agents, downloading in Canada, and bureaucracy in Norway. And so it has been with optimising my game for the iPad.¬†Unityprovides two useful pages to get one started on optimising; one for¬†optimising scripts¬†and one foroptimising graphics. There are still further, and often simple, code changes that help squeeze more frames per second out of the iPad.
Use¬†#pragma strict¬†at the top of all your scripts. It‚Äôll initially make component access more awkward but as you‚Äôll cache those lookups it‚Äôs a one time hassle. So with #pragma strict:GetComponent(Rigidbody)¬†would become¬†GetComponent(Rigidbody) as Rigidbody.
Avoid Object.Instantiate() & Object.Destroy()
Instantiating is bad. Destroying is bad. Both require memory allocation changes and cause a hiccup in performance when an object is created or destroyed. So instead of creating and destroying objects when needed, I predetermine what is required and my¬†SpawnManager¬†class instantiates all required objects at the beginning of the game - causing one large hiccup when it can be hidden. Then it disables the spawn, but they‚Äôre kept waiting in memory. When they‚Äôre needed the game object is enabled and ready to go.
Cache Component Lookups
This is an optimisation recommended by Unity on their¬†optimising scripts¬†page, and I whole heartedly agree. I‚Äôve found casual component lookups performed often enough cause a slow down.
Use iTween Sparingly
I hadn‚Äôt used iTween until midway through production, then after some positive encouragement I gave it a try. And it was awesome. Very easy to use and easy to chain together to create complex behaviours. I loved it, and I quickly incorporated it into my movement scripts. Then the performance hiccups followed.
A call to iTween typically happens midway through a game. An iTween component is instantiated, makes some expensive component lookups and then is destroyed. Each of these steps causes a performance hiccup, the worst being a substantial garbage collection on destruction. Instead of using iTween in my performance critical areas I now use my own easing and interpolation classes that slip into existing Update functions and can be called with cached nodes.
My¬†SpawnManager¬†class used to execute¬†gameObject.SetActiveRecursively(true)¬†on any node that was being spawned. The first disadvantage to this was sometimes I didn‚Äôt want all children to appear right away, so I‚Äôd hide them again. More performance offensive was thatSetActiveRecursively¬†performs several expensive component lookups.
To solve this I now cache the hierarchy in Awake for any game object that will be spawned bySpawnManager.¬†SpawnManager¬†then simply enables the top most node and the top node is responsible for enabling whichever children it needs. And because the children are cached in that initial Awake call, there is little to no performance hit during the game.
Use Builtin Arrays
Avoid String Comparison
Initially I had plenty of conditionals using tags to query objects. I‚Äôve collided with you, are you tagged with ‚ÄúThe Fancy Cliff Over Yonder‚ÄĚ? Great, lets form a club. Lets also slow down the game, because the longer the tags, the longer it takes to compare against. This may seem trivial, and for a few tag comparisons here and there not really a problem. But in an Update function with several objects this suddenly becomes several hundred queries a second. Soif(collision.gameObject.tag=="Cliffs")¬†becameif(collision.gameObject.layer==9)¬†which isn‚Äôt as easy to read, but a few explanatory comments nearby and the problem is solved.
Avoid Vector3.magnitude & Vector3.Distance()
Every moment of my game I‚Äôm comparing the position of the finger to the position of the interactive characters. Several characters on screen at once and this starts to amount to an awful lot ofVector3.magnitude¬†checks (Vector3.Distance uses .magnitude and is essentially the same). This becomes an awful lot of slow square roots calculations. So, wherever possible compare distances using¬†Vector3.sqrMagnitude.