Scene Management

Scenes or levels are a common feature of Unity games. They can be loaded from Unity scenes, custom level formats, or even be procedurally generated. In networked games, players should not be able to see entities that are in other scenes. To address this, coherence's scene feature gives you a simple way of controlling what scene you're acting in.

Each Coherence scene is represented by an integer index. You can map this index to your scenes or levels in any way you find appropriate. Projects that don't use scenes will implicitly put all their entities into scene 0.

Keeping a Connection alive between Scene changes

Since the connection to the Replication Server is done through the CoherenceBridge component, it means that if you switch Scenes, the current CoherenceBridge that holds the connection to the Replication Server will be destroyed.

In order to keep a CoherenceBridge with its connection alive between Scene changes, you will have to set it as Main Bridge in the Component inspector:

If this approach to keeping the connection alive is not a good fit for your game, see Custom Setup in the second part of this document.

Automating Scene transitions

In the CoherenceBridge inspector you will find all the options related to handling Scene transitions. First thing to know is that Client Connections must be enabled for this feature to work.

These are the options related to Scene transitions:

  • Main Bridge: This CoherenceBridge instance will be saved as DontDestroyOnLoad and its connection to the Replication Server will be kept alive between Scene changes. All other CoherenceBridge components that are instantiated from this point forward will update the target Scene of the Main Bridge, and destroy themselves afterwards.

  • Use Build Index as Scene Id: Every Scene needs a unique identifier over the network. This option will automate the creation of this ID by using the Scene Build Index (from the Build Settings window).

  • Scene Identifier: If the previous option is unchecked, then you will be able to manually set a Scene Identifier of your own (restricted to unsigned integers).

Using these options will automate Scene transitions.

The only requirement is having a single CoherenceBridge set as Main (the first one that your game will load). The rest of the Scenes you want to network should also have a CoherenceBridge component, but not set as main.

These options require no extra code on your part.

Authority

A client connection and all the entities it has authority over are always kept in the same coherence scene. Clients cannot have authority over entities in other scenes. This implies two things:

  1. When a client changes scene, it will bring along any entities it has authority over.

  2. If an entity changes ownership via authority transfer, it will be moved to the new owner's scene.

Orphans

An entity that does not have an owner is an orphan. Orphaned entities will stay in the scene where their previous owner left them.

Implicit destruction of entities when loading Unity scenes

Note that Unity will destroy all game objects not marked as DontDestroyOnLoad whenever a new Unity scene is loaded (non-additively). If the client has authority over any of those entities at that point, coherence will replicate that destruction to all other clients. If that is undesirable and you need to leave entities behind, make sure that authority has been lost or transferred before loading the new Unity scene. You can of course also mark them as DontDestroyOnLoad, which will bring them along to then new scene.

Since this process involves a bit of logic that has to be executed over several frames, coherence provides a LoadScene helper method (co-routine) on CoherenceSceneManager. Here's an example of how to use it:

private IEnumerator LoadNextScene()
{
    CoherenceSync[] bringAlong = new CoherenceSync[] { sword, torch, rope };
    yield return CoherenceSceneManager.LoadScene(bridge, ++sceneIndex, bringAlong);
}

Limitations

It is not possible to move entities to other scenes without the client connection also moving there. Additionally, you can't currently query for entities in other scenes.

Both of these limitations are planned to be addressed in future versions of coherence.

Custom setup

If your project isn't a good fit for the automatic scene transitioning support described above, it is possible to use a more manual approach. There are a few important things to take care of in such a setup:

If you ever load another Unity scene, the CoherenceBridge that connects to the server needs to be kept alive, or else the client will be disconnected. A straightforward way of doing this is to call Unity's DontDestroyOnLoad method on it. This creates two problems when replicating entities from other Clients:

  1. The bridge instantiates remote entities into the scene where it is currently located. To override this behaviour, set the InstantiationScene property on your CoherenceBridge to the desired scene.

  2. Any new CoherenceSync instances will look for the bridge in the same scene that they are located. If the bridge is moved to the DontDestroyOnLoad scene, this lookup will fail. You can use the static CoherenceSync.BridgeResolve event to solve this problem (see the code sample in the next section). Alternatively, if you have a reference to a Scene, you can register the appropriate bridge for entities in that scene with CoherenceBridgeStore.RegisterBridge before it is loaded.

Additionally, coherence queries (e.g. CoherenceLiveQuery) also look for their bridge in their own scene, so you might have to set its bridgeResolve event too.

If you load levels via your own level format, or by loading Unity scenes additively, it is quite possible that you can skip some of the steps above.

The only thing strictly necessary for coherence scene support is to call

CoherenceBridge.SceneManager.SetClientScene(uint sceneIndex);

so that the Replication Server knows in which scene each Client is located.

Code sample

Here's a complete code sample of how to use all the above things together:

using UnityEngine;
using UnityEngine.SceneManagement;
using Coherence.Toolkit;

public class Example : MonoBehaviour
{
    public CoherenceBridge bridge;
    public CoherenceLiveQuery query;

    void Awake()
    {
        // Move the bridge to DontDestroyOnLoad but still
        // instantiate into the active scene
        var scene = bridge.gameObject.scene;
        DontDestroyOnLoad(bridge);
        bridge.InstantiationScene = scene;

        // Make the query find the bridge
        query.BridgeResolve += _ => bridge;

        // Make new CoherenceSync:s find the bridge
        CoherenceSync.BridgeResolve += _ => bridge;

        // Get notified if the scene is changed
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        // Moves the client connection to another scene on the server
        bridge.SceneManager.SetClientScene(scene.buildIndex);

        // Instantiate remote entities into the new scene instead
        bridge.InstantiationScene = scene;
    }
}