Framerate is how many times your game can update every second. The higher the framerate, the more fluid and smooth your game will look. For static hardware, such as iOS and Android devices, as well as game consoles, most games target 30 frames per second. This framerate generally keeps the gameplay fast and smooth enough, and leaves a little bit of wiggle room for higher intensity moments of gameplay where the framerate might drop a little bit. When a game targets, and hits, 60 frames per second, it is usually a big enough deal to make it a “back of the box” bullet point, and a part of the marketing strategy for the game. When you’re running your game at 60 frames per second, your gameplay logic has to run twice as fast as if you were running at 30 frames per second.
There are two problem types to address when targeting a framerate. Expensive code that runs constantly and causes a constant framerate drop, and expensive code that runs infrequently and causes spikes in framerate. When you are working on improving framerate performance, I recommend starting at framerate spikes first. Both issues are bad news, but I believe framerate spikes are much more obnoxious to a user, and to other development staff, than a constant framerate drop. If I had to choose, I would much rather play a game that runs at a constant 15 frames per second than a game that runs at 30 frames per second and has brief pauses in gameplay every few seconds. If I didn’t have to choose, I would play neither of those and go find a game that is able to always run at 30 frames per second.
Another important note on this subject, I believe performance is the number one most important feature on any game, on any platform. It does not matter how novel, innovative, and interesting your game concept is if your game runs poorly. While there is a small audience of people looking for interesting new experiences, most people playing games just want something fun. A higher framerate increases the responsiveness of your game, makes things look and feel more fluid, which both very directly contribute to how fun your game feels to play. There is also no lack of existing games to play on any platform that run at a stable framerate. Finally, if you do have a great idea you ship, hampered by poor framerate, you make yourself open to someone taking your game concept and releasing it themselves, addressing these issues, making the money that you should have made in the first place.
Where do you begin addressing framerate? The first step is going to be to take a look at your development practices. Run and test your game on your target devices frequently, and treat changes in performance as high priority problems to be addressed before almost anything else. For some reason, many developers seem to dislike this practice, and treat it as if they are wasting their time by making builds to run on device, and testing them every day. The only benefit you get from never playing and testing your game on device is you might be able to check off features to implement on a checklist faster. Your game is not going to be finished any faster, you are just pushing off and piling up work to be done later. If you wait weeks between testing your build on device, you know have weeks of code to backlog through to identify problems in and address. You probably also have much more framerate performance you need to regain. It is far easier to tread water and keep your game at the performance targets than it is to dive deep in, and hope you can swim back up enough to hit your performance targets by the time you need to ship. Testing on device once a day versus once a week puts you in a position where you only have that one day of work to look through to address a new performance issue, instead of one week. If you think of your code a a sort of Jenga tower, with each piece of code placed on a block above a previous piece, if you are testing daily you only have a block at the top of the tower to take off and fix. If you waited a week, and spent that week building code dependencies, you now have to take off each of those blocks, to get to the problem block of code a few pieces down. I’m sure this all came off a bit preachy and accusing, but so many developers fall into the trap of putting things off. Especially when products begin to enter a crunch period, and you have to start working extra hours, it becomes very easy to tell yourself you can skip that step of running your code on target today, and save yourself an hour or so of work.
To actually maintain a framerate for your build, you will need a way to track your framerate. The best solution I have used for this has been a framerate counter on screen, on device at all times for development builds. It keeps the framerate in every member of the development staff’s face, so no one can say they did not know how fast the game was running, or if it was having issues. It also provides people actual data, if you are relying on peoples to guess when they think the framerate drops, you will get false positives all the time, people reporting a framerate drop when it was thirty the whole time, and false negatives, people not bothering to report a framerate drop because they might not have noticed it. It also helps emphasize the importance of performance, if you are willing to devote screen real estate to that number at all times during development, it just be important. The next thing I recommend is to de-emphasize that framerate counter when performance is good, and emphasize it when things are bad. If the framerate drops below a certain value, changing the color of the number in the corner can help a lot, people will start to ignore a green framerate counter, but the second they see a red number out of the corner of their eye they will know something has gone wrong. I also recommend applying some filtering and smoothing to the framerate number. Instead of displaying a raw number from one to thirty, rounding to intervals of 5 can help a lot. You don’t really care that your framerate is exactly 22 or 24, what you care about is that it has dropped past the range you find acceptable, and if the number shown here was 20 or 25 instead of the actual value, you would still know there was a performance issue you needed to address.
The next proactive step you can take to keep your framerate from dropping in the first place is to learn what code and game building patterns cause framerate problems in the first place. The first thing you can do here is to read the other posts I have made on this blog, they cover a lot of subjects that affect performance in different ways. Even other performance areas bleed over each other, if your game has issues with constant memory allocation and deallocation, then the garbage collector will need to run more frequently, and when the garbage collector runs, you will generally see a noticeable spike in framerate.
The number one way I have found to improve runtime performance on any Unity project for iOS and Android has been to cache more information. Generally the memory cost of holding a reference for the lifetime of an object is much less prohibitive than the cost of doing a lookup to find that reference every time you need it. If you need access to the transform or material of a gameObject frequently, keep a local reference, every time you call gameObject.transform or gameObject.renderer.material, Unity does a lookup, which can get expensive very quickly on iOS and Android. If you need to pull information out of your database, such as stats for the player’s equipment, keep a local cache of that as well.
Data is not the only thing you should be caching. Game objects, and things like hit and blood effects can also performance stability to your build if you cache them. Yes, your idle “best case” performance will drop if you are keeping data like this cached, but it will only bring your idle performance closer to your worst case performance, and will probably bring huge improvements to your worst case performance. Instantiating new game objects in Unity is expensive, especially on iOS and Android platforms. It is generally much cheaper to keep a cache of objects you might need disabled and offscreen somewhere, activating and resetting one when you might need it. As a specific example of this, Death Dome for iOS saw a huge reduction in framerate spikes when they began caching their blood effects. The only cost to this was keeping those blood effects in memory so they could be reactivated at any moment, but this actually still works in your favor, giving you a stronger idea of what your peak memory and performance needs will be for gameplay.
Proactive framerate tips are handy and all, but there are going to be times you need to be reactive. Your framerate has fallen below acceptable levels, and you need to fix it. The first step to this is to get yourself a stable test loop. You want a set of steps you can follow over and over to get to the problem area, with minimal variation, so you can properly test changes and address the problem. Once you have your test loop figured out, you will need to decide the next step for spending your time on. If you have been proactive and testing on device frequently, then the backlog of changes between the build with a problematic framerate and the current build might be small enough that you can look at your source control history to start identifying the problem. If there have been too many check ins since the last time framerate was acceptable, then you will need to start profiling.
There are many tools to help you profile your project with Unity on iOS and Android. The tools Unity provides for profiling are going to be a better place to start than the native tools, such as XCode and Instruments for iOS. Unity’s tools are going to help measure your game code’s performance, whereas native tools are going to measure the performance of all of Unity’s code as well, making it difficult to narrow down to things you can actually address. This is a point where Unity Pro becomes extremely useful. Unity Pro gives you access to all of these great profiling tools, if you are using the base version of Unity you will need to find other ways to identify framerate issues so you can address them. Before running through the Unity Pro solutions, I will give a rundown of ways to debug and profile with the base version of Unity.
There are a few things to keep in mind when addressing performance issues for any technology. You are going to want to address the lowest hanging fruit first. Yes, your framerate is a composite total of everything happening in your game at once, so could could pick almost any number of systems to speed up and hit your framerate target, but you want to minimize the time you spend addressing performance concerns to get back to other tasks. Things you are going to want to fix first are going to be the parts of your game taking up the most time, doubling the speed of something that is already really fast is not going to be that helpful. You are also going to want to look at how quick it will be to speed up different systems. There might be some easy and quick changes you can make to a few systems that are not the top costs in performance. Your goal is to get your overall game framerate back to that target number, it does not matter too much how you actually get there. You are going to need to know the importance of features, the pillars of development on the game you are improving the performance on. You might be able to get framerate back up to par if you disable hit effects, but those hit effects are probably an important part of the look and feel of the game. You also want to do most of your testing on target. While you can sometimes get a rough idea of relative performance costs by doing rapid iteration and testing on your Mac or PC, performance costs do not scale linearly, and you can even find yourself in a situation where the framerate drops on iOS after making a change that improves the framerate on your Mac.
If you do not have access to Unity Pro, or you have exhausted your options with Unity Pro and are still looking for ways solve your performance issues, you are going to need to do a little more legwork to identify what is causing your performance problems. The first step is to write down everything going on and happening at the point your performance drops. How many characters are on screen, if a group of characters recently spawned, what menus might be opened, what the player character is doing, are there any particle effects on screen, what makes this test case different and special from the default test case where performance is presumably fine. You might get lucky here and have something in that list that is an obvious problem, if not, then the next step I usually do I consider my “Fifty Percent Rule.” Whenever I want to test a change, I start by making a huge change, and then narrow down. If you are able to do so, run through a test case where you remove about half of the things on the list you just made. This can either be done by making a new build with things disabled, skipping steps in your repro steps, or following the reproduction steps in a different area of the game. Repeat this process, swapping as much of what you can between what you disabled and left enabled last test. You are going to get one of two results from this: Either one of the 50% test cases will still suffer from framerate issues, or both perform fine. From here you are going to continue narrowing things down, turning off systems until you have some identified some problem areas. If you are able to narrow things down, you have a good idea of where to focus your efforts on improving systems. There is still a chance that you make no progress by turning systems on and off, the framerate issue might just be an issue of everything being a little slow together, or a system shared between everything being slow. At this point you are going to have to get down and dirty with your code.
Unity Pro or not, you are probably still going to want to build yourself a library of code to address performance concerns. In my post on dealing with memory, I described some technology worth building to help with memory issues. Doing something similar for framerate problems will go a long way towards keeping your games framerate stable, and addressing problems when they come up. I recommend building a system for tracking time spent in various systems in your game, as well as the frequency of calls to functions in those systems. Yes, with Unity Pro you have access to Profiler.BeginSample and Profiler.EndSample to feed data into Unity’s profiler, but this might not be enough information, and with the base version of Unity you do not gain access to this functionality. Having a similar system you write yourself will give you a greater deal of control over measuring your performance. I would recommend you begin by using some #ifdefs to make it easy to toggle your performance tracking code on and off. From there, you you want your begin and end samples to take in a string or other identifying token to both match them to each other as well as let you track the information. When an external system calls in, you can use something like System.Time.RealTimeSinceStartup to help track the time spent between the begin and end calls. The first pass at this will probably feel identical to Unity’s Profiler.BeginSample and EndSample, but as you develop your system you can add new features as you think of them and need them.
So, you either wrote a system similar to what I described in the previous paragraph before you started profiling framerate, or you hit a point in managing your framerate and realized you needed to build it, and did so. Now you can pepper your code with calls to your own profiler, and use it to identify where time is spent every frame.
If you have Unity Pro, then you have access to Unity’s wonderful profiler. The primer on this is available here: http://docs.unity3d.com/Documentation/Manual/ProfilerCPU.html. When you make a Unity build for your device, you want to set your build settings in the build window to autoconnect the profiler. Next, you will need to make sure your device is on the same wireless network as your computer. Finally, on the computer you deployed the build from, open the profiler window. This profiler window will tell you how much time is spent in what function. If there is no foldout and you want a deeper profiler of some of your own code, you can add Profiler.BeginSample and Profiler.EndSample to your code to give you more data here. The first step to parsing the information here is to look a the overview in the bottom left corner, and leave it sorted by time spent in the function. Anything near the top, your code is spending more time processing. In the top of the profiler window, in the CPU Usage area, there will be a bar showing you the time it took to render each frame. This gives you a good idea of what your average framerate is, and lets you identify spikes. You can click on this graph to go to that point in time and see what functions were running, to identify why performance was spiking or not. I am going to cover this tool in greater detail in a future post, this should be enough to get you started and narrowing down what is causing your performance issues.
With whatever tools you use to find them, in Unity Pro or the base version of Unity, you should now have some data on time spent in various systems. At this point you want to visit the systems that are the bigger time sinks, and look for any obvious optimizations. The first thing you can probably do to speed up a system is to cache data. If you have not already done a performance pass on this system, chances are the reason why performance is slow is it is doing a lookup of some kind. If it is a function that is called frequently, you might be able to cache its output, and regenerate it when needed. The first time a function is called in a tick that looks up or generates a specific piece of data, you can look it up, and on subsequent calls you can use the saved information from the previous call. You also want to look at call frequency for some functions. You might catch something getting called more often than it should. When setting up complicated scene transitions, it can be easy to accidentally leave something active that you should only have a specific number of, such as cameras. If you find that logic is called more often than you expect, then you can look into what is triggering these extra calls, and resolve it. If you’re lucky, this might be as simple as having an extra camera placed in your scene, or a camera that sticks around through a scene transitions when it should not.
This has been a brief overview of managing framerate. If you didn’t think this was brief, then you haven’t spent too much time debugging framerate issues yet. I will be writing more posts on this subject, I will do a post covering using the Unity Pro Profiler soon.