In this article you will learn to create a transition screen between scenes in Unity

There are several ways to do this and if you search on the internet you will find many interesting and cool ways to do it. They all have there benefits and disadvantages.

The way I would prefer a loading* screen to behave is to be independent of any other scene. It should not rely on any components existing in a scene nor should it inject itself into a scene. From my perspective the scenes you want to load should not know about the transitioning scene and neither should the transitioning* scene know about the scenes you want to load.

*transitioning screen and loading screen are used as synonyms in this article

First. Setup the two scenes.

Say you have Scene 1 and Scene 2. On Scene 1 we simply have a button which, when pressed, takes you to Scene 2.

The button sits on Scene 1 and on Scene 1 we have a gameobject with a script attached which is responsible for sending a scene change command to our yet to be created system. Let’s simply call this script Scene1Script.cs

The script needs a reference to the button and adds a listener to it using an anonymous function.

The script won’t work right now because SceneTransitioner doesn’t exist yet. SceneTransitioner is the name I thought we could give our scene transitioning component. More about that soon.

Your Scene 1 should look something like this

Note on the image that we’ve connected the button to the script using the SerializeField attribute.

Now create Scene 2. Scene 2 doesn’t need to contain anything but I’ve decided to add a simple text to make it clear that we’ve arrived at the scene.

Scene 2

Second. The scene transitioning scene.

The system we are about to create works but having its own scene with the content we want to show when transitioning between scenes. This we will call the LoadingScene. So create a new scene and add at least a text element which we will use to display the loading progress.

When you’ve created the scene you should have a Canvas with a child text element. We want to control the opacity of the canvas to animate a fading effect and for that we will use a CanvasGroup component. It is part of Unity so we don’t have to create it ourselves. So on your canvas add a CanvasGroup component and set the options as shown on the image.

Now we will create a LoadingScript which we will add to a new gameobject on our LoadingScene scene. Create a new gameobject and attach a new script called LoadingScript.cs. To begin we will just write code for the dependencies.

As you can see we require a reference to the text element, canvas component and canvasGroup component. We need reference to the text element so we can show how far the scene has loaded. We need access to the canvas component so we can enable/disable it when appropriate. We need the canvasGroup reference so we can adjust the alpha value which we will use to animate. animSpeed we can adjust to adjust the speed of animation.

Attaching the references

Before we continue with LoadingScript we will now look at SceneTransitioner.

The Scene Transitioner

We are not pretty well setup so we can test the scene transitioner which we will create in this section.

The scene transitioner will work as a singleton in the way that it is itself responsible for instantiating itself and there can only ever be one instance of this. Why this way? By doing it this way the object which will use the scene transitioner doesn’t need to know how to instantiate and the scene transitioner object does not need to be instantiated in a preload scene. By doing it this way you aren’t forced to do anything in a specific order for the scene transition to work properly. The scene transitioner is also responsible for loading the LoadScene scene.

The scene transitioner will load the LoadScene scene the first time it is instantiated and it will load it Additively, meaning the the LoadScene scene will exists in the hierarchy alongside your other scene. This is why the LoadScript needs to enable/disable the canvas on the LoadScene scene. Create a script called SceneTransitioner.cs in your proejct but do not add it to any gameobject. See the code below for how to setup the singleton.

Let’s go through the code. It’s a monobehaviour meaning we have access to Awake and that we can add it to a gameobject. In the Awake function we will load scene with index 0 if it is not already loaded. The scene with index 0 will be our LoadScene, we will setup the indexes in just a minute.

When we want to use the scene transitioner we have to use the public Instance property and when we get the instance a few things happens. Firstly we create a new gameobject. We then name the gameobject the same name as the script. Then we add a SceneTransitioner component to that gameobject (this is when the Awake method will be called) and finally we call DontDestroyOnLoad on that gameobject. This means that the scene transitioner object won’t be destroyed when we load a new scene. All of this only happens the first time you try to access the Scene Transitioner.

Because the Scene Transitioner loads the LoadScene scene the first time it is accessed you can not actually get access to the LoadScene because when you call LoadScene you have to wait one frame before the scene is loaded. We have to be aware of this fact a bit later.

Scene indexes

Now let us setup the scene indexes. You do this by going to File->Build Settings.. Here you can simply drag the scenes from the project into the drag’n’drop window.

Loadscene is index 0. Scene 1 is index 1 and Scene 2 is index 2.

The Scene Transitioner is responsible for changing the scene but what should happen between the scene loads is the responsibility of our LoadingScript. We do this to seperate concerns so that you don’t have to change the Scene Transitioner just because you want to add new texts or changing animation behaviour. This means that the LoadingScript will need to know about the loading state. We do this with events and an easy way to create events is by using Actions. We will want 3 Actions. One for loading start, one for loading progress update and one for loading finished. Add these public fields to the SceneTransitioner.cs script

With these events created we obviously need to invoke them but before we complete the SceneTransitioner.cs script let’s go back to the LoadingScript and use these new events.

Setting up events on LoadingScript

As you can see we have added three event listeners which are attached during Start on the LoadingScript gameobject. The first thing we do it we disable the canvas. This is because when the LoadingScene is first created we don’t necessarily want to show anything. Let’s talk about the three events.

OnStartedLoading is raised when Scene Transitioner receives a command to start loading a new scene. This courses a coroutine to start called FadeIn() this method we will create soon and what it does is to enable the canvas and animate the canvasGroup alpha value from 0 to 1.

OnProgressUpdated is raised when Scene Transitioner receives a progress update from the SceneManager which loads the new scene. we then use that value to display the percentage to the user. The number we receive is normalized between 0 and 1 so we multiply by 100 to get the percentage.

OnDone this event is raised when Scene Transitioner is done loading the new scene. We need to know this so we can start fading out. When we’ve faded out we notify the Scene Transitioner that fading has completed and it can finalize the scene load.

Creating the animations

If you have already read my blog post about smooth movement you will already have an idea about how to do this. https://niclasgleesborg.home.blog/2019/05/15/make-a-gameobject-move-smoothly-from-one-point-to-another/

As you can see by the code we use coroutines to create our animation. We use Mathf.SmoothStep to create smooth effect. We use Time.deltaTime to make the animation independent of framerate.

Now let’s go back to the Scene Transitioner script.

You here see the full script.

Let’s break it down a bit. Firstly we have the LoadScene method. When we call StartCoroutine the coroutine is returned and if can use that to check if we are currently loading a scene. We don’t want to be able to call LoadScene if we are already loading a scene so we store the routine variable so we are able to make that check.

Secondly we have the big coroutine LoadSceneAsync. As you can see the first thing we do is check if the LoadScene is already loaded. If it isn’t we simply wait until it is. It might not be loaded because it’s the first time we use the Scene Transitioner instance and it needs frame for the LoadingScene to be loaded.

Because the scenes are so simply we also add some artificial waiting and that’s why we need the time since startup.

We invoke the OnStartedLoading event which the LoadingScript will receive. Then we use the SceneManager to start the SceneLoadAsync operation. Notice we load it additively as we don’t want to destroy the previous scene immediately because we want to animate a transition. On the operation we set allowSceneActivation to false. What this does is that it doesn’t allow the new scene to be activated. Why? Because we don’t want to activate the scene before our animation has finished.

We now enter a while loop. on the while loop report progress to the LoadingScript by getting the progress property on our operation. Because we set allowSceneActivation to false the progress will get stuck at exactly 0.9, this is when we can announce to the LoadingScript that the scene has finished loading. Because it goes so quick here we add an artificial wait of 5 seconds. It’s important to know that the isDone property on the operation is not true before the scene is activated. Therefore this while loop will run until the LoadingScript calls Finish on the Scene Transitioner where we set allowSceneActivation to true.

When everything is done we make sure to set routine and operation to null.

That’s it!

Congratulation. You know have a scene transitioning system which I hope you find some great uses for!