Customizable Navigation Bar

Sunday, April 10, 2011

Building A Game In Unity Part 7: Fine Tuning Your Code Before Release

Alright guys, this week I'm going to be covering some tips on what you should be keeping an eye out for during your last steps in your development cycle. Profiling and tuning your code is an extremely important step in development, because one tiny slip could bottleneck the whole performance of your game or application.

Memory leaks, overextended loops, dead code, all of these can mess around with your performance goals, and if you don't have performance goals, you need to set them. 

Where do you start with performance goals? Well something that you could start with is being able to run your game or application, with multiple programs loaded in the background taking up memory. Your game should always be using as little processor time and memory as possible at all times. 

Why? One thing you should keep in mind is that the lower your footprint left on the computer is, the more people will be able to play it, and at higher settings. Also, it's nice to have a smaller footprint left on the computer for those unavoidable instances where you need the extra resources, because they do happen. 

So where do I start? I will be giving some examples of what you need to look for during your profiling stage that may seem small, but will make a difference in the long run.

Example # 1: Declaring variable inside a loop

You should turn this:
        
        foreach(Entity currentEntity in GameObject.FindObjectsOfType(typeof(Entity)))
        {
               Transform example = currentEntity.transform;
                ...........
        }

into this:

         Transform example

        foreach(Entity currentEntity in GameObject.FindObjectsOfType(typeof(Entity)))
        {
               example = currentEntity.transform;
                ...........
        }

??? What difference does declaring a variable outside of the loop make? Well, declaring the storage variable outside of the loop allocates memory once, which is one step in the processor. Declaring the storage variable within the loop allocates memory as many times as the loop is long. These two work the exact same way, but declaring the variable outside of the loop gets rid of a tiny bit of overhead that the other way introduces. 

This is a small tweak, but if you have dozens of scripts that do the same thing, the overhead just grows and grows, and this is where the problem arises. Small seemingly insignificant code quarks add up, and they can do a number to your performance. 


Note: One test I always make sure my game passes. If you have Unity pro, turn on deep profiling and test your game. Tweak and test your code until you get a smooth frame rate with it turned on, because it takes a huge hit out of performance. Deep profiling will tell you how much processor and memory each function is using which is a HUGE benefit when you are trying to remove a bottleneck.


Example 2: Creating Static Variables

Now lets say that your enemy class needs reference to the main player. What you would obviously want to do is create a variable to store it:

         public Player mainPlayer;

Then you will want to store the player reference here like so.

         enemy1.mainPlayer = mainPlayer.....

Sure, good to go, but there is one problem. That only changes the single enemy instance. If you want to change it in every instance you need to use either a for or a foreach statement:

         foreach(Enemy currentEnemy in GameObject.FindObjectsOfType(typeof(Enemy)) as Enemy[])
         {
                  currentEnemy.mainPlayer = mainPlayer;
         }

and that is just unnecessary processor cycles being used, and extra memory being used for each enemy! How do we fix this?

Make it static:

         static public Player mainPlayer;

then you would use:

         Enemy.mainPlayer = mainPlayer;

instead of the for loop and whatnot. It is one cycle, and every enemy's mainPlayer variable changes, and there is only one chunk of memory taken up for that single static mainPlayer variable. This is definitely something that you should keep in mind.


Example 3: Coroutines Coroutines Coroutines!

Coroutines are awesome. Taking stuff out of the Update function and putting it into a coroutine is a great way to save processor time. Square roots for example, one of the more expensive things to do. Do they need to be done every frame? Sometimes, but not all of the time. What you need to do as a programmer, is see what you can get away with. As bad as it sounds, if you cannot see the difference between how something moves every 5 frames and every frame, then go nuts and take it out of the update loop.

        void Update()
        {
                movementDirection = Vector3.Normalize(other.transform.position - transform.position);
        }

Now the normalize uses a square root function. It is fairly expensive to use every frame, and if we say did it 5 times a second, we will get a very similar effect with a huge chunk of the overhead gone!

        void Start()
        {
                StartCoroutine(getDirection());
        }

        IEnumerator getDirection()
        {
                while(true)
                {
                        movementDirection = Vector3.Normalize(other.transform.position - transform.position);
                        yield return new WaitForSeconds(0.2f);
                }
        }


These three are the most common steps that people either don't think about, or just get lazy and skip over. This is a good starting point for optimizing your scripts in Unity. A part 2 to this article will go over a few optimizations that are extremely nit picky, and are definitely things that few programmers do. The overhead reduction from these optimizations are very small, but they still exist. If you are an OCD programmer, these will be the optimizations for you.

This is it for this Sunday. Next Sunday I will go over the steps you can take to build your game for release, and what you can do with the settings. Keep coding!