Skip to content

Scenes

How to load, unload, and switch between scenes from your own C# code while the game is running. For the editor side of scenes — the Scene Hierarchy panel, opening scenes, turning things on and off — see Scenes.

Every scene your script can load must be in the project's build scene list (Project Settings → Scenes). A scene that exists on disk but isn't in that list can't be loaded by name or index at runtime.

Scenes

The static class you call to change what's loaded. Every method below takes effect at the end of the current frame — it's always safe to call from OnUpdate, even while you're in the middle of looping over entities.

Scenes.Load(string name)

Replaces every currently loaded scene with the one named name — the normal "go to the next level" call. name is the scene's file name without the folder or the .jscene extension ("Level2" for Scenes/Level2.jscene), and is case-sensitive.

If the name doesn't match anything in the build scene list, or the file can't be loaded, this logs an error and does nothing — your current scene keeps running. You'll never end up with a black screen or a crash from a typo'd scene name.

csharp
Scenes.Load("Level2");

Scenes.Load(int index)

Same as above, but by position in the build scene list instead of by name — index counts only the enabled entries in that list, in the order they're listed. Handy for a level-select screen where you already have a number.

csharp
Scenes.Load(currentLevelIndex + 1);

Scenes.LoadAsync(string name)

Same as Scenes.Load(string), but starts loading in the background and returns immediately instead of waiting — use this for a loading screen, so the game keeps responding while a big scene loads. Returns a SceneLoadOperation you check on later frames. See that section below for how to use it.

Scenes.LoadAdditive(string name)

Loads name on top of whatever's already loaded, instead of replacing it. Use this for a HUD, a pause menu, or anything else that should exist alongside your level rather than being part of it.

The newly loaded scene becomes the Active scene (see Scenes.SetActive below) unless you set it back afterward — so if you're loading something like a HUD that shouldn't become the target for new entities, call Scenes.SetActive right after to switch back.

csharp
Scenes.LoadAdditive("HUD");

One camera at a time

If the additively-loaded scene has its own camera, which camera actually renders the frame is undefined — only one scene's camera drives rendering at a time. A HUD or manager scene with no camera of its own (the normal case) isn't affected by this.

Scenes.LoadAdditive(int index)

Same as Scenes.LoadAdditive(string), indexed against the build scene list — see Scenes.Load(int) above.

Scenes.LoadAdditiveAsync(string name)

The background-loading version of Scenes.LoadAdditive — see Scenes.LoadAsync above.

Scenes.Unload(string name)

Unloads one loaded scene by name, leaving every other loaded scene running. If name was the Active scene, another loaded scene automatically becomes Active — there's always exactly one. Joystick won't let you unload the only loaded scene (it logs an error and does nothing instead), since there always has to be something loaded.

csharp
Scenes.Unload("HUD");

Scenes.Reload()

Reloads whatever scene is currently Active from its saved file — "restart this level exactly as it was." Does nothing if the Active scene was never saved to a file.

csharp
Scenes.Reload();

Scenes.SetActive(string name)

Switches which already-loaded scene is Active — it doesn't load anything. name must already appear in Scenes.Loaded (below). New entities you create, and anything you spawn without specifying a scene, land in whichever scene is Active.

csharp
Scenes.LoadAdditive("HUD");
Scenes.SetActive("Level2");   // keep spawning into the level, not the HUD

Scenes.Active

Read-only. The Active scene's name. Empty outside of Play.

csharp
Log.Info($"Now playing: {Scenes.Active}");

Scenes.Loaded

Read-only. Every currently loaded scene's name, in the order each one was loaded. Useful for checking "is the HUD already up?" before loading it again.

csharp
if (System.Array.IndexOf(Scenes.Loaded, "HUD") < 0)
    Scenes.LoadAdditive("HUD");

SceneLoadOperation

The handle Scenes.LoadAsync/Scenes.LoadAdditiveAsync hand back. Check its properties from your own OnUpdate — there's no "call me when it's done" event, so polling is how you find out.

MemberTypeMeaning
Progressfloat0 to 1. Jumps to 0.9 the instant the scene has finished loading in the background, then holds there until AllowActivation is true, then reaches 1.0 once the scene actually goes live.
IsDoneboolTrue once the scene has gone live, or the load has failed.
FailedboolTrue if the file was missing or corrupt. Your current scenes kept running the whole time regardless.
AllowActivationboolDefaults to true. Set to false to hold the new scene at 90% loaded — everything ready, nothing shown yet — until you're ready to reveal it (behind a fade, say).
csharp
SceneLoadOperation op = Scenes.LoadAsync("Level3");
op.AllowActivation = false;   // hold until the loading screen says so

// later, once your own loading UI is ready to hand off:
op.AllowActivation = true;

SceneTransition

Fades the screen out, swaps the scene, and fades back in — a nicer alternative to Scenes.Load popping straight to the new scene with no warning.

SceneTransition only handles the timing; it doesn't draw anything itself. Alpha/Color below are what a UI script reads to actually paint the fade overlay on screen.

SceneTransition.Cut(string sceneName)

An instant swap with no fade — the same as calling Scenes.Load(sceneName) directly.

SceneTransition.Fade(string sceneName, float duration = 0.5f, Vector4? color = null)

Fades to color (opaque black by default) over the first half of duration seconds — loading sceneName in the background the whole time — then fades back in over the second half once the new scene is ready. If the load fails, it fades back in on whatever was already running rather than getting stuck on a black screen.

csharp
SceneTransition.Fade("Level2", duration: 1.0f);

SceneTransition.CrossFade(string sceneName, float duration = 0.5f)

Currently behaves the same as Fade above (a true simultaneous blend between two scenes isn't built yet) — kept as its own name so existing calls don't need to change once it is.

SceneTransition.IsTransitioning, Alpha, Color

Read-only. IsTransitioning is true while a fade is in progress. Alpha is 0 (scene fully visible) to 1 (fully covered). Color is the fade color currently in use. Joystick doesn't draw the fade overlay for you — read these from your own script and apply them to whatever you're using to cover the screen, for example a full-screen sprite entity's color:

csharp
protected override void OnUpdate(float ts)
{
    var overlay = GetComponent<SpriteRendererComponent>();
    Vector4 c = SceneTransition.Color;
    c.W = SceneTransition.Alpha;   // alpha channel
    overlay.Color = c;
}

Session

Loading and unloading scenes is only half the picture — most games also need to carry a score, a checkpoint, or a flag across that scene change. That's what Session is for: a simple key/value store that survives a Scenes.Load. See its own page for the full reference; the worked example below uses it alongside Scenes and SceneTransition.

Example: level progression with a HUD

Putting the pieces together — a level that tracks a score in Session, loads a shared HUD additively, and fades to the next level when the player reaches the goal.

csharp
using JoystickEngine;

public class LevelManager : GameEntity
{
    protected override void OnCreate()
    {
        // Make sure the HUD is up, without stealing focus from this level.
        if (System.Array.IndexOf(Scenes.Loaded, "HUD") < 0)
        {
            string thisLevel = Scenes.Active;   // read BEFORE LoadAdditive changes it
            Scenes.LoadAdditive("HUD");         // LoadAdditive makes "HUD" the new Active scene...
            Scenes.SetActive(thisLevel);        // ...so switch back to the level explicitly
        }

        // Start (or continue) the score for this run.
        if (!Session.Has("score"))
            Session.Set("score", 0);
    }

    public void AddScore(int amount)
    {
        Session.Set("score", Session.Get("score", 0) + amount);
    }

    public void OnPlayerReachedGoal()
    {
        // Fade out, load the next level, fade back in.
        SceneTransition.Fade("Level2", duration: 0.75f);
    }
}

Because Session isn't tied to any one scene, Level2 can read the same "score" key back with Session.Get("score", 0) and keep counting from where the player left off.

A regular Load/Fade replaces every loaded scene, HUD included

Scenes.Load (and SceneTransition.Fade, which uses it) don't just swap out the level — they replace everything currently loaded, so the additively-loaded HUD gets unloaded along with Level1 when Level2 comes in. That's exactly why LevelManager.OnCreate above checks Scenes.Loaded and re-adds the HUD if it's missing: the same script runs again in Level2 (assuming you've put a LevelManager entity in every level) and puts the HUD right back. If you'd rather the HUD survive scene changes untouched instead of being reloaded each time, put a Persist() call on one of its entities instead of loading it fresh per level — see Session vs. GameEntity.Persist() for the tradeoff.

See also

Two other API Reference topics come up often alongside scene management:

  • Session — the full reference for the key/value store used in the example above; the recommended way to carry data across a Scenes.Load.
  • Physics2D — raycasts and overlap queries, the kind of check that often decides when to call Scenes.Load/SceneTransition.Fade (a goal trigger, a ground check gating a level-end cutscene).