There are as many different ways to optimize code as there are reasons for performance problems. In general, it’s strongly recommended that developers closely profile their applications before attempting to apply CPU optimizations. However, there are several simple CPU optimizations that are universally applicable.
Unity doesn’t use string names to address Animator, Material, and ShaderA program that runs on the GPU. More info
See in Glossary properties internally. For speed, all property names are hashed into property IDs, and it’s these IDs that are actually used to address the properties.
Therefore, whenever using a Set or Get method on an Animator, Material or Shader, use the integer-valued method instead of the string-valued methods. The string methods perform string hashing and then forward the hashed ID to the integer-valued methods.
The property IDs created from string hashes are deterministic over the course of a single run. The simplest way to use them is to declare a static read-only integer variable for each property name, and use the integer variable in place of the string. These are automatically initialized during startup with no further initialization code needed.
The appropriate APIs are Animator.StringToHash for Animator property names, and Shader.PropertyToID for Material & Shader property names.
In Unity 5.3 and onwards, non-allocating versions of all Physics query APIs have been introduced. Replace RaycastAll calls with RaycastNonAlloc, SphereCastAll calls with SphereCastNonAlloc. For 2D applications, there are also non-allocating versions of all Physics2D query APIs.
The Mono and IL2CPPA Unity-developed scripting back-end which you can use as an alternative to Mono when building projects for some platforms. More info
See in Glossary runtimes treat instances of classes that derive from UnityEngine.Object in a specific way. Invoking methods on the instances actually calls into engine code, which must perform lookups and validations to convert the script references to the native references. It’s more resource intensive to compare a variable of this type to null than to compare against a purely C# class. For this reason, avoid these null comparisons in tight loops or in code that runs every frame.
For vector and quaternionUnity’s standard way of representing rotations as data. When writing code that deals with rotations, you should usually use the Quaternion class and its methods. More info
See in Glossary math that’s located in tight loops, remember that integer math is faster than floating-point math, and floating-point math is faster than vector, matrix or quaternion math.
Therefore, whenever commutative or associative arithmetic allows, attempt to minimize the resource intensity of individual mathematical operations:
Vector3 x;
int a, b;
// Less efficient: results in two vector multiplications
Vector3 slow = a * x * b;
// More efficient: one integer mult, one vector mult
Vector3 fast = a * b * x;
It’s common for applications that must convert between HTML-formatted color strings (#RRGGBBAA
) and Unity’s native Color
and Color32
structures to use a script from the Unify Community. This script was both slow and caused extensive memory allocation due to string manipulation.
As of Unity 5, there is a built-in ColorUtility API that performs these conversions efficiently. Usage of the built-in API should be preferred.
It’s a general best practice to eliminate all usage of GameObject.Find
and Object.FindObjectOfType
in production code. As these APIs require Unity to iterate over all GameObjectsThe fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it. More info
See in Glossary and Components in memory, they rapidly become non-performant as the scope of a project grows.
An exception to the above rule can be made in accessors for singleton objects. A global manager object often exposes an “instance” property, and often has a FindObjectOfType
call in the getter to detect pre-existing instances of the singleton:
class SomeSingleton {
private SomeSingleton _instance;
public SomeSingleton Instance {
get {
if(_instance == null) {
_instance =
FindObjectOfType<SomeSingleton>();
}
if(_instance == null) {
_instance = CreateSomeSingleton();
}
return _instance;
}
}
}
While this pattern is generally acceptable, it’s important to examine the code and ensure that the accessor is be called in ScenesA Scene contains the environments and menus of your game. Think of each unique Scene file as a unique level. In each Scene, you place your environments, obstacles, and decorations, essentially designing and building your game in pieces. More info
See in Glossary where the singleton object doesn’t exist. If the getter doesn’t automatically create an instance of a missing singleton, it’s common to discover that code looking for the singleton results in repeated calls to FindObjectOfType
(often multiple times per frame) and creates an undesirable drain on performance.
Internally, Unity’s Camera.main
property calls Object.FindObjectWithTag
, a specialized variant of Object.FindObject
. Accessing this property is no more efficient than a call to Object.FindObjectOfType
. If code must address the main cameraA component which creates an image of a particular viewpoint in your scene. The output is either drawn to the screen or captured as a texture. More info
See in Glossary, it is strongly recommended to do one of two things:
Access Camera.main
in a Start
orOnEnable
callback and cache the resulting reference.
Construct a Camera Manager
class that can provide or inject a reference to the active camera.
The UnityEngine.Debug
logging APIs aren’t stripped from non-development builds, and do write to log files if called. As most developers don’t intend to write debug information in non-development builds, it’s recommended to wrap development-only logging calls in custom methods, like so:
public static class Logger {
[Conditional("ENABLE_LOGS")]
public static void Debug(string logMsg) {
UnityEngine.Debug.Log(logMsg);
}
}
By decorating these methods with the [Conditional] attribute, the define or defines used by the Conditional attribute determine whether the decorated method is included in the compiled source.
If none of the defines passed to the Conditional attribute are defined, then the decorated method and all calls to the decorated method are compiled out. The effect is identical to what would happen if the method and all calls to the method were wrapped in #if … #endif
preprocessor blocks.
For more information on the Conditional
attribute, see the MSDN website: msdn.microsoft.com.