Back To Home

Optimization

19-03-2024


Slay the Lag Dragon: Scripting Tips for a Smooth-Running Unity Game

We spend most of our development time in scripting, so it is very important to learn some best practices . We will be focusing on problems surrounding MonoBehaviours, GameObjects, and its related functionality.

post 2

1. Obtaining Components :

  1. We use GetComponent() to get the component of the GO and it comes with 3 different variations: GetComponent(), GetComponent(typeof(T)) and GetComponent(string). The slowest one of these three is GetComponent(string). It is a very expensive call as it uses a string to search. we can use other two variants as these two have very similar performance.

  2. We should not use it very often as it searches in the scene hierarchy, if the hierarchy is very large then it would take much more time to search and get that component making our game run slow.

  3. We should take direct reference of the objects to the script if possible to avoid using this API. If we need to use it then we should use it in the Awake() or Start() function to initialize the variable.

  4. Do not use this API in those function which are being called continuously.

2. Avoid using expensive unity APIs :

  1. Avoid using Find , SendMessage and BroadcastMessage functions. These all methods uses string search in the scene hierarchy or in GO’s component.

  2. Avoid using Camera.main, Take the direct reference of the camera. Behind the hood it also calls a similar method like Find() which searches for the tag “Main Camera” in whole scene and returns the first GO it finds.

3. Strings and strings operations should be used carefully :

  1. Strings generates a lot of garbage. Wonder why? Strings are just arrays of character or you can say an array of character is a string, and both string and arrays are immutable in nature means when we change our string its gonna create a new string and the old one becomes garbage. Why is that? Strings are just arrays and these arrays are immutable.

  2. Arrays are always mutable, in that you can assign new values to elements of the array. However the length of an array is immutable. If you need to change the array length, you need to allocate a new array, copy over any elements you want to keep, and assign the new array to your variable. Thankfully you can use StringBuilder to avoid or minimise this garbage allocation.

4. Caching component references :

  1. Don’t get components everytime when needed. Get them only once in the starting and cache them, saving us some CPU overhead each time. The cost is a small amount of additional memory consumption, which is very often worth the price.

5. Removing empty callback definitions :

  1. Whenever a MonoBehaviour component is first instantiated in our scene, Unity will add any defined callbacks to a list of function pointers, which it will call at key moments. However, it is important to realize that Unity will hook into these callbacks even if the function body is empty. The core Unity Engine has no awareness that these function bodies may be empty and only knows that the method has been defined and, therefore, that it must acquire it and then call it when necessary.

  2. Consequently, if we leave empty definitions of these callbacks scattered throughout the code base,then they will waste a small amount of CPU due to the overhead cost of the engine invoking them. So dont leave any unity function empty whether its Start(),Awake(),Update() etc.

6. GameObject null reference checks :

  1. Whenever we perform a null check against a GameObject will result in some unnecessary performance overhead.

  2. we should avoid :

      
      if (gameObject != null) { // do work }  
    
  3. what we should be doing is :
      
      if (!System.Object.ReferenceEquals(gameObject, null)) { // do work } 
    

post 2

7. Avoid retrieving string properties from GameObjects :

  1. Gameobject has two string properites “name” and “tag”. We should avoid their use during gameplay. It takes additional memory allocation Whenever we use them.

  2.   
      if ( gameObject.tag == “Player”) { // do something } 
    
  3. Instead we should use CompareTag() to compare tags of object. It is well optimized method to do this task and doesnt need extra memory.

  4. It is often a better practice to identify objects by their components and class types and to identify values that do not involve string objects.

8. Using appropriate data structures :

  1. C# offers many different data structures to use and we shouldn’t become too accustomed to using the same ones over and over again because they are commonly used everywhere. Pick the correct data structure that is required or most suitable to perform the task.

9. Using appropriate data structures :

  1. Unity uses a memory buffer to store parent-child gameobjects. Its more like a dynamic array. Unity attempts to store all Transforms that share the same parent sequentially in memory inside a pre-allocated memory buffer and are sorted by their depth in the Hierarchy window beneath the parent.

  2. if we re-parent GameObject to another one, the parent must fit the new child within its pre-allocated memory buffer as well as sorting all of these Transforms based on the new depth. Also, if the parent has not pre-allocated enough space to fit the new child, then it must expand its buffer to be able to fit the new child, and all of its children, in depth-first order. This could take some time to complete for deep and complex GameObject structures.

10. Moving common data into ScriptableObjects :

  1. If we have a lot of different Prefabs with components that contain a lot of properties that tend to share data, such as game design values such as hit points, strength, and speed, then all of this data will be serialized into every Prefab that uses them. A better approach is to serialize this common data in a ScriptableObject, which they load and use instead. This reduces the amount of data stored within the serialized file for the Prefab and could significantly reduce the loading time of our scenes by avoiding too much repetitive work.

11. Don’t serialize everything :

  1. Unity uses serialization both in editor and scripting. In editor it is used for scenes, prefabs, GameObject, ScriptableObject etc. When one of these object types is saved to disk, it is converted into a text file using the Yet Another Markup Language (YAML) format, which can be deserialized back into the original object type at a later time.

  2. In scripting All GameObjects and their properties get serialized when a Prefab or scene is serialized, including private and protected fields and all of their components, as well as child GameObjects and their components and so on.

  3. The more public and serialized-field varaibles we have in our scripts the more time it takes to deserialize them.Reading and deserializing this data from disk at runtime is an a slow process and so all deserialization activity comes with a significant performance cost.

Tips :

  1. Use String.Empty instead of “”.
  2. Avoid usage of C# reflection (slow!).
  3. Store references and reuse them.
  4. Create Once and Reuse (Pooling).
  5. Run the Code Only When It Is Needed.
  6. Avoid empty Start, Update and destructor functions.
  7. Do not target Mono but il2cpp in master mode.
  8. Use structs instead of classes for shorttime data storage.
  9. Implement DOTS for massive amount of homogeneous elements.
  10. Consider CPU Slicing to reduce per-frame CPU cost.
  11. Cache C# StringBuilders instead of concatenating strings with ‘+’.
  12. Disable logging and OnGUI for release builds and for profiling.
  13. Do not use Instantiate during gameplay prefer during loading screens for pooling.
  14. Avoid memory-allocating functions; use non-allocating versions, e.g. Physics2D. CircleCast().
  15. Avoid Camera.main calls as it does Object.FindObjectWithTag every time it’s accessed. Have a direct reference to the camera.
  16. Avoid repeated access to MonoBehavior accessors such as transform, as it internally calls GetComponent(Transform).

I hope you found this blog post enjoyable!


More Blogs

See All