Game Setup - Pt 2: Lobby to Game
Published 4 years ago
1.4 K
How to setup a game with multiple players
In the last post we covered how to setup a multiplayer game with customisation. We have all players in the lobby setup with their data. In this post we will cover the following:
  • Determining when a scene has loaded with all the components we need.
  • Transferring data from lobby players to game players.
  • Setting up all players in the game scene.
  • Clients send message to server when they have fully setup.


NetworkLobbyPlayer uses OnLevelWasLoaded in conjuction with theNetworkLobbyManager to determine that the player has either entered the lobby or entered the game. Our focus is on the latter.
I was noticing an issue when I was using the NetworkStartPosition that comes with UNet. The issue was sometimes a player object would spawn at the center of the world and not the spawn positions that were setup. After digging through the source code and running some tests it was apparent that the code that created the game player was running before theNetworkStartPositions had registered themselves with the NetworkManager. Even though the NetworkStartPositions register themselves in Awake the call toOnLevelWasLoaded was executed before that. The code within NetworkLobbyPlayer that tells the server that the scene was loaded was running before the scene was ready to be used.
There are certain objects within the scene that also need to be loaded and setup in our game before the SendSceneLoadedMessage funciton should be run. That will be different from game to game but I created a solution that I think we will work for most cases.
All objects that are required to be setup before running the code for the new scene either implement the ISceneDependency interface or extend the SceneDependencyBehaviourclass. Additionally a SceneDependencyManager must exist in the loaded scene already. The manager keeps a reference to all ISceneDependency objects and will runOnSceneWasLoaded when all dependencies have reported that they are ready. Full source code is available here on Github.
Here is our version of the NetworkStartPosition which I've calledNetworkSpawnPosition which extends the SceneDependencyBehaviour.
using System; using UnityEngine; using UnityEngine.Networking; [DisallowMultipleComponent] public class NetworkSpawnPosition : SceneDependencyBehaviour { public void Awake() { NetworkManager.RegisterStartPosition(transform); SetDependencyReady(); } public void OnDestroy() { NetworkManager.UnRegisterStartPosition(transform); } }


In part 1 we covered getting the player data for everyone in the lobby and now we are ready to create the game player. The next challenge is passing the data between the lobby player and the game player. UNET has a simple method the is called with both the lobby and game player objects allowing us to setup players for the game easily. However this is only called on the server so any setup the client needs to do has to be handled another way. A simple way to handle this is to create a SyncVar on the lobby player and copy that value into a SyncVar on the game player.
public override bool OnLobbyServerSceneLoadedForPlayer(GameObject lobbyPlayerObject, GameObject gamePlayerObject) { LobbyPlayer lobbyPlayer = lobbyPlayerObject.GetComponent(); Player gamePlayer = gamePlayerObject.GetComponent(); // set the SyncVar gamePlayer.m_PlayerNumber = lobbyPlayer.m_PlayerNumber; // setup for the server gamePlayer.SetPlayerDef( lobbyPlayer.playerDef ); return true; }


Now when the game player calls OnStartClient the client can take the shared identifier (we use m_PlayerNumber) and find the matching lobby player. With that they have access to all the data we syncronized in part 1 and the players will have the correct name and color for every player.
A benefit of this system is that any time a new game player needs to be made the data needed for their setup doesn't need to be sent again. Players can return to the lobby, change the map and play again without having to transmit some potentially sizeable data. UNet has a bandwidth limit of 4kbp/s before it will disconnect players but the limit for the first 2 minutes is 40kbp/s to allow for any expensive setup.


It's important that the server starts the game when all the connected players have fully setup their instances of the game. We know that the scene is setup correctly as we did this earlier and we know that was can setup each player in each game instance correctly but theres some more we need to setup before the game can begin. Let's consider just one instance of the game for ease of understanding.
When you are setting up your local instance of the game the instructions won't always arrive from the server in the same order. When you first start with developing games over the network this can be one of the toughest realities to wrap you're head around. The server can send instructions to create Player A, B and C but your game could process them in any order - remote players could be created in your game before you are.
Here is a snippet of code from our NetworkLobbyManager that our Player class invokes methods on. This code will only run on clients and is called from Player.OnStartClientand Player.OnStartLocalPlayer.
private List m_ClientPlayerObjects = new List(); private bool m_HasSentGameSceneReady = false; private bool ShouldSendGameSceneReady { get { return AllPlayersReady && // are all the clients created + ready? Player.LocalPlayer != null && // and we exist as a local player m_HasSentGameSceneReady == false; // and we haven't already sent the message } } private bool AllPlayersReady { get { return m_ClientPlayerObjects.Count == numberOfPlayers; } } // called from Player.OnStartClient public void OnCreatedClientPlayerObject( Player clientPlayer ) { m_ClientPlayerObjects.Add( clientPlayer ); if( ShouldSendGameSceneReady ) { SendLocalPlayerGameSceneReady(); } } // called from Player.OnStartLocalPlayer public void OnCreatedLocalPlayerObject( Player localPlayer ) { if( ShouldSendGameSceneReady ) { SendLocalPlayerGameSceneReady(); } } private void SendLocalPlayerGameSceneReady() { m_HasSentGameSceneReady = true; // send your game scene ready message to the server here }
The server will do a similar thing and keep a record of all clients that have send their 'game scene ready' message, then once all players are ready the server will send the message telling all players to start their games.
This concludes the game setup with lobby and player customisation. Using the HLAPI for UNET is a great way to get started in networking with Unity and I hope these posts could help you understand a possible way to get started with a real world game example.
Game Design Director - Executive