Basics of Optimization in Unity
Now that we’ve spent some time creating code for various different projects and have a good idea of how to build code, let’s go back and take a look at how we can become more optimized in our creation of code. There are endless ways that we can optimize our methods. I’ll just be outlining a few, but there are definitely more that can be found out there.
- Avoid GetComponent as much as possible
The use of GetComponent could use up some GC allocation. Having it be used multiple times for the same purpose, or for a key element like the camera, will cause a build up of GC. In order to prevent these issues, it is generally best to cache the component to it’s own object and then when we need to call upon it later, we can use that cached value.
A good basic rule is that if the object happens to be a class, we want it cached, if it is a struct we can use GetComponent without needing to cache it.
Going through some of the code within the first project that I worked on, I noticed that I have this code built to change the colour of my ships shields. This little bit of code can be optimized by caching the material class:
So, as the material is a class, we can create a new private material of _shieldColour and assign it within our void start:
With this assigned here, we can go back to our original code and replace the GetComponents:
This may seem a little trivial, but making changes like this through the code we write can help save the GC allocation.
Let’s just say this would have used up 200b of GC allocation before we changed it. If we had 5 different methods using GetComponent like this we are already up to 1kb of GC. Small values can quickly add up and create fps issues within the projects because it is having to deal with all the excess GC of unoptimized code.
2. Optimize Coroutines
Within Coroutines, we can create a cache value for the WaitForSeconds:
Here we have 2 separate coroutines that do the exact same process. However, 1 has it’s WaitForSeconds cached, while the other is not. In order to see the difference between the 2, we can create a sample group and give it a heading that we can understand when looking through our hierarchy:
Now, looking through our profiler after letting it run for a bit we can see that there is 20B of GC alloc built up every so often. Upon further investigation, we can see that it is coming from our slow routine. This is because of the fact that we are using new WaitForSeconds within the actual coroutine over creating a cache for it and assigning it there.
3. Use NonAlloc versions when you are able to
When we are using raycast within our projects, there is the option to choose RaycastNonAlloc, which will do the same process, however it will not create any garbage.
4. Use a list over an array when possible
Lists are easier to use, can be cleared within code much easier and are less memory intensive than an array.
5. Use Active and Static Canvas’s
When building a project, a good habit to get into is creating an active and a static canvas. Having all of your UI elements within 1 canvas will dirty up the whole canvas. This is because when you have something that changes it’s value, the whole canvas will be refreshed. This includes everything else that is there for visuals but don’t change. Instead, the UI elements that will be changing to 1 canvas and the static ones to it’s own canvas.
6. Hide the Canvas component, not the Object
These are just a few of many ways we can optimize our applications within Unity. There are a lot more out there, so take some time and see what you can find online.