• Register
Post news Report RSS Optimizing Performance in Unity 3D Game Development - Tips & Tricks

Picture this - You've developed a game that you’re excited to share. You’ve fine-tuned the game-play, aced 3D character modeling, confident in it’s story line, and cracked the tough nut of a consistent art style. As you click on the publish button and make it roll-out for a wider audience, your excitement is mixed with nerves. Strangers across the globe start playing your game, and it's an amazing feeling.

Posted by on

Although, as feedback pours in, you apprehensively scroll through the comments. While most of them are positive, there’s common issues you can’t skip: “frame drops”, “long loading times”, “unable to run”. “Lagging”, etc. However, one big challenge with the game was how imperfectly it ran.

So, if you are planning to make your Unity 3D game development run better, here are the key techniques that’ll help your game run faster and yet is stable.

Key Techniques to Maximize Your Unity Game Performance

Here are the practices that can enhance your code quality and optimize your game, contributing towards improved performance exceeding 60 FPS.

Use Profiler


Before you start editing code, refining prefabs and optimizing assets, it's important to identify the root of performance issues. The profiler is a great way to get an in-depth insight of how the game is performing.

Utilize the Profiler, accessible under Window > Profiler. Keep the window visible to monitor categories like CPU usage, GPU usage, Rendering, Physics, and Audio. The top half shows data over time, revealing performance spikes, while the bottom half provides detailed information on the selected profiler and the current frame's data, allowing you to inspect script code and assess the impact of assets on your game's speed.

Always Cache Your Components


A recommended practice is to always cache your variables for optimal performance. Code like

void FixedUpdate()

{

GetComponent<Rigidbody>().AddForce(new Vector3(200f, 300f, 300f));

}

Is expensive for game performance, especially for mobile devices. Rather, than repeatedly accessing components, declare the type of components you require, like:

Rigidbody body;

and then in Awake get a reference to that variable:

void Awake()

{

body = GetComponent<Rigidbody>();

}

Although Start or OnEnable functions can serve this purpose, it’s recommended to use Awake in this example as it's the first initialization function called when the game starts. Afterward, you can efficiently apply forces to your cached Rigidbody variable.

void FixedUpdate()

{

body.AddForce(new Vector3(200f, 300f, 300f));

}

Cache Your Non-Components as Well


It’s a common practice in Unity 3D game development as well as tutorials to create new variables within the Update function, like in the example:

void Update()

{

Vector3 distanceToEnemy = transform.position - enemyPosition;

}

However, during profiling of a mobile game, it was discovered that this approach added 0.02 ms to the code execution. Given the need for all code to execute within 16 ms for smooth performance at 60 FPS, such unoptimized code seems to be a challenge. The solution is to cache the variable before use:

Vector3 distanceToEnemy;

void Update()

{

distanceToEnemy = transform.position - enemyPosition;

}

This caching principle applies to various variable types. For instance, it's better to do:

float distance;

void Update()

{

distance = Vector3.Distance(transform.position, enemyPosition);

}

Than:

void Update()

{

float distance = Vector3.Distance(transform.position, enemyPosition);

}

So, make it a habit to cache variables, especially object types like vectors, components, custom classes, along with basic types like floats, ints, booleans, and strings.

Don’t Use Camera.in


Often, performance issues in Unity 3D game development are—frequently accessing the camera using Camera.main. It slows down your game, especially if used extensively. Connected to the caching principle mentioned earlier, the solution is to cache the camera variable before utilizing it:

Camera mainCam;

private void Awake()

{

mainCam = Camera.main;

}

Use Culing to Limit What is Rendered


Frustum culling, a default feature in Unity, is a tried-and-tested approach for performance improvement.

However, it may still render objects without direct line-of-sight, such as rendering objects behind a closed door. But, this is where Occlusion Culling becomes inevitable. By designating specific parameters in the occlusion culling window, it turns easy to instruct Unity not to render objects that are obstructed while optimizing the rendering process for objects in direct line of sight. Plus, for enhanced control, there are third-party assets like SECTR VIS that offer advanced features to optimize rendering in specific scenarios.

Not to forget, like many of Unity's default features, there are excellent assets available to elevate performance. SECTR VIS (my personal favorite) for enhanced control over rendering in your projects.

Remove Empty Callback Functions


As you comprehend, Awake, Start, and OnEnable functions are triggered when a game object is spawned, while Update and LateUpdate run each frame, and LateUpdate also at a fixed frame rate.

The drawback lies in Unity calling these functions even if they're empty. Leaving them defined in your script, even without code like in the example below, causes Unity to execute them.

private void Awake()

{

}

private void Start()

{

}

private void Update()

{

}

private void FixedUpdate()

{

}

Though it may seem harmless since there's no code to execute, the issue arises when Unity adds defined callbacks to a list for every instantiated game object. This unnecessary invocation wastes CPU power, becoming problematic when, for example, you have empty callback functions in frequently instantiated objects like bullets or collectibles.

In bigger games, populating scenes with numerous game objects having empty callbacks often lead to slow scene load times. A comparison using two classes, one with empty callbacks and one without, demonstrates the impact on performance.

public class NoEmptyCallbacks : MonoBehaviour {}

public class WithEmptyCallbacks : MonoBehaviour { void Start() {} void Update() {} }

Instantiating 1000 copies of each prefab, one with functions and one without, reveals the performance implications. The code snippet below accomplishes this:

[SerializeField] private GameObject withFunctions, noFunctions;

private int spawnNum = 1000;

[SerializeField]

private bool instantiateWithFunctions;

private void Awake()

{

for (int i = 0; i < spawnNum; i++)

{

if(instantiateWithFunctions)

Instantiate(withFunctions);

else

Instantiate(noFunctions);

}

}

Examining the Profiler reveals the impact of instantiating objects with empty callback functions versus those without.

When Raycasting Use Zero Allocation Code


Steer clear of raycast code that involves memory allocation. Instead, choose the non-memory allocation versions of raycast functions:

// instead of

Physics.OverlapBox

// use

Physics.OverlapBoxNonAlloc

// instead of

Physics.OverlapCapsule

// use

Physics.OverlapCapsuleNonAlloc

// instead of

Physics.OverlapSphere

// use

Physics.OverlapSphereNonAlloc

// instead of

Physics.RaycastAll

// use

Physics.RaycastNonAlloc

Bake Your Lights


Understanding lighting can be intricate, but as a general rule, strive to use the minimum number of lights necessary to achieve your desired style.

After all, lights significantly impact a game's performance, especially on lower-end platforms that may struggle with dynamic lighting. Fortunately, in Unity 5.6, there are three light modes:

  • Realtime: Realtime offers excellent visual quality but comes with a performance cost, especially when using dynamic shadows.
  • Baked: Baked lighting is a preferable option whenever feasible. It allows you to illuminate your world while providing performance benefits by avoiding the constant calculation of dynamic light.

Consider the choice of "faking" lighting using emissive maps, where specific areas of a texture appear to emit light. For instance, the dashboard of a plane with multiple small lights could be efficiently represented by using emissive areas on a single texture map. This not only serves the same purpose but is also more performance-friendly compared to creating individual point lights for each element.

Use Efficient Shaders

Delving into the intricacies of optimizing shader code is beyond the scope here, but as known shaders play a crucial role in controlling visual elements in the game, impacting performance because of their intensive calculations. Whether you create shaders or use built-in ones, optimizing them is inescapable.

Considerations for shader use consist of camera-based effects that affect the entire screen, like Global Fog or Fisheye. While they are visually appealing, these effects often come with a performance cost, requiring the need for optimization. So, be careful about stacking too many image effects, as it can slow down your game.

Besides, optimizing performance also involves using mobile-friendly shaders, even on high-end platforms, to reduce computational demands. Assess what is essential for your game and tailor shaders accordingly, especially for mobile development.

A noteworthy performance upgrade in Unity 5.6 is the "Enable Instancing" option, available on Standard, Mobile, and Nature Shaders. Enabling this option in the Advanced Options of the material significantly reduces draw calls for objects sharing the same material. This is specifically beneficial for scenes with numerous instances of objects using identical materials, such as asteroid fields, bullet hole impacts, or trees.

The Takeaway:

Though specific game genres may have unique performance improvement techniques, hopefully this general overview of common performance tweaks provides valuable insights applicable across various game types.

For more tailored advice and assistance in optimizing your game's performance, consider partnering with an entrusted metaverse development company that sheath experienced developers offering personalized solutions to elevate your project. After all, your roaring success in the gaming industry is our shared goal, and we're here to help you achieve it.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: