Tags

It’s a common enough feature request: add a loading screen between levels. This isn’t quite as easy as you might expect, but it’s pretty quick once you’re familiar with Unity’s scripting features.

Because Unity allows us to control the way levels are loaded, we can put up a very simple “transition” scene while we wait for a bigger level to load. The transition scene could be just about anything, but here are some ideas:

  • Completely empty, except for an OnGUI hook showing some text.
  • An orthographic camera staring directly at a textured quad.

If you have Unity Pro, you could even take advantage of async level loading to create a quick animation or mini-game; otherwise, I’d recommend you stick with something that renders one frame (think “splash screen”).

Point is, the particular contents of the transition scene don’t matter very much. This tutorial just gets you as far as having one.

Creating a transition scene

We’re going to need a transition scene. I strongly recommend keeping it simple, so that it can load quickly and won’t slow down the more expensive target scene load.

  • Create and save an empty scene, name it “Loading”.
  • Add the scene to your build settings (File > Build settings…).
  • Populate the scene with some simple stuff.

As I mentioned above, I’m a fan of the ortho camera staring into one or more textured quads. You could whip up some scripts to show a semi-randomized texture, show some helpful tips, or whatever you want.

For the purpose of this tutorial, we’ll get up and running with the simplest possible thing:

public class LoadUI : MonoBehaviour {
    public Color backgroundColor = Color.black;
    public Color textColor = Color.blue;
    public string message = "Loading...";

    void Start() {
        Camera.main.backgroundColor = backgroundColor;
    }

    void OnGUI() {
        //cache and update GUI settings
        Color cachedColor = GUI.contentColor;
        GUI.contentColor = textColor;

        //draw label
        float width = 60f;
        float height = 20f;
        float left = Screen.width / 2 - width;
        float top = Screen.height / 2 - height;
        Rect rect = new Rect(left, top, width, height);
        GUI.Label (rect, message);

        //restore GUI settings
        GUI.contentColor = cachedColor;
    }
}

Attach that script to something in your scene and press play. You should see the loading message. Nothing else will happen (yet).

Creating a script hook

Most people switch levels by calling Application.LoadLevel. Since we want to control the process, we need to provide a single hook for other scripts to call. We’ll make a new class called LevelManager that’s just simple enough to keep working:

public class LevelManager {
    public static void Load(string name) {
        Application.LoadLevel(name);
    }
}

This is just a wrapper, but it gives you a single access point: all other level switching should be done by calling LevelManager.Load, so that you can swap it out later without breaking the project.

Here’s a very simple test script:

public class LevelManagerTest : MonoBehaviour {
    void Start() {
        Invoke("TestLoad", 2f);
    }

    void TestLoad() {
        LevelManager.Load("SomeOtherLevel");
    }
}

The above will wait two seconds before calling LevelManager.Load. You should probably replace "SomeOtherLevel" with the name of an actual level that exists in your project. Or, if you’re in a hurry, use Application.loadedLevelName to reload the current level.

As an aside, remember that Application.LoadLevel can only load levels which are configured in your build settings.

A single-frame loading screen

This next step will get us to render one frame in the transition scene, then load the target scene. While performing a blocking load, Unity displays the last rendered frame; in our case, this is an advantage.

We’re going to start mixing static and instance members of the LevelManager class. If you don’t understand the difference, you might want to look it up.

We’re also going to use a coroutine. They enable us to create asynchronous functions which can execute across multiple frames.

Let’s flesh out the LevelManager class a bit more:

public class LevelManager : MonoBehaviour {
    public static void Load(string name) {
        GameObject go = new GameObject("LevelManager");
        LevelManager instance = go.AddComponent<LevelManager>();
        instance.StartCoroutine(instance.InnerLoad(name));
    }

    IEnumerator InnerLoad(string name) {
        //load transition scene
        Object.DontDestroyOnLoad(this.gameObject);
        Application.LoadLevel("Loading");

        //wait one frame (for rendering, etc.)
        yield return null;

        //load the target scene
        Application.LoadLevel(name);
        Destroy(this.gameObject);
    }
}

Reminder: unless you have access to Pro’s async loading, the transition level will freeze during the load. Keep it simple.

Try your LevelManagerTest script again. You should notice that your loading scene gets used!

Advertisements