2D Mobile Game Performance in Unity Part 1

When we develop games for Android or iOS, at some point we must wrestle with the very real limitations imposed by mobile devices. Despite taking massive leaps forward, mobile device performance still lags behind that of console and desktop systems. It’s frustrating and disconcerting to discover that your shiny new mobile game waddles in at 20 fps or falls flat in the face of RAM or disk space limitations. How do we make our mobile game run at a decent frame rate? How do we ensure that our game doesn’t exceed a device’s memory limitations? How do we avoid making a prohibitively large installation footprint?

Although there is no single solution to any of this, I have put together a sort of mobile quickstart guide to help tackle some of these issues. Caveat emptor: I’m going to focus on 2D games using Unity simply because those are the sort of projects I’ve been working on the past couple of years. Nonetheless, many of the lessons here will apply to all types of game development. Neophyte mobile developers, use this guide as a starting point to address these common questions:
– How do I improve my mobile game’s frame rate?
– How do I optimize scenes to reduce memory usage (to avoid frame lag or even crashing)?
– How do I optimize my mobile build to reduce the installed size (which means faster download, user is less likely to uninstall the app when freeing space on their device, don’t have to deal with Google Play 50 MB APK limit, etc.)?

Set Target Frame Rate
Let’s start with the obvious. In Unity, we need to set an application target frame rate. The application target frame rate sets an upper-bound on frame rate. You may be surprised to discover that the default frame rate on some platforms is 30 fps. I like to make this call in the Start function of my game management object:
Application.targetFrameRate = 60; //set to desired maximum frame rate

Be sure to set a target frame rate

Load Game Objects when the Scene is Loaded
It’s easy to instantiate and destroy objects when needed, but creating and freeing resources requires expensive, time-consuming operations that can result in a drop in frame rate. Whenever possible, instantiate Players, Enemies, Objects, Projectiles, Particles, Backgrounds, Platforms, Power-ups, and other objects when the scene is loaded (whether this is done at design time or programmatically depends on the specific game architecture). If we create or destroy game objects while cycling through the main game loop, then we need to be prepared to deal with potential frame hiccups and lag.

Washington’s Wig: all scene objects are created at design time.

Pool Objects
If you’re making games, you’re probably already familiar with the practice of using object pools. If not, now that we’re working in mobile, it’s a good time to start. As mentioned above, when possible, we create a majority of our game objects when the scene is created. However, we obviously don’t want to make 5,000 bullets for our shmup or 1,000 platforms for our endless runner. Instead, create an object pool for each unique component that would typically be created and destroyed during the lifetime of a game scene. An object pool is a data structure containing a collection of objects that are constantly recycled to avoid unnecessary allocation and deallocation. The internet is filled with resources explaining how to implement an object pool, so I’ll only give a brief example. Imagine that we are building a Contra-style platformer, and we want to pool the player’s bullets. Pooling bullets works kind of like this:
– pick a data structure to hold our bullet objects (I like to use Lists, but an Array or any sort of flexible storage data structure can be used)
– determine the maximum number of objects that will appear at a time during a scene, and populate the storage data structure with that many objects. If we determine that the maximum number of bullets that will ever appear on-screen at any given time is 20, we’ll create 20 bullet objects and populate our storage data structure with these objects (I like to add a little slop space above my estimate).
– create a flag that marks if a bullet is in use or not in use. In our example, we might make a BulletScript class, add a boolean inUse member, and attach it to a bullet object.
– write a management function that accesses the bullet pool to get a “new” bullet. In other words, if the Player presses the shoot button, we search through the bullet pool (we can user a linear search with a for or do-while loop or implement a more complex search algorithm based on the structure and layout of our storage data structure), find a bullet with inUse == false, and then update the state of the pooled bullet so that it is put into use in our game (turn on physics, turn on collision, make visible, set position, etc.).
– when a bullet would typically be destroyed (collides with enemy, goes off-screen), return it to the pool. Mark the bullet as inUse = false, and update its state so that it is not in use (turn off physics, turn off collision, make invisible, etc.)

Washington’s Wig: pool of 50 coins are created at design time and recycled throughout scene.

Load Prefabs from Resource Folder
Upfront loading and object pools work great in *some* cases, but are not always a plausible option. Is it worth loading 50 MB of images when there is no guarantee that the user will ever access the portion of the scene that displays those images? What if loading the entire set of scene assets causes an enormous memory burden and the application runs out of RAM and crashes? In real-world development, we’re often forced to load some scene assets only when needed. The obvious route in Unity is to store game “chunks” as prefabs, a game object complete with components and properties, that we can selectively create and destroy when needed. Instantiating a prefab is easy. Add a reference to your prefab in script, attach the prefab to the script by dragging it from your asset folder to the inspector, and use the Instantiate function to create an instance of the prefab:

public GameObject myPrefab;
GameObject myobject = (GameObject)Instantiate(myPrefab, new Vector3 (0, 0, 0), Quaternion.identity);

Destroying the prefab instance when it is no longer needed is equally simple using the Destroy function:
Destroy(myobject);

Once an instance of a prefab is created, any assets (textures, sounds, etc.) associated with the prefab will also be loaded into memory. However, destroying a prefab does not automatically unload the associated assets. Instead, these assets are unloaded when the scene is unloaded. This causes an obvious problem. Imagine we have 3 prefabs all of which contain a unique set of high resolution image assets. If our game flow permits only 1 prefab to be loaded at a time, we would hope that unloading a prefab would also free any associated assets from memory to reduce our memory burden (why have the assets from multiple prefabs loaded if only 1 prefab instance can ever be displayed at any given time?). When all instances of a prefab are unloaded, its associated assets will *still* occupy space in memory. On mobile devices where memory is at a premium, this is unacceptable. We can work around this, however, with a simple trick. Loading the prefab from the “Resources” folder and explicitly freeing resources will ensure that any underlying prefab assets are freed when the prefab instance is destroyed.

The Unity “Resources” folder is a special folder that allows us to load assets at runtime. Start by putting your prefabs here.

Now with a minor tweak to our code, we can load prefabs directly from the prefab folder rather than connecting them to a script in the inspector (note that I like to make a “prefab” subfolder in the “Resources” folder):
GameObject MyPrefabInstance;
GameObject MyPrefabFromRes = Resources.Load(“prefabs/myprefab”);
MyPrefabInstance = (GameObject)Instantiate(MyPrefabFromRes,new Vector3(0,0,0),Quaternion.identity);

When the prefab instance is no longer in use, we can destroy it AND free all associated assets by explicitly requesting that unused assets are unloaded and then manually calling the garbage collector:
Destroy(MyPrefabInstance);
Resources.UnloadUnusedAssets();
System.GC.Collect();

What we gain with this technique is simple yet powerful. We can now ensure that any associated prefab assets (textures, sounds, etc.) are ONLY loaded when an instance of the prefab is loaded. When the prefab is destroyed, the associated prefabs assets are unloaded from memory (assuming that they are not loaded on some other game object or prefab instance). This sort of asset juggling is paramount to creating mobile game scenes that need to load and unload a wide array of assets in response to a variety of user interactions.

Monster Heart Medic: players can choose from a variety of mutually exclusive tests that require massive, high resolution images to be loaded. Once a test is completed, these images are unloaded using the above technique.

In part 2, I’ll talk about Sprite Packing and coding methods that I use to increase mobile game performance.