Our website uses cookies to enhance your browsing experience.
Accept
to the top
>
>
>
GameDev Guardian: static analysis...

GameDev Guardian: static analysis and Unity

Jul 30 2025
Author:

The video game industry is booming, yet it seems like games aren't evolving. Performance issues, bugs, and crashes are just the tip of the iceberg. The most efficient way to solve problems is to identify them in code before they backfire—during the development stage rather than during testing. This article explores how static analysis can help with this.

Introduction

The most important thing I have done as a programmer in recent years is to aggressively pursue static code analysis. Even more valuable than the hundreds of serious bugs I have prevented with it is the change in mindset about the way I view software reliability and code quality.

The Static Code Analysis article by John Carmack

In the realm of game development, it's important to accept that the dream of eliminating all bugs will never come true. However, developers don't give up without a fight—they keep battling issues in the code using various tools. Today, we'll talk about one of the most powerful power-ups for catching bugs in Unity projects.

PVS-Studio analyzer has a long history with Unity, beginning with a simple check of its C# code. Today, the analyzer incorporates entire sets of specialized diagnostic rules and mechanisms tailored specifically for working with Unity. So, let's see what almost 10 years of improving the PVS-Studio integration with the Unity game engine has led to.

If you're wondering, "Why do we even need this static analysis thing? We've got linters and QA testers—isn't that enough?" you might want to check out the article "Why should Unity game developers use static analysis?".

Well, why not start by examining how issues are detected in Unity project code?

How it works

PVS-Studio's integration with Unity is notable for detecting a wide range of issues. This is made possible through several technologies that work in different areas. Let's take a closer look at them.

Static analysis

Note that analyzing Unity projects doesn't always require a radically different approach or new custom mechanisms. Since most of the project is classic C# code, all core analysis technologies (syntactic, semantic, data-flow analysis, etc) work to their fullest potential.

Note. If you'd like to know more about how PVS-Studio works under the hood, I recommend reading the article "How does static analysis work?"

I'd also like to address a common question that arises in discussions about static analysis, "Why bother with syntax trees and whatnot when you can use regular expressions?" Unfortunately, one can't use only regular expressions, and they're rarely used anyway. Most of the code issues that a static analyzer detects require a more complex approach.

To avoid beating around the bush, let's look at an example that illustrates the differences between the use of regular expressions and static analysis technologies.

Look at the V3001 diagnostic rule, which is designed to detect the following incorrect pattern:

if (x > 0 && x > 0)

At first glance, it may seem easy to catch with regular expressions. First, we look for &&, and then check whether the enclosed by parentheses expressions on the left and right sides of the operator are the same.

However, this is where things start to get complicated because the code won't always look this way. It may look like this:

 if (x == x && y)

Okaaay, now we need to consider ==, because the error is still there, even though different expressions enclose &&. We'll also have to consider operation precedence. Let's also keep in mind all the other operators, such as <, >, <=, >=, ==, !=, &&, ||, -, /, &, |, ^, as well as various coding styles... I think you get the point :)

Now, what if we take a look at how this pattern can be detected using a syntax tree?

if (Equal(left, right))
{
  // the analyzer issues a warning
}

That's it :)

After encountering a comparison operator in the tree, we look at its left and right branches. As a result, things like parentheses, operation precedence, and similar are no longer as scary.

This example and others are discussed in more detail in the article "Static analysis and regular expressions."

Method annotation

To better detect issues in code, the analyzer may need additional context. Method annotations provide this context, allowing the tool to verify that the methods are used correctly.

Annotations are one of the analyzer's most important mechanisms. They provide additional information about arguments, return values, specifics, and so on, enabling the analyzer to understand the specifics of annotated Unity methods.

Note. By the way, PVS-Studio annotations are used not only for Unity methods. For example, methods of classes from the System namespace are annotated in the analyzer. In addition to built-in annotations, PVS-Studio users can annotate their code themselves to achieve better, more deep analysis. You can learn more about this topic in the article "Catch vulnerability on your own: user annotations for C# code."

To see a clear example, let's look at one of the popular Unity methods, GetComponent. It's obvious from the name alone that the return value should be used (for the sake of clarity, the documentation also confirms this).

However, sometimes a programmer makes a mistake and forgets to use it, just like in the MixedRealityToolkit-Unity project:

void OnEnable()
{
  GameObject uiManager = GameObject.Find("UIRoot");

  if (uiManager)
  {
    uiManager.GetComponent<UIManager>();
  }
}

The PVS-Studio warning: V3010 The return value of function 'GetComponent' is required to be utilized.

The most frustrating thing about these errors is that they're difficult to notice after they've been made: the IDE probably won't highlight them, the compiler won't issue any warnings, but something will break because of the flawed logic.

Static analysis can detect these errors when it has the necessary context about the analyzed methods, which the annotations provide. These annotations indicate to the analyzer that the return value should be used, so it can detect an anomaly in the example.

You can read more about annotating Unity methods in the article "How the PVS-Studio analyzer began to find even more errors in Unity projects."

Optimization

One feature of the PVS-Studio integration with Unity is the micro-optimization warning group. As the name suggests, these are diagnostic rules aimed at improving project performance.

Note. At the time of the PVS-Studio 7.37 release, micro-optimizations in C# analyzer diagnostic rules are available only for Unity projects. However, they have a more universal use in C++ projects. You can find a full list in the documentation.

Micro-optimization diagnostic rules for Unity are based on recommendations from the official engine documentation, among other sources. The main challenge in developing this category was that many recommendations essentially amounted to one rule, "Don't use heavy constructions." However, such constructions can include variable capture, concatenation, boxing/unboxing, and so on. Sure, these are resource-intensive processes, but they're also common in code. So, providing warnings for every instance of their use would eliminate the incentive to use the analyzer.

To avoid tons of irrelevant warnings, we developed a system that calculates the potential frequency of method execution. Optimization is at risk in some parts of the code. For example, the Unity Update method is executed per frame, so if a programmer adds similar methods to it, the result may be messy. It's not that bad, though—the impact on performance isn't as severe.

But let's take concatenation as an example—in C#, each concatenation creates a new string object. This results in more garbage objects and more garbage collector calls. Now, let's create a few concatenation expressions, run them in a loop for a few hundred iterations, and put the entire thing in the Update method. What can go wrong? Something to think about, as they say...

Note. The V4002 diagnostic rule for Unity detects issues with repeated concatenation.

In real code, the performance issues aren't that obvious. For example, in the Daggerfall project, the V4001 rule points out several cases of boxing when the string.Format method is called:

public static string GetTerrainName(int mapPixelX, int mapPixelY)
{
  return string.Format("DaggerfallTerrain [{0},{1}]",
                       mapPixelX,
                       mapPixelY);
}

Here, the string.Format overload with the string.Format(string, object, object) signature is called. As a result, implicit boxing occurs upon calling the method, which can harm performance. To avoid boxing, just call the ToString method of the mapPixelX and mapPixelY variables.

You can find more details on this example and other specifics of micro-optimizations in the article "PVS-Studio helps optimize Unity Engine projects"

Note. But why use the term "micro-optimization" instead of just "optimization"? The point is that static analysis helps optimize projects in ways that may not be immediately apparent. Although the impact of a single fix may seem insignificant, regular analysis and issue resolution build a cumulative effect that ultimately leads to improved performance and code efficiency. For those curious about optimization via static analysis, I invite you to read the article "Exploring Microoptimizations Using Tizen Code as an Example."

How to start the analysis

Now, let's take a step back from the specifics of Unity integration and learn how to start analyzing a Unity project. You can do this in a few simple steps:

First things first

PVS-Studio must be installed on your computer :)

If you haven't installed it yet, this page will help you do so.

Open your project in Unity

Next, set your preferred script editor in the Unity settings.

To open the Preferences window, go to Edit > Preferences in the Unity Editor menu.

You can do this via the External Script Editor option on the External Tools tab in the Preferences window.

Then, you can open your project in the chosen IDE using the Assets > Open C# Project option in the Unity Editor.

Run the analysis in the IDE

I'm using Visual Studio 2022. In this IDE version, go to Extensions > PVS-Studio > Check Solution to analyze the project.

Done! The code analysis will start, after which you can review the analyzer warnings.

Refer to the documentation for more details on analyzing Unity projects.

Note. If this is your first time using PVS-Studio, the project analysis may yield a lot of warnings for your entire code base.

You don't have to fix everything at once. You can suppress all warnings and use analysis to regularly check only new code. Occasionally, you can return to old warnings.

If you'd like to see PVS-Studio in action but don't want to spend a lot of time setting up and configuring it—which is, by the way, an important step when integrating the tool into the project development process—I recommend trying out the Best Warnings feature. It selects the most "interesting" and relevant warnings in the project that are worth reviewing first.

Learn more about using the Best Warnings mechanism in project analysis in the article "Simple & quick search for bugs in Unity games."

Issues that we can detect

The analyzer has three diagnostic rule groups relevant to the Unity engine:

  • The General Analysis group—classic C# rules;
  • The Unity-specific group—rules aimed at detecting Unity-specific code quality issues;
  • The micro-optimization group—rules designed to detect "weak" code fragments that could be more efficient in terms of optimization.

Now, let's look at examples from each group.

General Analysis

Note that classic diagnostic rules for C# are still relevant for Unity projects.

Example N1

public void RemoveData(Data data)
{
  if (data == null)
  {
    throw new GameFrameworkException(Utility.Text.Format("Data '{0}' is null.", 
                                                         data.Name.ToString()));
  }
  ....
}

The PVS-Studio warning: V3080 Possible null dereference. Consider inspecting 'data'.

Developers check whether the data parameter is valid. If it's null, an exception is thrown. However, when an exception message is generated, the data parameter is accessed, which is null at that moment. As a result, instead of GameFrameworkException we get a NullReferenceException.

This example is from the article "Simple & quick search for bugs in Unity games."

Example 2

override NNInfoInternal GetNearestForce (....) 
{
  ....
  for (int w = 0; w < wmax; w++) {
    if (bestDistance < (w-2)*Math.Max(TileWorldSizeX, TileWorldSizeX)) break;
  }
}

The PVS-Studio warning: V3038 The 'TileWorldSizeX' argument was passed to 'Max' method several times. It is possible that other argument should be passed instead.

The TileWorldSizeX variable is passed to the Math.Max method as the first and second arguments. But this method is supposed to return the larger of the two values. Something went wrong, and developers forgot to change one of the variables. As a result, the same value will always be returned.

Unity-specific diagnostic rules

PVS-Studio analyzer includes a Unity-specific diagnostic rule group. These rules are designed to identify code quality issues in Unity projects and are tailored to the specifics of Unity projects.

You may read about new specialized diagnostic rules in the article "PVS-Studio in development of Unity projects: new specialized diagnostic rules." Let's take a look at some of them:

V3214. Unity Engine. Using Unity API in the background thread may result in an error.

Let's start with a diagnostic rule, which is new not only for our tool but also for Unity, as it's related to the new Awaitable class.

If you'd like to know about other new features in Unity, you can read our overview article "What's New in Unity 6? Overview of release updates and source code issues."

The analyzer has detected that a property, method, or constructor is used after calling Awaitable.BackgroundThreadAsync. It may cause issues such as application hangs or exceptions when executed on a background thread.

The Unity documentation states: all APIs that interact with the engine must execute exclusively on the main thread.

Here's the code example that makes PVS-Studio issue a warning:


private async Awaitable LoadSceneAndDoHeavyComputation()
{
  await Awaitable.BackgroundThreadAsync();
  await SceneManager.LoadSceneAsync("MainScene");
  ....
}

public async Awaitable Update()
{
  if (....)
    await LoadSceneAndDoHeavyComputation(); 

  ....
}

When the LoadSceneAndDoHeavyComputation method is executed, the call to the Awaitable.BackgroundThreadAsync method switches the subsequent code execution within the same method to the background thread.

This may cause issues when the SceneManager.LoadSceneAsync method is called.

V3216. Unity Engine. Checking a field with a specific Unity Engine type for null may not work correctly due to implicit field initialization by the engine.

Here's another diagnostic rule, this time concerning the less obvious properties of the Unity engine.

The analyzer has detected an unreliable null check for a field that can be initialized in the Unity inspector.

Here's the code example that makes PVS-Studio issue a warning:


public class ActivateTrigger: MonoBehaviour
{
  [SerializeField]
  GameObject _target;

  private void DoActivateTrigger()
  {
     var target = _target ?? gameObject;
    ....
  }
}

In this case, if the _target value hasn't yet changed at runtime, the ?? check treats _target as not null, even if the field value has been assigned in the Unity inspector.

You can find more details on why this is the case in the documentation.

Micro-optimizations

As we discussed earlier in the article, in addition to Unity-specific rules, there is a separate group of micro-optimization diagnostic rules. Let's take a look at a real warning:

private void LateUpdate()
{
  ....
  if (ped != null)
    this.FocusPos = ped.transform.position;

  else if (Camera.main != null)
    this.FocusPos = Camera.main.transform.position;
  ....
  float relAngle = Camera.main != null ?
    Camera.main.transform.eulerAngles.y : 0f;
....
}

The PVS-Studio warning: V4005 Expensive operation is performed inside the 'Camera.main' property. Using such property in performance-sensitive context can lead to decreased performance.

The issue is the repeated use of Camera.main, which increases the CPU load.

The correct approach in this case is to create an additional variable to store the return value of the Camera.main property. You can access this variable in the future without creating unnecessary instances.

Conclusion

As the industry rapidly grows, developing games becomes more difficult and expensive. So, developers need to pay attention to tools that boost code quality and streamline the process of detecting errors. PVS-Studio static analyzer is a solid choice for developing a new game or maintaining an existing project.

An announcement. Another article about game engine integration is coming soon, this time focusing on Unreal Engine. And for our Unity enthusiasts, we'll release an article that breaks down the issues in VR games. If you're interested in these topics, we hope to see you on our blog!

You can try out PVS-Studio on your project to experience all the analyzer features with the full trial version.

If you have any ideas for enhancing the analyzer, requests for the articles, or questions, don't hesitate to send them via the feedback form. Last but not least, we'd love to hear your thoughts in the comments :)

Posts: articles

Poll:

Subscribe
and get the e-book
for free!

book terrible tips
Popular related articles


Comments (0)

Next comments next comments
close comment form