There are as many different ways to optimize code as there are reasons for performance problems. In general, it is 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 does not 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 is 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 simply 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, and so on. 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. While small, the cost of comparing a variable of this type to null is much more expensive than a comparison 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 is 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 cost 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 is 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 is 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 is 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 does not exist. If the getter does not automatically create an instance of a missing singleton, it is very 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.
The UnityEngine.Debug
logging APIs are not stripped from non-development builds, and do write to log files if called. As most developers do not intend to write debug information in non-development builds, it is 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.