OnTriggerEnter, Builds and SceneManagement

A fundamental part of game production is executing code while an object enters a trigger. A trigger is essentially an game object whose collider doesn't actually collide; instead, it sends a signal to the game object entering it.

Making triggers is relatively easy; essentially we just need to give an object a Rigidbody component, tick 'Is Kinematic', and then tick the 'Is Trigger' option on the object's collider component:

Note here that when a collider becomes a trigger, it cannot be 'solid' anymore. An object can either be a solid or a trigger. One of the two objects triggering needs to have Rigidbody for this to function.

OnTriggerEnter() Messages

To run code if an object enters a trigger, we need to use the OnTriggerEnter() message. This is a message that Unity can send to a script when a certain condition is met at runtime. We therefore need to add it to some script. Let's make a 'player' script and add the message:

Importantly, this message can't be declared inside of other messages. This means it should be after your Start() or Update() messages, not inside them:

Note that the message is not inside the braces of Start() or Update(). Unity message's aren't if() statements, they're specific conditions that the game engine itself is tracking.

Let's get specific here; OnTriggerEnter() will run code inside of its braces on the first frame that a collider with this script on enters any trigger.

We also have this variable instanced in the message called other. other is information about the trigger we entered (or specifically, the collider). This means we can use to check which specific trigger we have entered.

Of course, we don't always want to run code when we enter any trigger; instead we want to run specific code for a specific trigger. For example, we definitely want to run different code when we enter a collectible than, say, a hazard. We can do this by checking the 'tag' of an object with an if() statement inside the OnTriggerEnter() message:

Inside our message, we are checking the 'tag' of an object. Then, if the trigger object's tag matches the string inside the if() statement, we can run some code.

Assigning Tags to GameObjects

Tags are a way of defining what a game object does. Game objects cannot have more than one tag.

We define tags in the Unity editor:

As you can see, this cube is 'Untagged'. Simply click the word Untagged to add tags:

You can also define your own tags by selecting Add Tag. However, after you define your own tag, you then have to go back and actually attach it to the object.

Here, I am going to use the tag system to account for two different potential triggers in my code; one for Hazard and one for Goal. One of these is going to restart the level, the other will take me to the next level:

Debug.Log(); is a function we can use to simply print some text in the Unity console log. We typically use this to check if a function or element of code actually triggers correctly, as well as reporting errors.

Using the Scene Manager

Let's imagine a basic ball-rolling game. We might have our player character (ball) roll into different triggers. Some might be a hazard that restarts the level, while others might be a trigger that represents the goal and loads the next level.

If we want to be able to change or restart a level, we need to import the SceneManager namespace.

In the past, we've ignored the first few lines of the scripts we have made. This section is for library and namespace imports, essentially collections of functions and variables that allow our script to run and give us access to certain elements of the Unity engine. The import section typically looks like this:

We are going to add a fourth library import:

We've added using UnityEngine.SceneMangement; which will allow us to access Unity's scene manager class. This class enables us to change or reload level in this script.

Our SceneManager class has a function called 'LoadScene' which requires an argument. Specifically here, we can pass it the index number of the scene we want to load. But what are these numbers referring to?

Build Settings

Games contain a large amount of files in their assets folder. You may have, for example, a number of different scenes (levels) in your assets folder. However, you might not want all these scenes to be included in your 'final' game. For example, you might have a debut scene where you test out certain mechanics. This wouldn't be something you'd want your players to access. Therefore, we need to specify which scenes are in our game 'build', the version of it that is packaged up for players.

We access build settings in Unity by choosing File > Build Settings in the editor:

Build Settings allows us to, amongst other things, attach scenes to the build:

We now have two scenes in our build, each with an index associated with it (e.g. 0, 1 etc), as seen on the right hand side of the Build Settings window.

We could make our script load specific scenes by index, but this might be clunky if we have a game with many scenes in it. Instead, we can create a system where we load by checking our currently loaded scene's index (number) and then either loading that number or the number after it, depending if we have hit a hazard or a goal.

We can load our current level, whichever index it is, using the following code:

We can load the level after it by simply adding a +1 at the end of this code:

Combining this with our trigger logic, we are able to create multi-level games!

Extra: OnTriggerEnter() variations

There are some variations of OnTriggerEnter() that you might want to use:

OnTriggerStay() will run the code for every frame that this object is inside of a trigger. This could be used for, for example, entering an area that slows a character down, or reduces health over time (the dreaded poison swamp).

OnTriggerExit() will run code when this object leaves a trigger. This might be used to assign some variables when a player leaves the 'safe' zone in a multiplayer shooter like Overwatch (2016) for example.

Reference Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Roller : MonoBehaviour
{
    Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.W)) { rb.AddForce(0, 0, 1); }
        if (Input.GetKey(KeyCode.S)) { rb.AddForce(0, 0, -1); }
        if (Input.GetKey(KeyCode.A)) { rb.AddForce(-1, 0, 0); }
        if (Input.GetKey(KeyCode.D)) { rb.AddForce(1, 0, 0); }
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Hazard")){
            Debug.Log("Touched a hazard");
            SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
        }

        if (other.gameObject.CompareTag("Goal")){
            Debug.Log("Touched a goal");
            SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
        }
    }
}

Last updated