Navigate Between Scenes Using ClientServerBootstrap

Code and workflows to navigate between NavigationScene and MainScene using UI buttons

What will be developed on this page

Navigating between scenes triggered by UI Toolkit elements and creating client/server worlds appropriately

We will adding logic to navigate between NavigationScene and MainScene and properly handle creating and destroying ECS worlds.

Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Navigating-Between-Scenes

We have 3 views that could take us to MainScene:

  • HostGameScreen

  • JoinGameScreen

  • ManualConnectScreen

Asteroid NavigationScene view flow diagram

Rather than have separate logic in each of their associated custom Visual Elements (cVEs), we are going to create a new script that handles all transitions from NavigationScene to MainScene, named "ClientServerLauncher." We will pull in references to each of these views and will add callbacks to the "Host Game" and "Join Game" buttons that will trigger the transition. Like most software engineering, this is an opiniated decision for where to put the "brains" for transition. We have gotten feedback that we maybe we have abstracted too much, and made it more confusing which is a fair critique. We wanted to show all the different ways elements can interact in this UI section.

Later on in the next Multiplayer section we will add additional logic to ClientServerLauncher so that we can select which IP address we connect to as a client.

In this page we will implement ClientServerBootstrap to prevent triggering Client/Server world creation until we are ready to transition to MainScene and click one of our transition buttons.

Why? Well right now in the NavigationScene DOTS NetCode bootstrap does its thing and creates server/client worlds. We want to create a build where a user can "host" (which means be both client and server) or "join" (which means they are just a client. So we cannot make the decisions whether to build a server world in the NavigationScene, that decision should be made in MainScene (once we know what decision has been made).

Bootstrap

he default bootstrap creates client server Worlds automatically at startup. It populates them with the systems defined in the attributes you have set. This is useful when you are working in the Editor, but in a standalone game, you might want to delay the World creation so you can use the same executable as both a client and server.

To do this, you can create a class that extends ClientServerBootstrap to override the default bootstrap. Implement Initialize and create the default World. To create the client and server worlds manually, call ClientServerBootstrap.CreateClientWorld(defaultWorld, "WorldName"); or ClientServerBootstrap.CreateServerWorld(defaultWorld, "WorldName");.

The following code example shows how to override the default bootstrap to prevent automatic creation of the client server worlds:

From NetCode's Bootstrap documentation

  • Currently, because we have the NetCode package installed, NetCode automatically creates Client/Server worlds when the application starts

    • If you don't have the NetCode package installed, please go visit the previous "DOTS NetCode" section for installation instructions

  • Click play while NavigationScene is open then navigate to the DOTS Windows and see that both Server and Client worlds are created

Client and Server worlds automatically being created in NavigationScene
  • We want to limit the worlds to DefaultWorld while in NavigationScene

    • This is a preemptive measure we are taking because later on, in the Multiplayer section, we won't know whether we want to join a game as just a client or as a client/server, so we want to hold off on creating these NetCode worlds

    • So we will implement NetCodeBootstrap

    • I know it seems like we have repeated ourselves like 3 times but we have gotten feedback that "World creation delay" was a big confusing so we wanted to hammer the point home!

  • Un-click Play and right-click inside the Multiplayer Setup folder, select "Create" to create a new C# script called NetCodeBootstrap

  • Paste the code snippet below into NetCodeBootstrap.cs:

  • Please be patient! We noticed in our testing that it takes a couple tries for Unity to pick up this new bootstrap script ⏳

  • Now hit play in NavigationScene, then navigate to DOTS Windows to take a look at the available worlds

  • Good, NetCodeBootstrap has prevented automatic creation of worlds 👍

    • Only DefaultWorld is available

  • Now let's move onto creating ClientServerLauncher that will manually build (the previously automatically-created) Client and Server worlds

  • Right-click in the NavigationScene Hierarchy and create a new empty GameObject called "ClientServerLauncher"

    • Move it just below "LocalGamesDiscovery" in the scene Hierarchy

    • Add a component to the GameObject that is a new script named "ClientServerLauncher"

      • First create the file by right-clicking in the Multiplayer Setup folder and selecting Create > C# Script

🔑MAJOR KEY ALERT 🔑

It is recommended by Unity to create the Client and Server worlds BEFORE navigating to a scene with converted SubScenes (as opposed to first navigating to MainScene then creating client and server worlds).

When the scene is loaded it automatically triggers loading of all SubScenes it contains into all worlds. If you manually create the worlds you need to do so before you load the scene with your content or they will not stream in any sub-scenes. It would also be possible to manually trigger SubScene streaming - but in this case it would mean you need to manually keep track of all SubScenes.

- From Tim Johansson (NetCode lead)

  • Now let's update ClientServerLauncher

  • Paste the code snippet below into ClientServerLauncher.cs:

  • You'll notice that there are 3 key functions in ClientServerLauncher:

    • ServerLauncher (creates server world)

    • ClientLauncher (creates client world(s) if Thin Clients exist)

    • StartGameScene (triggers loading MainScene)

  • Depending on whether we click "Host Game" or "Join Game" we trigger 2 or 3 of these functions

    • We always trigger StartGameScene and ClientLauncher

    • We launch ServerLauncher only if we are a host

  • Select ClientServerLauncher in the Hierarchy, click Add Component in Inspector and add Client Server Launcher

  • Drag TitleScreenUI GameObject from the Hierarchy onto the "Title UI Document" field under the Client Server Launcher component in Inspector while ClientServerLauncher is still selected in Hierarchy

  • We now need to add our scenes to our build settings so we can navigate to them

    • Navigate to MainScene, go to "Build Settings..." and click "Add Open Scenes"

    • Return to NavigationScene, go to "Build Settings..." and click "Add Open Scenes"

  • Go to Multiplayer > PlayMode Tools > set the Num Thin Clients equal to 1

Updating Title UI Document, build settings, and Num Thin Clients
  • Hit play, and then click Host Game

Navigating to MainScene with the proper amount of ClientWorlds

Similar to how we created client/server worlds to transition to MainScene, we now need to destroy those client/server worlds when we return to NavigationScene. We will also delete all entities created so we can start again in NavigationScene with a "blank slate".

  • Let's update our ClientServerConnectionHandler to be able to take over these new "clean-up" duties

    • We think this is a good place to put "clean-up" functionality because it's part of handling the connection between servers and clients

  • Paste the code snippet below into ClientServerConnectionHandler.cs:

  • Within MainScene, click on ClientServerConnectionHandler in the Hierarchy and drag the GameUI GameObject (also in Hierarchy) onto the "Game UI Document" field under Client Server Connection Handler component in Inspector, save, and navigate back to NavigationScene

Updating ClientServerConnectionHandler and returning to NavigationScene
  • Hit play, host a game, play around, quit, and host again

Navigation between scenes functioning
  • Note that hitting play in MainScene will no longer trigger starting the gameplay

    • This is because client/server worlds are created in NavigationScene

  • Note that hitting "Join Game" will not trigger gameplay because we are not connected to a server

    • This will be updated in the Multiplayer section

Github branch link:

git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/ git checkout 'Navigating-Between-Scenes'

Last updated