In this blog post, I dive into the development of a dynamic multiplayer lobby system using Photon (PUN2) in Unity game engine.
PUN is a popular framework for developing multiplayer games in Unity, offering features like matchmaking, room management, and synchronisation of game objects across the network.
Introduction:
I started by importing the Photon PUN2 FREE package from the Unity Asset Store. This package provided all the necessary tools for networking in Unity, which I needed for implementing the multiplayer lobby system.
Configuring Networking Settings:
To establish communication between clients and the server, I configured the networking settings in the project. I set up an App ID provided by Photon and ensured that the project was connected to the Photon cloud.
Designing the Lobby and Room Interface:
After setting up the project, I designed the user interface (UI) for the lobby system. I included elements such as input fields for room names, buttons for creating/joining rooms, and lists to display available rooms and players. I made sure the UI was intuitive and user-friendly.
![]() |
Fig 1. UI demo of the Lobby and the Room interface |
Technical Implementation:
Upon the start of the game, after the authentication process as discussed in earlier blog post, initialisation of the lobby system is done using the following script.
🟢 Initialization snippet
🟨 C#
🟦 Unity3D
public class InitializeGame : MonoBehaviour {
public TMP_Text playerName;
PlayerDataSer playerData;
void Start() {
if (!CheckForAuthenticatedPlayer()) {
LobbyManager.Disconnect();
ManageAccount();
return;
}
LobbyManager.Init(playerData);
}
}
If the player is authenticated, it proceeds to initialise the lobby system by calling LobbyManager.Init(playerData), passing in the player's data.
This `Init` method, belonging to the LobbyManager class, is responsible for initialising the Photon Unity Networking (PUN) framework and setting up the player's connection to the multiplayer environment. Let's delve into the details of this method:
🟢 Lobby Init snippet
🟨 C#
🟦 Unity3D
public static void Init(PlayerDataSer playerData) {
if (PhotonNetwork.IsConnectedAndReady) {
if (!PhotonNetwork.InRoom) {
PhotonNetwork.JoinLobby();
}
return;
}
PhotonNetwork.NickName = playerData.username;
PhotonNetwork.EnableCloseConnection = true;
PhotonNetwork.AutomaticallySyncScene = true;
PhotonNetwork.ConnectUsingSettings();
}
Upon successful connection to the master server, a photon callback `onConnectedToMaster` is called, in which I've handled showing/hiding of required UI elements and Joining Lobby. Because the players should be in a Lobby to create/join a room, or even to view a list of created room, I've joined a default lobby right after the connection to the master.
🟢 Connection established
🟨 C#
🟦 Unity3D
public override void OnConnectedToMaster() {
createButton.interactable = true;
playButton.interactable = false;
roomPanel.SetActive(false);
lobbyPanel.SetActive(true);
if (connectionPanel) connectionPanel.SetActive(false);
PhotonNetwork.JoinLobby();
}
After the player gets joined into a lobby, a photon callback is fired with the list of available room information. The `OnRoomListUpdate(List<RoomInfo> roomList)` callback gives the list of RoomInfo for available rooms.
Iterating through the list, UI element for each of the room has been created.
🟢 Room update
🟨 C#
🟦 Unity3D
public override void OnRoomListUpdate(List<RoomInfo> roomList) {
UpdateRoomList(roomList);
}
void UpdateRoomList(List<RoomInfo> roomList) {
if (Time.time < nextUpdateTime) return;
nextUpdateTime = Time.time + timeBetweenRoomListUpdates;
foreach (RoomListItem roomListItem in roomListItems) {
Destroy(roomListItem.gameObject);
}
roomListItems.Clear();
foreach (RoomInfo roomInfo in roomList) {
if (roomInfo.RemovedFromList) {
continue;
}
RoomListItem roomListItem =
Instantiate(roomListItemPrefab, roomListItemHolder.transform);
roomListItem.SetName(roomInfo.Name);
roomListItem.SetRoomInfo(roomInfo);
roomListItems.Add(roomListItem);
}
}
roomListItem.SetName(roomInfo.Name);: It sets the name of the room on the room list UI element.
roomListItem.SetRoomInfo(roomInfo);: It sets additional room information on the room list UI element.
roomListItems.Add(roomListItem);: It adds the newly created room list UI element to the list of room list items for future reference.
Upon successfully joining a multiplayer room, the script updates the room's custom properties to reflect the increased count of players in the selected game mode ("Team Cat" or "Team Rat"). After joining the room, the UI elements are adjusted accordingly: the button for creating rooms is disabled, the room panel is activated while the lobby panel is deactivated, and various UI text elements are updated to display relevant information such as the room name and the number of players in each team. Additionally, the player list UI is refreshed, and gameplay-related functionality is triggered, likely enabling the play button to initiate gameplay.
🟢 After Joining Room
🟨 C#
🟦 Unity3D
public override void OnJoinedRoom() {
if (!PhotonNetwork.LocalPlayer.IsMasterClient) {
Hashtable roomProperties = PhotonNetwork.CurrentRoom.CustomProperties;
int count = (int)
roomProperties[PhotonNetwork.LocalPlayer.CustomProperties["mode"]];
roomProperties[PhotonNetwork.LocalPlayer.CustomProperties["mode"]] =
count + 1;
PhotonNetwork.CurrentRoom.SetCustomProperties(roomProperties);
}
AfterJoiningRoom();
}
void AfterJoiningRoom() {
createButton.interactable = false;
roomPanel.SetActive(true);
lobbyPanel.SetActive(false);
roomNameLabel.text = "Room: " + PhotonNetwork.CurrentRoom.Name;
teamCatText.text =
"Team Cat (" + PhotonNetwork.CurrentRoom.CustomProperties["Cat"] + "/4)";
teamRatText.text =
"Team Rat (" + PhotonNetwork.CurrentRoom.CustomProperties["Rat"] + "/4)";
UpdatePlayerList();
PlayButton();
}
As highlighted in the above code snippet, the `UpdatePlayerList()` method is responsible to refetch the players in the current room and populate them in the UI list according to their properties (i.e. Add to Cat section list if the player is a cat, and add to Rat section list if the player is a rat). This method is always called when the player joins a room or leaves the room, and also when other players enter or leave the room.
🟢 Updating the players
🟨 C#
🟦 Unity3D
void UpdatePlayerList() {
if (PhotonNetwork.CurrentRoom == null) return;
foreach (PlayerListItem playerListItem in playerListItems) {
Destroy(playerListItem.gameObject);
}
playerListItems.Clear();
foreach (KeyValuePair<int, Player> _player in PhotonNetwork.CurrentRoom
.Players) {
Hashtable playerProps = _player.Value.CustomProperties;
Transform holder = (string) playerProps["mode"] != "Cat"
? playerListItemRatHolder.transform
: playerListItemCatHolder.transform;
PlayerListItem playerListItem = Instantiate(playerListItemPrefab, holder);
playerListItem.SetPlayer(_player.Value);
playerListItems.Add(playerListItem);
}
roomOwnerLabel.text =
"Master: " + PhotonNetwork.CurrentRoom
.GetPlayer(PhotonNetwork.CurrentRoom.MasterClientId)
.NickName;
}
On changing game mode: When the player changes game mode (using the dropdown) from outside the room, or after joining the room, the player list is updated and the property of that player is updated as well to show the updated mode.
🟢 Changing game mode
🟨 C#
🟦 Unity3D
public void OnGameModeChange(TMP_Dropdown teamDropdown) {
if (PhotonNetwork.InRoom) {
Hashtable roomProperties = PhotonNetwork.CurrentRoom.CustomProperties;
string new_mode = teamDropdown.options[teamDropdown.value].text;
if ((int) roomProperties[new_mode] >= 4) return;
string old_mode =
(string) PhotonNetwork.LocalPlayer.CustomProperties["mode"];
int countPrevTeam = ((int) roomProperties[old_mode]);
roomProperties[old_mode] = countPrevTeam - 1;
Hashtable playerProperties = PhotonNetwork.LocalPlayer.CustomProperties;
playerProperties["mode"] = new_mode;
PhotonNetwork.SetPlayerCustomProperties(playerProperties);
int countNewTeam = (int) roomProperties[new_mode];
roomProperties[new_mode] = countNewTeam + 1;
PhotonNetwork.CurrentRoom.SetCustomProperties(
PhotonNetwork.CurrentRoom.CustomProperties);
} else {
string new_mode = teamDropdown.options[teamDropdown.value].text;
Hashtable playerProperties = PhotonNetwork.LocalPlayer.CustomProperties;
playerProperties["mode"] = new_mode;
PhotonNetwork.SetPlayerCustomProperties(playerProperties);
}
}
If the player is in a room:
- It retrieves the current room's custom properties and the selected game mode from the dropdown.
- It checks if the selected game mode already has 4 players. If so, it prevents further changes.
- It updates the player's custom properties to reflect the new game mode selection.
- It adjusts the player count in both the previous and new game modes within the room's custom properties.
- It simply updates the player's custom properties with the new game mode selection.
🟢 Load the game scene
🟨 C#
🟦 Unity3D
public void PlayGame() {
if (!PhotonNetwork.InRoom) return;
if (!PhotonNetwork.IsMasterClient) return;
PhotonNetwork.LoadLevel("GameScene");
}
Comments
Post a Comment