Isometric Question Mark

Why your game works in the Unity editor (but not the build)

In Unity Editor by John FrenchUpdated Leave a Comment

When you start building a game in Unity then, at first, you’ll probably do most of your testing in the editor as you work on it.

And why not?

After all, that’s what Play Mode is for, right?

It’s quick, it’s easy and it lets you see if your game is actually working how it’s supposed to.

But, when you make a Build of your project for the first time, you might find that you have a problem that wasn’t there before.

What’s worse, you might find that it’s not just in the standalone player.

Sometimes, simply opening the same project, using the same version of Unity, but on a different computer can cause an error in one project that doesn’t happen in the other.

Which can be incredibly frustrating, since trying to find an error that’s difficult to recreate, and that only happens some of the time, can be very difficult to do.

But, while there are a lot of issues that cause a problem or an error in your game, when it happens only some of the time, and especially when it happens in a build, or a different version of the same project, there’s usually one common cause.

So what is it?

And how can you fix it?

The most common cause of unexplained errors in Unity

It’s important to remember that there are many, many different reasons for errors in a Unity project and, by no means, does this article cover all of them.

However, that’s not necessarily a problem, since a lot of errors can be solved pretty easily by reading the error log, doing some basic debugging or, if it’s not related to the logic in your game, simply restarting the editor.

And, in most cases, that’s where you should start.

But, when you find an error that just doesn’t seem to make any sense, something that should work, but doesn’t, or something that was working before, but isn’t now, in my experience, it usually ends up being the same thing every single time…

Timing.

Or, in other words, when one thing happens before something else, but it shouldn’t do.

I’ll explain.

The logic in your game, meaning everything that actually happens, will usually be triggered using one of only a couple of methods.

For example, it’ll either be triggered in an Event Function, such as Update, Start, or a physics interaction, like On Collision Enter, or it will be triggered by Player Input, which typically happens in Update if you’re using the old Input Manager, or just before it if you’re using the new Input System.

These functions are called, by Unity, on every Monobehaviour script at specific times during the lifecycle of an object and in a particular order during a frame.

For example, Awake is always called before Start, Update is always called before Late Update, and Fixed Update, when it is called, is always called before Update.

Exactly when each function is called can be checked using the execution order section in the Unity Documentation.

However, while the order of different event functions is guaranteed, the order in which each script instance will have theirs triggered is not.

Which can be a problem.

This is because Unity, generally, does things one at a time, on a single thread.

Meaning that, even though all of the Update functions will be called on all of the objects in the scene during the same part of the frame’s execution order, two functions cannot be called at the same time.

One must always be called before the other, and it must be finished before moving on to the next.

The order in which each script will have its event functions called is undetermined, meaning that it’s, basically, random, and that some scripts will have their event functions called before others.

However, even though the order isn’t guaranteed, it typically doesn’t change, meaning that, in the editor, in your first version of the project, the script execution order is likely to be the same each time you run the game.

But, when you build the project or if you move it to another computer, you might find that the objects in your scene are now processed in a different order, causing an error that wasn’t there before.

But what kind of problems can this cause?

Let’s say, for example, that you want to place an object in a simulation game and you’re using two scripts to make that work.

You might use one script on a Moveable Object that constantly checks to see if it can be put down.

Like this:

public class MoveableObject : MonoBehaviour
{
    public bool canPlace;

    void Update()
    {
        canPlace = CheckIfCanPlace();
    }

    bool CheckIfCanPlace()
    {
        // Check if clear
        return true;
    }
}

While in the script that moves the object, you might check for a mouse button click and try to put down the held object when a click is received.

Like this:

public class ObjectMover : MonoBehaviour
{
    MoveableObject heldObject;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            PutDownObject();
        }
    }

    void PutDownObject()
    {
        if (heldObject.canPlace)
        {
            // Put down object
        }
    }
}

This basic example is relatively straightforward, but the main issue with it is that it assumes that the object will always check to see if it can be placed before the mover actually tries to put it down.

Which, in the editor, it might do.

But, when you build the game, or try it on another computer, you might find that the mover script has its update function called first now.

Why is this a problem?

In this case, it means that it would be possible for the object mover to put the object down before it’s had a chance to check if it can be placed in that position or not.

This means that, if you move the object off of a valid position to an invalid one and immediately try to place it, you’ll be able to put down, even though it would correctly report that it can’t be placed there later in the frame.

This happens because the scripts are dependent on each other’s update functions.

But, one must always be called before the other which, in a build, or on a different system, might not be the same as when you were working in the editor.

And, while it may seem that this particular problem would be a rare issue that’s difficult to cause, this did actually happen in one of my early projects.

It was difficult to reproduce, it only happened in the build and, at the time, I had no idea what was causing it.

What’s worse, while this specific example causes a problem, but not necessarily an error, the more likely result of this kind of timing difference is Null Reference Errors that are caused by trying to access objects, assets and scripts that, either, haven’t been set yet, or no longer exist.

So what can you do about it?

How to fix execution order problems in Unity

Generally speaking, a surprisingly large number of errors are caused by the order in which things happen, and not necessarily the things that happen themselves.

But, helpfully, problems like this can be easy to avoid and, even if they are causing an issue in your project, can also be relatively straightforward to fix.

So how do you avoid execution order problems?

Avoid Update for most logic

One of the most important things you can do to avoid execution order errors and issues is to avoid using Update for everything.

When you first start using Unity, you might end up putting a lot of logic in Update which, generally speaking, is fine.

What can cause a problem, however, is when one update function assumes that another has already been run.

A general approach is to assume that the order of all other event functions cannot be guaranteed.

Because they can’t, not really.

Which means that, if you have two update functions, and one of them is supposed to happen before the other one, you have to assume that it may not be, meaning that it may be a frame behind.

In the object movement example earlier in this article, this creates a problem where an object may briefly report that it can be put down when really it can’t.

So how do you avoid a problem like that?

One solution is to change how the Checking Function is triggered.

Instead of running it in its own update function, the object mover could, instead, call the checking function on the moveable object when it wants to try to put it down.

Like this:

void PutDownObject()
{
    if (heldObject.CheckIfCanPlace())
    {
        // Put down object
    }
}

This means that now, not only is the function not being constantly called for no reason, but the check is made immediately before the code that relies on it.

And, because the function is being controlled from just one Update call, from the Object Mover script, it means that it doesn’t rely on another update function in a different script, avoiding the error.

As a result, it can help to avoid using Update for logic that is already tied to other events anyway.

In this example, there’s no point in using Update because the check only needs to happen when something else does.

And, typically, you’ll be able to execute the majority of codependent logic in your game in a similar way, by connecting it to a single trigger, such as player input, or another event in your game, making it easier to control the order of logic in your project.

But this might not be possible for everything in your game, so when is it ok to use Update?

As a general rule of thumb, to avoid execution order problems between event functions, try to only use Update if the function is completely passive, meaning that it doesn’t rely on other logic and other logic doesn’t rely on it, and needs to be called every frame, such as input checks (when using the old input manager) or anything that needs to happen constantly.

However, while you can’t rely on the order of the same kinds of event messages, there are functions that are designed to be called in a set order, such as Start and Awake for example.

Use Start and Awake to avoid Null Reference errors

Start and Awake are initialisation functions and they’re generally used for setting up an object when it’s first loaded or the first time it’s enabled.

On the surface, they appear to do the same thing and, in a way, they kind of do, except that one is always called before the other.

One reason for this is so that you can split initialisation into two separate stages:

  • First, use Awake for Internal references,
  • then use Start for External references.

What this means is that a script should usually set its own, local references first, in Awake.

An example of this might be a script getting a reference to a component on its own object, or setting an instance of a Singleton.

Then, if the script needs to do something that may rely on other Awake functions having already been called, such as access a singleton reference, it should do it in Start.

Just like when placing logic in Update, this is to avoid the possibility that, in one version of your project, a different Awake function will be called first, causing errors, meaning that you shouldn’t rely on any of the same type of event messages being called before or after one another.

That is, unless you force them to by changing the Script Execution Order.

Use a custom Script Execution Order

It’s possible to change a script’s execution order by using the Script Execution Order settings menu.

This involves giving a particular class a priority value, that is either higher or lower than the default time allowing it to be called earlier or later than it normally would.

The Script Execution Order settings menu allows you to modify when a script is processed relative to other scripts.

The numeric value is a relative priority. Lower values will cause a script to be executed before its default time, while higher values will force it to be processed later.

The values only affect their relative order, meaning that there’s no difference between a value of -1 and -100, unless other scripts exist with values between that range.

It also doesn’t affect the general execution order, meaning that you cannot force Late Update to be called before Update, for example, no matter how low you set the priority value.

Instead, it simply means that, if you add a script to the execution order and give it a negative value then its Awake, Start and Update functions will be called before other Awake, Start and Update functions, essentially controlling the order that scripts are processed in.

While you would typically want to use the script execution order sparingly, it can be useful for the few special scripts that absolutely must be processed first or last, such as input-based scripts, for example.

Now it’s your turn

Now I want to hear from you.

What frustrating errors have you found in your project when building it?

Were they related to the timing between your scripts? Or something else?

And what have you learned about avoiding errors in Unity, that you know someone else would find useful?

Whatever it is, let me know by leaving a comment.

John French profile picture

by John French

Game audio professional and a keen amateur developer.

Get Game Development Tips, Straight to Your inbox

Get helpful tips & tricks and master game development basics the easy way, with deep-dive tutorials and guides.

How this content was created

This article was written using first-hand research and experience, without the use of AI. For more information on how Game Dev Beginner articles are written, see my writing policy.

Leave a Comment