Good Practices for Physics, Scripts and Shader Based Optimization

In Our Previous Article, We Talked about Techniques of Rendering Optimization, how we can optimize our game’s rendering to use as much lesser CPU as it can to save resources for other operations like Physics, Logic Compilation. 
In This article, we will go into further topics involved in running of game on a particular device or platform.
Main Contents of the Article Are:
  • Shaders Optimization
  • Scripting Optimization
  • Physics Optimization

Shaders Optimization

Continuing further into rendering optimization, shaders play an important role in both quality of the game and performance factor. 
Shaders are basically draw commands that a platform uses to draw() a mesh on screen.  
This is what a shader looks like in unity, it is normally accessed through materials assigned to a mesh.
Shader Inside Unity Engine
Shader Optimization Performance In Unity

Shaders are an integral part of beautifying your game, but shaders do have some render passes, draw calls that are to be beared on your platform. 
The more texture or color channels your texture contains, the more complex its rendering and cost will be, because in the case, your platform has to draw the object multiple times with all the textures being overlapped in queue pattern to give you the result. 
For Example,
Standard Shader Performance Optimization Unity

Look at the standard shader, it is very powerful when it comes to beautiful results, but looking at the amount of alpha channels, a standard shader has, means it needs to be drawn over multiple render cycles or draw calls to be rendered with all the textures that we use, so this is more complex and costly.
While if we take a look at a legacy shader,
Legacy Shader Optimization Unity performance
The legacy shader is a lot simpler,than the standard shader, it has just 1 additional alpha channel, that is the color overlay on the texture. Its rendering may not be as good as standard shader, but it will be a lot simpler to render and will take up less resources and draw calls.
While working on mobile platforms, we have a lot of performance and hardware constraints, that's why even legacy shaders are not advisable to use on those platforms.
For such platforms, mobile shaders are designed, which is shown as below.
Mobile Shader Performance Unity Optimization

You can see the mobile shader is even more simpler, than the legacy shader, as it doesn’t contains even the additional color overlay channel, hence saving the additional cost for mobile platforms. That’s why unity has provided dedicated shaders for mobile and low end platforms to support good performance. We can still use the costly shaders at low end platforms but to a certain limit, like using it on main game elements or characters which are not widely present in the game. 
Like your main hero or main villian wont be there in too much of amount, and they are the focused game objects aswell so they can be assigned costly shaders but for NPCs and environment objects, they can be more in amount so they should be used simpler shader with.
A useful Tip: For using transparency on mobile or lower platforms, we sometimes need to use legacy or costly shaders, we can use Mobile/Particle shaders for achieving transparency on the lower platforms at a lesser cost.
Mobile Shader Optimize  Unity Rendering

Scripting Optimization

In this Section we will move towards optimization of scripting or game Code, this is a very crucial optimization and probably a tricky one to achieve.
Lets first check, how can we profile to see the issues in scripts of check which script is eating more of the CPU time.
Lets first take into account a very exciting algorithm with its properties and needs.
We all know that whenever GC is triggered, it stops all process, until it does its operation.
Whenever we destroy an object, to clean the object from memory, unity’s automatic memory management triggers a Garbage Collector (GC) call to remove it from memory, GC calls have overhead on your CPU Time.
Also spawning gameobjects repetitively on runtime may also cause overheads on performance. 
Because you are allocating memory, and if you keep allocating GC eventually enters to clean up the memory for further allocations, this is how unity’s Automatic Memory Management handles spawnings.
Like for runner games maybe or for a shooting mechanism, so to handle this elementary problem, we use an algorithm known as “Object Pooling”.

Object Pooling:

Object pooling algorithm, says that “ If you constantly need to spawn destroy objects, It is better to spawn objects in start and re use them later as per your need to avoid constant GC calls in this situation”.
Here is Link to a simple Object Pool ScriptObject Pool
In an object pool script, we simple Instantiate objects at start and then deactivate them to like archive them, and whenever we need the objects, we get from a data structure in which we have stored them and then reactivate them at our desired Transform. And when we need them to destroy, we simply deactivate them and put them in the data structure.

Avoiding CPU Costly Code:

As we all know unity provides us with ‘MonoBehaviour’ as a tool for coding our games and doing some utility based editor operations. It meets almost all of our requirements to a good extent, but using the APIs constructing our algorithms and logic has to be done pretty carefully, because doing it without any prior consideration or careful steps may lead us to a horrible code, that will be hard to optimize.
When we talk about scripting in unity, we are first being introduced with the ‘Routine Calls and Methods’ that unity provides us.
  1. Awake
  2. OnEnable
  3. Start
  4. OnDisable
  5. Update
  6. FixedUpdate
  7. LateUpdate
Unity provides us with a list of call backs but these are the most commonly used ones from all of them. 
Awake,and Start are called when a monobehaviour(script) is first enabled or run in a scene, while OnEnable is called every time when the script is enabled.
While Update,FixedUpdate and LateUpdate are continuously calling functions that are called over the lifetime of a MonoBehaviour or script.
Update is called every frame, FixedUpdate is called Fixed times (Approximate 50-60 times) in a second, whereas LateUpdate is called after every Update function.
When writing game code we should avoid cpu demanding codes from being written in the continuous routines, the more complex the code is,the longer CPU has to wait on a frame to finish its execution to run the next frame and it will lead to lower FPS and Lags.
For example, 
FindObjectOfType<T> it is a very complex and costly call as it will iterate upon the whole scene objects in order to find objects of Type<T>, and if we use this call in the continuous routines, this may lead to iterating over all the scene objects almost every frame leading to costly operations per frame or more.
Same for GetComponent<T>  this is a pretty common mistake done, it iterates over all components of an object and returns the component that we request using it in routines may Cause performance issues as it will search through components at every game cycle or frame over gametime.
Looping through in the routines can also be a very costly feature, as in algorithm forms you will get ‘n’ cost or more on every frame depending upon your scenarios, so it is better to avoid the routines as much as we can or use them wisely for only certain operations where they are needed like.

Delayed Routines for saving CPU:

If we need to use the routines, we should avoid using multiple instances of such scripts as they may cause higher CPU usage, we can maybe use routines in a behaviour on Player, but using them on NPCs may let the game use too much CPU resulting in fps drop.
So in this case we should use event driven approach, call backs based approach for different operations, but still if we have to use routine methods in such cases, we can use a delayed routine behaviour in this case.
Delayed Routines are basically, CoRoutines(IEnumerator) running with a loop,
Here is an Example Code: Delayed Routine
In Above example, you can see that we can control the execution count of the code and adjust the execution cycles as per our need of performance and functionality.
This approach can be used to optimize AI Agents, that are to find and attack you, we can even put a variety of execution time to evade the concurrent agent routine calls. 
We know that AI Agents are to be continuously updated as per the given data feed, so this approach can be helpful in saving the CPU time.

Avoiding Garbage Calls in Scripts:

When you repetitively assign an object or variable same value over the frames, then unity’s scripting engine kicks in the garbage collector as the assigning calls are then treated as garbage calls, so we should avoid these garbage calls to save our execution going through garbage collector.

Physics Optimization

Physics optimization is an important point when working on the game, let’s discuss some of its core topics.

Optimizing Raycast:

Raycast is an important function when it comes to game development, mainly used for detection of an object from another object. But Raycast is a costly feature, especially when using multiple objects, like NPCs. Raycast basically applies Intersection algorithm on objects, so for every object it hits, it applies intersection algorithm on that. So it can be costly if not handled properly and used in a continuous Routine over multiple gameObjects.
We can optimize Raycasts, in a several ways like, 
  • We can filter the layers on which raycast is to hit, in this way, we can save our CPU from calculating the intersection on non important objects.
  • We can cast ray in delayed routines, to save ourselves from continuous CPU usage for the purpose, this can come handy especially in case of the AI agents or traps stuff.
  • Another technique may be used is limiting the Raycast Distance so that we don't hit the calculation on the objects which are not to be considered from a distance. Rather than checking the distance on detected objects, we should limit the raycast distance.
  • Another technique that might help a bit but maybe not important to many is using the lesser complex colliders, to refrain the engine from doing complex calculations on ray hit.

Optimizing Collider:

Colliders play an important role in game development, they are used to detect interaction between different gameObjects, but colliders do play an important role in the performance of our games.
Below are some important things to do to optimize performance colliders
  • Avoid moving static Colliders, as it may cause overhead on CPUs, now when we talk about moving colliders, its not only about colliders marked static, but unity considers colliders without Rigidbody static, so incase moving them will also have CPU overhead. We should be very careful in such cases.
  • Avoid using complex mesh colliders, as they will have a good amount of CPU overhead when being interacted in game, like during collision or during raycast process, when unity will apply intersection algorithm upon the collider, the execution time or cpu time of the algorithm will increase due to complexity of the collider. Instead in such cases, we could use multiple primitive colliders even on children which will be taken by parent or higher object rigidbody.
  • Using Collision Matrix to avoid unnecessary collisions is a good way to optimize game performance. The objects which are not to collide or whose collision create no sense or impact can be processed this way.

Optimizing Rigidbodies:

Rigidbody is a component given by unity to use physics operations on a gameObject, unity uses PhysX Engine 
(https://developer.nvidia.com/gameworks-physx-overview) for its physics operations, its a great feature, but its irregular use can cause performance issues in the game as it impacts CPU and game world calculations stuff, so we should consider some practices of carefulness when using the feature,
  • Too many active rigidbodies in game can cause performance drop, as even idle rigidbody in unity causes physics engine to process physics on it and when there are too many of them, they will eventually cause more of the resource CPU execution time usage. So if there are idle rigibodies in game, we should consider making the rigidbodies to fall asleep, using Rigidbody.Sleep() or mark them kinematic on rigibody objects, this process can be done automatically by setting sleep threshold, 
Physics Performance Optimize Transpo Unity

  • Threhsold is basically kinematic energy of a rigidbody with normalized mass,
Kinematic energy = ½ mv^2, so in this case mass is to be ignored this is how we calculate sleep threshold.
  • Another tip would be as discussed before is to avoid moving static colliders, as it causes unity physics engine to recalculate
Another tip would be to tweak FixedTimeStamp and Maximum Allowed Time value to tweak the time given to process or calculate physics in game, as we decrease or lessen the timestamp it will reduce the time given to physics but may cause some irregular or wierd behaviour so we have to playcaluclate the value inorder to get a value suitable.
Physics Stamping Optimize Unity Performance


Comments