Game Manager
Component
The GameManager
component is the central managing script for the actual game logic in a match. It should exist in each game scene.
In its inspector, you are able to define highly important game aspects, such as:
- the number of teams in the game
- the color they should be visualized in
- the spawn area for each team
- the maximum score count for the match
- the respawn delay
- whether friendly fire should be active
Please see the Scripting Reference for a full description for each variable.
Additionally, for the Offline network mode this game object also has the BotSpawner
script attached, defining the type and count of bots (distributed across teams) to be spawned in this map.
Hidden from the public inspector variables, the GameManager
also takes care of the score count and team fill for each team, stored in separate networked lists with their initial size equal to the team size. If a player died (meaning a team scored), the respective value in the score list for that team goes up. The same happens for the fill list when a new player connected, but here it can also decrease on player disconnects. The last part is fully handled by the NetworkManagerCustom
script, as it is aware of all player connection states. What the GameManager
now does is to reflect these value changes to UIGame
by telling it to update the labels and sliders responsible for showing current scores and team fill to the end user.
When a client connection to a game scene has been made, the host (via NetworkManagerCustom
) finally assigns a team to the newly created player object. The logic behind this is also contained in the GameManager
script. In simple words: it iterates over all existing teams, tries to find the team with the lowest count of team members and assigns the new player to that team.
The GameManager
component is also the main access point for checking whether the game has ended (in case the maximum score limit has been reached), and because it has a reference to UIGame
, it toggles displaying the player death and game over windows too. With the player respawn logic showing a delay timer or video ad in-between player deaths, that's basically everything needed for handling the game logic in one place. You can find more details on the Ads integration within the respawn workflow over here.
Room Data
As mentioned above, the GameManager
manages the teams including their scores in networked lists. We call this Room Data, since these values are associated to the state of a match. We already talked about networked variables in a previous chapter, but not about networked lists - here they come! The implementation is very different depending on whether you are using Netcode or Photon, so read on below.
- Netcode
- Photon
For storing multiple values in one variable, as done in arrays and lists, Netcode offers NetworkVariables
in containers, called a NetworkList
. For syncing an array of values across the network, here we make use of NetworkLists
. First, you define the variable and list type, then subscribe to network changes, and finally tell your game what do to when an updated value was received. Just like an event callback, basically.
//list definition
public NetworkList<int> score;
//subscribe to updates, added to *OnNetworkSpawn()* method
score.OnListChanged += ui.OnTeamScoreChanged;
//at some point the server changes a value
score[teamIndex] += 1;
//server and clients receive the update and use it accordingly
public void OnTeamScoreChanged(NetworkListEvent<int> changeEvent)
{
teamScore[changeEvent.Index].text = changeEvent.Value.ToString();
}
We already used Player Properties for syncing individual properties on players in the past. Now, Photon offers another type of networked variables that are not bound to a specific player, but to the match (room) as a whole! This feature is called Room Properties. Like with Player Properties, Room Properties are synced as soon as possible and is this matter they are perfect for storing scores and team fill of the match as done here.
Similar to Player Properties, for Room properties and quick access, all custom room property definitions and accessors are contained in its own RoomExtensions
class added to the NetworkManagerCustom
script at the bottom, which offers static methods for each important property you can call from everywhere. Internally, it uses the SetCustomProperties() method on a Photon Room
object to manipulate values in its networked key-value Hashtable.
Whenever a Room
property changes, Photon notifies all clients within OnRoomPropertiesUpdate() and you then have to define what do to when the updated values were received. Just like an event callback, basically.
//method definition for increasing a team's score used by the server
public static int[] AddScore(this Room room, int teamIndex, int value)
{
...
room.SetCustomProperties(new Hashtable() {{score, scores}});
}
//server and clients receive the update and use it accordingly
public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
OnTeamScoreChanged(PhotonNetwork.CurrentRoom.GetScore());
}
//assign score value to UI
public void OnTeamScoreChanged(int[] score);
{
...
teamScore[i].text = score[i].ToString();
}