Hi everyone! My name is Warren and I am a co-founder and developer at Qoobit Productions Inc. This is the first installment of a Primer we will be creating, which will feature a few optimization tips and tricks we’ve adopted over the years using Unity.
We will be expanding this primer as we go along, but since this one is the first one, we will be use a more loose structure until we have create enough primers to start categorizing these tips into their own silos.
In this installment, we will cover FIVE common performance pitfalls.
1) Avoid using GameObject.Find!
Many learning resources will teach to use GameObject.Find in the early stages of learning scripting. As convenient as it can be at times, try your best not to use it.
Reasons:a) Whenever you calls this function, it will traverse through your entire Hierarchy to look for a GameObject with the matching name. If you have a big Hierarchy with many GameObjects and you’re calling this constantly, it will significantly decrease your performance
b) Renaming your GameObject will break scripts! If you have a working reference in your script to an existing GameObject and for some reason you rename it down the line, the GameObject will not be found and return null for the function call.
2) When and where to use GetComponent
Similarly to #1, whenever you call GetComponent, it does a linear search of your current GameObject’s components to find the MonoBehaviour reference. If you’re doing this constantly, or if you're inside your Update function, and your GameObject has many components on it, it can add up.
So when is it OK to use GetComponent?
If you use GetComponent to assign your references at the start of your scene, (For example, in your Start() or Awake() function) it is only called once. You will only take a small performance hit at the start of your scene, but after everything loads, you will have a direct reference to the MonoBehaviour meaning everything will be accessed directly and fast.
What are my alternatives if I just don’t want to use GetComponent?
Inside your MonoBehaviour script, you can either do the following to serialize your reference.
private AudioSource audioSource;
--- or ---
public AudioSource audioSource;
When you do either of these options inside your MonoBehaviour, the variable is exposed for access in the editor Inspector. From there, you can drag and drop the reference directly onto the field and you will have direct access to the reference.
To learn more about serialization, visit https://docs.unity3d.com/Manual/script-Serialization.html
3) Camera.main woes
Many tutorials will use Camera.main to get the main camera in the scene. Whenever you call this function, under the hood, Unity actually calls GameObject.FindGameObjectsWithTag(“MainCamera”). This call traverses your entire hierarchy until it finds the first enabled GameObject with a tag of MainCamera and retrieve its Camera component if it exists. This is not cached.
Where else can this be a pitfall?
When using Unity UI (UGUI), whenever you create a Canvas and you set the Render Mode to Screen Space - Camera or Screen Space - World, it asks you to select a Render Camera. If you don’t select one, it actually calls the Camera.main function to try and fit the current Canvas to certain size.
4) Instantiate GameObjects smarter
Most people don’t realize that the GameObject.Instantiate function has many overload constructors and the most important one should be this one:
public static Object Instantiate(Object original, Transform parent);
Using this method of instantiation, the new GameObject is created directly under the provided parent Transform. Alternatively, if you didn’t provide this parent Transform, you would have to create it first under the root Transform and re-parent the newly instantiated GameObject. This extra allocation can be wasteful. Making this small change can improve performance by 5-10%.
5) Optimize SkinnedMeshRenderers with one click
Whenever you import a FBX with a bone rig, Unity will treat it as a SkinnedMeshRenderer and as such a Transform will be created in the Hierarchy for each bone in the FBX.
If you’re not doing anything with the Transforms of the rig (ie. binding weapons to hands or hats to the head), these transforms don’t do anything and are meaningless calculations being performed every frame.
To optimize this, select your FBX in your Project window, and navigate to the Rig tab.
There should be a checkbox label Optimize Game Objects located at the bottom. After you have checked it and applied the settings you will see that the Transforms have disappeared.
If you still need the hands and the head for binding other objects to the rig, you can expose those individually as required by expanding the Extra Transforms to Expose and adding the individual Transforms in the rig.
Special thanks to Ian Dundore and the Unity Enterprise Team for gathering and sharing the information that we have learned over YouTube and as well as the various live events we’ve been to.