Running Multiple / Parallel Scenes At Once

Hoping to use Stride for a particular project, but it’s not coming easily.

One of the key things I need is the ability to run multiple scenes simultaneously while also having the ability to switch between them instantaneously.

Unless I really missed something, this is not a use-case Stride supports out-of-the-box. I don’t really need editor support for this kind of thing, so that’s fine.

I’m running into 2 separate issues here:

  1. Finding a good way make alterations to something like Stride.Engine.Game without recompiling the engine (e.g. by inheriting Game.cs and accessing protected properties rather than private or internal properties and classes)
  2. I’ve been able to set up a janky prototype that will run multiple scene instances simultaneously. Logic can update, but adding any physics components cause a crash.

The idea is to have several discreetly isolated ‘worlds’. Each would be their own separated scene containing entities, physics sim, audio sim. The user would only be looking at one at a time, but for gameplay reasons, each world must be continuously updated (physics/logic), even while offscreen.

UE4 has something called a UWorld, and Godot has Viewports (which allows for their children nodes to act as their own discreet world). After a bit of poking through the source code, SceneInstance appears to be Stride’s equivalent of a ‘world’.

Stride’s Scene class is simply a list of entities and references for supporting the creation of a scene hierarchy. The SceneInstance class contains a single scene hierarchy and exposes methods for manipulating the scene hierarchy (and since it inherits EntityManager, also exposes the ability to execute the update/process and drawing logic on the entities living in that hierarchy).

SceneSystem is a GameSystem that contains a single SceneInstance, and when it is updated, ‘tells’ the SceneInstance to perform drawing and update/process.

It seemed like what I would want to do is derive my own ‘ParallelSceneSystem’ class from SceneSystem that would maintain/manage a List of SceneInstances to swap out of the main SceneInstance when desired. I started to go down that road, but ran into this roadblock:

Game.cs
...
public InputManager Input { get; internal set; }
public SceneSystem SceneSystem { get; }

In my project I’m deriving a custom class (‘MyGame’) from the engine’s Game class. The Game has properties for all the major game systems (including SceneSystem). Trouble is the SceneSystem property is private set – in effect, readonly, even for an inherited Game class. So I can’t actually replace the normal SceneSystem with something like ‘ParallelSceneSystem’. I have no idea if that is entirely intentional/by-design, or an over-site. Most of the other systems are similarly readonly, although some (like InputSystem) are internal set. My options then became:

  1. modify Stride’s source to make the property protected set

  2. duplicate the Game class (instead of deriving from) and make it use whatever system I wanted (not a great way to go – Game touches a number of internal classes and properties that mean you are going to need to

  3. ditch my custom ‘ParallelSceneSystem’ and manage the list of SceneInstances directly in MyGame (not a great approach imho – good enough for quick and dirty prototype)

I went with the last option just so I could determine if this idea would work.

public class MyGame : Game  (removed ui/scene selection code for brevity)
...

List<SceneInstance> instances = new List<SceneInstance>();
protected override Task LoadContent()
{
    SceneSystem.SceneInstance.Name = "main";
    instances.Add(
        new SceneInstance(this.Services,
        Content.Load<Scene>("Scene01")) { Name = "Scene01" });
    instances.Add(
        new SceneInstance(this.Services,
        Content.Load<Scene>("Scene02")) { Name = "Scene02" });
    return base.LoadContent();
}
protected override void Update(GameTime gameTime)
{
    base.Update(gameTime);

    // manually update instances not loaded to the SceneSystem
    foreach (var instance in instances)
    {
        // prevent update from firing twice on the scene living in SceneSystem
        {
            if (instance == SceneSystem.SceneInstance)
                continue;
            if (SceneSystem.SceneInstance.RootScene.Children
                        .Contains(instance.RootScene))
                continue;
        }
        instance.Update(gameTime);
    }
}
private void AttachScene(int sceneIndex)
{
    // recapture the current scene to prepare for loading a different scene
    var currentScene = 
        SceneSystem.SceneInstance.RootScene.Children.FirstOrDefault();
    if(currentScene != null)
    {
        SceneCollection sc = 
            SceneSystem.SceneInstance.RootScene.Children as SceneCollection;
        sc.Remove(currentScene);
        SceneInstance originalInstance = 
            instances.Where(x => x.Name == currentScene.Name).FirstOrDefault();
        originalInstance.RootScene = currentScene;
    }

    // get the desired scene and put it in place
    var instance = instances[sceneIndex];
    var scene = instance.RootScene;
    instance.RootScene = null;
    SceneSystem.SceneInstance.RootScene.Children.Add(scene);
}

Originally I was trying to completely swap out the SceneInstance in the SceneSystem. But I quickly ran into issues with the graphics compositor and cameras assignments and slots. I might revisit that since I want to do some complex compositing with cameras living in different ‘worlds’ down the line.

At the moment I have a main scene with a camera (and controller) and I’m attaching the scene I want as a child. Nothing remarkable here. Dynamically adding/removing children scenes at runtime is a noted feature of Stride.

The only unusual thing I’m really doing here is taking the scenes that are loaded in memory, packing them into SceneInstances, and then manually updating those SceneInstances on every update call. Admittedly it feels a bit kludgy. Since a scene can only belong to a single SceneInstance, I have to be careful when swapping scenes. I take the current scene, detach it, and put it back in the list of instances, then grab the scene I want, and detach it from it’s enclosing instance, and reattach it to the SceneSystem’s instance.

This partly works. You can have n-number of visually discreet scenes, and the entities living in each scene will be updated, meaning the scripted logic on entities runs. But the moment you add a physics component to an entity, when that scene is attached Stride crashes. No error message.

It’s entirely possible I missed something, or I’m barking up the wrong tree. Hoping someone might have some insight. I’ve been poking around in Stride’s source code and can’t quite figure out how physics components get their updates, and where this crash is happening.

The take away is that at present, to support this use case requires modification of the engine’s source.

That’s an interesting scenario and is not supported OOB. Let me try to give you some input.

First of all, I can see that SceneSystem is not really referenced anywhere (except Game and ScriptComponent but they only get a reference from Services) except for when PhysicsProcessor initializes the scene where to put debug physics objects. This means you should be able to create additional SceneSystem instances and just add them into the Game.GameSystems collection - although I’m not sure how it will react wrt multiple ProfilingKeys started in the Profiler with the same name.

But suppose your way of updating SceneInstances is good enough. Let’s look at the physics. When you add an entity with a PhysicsComponent to your SceneInstance it’s going to create a new instance of the PhysicsProcessor - in its OnSystemAdd it creates a new Simulation by calling Bullet2PhysicsSystem.Create() and each simulation is updated separately.

This should work, but for you Stride crashes - have you run this with a debugger to observe any (potentially uncommon) exceptions? Would you be willing to share a minimal repro of your code to see what goes wrong?

A lot of spots in the engine are internal only which had its reasons at the time, but seems to make some things harder to override - it may change in the future, but will require additional documentation.

I’ve added my code to Github: