by Ryan Green

Preparing your game for iOS in Unity3d

So if there’s one thing I’m learning in my career shift to “mobile developer,” it’s that computer programming is a science and that I’m not a scientist.

Previous thesis statement: memory is a magical unlimited resource.  200 mb webapp?  No big deal – my desktop has plenty to spare.  Zombie objects?  Let ’em be, they’re not hurting any one.

New thesis statement: memory management stinks. Texture memory?  Draw calls? GPU? Power of 2 Textures?  This is starting to smell a lot like the low lands of computer science and less like the flowered fields of Scriptable Mesa in the land of GigaBytes O’Ram.

Now I’m not going to pose here, I’m still ultimately a web developer but I’m hoping to share the little morsels of knowledge I pick up while attempting to make my Unity3d Games work on mobile as well as they do on the desktop.

Morsel 1:  Compress your Images!

There are a few methods I’ve found via Google for reducing the footprint of an iphone project that uses lots of textures.

  1. Create “power of 2” images.

    This means that your image dimensions should be a power of 2.  eg. 32, 64, 128, 256, 512, 1024.  This doesn’t mean square, as a 256×128 image is also power of 2.

  2. If you can’t size an image to a power of 2, and quality matters (ie GUI Menu image), then use 16 bit compression rather than 32 bit to save 50% memory.
  3. Buy a tool like EZ GUI. The price of this little library seems a bit steep at first, but then you realize how long it’d take to do it yourself.  This library has the ability to create Atlases of Textures from your buttons and GUI elements.  Thus, your GUI draw call count can be reduced to ONE! And it has features that allow you to add GUI transitions, menu wizards and animation.  Pretty cool.

Morsel 2:  There are more items in memory than you think!

To find them, paste the following code into the onGUI function in a javascript and attach said script to your MainCamera in a blank scene:


function OnGUI () {
GUILayout.Label("All " + Resources.FindObjectsOfTypeAll(typeof(UnityEngine.Object)).Length);
GUILayout.Label("Textures " + Resources.FindObjectsOfTypeAll(typeof(Texture)).Length);
GUILayout.Label("AudioClips " + Resources.FindObjectsOfTypeAll(typeof(AudioClip)).Length);
GUILayout.Label("Meshes " + Resources.FindObjectsOfTypeAll(typeof(Mesh)).Length);
GUILayout.Label("Materials " + Resources.FindObjectsOfTypeAll(typeof(Material)).Length);
GUILayout.Label("GameObjects " + Resources.FindObjectsOfTypeAll(typeof(GameObject)).Length);
GUILayout.Label("Components " + Resources.FindObjectsOfTypeAll(typeof(Component)).Length);
}

Thank you, UnityDocs

You can further discover the contents of each set of objects in a scene with the following alterations:


var textures = Resources.FindObjectsOfTypeAll(typeof(Texture));
GUILayout.Label("Textures " + textures.length);

var g = "";
for(var i = 0; i < textures.length; i++) {
var t = textures[i];
g += " " + t.name + "-" + t.width + "x" + t.height + "";
}

GUILayout.Label(g);

Now export this to an iOS build and run it on the iPad, and you’ll find that 39 textures load by default in an empty scene. From here you can now monitor how your assets load in memory.

Morsel 3:  Any reference to a prefab in your script is going to load it into memory on launch.

This really bit me recently as I attempted to integrate the GUI into my game.  I have several 1k / Full screen GUI Textures in my assets folder.  For one, a lot of compression on the image was unacceptable, So my 1k textures needed to be GUI / 16 bit which made them about 2 mbs apiece.  For two, for organizations sake, I placed my GUI screens into separate prefabs, and then programmatically insert them into the scene when I need them.

This presented a problem, in order to instantiate the prefabs, I needed a reference to the prefabs in my script.  Holding a reference to a prefab on my GameObjects, loaded that prefab into memory…

When I launched my game on the iPad, my app crashed without warning.  Profiling revealed my app consuming upwards 80 mb of memory, far beyond my sub 25 mb target.

Morsel 4: Load Assets only when you need them.

In my googling, I discovered a nifty little method of only loading assets into memory when you need them via script.

Loading Resources at Runtime

Step 1:Create a “Resources” folder in your project folder.

Step 2:Move assets (including prefabs) that you want to load on demand into this folder, and organize how you see fit.

Step 3:Change your reference to your prefab in script to a string and set it to the path of your prefab.

C# Example:


public class TestGUI : MonoBehaviour {
public GameObject TEST_PREFAB;

becomes…

//C#
public class TestGUI : MonoBehavior {
public string FACTORY_ITEM = "Prefab/TestPrefab";

All path references are relative to your Resources folder.   Your instantiation code then becomes:


//C#
GameObject instance = (GameObject)Instantiate(Resources.Load(FACTORY_ITEM));

when you’re done with your prefab, you can unload the assets from memory via:


//C#
if(instance != null) {
Destroy(instance);
Resources.UnloadUnusedAssets();
}

Now comes the part where you decide how much should be loaded up front and how much deferred.  Remember, instantiating any asset that hasn’t been preloaded will load with a delay, but I found this helped me selectively load textures that otherwise would have crashed my iPad app.  

Thanks for reading, I’m off to squash bugs on my latest Unity project.  Perhaps next time I’ll reveal the Portions of Performance Enhancements I picked up while playing with particles, prefabs and physics.

About the author
Leave Comment

Your email address will not be published. Required fields are marked *

clear formSubmit