Skip to main content

Lobby

UILobbyPlayer

Within a lobby, each row entry represents a player. Player objects have the UILobbyPlayer component attached to them and are spawned by the server, then parented to that player list. Each player can only change their own options.

PlayerLobby010

For visual representation, the UILobbyPlayer component makes use of all UI controls that have been added to the UILobbyPlayer prefab. For more information on what controls are referenced, please see the Scripting Reference. The entry that can be changed by the local player is highlighted with an additional border element.

For each UI control, the UILobbyPlayer script contains a NetworkVariable that sychronizes the modified selection to other players automatically. Therefore, each control's OnValueChanged callback invokes a method that sends an RPC to the server with the new UI element value. Meaning, there is a 1-1-1 relationship between control-NetworkVariable-RPC method.

Data Persistence

Variable values like the player name, color and class would usually only exist in this Lobby scene, where the UILobbyPlayer prefab is instantiated. However, we also want them to be available in the map scenes: for example when displaying the Stats window for each player with their name, but also for checking their selected class when placing new towers. For this reason, we somehow need to persist these references and values from the Lobby to the map scenes.

As mentioned in the GameManager, we already know how to manage currency and scores in networked lists that can hold a built-in data type, like an integer. However, for persisting a combined set of data for each player, we would want to synchronize multiple values of the UILobbyPlayer class. Since we cannot send class references or collections over the network (List< UILobbyPlayer > or NetworkList< UILobbyPlayer > does not work), we convert each UILobbyPlayer reference to a struct: the PlayerStruct.

It has to be a struct that implements INetworkSerializable and its corresponding methods to be accepted into a RPC method. In the PlayerStruct, located at the bottom of the UILobbyPlayer class, we take over the variables we are interested in. Note that string cannot be used as a type, since it has a variable length. Instead we have to use a fixed length type like FixedString32Bytes, for example for the player name.

Before starting a match, we let the server get all current UILobbyPlayer references in the lobby and saves them temporarily in a List< PlayerStruct > in the UILobby script. This list is then sent as an array to all clients in a RPC, including the server, to persist it in the NetworkManagerCustom component (which lives until the map scene). The reason we do this just before starting the match is because we want to persist only players who actually participate in the match, not some who joined the lobby and left again.

info

Maybe you are wondering, why are we not using a NetworkList< PlayerStruct> that would be synchronized to clients automatically? It would definitely work and this is what you could do in another case - but not here. Since NetworkList synchronization needs a NetworkObject component on the same game object that contains it, it is not possible to add it to the NetworkManagerCustom component because of the underlying NetworkManager interface preventing it. Therefore, when using a NetworkList on the NetworkManagerCustom, it would not be synchronized at all. However the NetworkManagerCustom component is the only script that is not destroyed when switching scenes between the Lobby and Map, so in order to be able to use it, we are going that RPC route.