Common Types and Errors in Unity

In this section, we will focus on the most common types of errors and bugs that may occur when coding in C # and Unity.

When coding with Unity, you will undoubtably come across errors, and it is part of the learning process; I have listed below some of the most common types of errors and bugs, along with how they manifest, and how they can be fixed. This way, you will find it easier to identify your errors, understand what they mean, and ultimately be able to fix errors and avoid them on the long run.

Null Reference Exception

  • Meaning of this error: Null reference exceptions typically manifest as runtime errors in the Console window, indicating that a variable or object being accessed is null.
  • How to fix it: To fix null reference exceptions, you need to ensure that variables are properly initialized before use and to perform null checks where necessary.
  • Preventive Actions: Initialize variables when declared, use null checks before accessing object properties or methods, and follow best practices for object instantiation and management.
  • Example:

//Before
GameObject player;

void Start()

{

    player.transform.position = Vector3.zero; // Error: player is null

}

// After:

GameObject player;

void Start()

{

    if (player != null)

        player.transform.position = Vector3.zero;

}

In the previous code, while using the player variable may trigger an error, we correct this code by checking that the player variable is not null before using it.

Logic Error

  • Meaning of this error: Logic errors result in unexpected behaviors during gameplay, such as incorrect AI behavior or game mechanics (e;g., an NPC that keeps walking into walls).
  • How to Fix it: To fix  logic errors, you need to review and correct the logic in the code to match the desired behavior.
  • Preventive Actions: Break down complex logic into smaller, testable parts, use comments to clarify intentions, and perform thorough testing.
  • Example:
// Before:

int health = 100;

void Update()

{

    if (health = 0) // Error: Assignment instead of comparison

    {

        Debug.Log("Player defeated!");

    }

}

// After:

int health = 100;

void Update()

{

    if (health == 0) // Comparison corrected

    {

        Debug.Log("Player defeated!");

    }

}

In the previous code, we make the mistake of using = for a comparison instead of ==; this is corrected in the second version.

Array Index Out of Bounds

  • Meaning of this error: Array index out of bounds errors occur when trying to access an array element at an invalid index, leading to runtime exceptions. Typically, we may want to access an index in an array that is beyond its size.
  • How to Fix it: Ensure that array indices are within bounds before accessing them.
  • Preventive Actions: Always check the length of arrays before accessing elements, validate user input, and use data structures that automatically handle bounds checking where possible.
  • Example:

// Before:

int[] numbers = new int[3];

void Start()

{

    int value = numbers[3]; // Error: Index out of bounds

}

// After:

int[] numbers = new int[3];

void Start()

{

    if (numbers.Length > 3) // Check array length before accessing

    {

        int value = numbers[3];

    }

}

In the previous code, we try to access the element at the index 3 for the array numbers; however, because this array has a size of 3, its elements would be at the indexes 0, 1, and 2, but not 3. Therefore, an error will be generated when accessing the 4th element at the index 3. To fix this issue, we check that the array has more than 3 elements (or at least 4) before trying to access the 4th element (at the index 3).

Infinite Loops

  • Meaning of this error/concept: Infinite loops cause the programme (or game) to become unresponsive because the condition to exit the loop is never true; this causes the programme to loop indefinitely and required players to force quit the game.
  • How to Fix it: Review loops and ensure there’s a condition for termination.
  • Preventive Actions: Double-check loop conditions, use break statements when necessary to exit loops, and perform thorough testing.
  • Example:
// Before:

void Update()

{

    while (true) // Error: Infinite loop

    {

        // Code that never exits loop

    }

}

// After:

void Update()

{

    int counter = 0;

    while (counter < 100) // Terminate condition added

    {

        // Code that eventually exits loop

        counter++;

    }

}

In the previous code, we initially create an infinite loop as the condition to exit the loop is always false; to fix this issue, we modify our loop to include an exit condition that we know will be reached at some stage (the value of the variable counter will reach 100 as it is increased by one every time).

Performance Bottlenecks

  • Impact of this bug: Performance bottlenecks result in decreased frame rate, stuttering, or lag during gameplay.
  • How to Fix it: Optimize code by reducing unnecessary calculations and use efficient resource management techniques.
  • Preventive Actions: Profile code regularly to identify performance bottlenecks, optimize critical sections, and follow best practices for performance-conscious development.
  • Example: (Continued below)
// Before:

void Update()

{

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

    {

        Instantiate(prefab, transform.position, Quaternion.identity); // High instantiation frequency

    }

}

// After:

void Start()

{

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

    {

        GameObject obj = Instantiate(prefab, transform.position, Quaternion.identity); // Instantiate once

        obj.SetActive(false); // Deactivate objects if needed

    }

}

In the previous code we instantiate a prefab 10000 times; however, this could be resource intensive; to fix this issue, we deactivate some the objects created if they are not used.

Memory Leaks:

  • Impact of this bug: Memory leaks result in a gradual increase in memory usage over time, potentially causing performance degradation or crashes.
  • How to Fix it: Identify and release unused resources or objects when they’re no longer needed.
  • Preventive Actions: Use Unity’s built-in memory profiler to detect leaks, ensure proper cleanup of resources, and follow best practices for memory management.
  • Example:
// Before:

List<GameObject> objects = new List<GameObject>();

void Update()

{

    GameObject obj = Instantiate(prefab);

    objects.Add(obj);

}

// After:

List<GameObject> objects = new List<GameObject>();

void Update()

{

    GameObject obj = Instantiate(prefab);

    objects.Add(obj);

    Destroy(obj); // Ensure to destroy objects when no longer needed

}

In the previous code, we create objects as the game progresses; however, this can start to use significant memory; so, to solve this issue, we make sure that we destroy the objects that are no longer needed.

Floating-Point Precision Errors

  • Impact of this bug: Floating-point precision errors result in inaccurate calculations or unexpected results due to limited precision of floating-point numbers.
  • How to Fix it: Use appropriate rounding techniques and avoid relying on precise equality checks for floating-point numbers.
  • Preventive Actions: Be aware of floating-point precision limitations, use Mathf.Approximately() for comparisons, and perform thorough testing with various input values.
  • Example:
// Before:

float result = 0.1f + 0.2f;

Debug.Log(result); // Might not print expected result due to floating-point precision

// After:

float result = Mathf.Approximately(0.3f, 0.1f + 0.2f);

Debug.Log(result); // Use Mathf.Approximately for floating-point comparisons

In the previous code, because comparing floating numbers using the = sign may be often inaccurate, we, instead, compare two float values (0.3f and 0.2f + 0.1f) by using the function Mathf.Approximately.

Concurrency Issues

  • Meaning and Impact of this bug: Concurrency issues lead to unexpected behaviors or data corruption when multiple processes or threads access shared resources concurrently (i.e., at the same time).
  • How to Fix it: Use synchronization mechanisms such as locks or mutexes to control access to shared resources.
  • Preventive Actions: Design code with concurrency in mind, avoid shared mutable state (where two or more entities have access to the same data) where possible, and use thread-safe data structures (i.e., structures that can function correctly and predictably when accessed by multiple threads concurrently).
  • Example:
// Before:

int counter = 0;

void Update()

{

    counter++;

    Debug.Log("Counter: " + counter);

}

// After:

int counter = 0;

void Update()

{

    // Use Unity's synchronization mechanism for thread-safe access

    Interlocked.Increment(ref counter);

    Debug.Log("Counter: " + counter);

}

In the previous code:

  • Before the change, we simply increment the counter using counter++, which is not thread-safe.
  • After the change: the code uses Interlocked.Increment to increment the counter in a thread-safe manner.
  • This way, using Interlocked.Increment, we ensure that even if multiple threads are accessing and modifying the variable counter, each increment operation will be atomic, meaning it will complete without interference from other threads. This is crucial in a multi-threaded environment to maintain data consistency and prevent race conditions.

Resource Loading Failures

  • Meaning and Impact of this bug: Resource loading failures occur when the game fails to load assets or resources, resulting in missing textures, models, or audio files.
  • How to Fix it: Verify file paths and ensure assets are correctly imported into the project.
  • Preventive Actions: Organize project assets properly, use Unity’s asset management system, and double-check file paths when loading resources.
  • Example: 
// Before:

AudioClip clip = Resources.Load<AudioClip>("SoundEffect"); // Error: Incorrect resource path

// After:

AudioClip clip = Resources.Load<AudioClip>("Audio/SoundEffect"); // Provide correct resource path

In the previous code, we correct the file path so that access to resources is granted.

Related Articles: