Curitor
Level & Flow · Topic Guide

Scene Flow & Level Loading

A lobby scene, a catalog of levels, and one controller that outlives every load. Adding a level to your game is authoring an asset and dropping it in a list — the lobby, the loading screen, the spawn placement and the return path all already know what to do with it.

The shape of a session

There is one lobby scene — Main by default — and any number of level scenes. SceneFlowController lives in the lobby, survives every load, and owns the transitions: play a level locally, host a session, join one, reload the current level, or come back to the lobby.

Loads are Single mode, not additive. The lobby is genuinely unloaded while you play, and coming back re-loads it. That is why persistence across a load is handled explicitly rather than by leaving the lobby lying around underneath.

FieldTypeDefaultWhat it does
mainSceneNamestring"Main"Scene the return path loads. Must match the lobby scene's file name and be registered in Build Settings.
loadingScreenLoadingScreenUIThe screen shown during a transition. Assigned by the installer; there is one in the lobby already.

Levels are assets, listed in a catalog

A LevelDefinitionis one entry in the lobby's level list: a name, an optional thumbnail, the scene, and how many players fit. A LevelCatalog is the ordered list of them the lobby offers.

FieldTypeDefaultWhat it does
displayNamestringName shown in the lobby's level list.
thumbnailSpritePreview image shown left of the name. Optional — leave it empty for a name-only row.
sceneAssetSceneAssetThe level's scene. Must be registered in Build Settings — the Setup Window adds missing entries and never removes yours.
maxPlayersint4Player capacity — DERIVED from the scene's spawn points and auto-synced whenever the level scene is saved. Add spawn point objects to raise it. The host picks a session size up to this.

The catalog loads by name from Resources LevelCatalog— so nothing in the lobby needs a direct reference wired to it, and a level list is a list you edit rather than a scene you re-wire. The same asset also holds the list of folders the catalog scanner watches (kit's own SceneFlow/Levels plus Assets/Levels by default, editable in the Setup Window) — see Level Authoring for adding your own.

Adding a level

  1. Build the scene

    Any scene works. Give it a LevelSpawnPoints object with a child transform per player position — those children are collected automatically, so you can just place empties.
  2. Author the definition

    Create → Party Core Kit → Level Definition. Set the display name, assign the scene asset, add a thumbnail if you have one. Leave maxPlayers alone; saving the scene sets it.
  3. Add it to the catalog

    Append the definition to the LevelCatalogasset's list. Order in the list is order in the lobby.
  4. Check Build Settings

    The scene must be registered. The Setup Window adds missing entries for you and never removes yours, so running Install is the safe way to do it.

Spawn points

LevelSpawnPointsholds the positions players appear at, used round-robin by player index or client id. Leave the array empty and its children are collected automatically, which means "add a spawn point" is "drag an empty into the object".

It is also the source of a level's maxPlayers, so spawn points are not decoration — they are the level's declared capacity. Placing players is handled for you in both shapes: PlaceLocalNetworkPlayer for an online session and PlaceLocalCoopPlayers for split-screen.

Surviving a scene load

Some objects have to outlive the lobby: the scene flow controller itself, the network manager, the save manager. Because loads are Single-mode and returning to the lobby re-loads Main, every persistent root authored there would be re-created on return — stacking a second copy on every trip.

PersistentCore is the guard. It keys instances by an id string, so each persistent root destroys its own later duplicates and nothing else.

FieldTypeDefaultWhat it does
coreIdstring"core"Identity of this persistent root, e.g. 'scene-flow-core'. Duplicates with the same id are destroyed on load. Two different roots need two different ids.

Driving it from code

Everything the lobby buttons do is a public method, so a custom front-end drives the same paths:

using Curitor.PartyCoreKit.SceneFlow;
using UnityEngine;

public class QuickPlay : MonoBehaviour
{
    [SerializeField] private LevelDefinition level;

    private void StartOffline()
    {
        // Single-player or local co-op, depending on the session settings.
        SceneFlowController.Instance.PlayLocal(level);
    }

    private void StartOnline()
    {
        // Returns false when the session could not be started.
        bool hosting = SceneFlowController.Instance
            .StartHostSession("Clem's game", "abc123", level, maxPlayers: 4);
    }

    private void GoHome()
    {
        SceneFlowController.Instance.ReturnToLobby();

        // Anything the flow wanted to tell the player -- "host left",
        // "connection lost" -- is waiting here. Static, and read-and-clear:
        // the notice shows exactly once.
        string notice = SceneFlowController.TakeSessionNotice();
        if (!string.IsNullOrEmpty(notice)) Debug.Log(notice);
    }
}

The rest: JoinSession, LoadLevel, ReloadCurrentLevel (which is how a round starts its next run), SetRoundJoinLock, and IsInLobby.

Multiplayer