Posted on

Unity Multiplayer – Code Matchmaking

For this lesson, we will teach you how to create a code matchmaking system for the Photon Pun 2 plugin in Unity. This system allows players to create a room that will give them a room code. They can then share the code with their friend so their friends can then join the same room. Code matchmaking can be found in a number of popular games like Random Dice. Code matchmaking is a good way to play games with friends if your game does not have friends or party mechanics.

CodeMatchmakingLobbyController.cs

using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.UI;
public class CodeMatchmakingLobbyController : MonoBehaviourPunCallbacks
{
    [SerializeField]
    private GameObject lobbyConnectButton;
    [SerializeField]
    private GameObject lobbyPanel;
    [SerializeField]
    private GameObject mainPanel;
    [SerializeField]
    private InputField playerNameInput;
    private string roomName;
    private int roomSize;
    [SerializeField]
    private GameObject CreatePanel;
    [SerializeField]
    private InputField codeDisplay;
    
    [SerializeField]
    private GameObject joinPanel;
    [SerializeField]
    private InputField codeInputField;
    private string joinCode;
    [SerializeField]
    private GameObject joinButton;
    public override void OnConnectedToMaster() //Callback function for when the first connection is established successfully.
    {
        PhotonNetwork.AutomaticallySyncScene = true; //Makes it so whatever scene the master client has loaded is the scene all other clients will load
        lobbyConnectButton.SetActive(true); //activate button for connecting to lobby
        //check for player name saved to player prefs
        if (PlayerPrefs.HasKey("NickName"))
        {
            if (PlayerPrefs.GetString("NickName") == "")
            {
                PhotonNetwork.NickName = "Player " + Random.Range(0, 1000); //random player name when not set
            }
            else
            {
                PhotonNetwork.NickName = PlayerPrefs.GetString("NickName"); //get saved player name
            }
        }
        else
        {
            PhotonNetwork.NickName = "Player " + Random.Range(0, 1000); //random player name when not set
        }
        playerNameInput.text = PhotonNetwork.NickName; //update input field with player name
    }
    public void PlayerNameUpdateInputChanged(string nameInput) //input function for player name. paired to player name input field
    {
        PhotonNetwork.NickName = nameInput;
        PlayerPrefs.SetString("NickName", nameInput);
    }
    public void JoinLobbyOnClick() //Paired to the Delay Start button
    {
        mainPanel.SetActive(false);
        lobbyPanel.SetActive(true);
        PhotonNetwork.JoinLobby(); //First tries to join a lobby
    }
    public void OnRoomSizeInputChanged(string sizeIn) //input function for changing room size. paired to room size input field
    {
        roomSize = int.Parse(sizeIn);
    }
    public void CreateRoomOnClick()
    {
        CreatePanel.SetActive(true);
        Debug.Log("Creating room now");
        RoomOptions roomOps = new RoomOptions() { IsVisible = true, IsOpen = true, MaxPlayers = (byte)roomSize };
        int roomCode = Random.Range(1000, 10000);
        roomName = roomCode.ToString();
        PhotonNetwork.CreateRoom(roomName, roomOps); //attempting to create a new room
        codeDisplay.text = roomName;
    }
    public override void OnCreateRoomFailed(short returnCode, string message) //create room will fail if room already exists
    {
        Debug.Log("Tried to create a new room but failed, there must already be a room with the same name");
        RoomOptions roomOps = new RoomOptions() { IsVisible = true, IsOpen = true, MaxPlayers = (byte)roomSize };
        int roomCode = Random.Range(1000, 10000);
        roomName = roomCode.ToString();
        PhotonNetwork.CreateRoom(roomName, roomOps); //attempting to create a new room
        codeDisplay.text = roomName;
    }
    public void CancelRoomOnClick()
    {
        if(PhotonNetwork.IsMasterClient)
        {
            for(int i = 0; i < PhotonNetwork.PlayerList.Length; i ++)
            {
                PhotonNetwork.CloseConnection(PhotonNetwork.PlayerList[i]);
            }
        }
        PhotonNetwork.LeaveRoom();
        CreatePanel.SetActive(false);
        joinButton.SetActive(true);
    }
    public void OpenJoinPanel()
    {
        joinPanel.SetActive(true);
    }
    public void CodeInput(string code)
    {
        joinCode = code;
    }
    public void JoinRoomOnClick()
    {
        PhotonNetwork.JoinRoom(joinCode);
    }
    public void LeaveRoomOnClick()
    {
        
        if (PhotonNetwork.InRoom)
        {
            PhotonNetwork.LeaveRoom();
        }
    }
    public override void OnLeftRoom()
    {
        joinButton.SetActive(true);
        joinPanel.SetActive(false);
        codeInputField.text = "";
    }
    public void MatchmakingCancelOnClick() //Paired to the cancel button. Used to go back to the main menu
    {
        mainPanel.SetActive(true);
        lobbyPanel.SetActive(false);
        PhotonNetwork.LeaveLobby();
    }
}

CodeMatchmakingRoomController.cs

using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.UI;
public class CodeMatchmakingRoomController : MonoBehaviourPunCallbacks
{
    [SerializeField]
    private GameObject joinButton;
    [SerializeField]
    private Text playerCount;
    [SerializeField]
    private Text playerCount2;
    public override void OnJoinedRoom()//called when the local player joins the room
    {
        joinButton.SetActive(false);
        playerCount.text = PhotonNetwork.PlayerList.Length + " Players" ;
        playerCount2.text = PhotonNetwork.PlayerList.Length + " Players";
    }
    public override void OnPlayerEnteredRoom(Player newPlayer) //called whenever a new player enter the room
    {
        playerCount.text = PhotonNetwork.PlayerList.Length + " Players";
        playerCount2.text = PhotonNetwork.PlayerList.Length + " Players";
    }
    public override void OnPlayerLeftRoom(Player otherPlayer)
    {
        playerCount.text = PhotonNetwork.PlayerList.Length + " Players";
        playerCount2.text = PhotonNetwork.PlayerList.Length + " Players";
    }
    public override void OnLeftRoom()
    {
        playerCount.text =  "0 Players";
        playerCount2.text =  "0 Players";
    }
    public override void OnJoinRoomFailed(short returnCode, string message)
    {
        Debug.Log(message);
    }
    public void StartGameOnClick()
    {
        PhotonNetwork.LoadLevel(1);
    }
 
}
using Photon.Chat;
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PhotonChatManager : MonoBehaviour, IChatClientListener
{
    #region Setup

    [SerializeField] GameObject joinChatButton;
    ChatClient chatClient;
    bool isConnected;
    [SerializeField] string username;

    public void UsernameOnValueChange(string valueIn)
    {
        username = valueIn;
    }

    public void ChatConnectOnClick()
    {
        isConnected = true;
        chatClient = new ChatClient(this);
        //chatClient.ChatRegion = "US";
        chatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new AuthenticationValues(username));
        Debug.Log("Connenting");
    }

    #endregion Setup

    #region General

    [SerializeField] GameObject chatPanel;
    string privateReceiver = "";
    string currentChat;
    [SerializeField] InputField chatField;
    [SerializeField] Text chatDisplay;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (isConnected)
        {
            chatClient.Service();
        }

        if (chatField.text != "" &amp;&amp; Input.GetKey(KeyCode.Return))
        {
            SubmitPublicChatOnClick();
            SubmitPrivateChatOnClick();
        }
    }

    #endregion General

    #region PublicChat

    public void SubmitPublicChatOnClick()
    {
        if (privateReceiver == "")
        {
            chatClient.PublishMessage("RegionChannel", currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    public void TypeChatOnValueChange(string valueIn)
    {
        currentChat = valueIn;
    }

    #endregion PublicChat

    #region PrivateChat

    public void ReceiverOnValueChange(string valueIn)
    {
        privateReceiver = valueIn;
    }

    public void SubmitPrivateChatOnClick()
    {
        if (privateReceiver != "")
        {
            chatClient.SendPrivateMessage(privateReceiver, currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    #endregion PrivateChat

    #region Callbacks

    public void DebugReturn(DebugLevel level, string message)
    {
        //throw new System.NotImplementedException();
    }

    public void OnChatStateChange(ChatState state)
    {
        if(state == ChatState.Uninitialized)
        {
            isConnected = false;
            joinChatButton.SetActive(true);
            chatPanel.SetActive(false);
        }
    }

    public void OnConnected()
    {
        Debug.Log("Connected");
        joinChatButton.SetActive(false);
        chatClient.Subscribe(new string[] { "RegionChannel" });
    }

    public void OnDisconnected()
    {
        isConnected = false;
        joinChatButton.SetActive(true);
        chatPanel.SetActive(false);
    }

    public void OnGetMessages(string channelName, string[] senders, object[] messages)
    {
        string msgs = "";
        for (int i = 0; i &lt; senders.Length; i++)
        {
            msgs = string.Format("{0}: {1}", senders[i], messages[i]);

            chatDisplay.text += "\n" + msgs;

            Debug.Log(msgs);
        }

    }

    public void OnPrivateMessage(string sender, object message, string channelName)
    {
        string msgs = "";

        msgs = string.Format("(Private) {0}: {1}", sender, message);

        chatDisplay.text += "\n " + msgs;

        Debug.Log(msgs);
        
    }

    public void OnStatusUpdate(string user, int status, bool gotMessage, object message)
    {
        throw new System.NotImplementedException();
    }

    public void OnSubscribed(string[] channels, bool[] results)
    {
        chatPanel.SetActive(true);
    }

    public void OnUnsubscribed(string[] channels)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserSubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserUnsubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    #endregion Callbacks
}
Posted on

Unity Multiplayer – Custom Matchmaking

Learn to make a Custom Matchmaking system using the Photon Plugin

For this tutorial on how to use the Photon 2 plugin in Unity, I will be teaching you how to make a custom matchmaking system. This will allow you to connect to lobbies, create rooms, search rooms, and join rooms. This type of matchmaking system can be found in popular games such and Battlefield, PUBG, and Insurgency.

make sure you watch the first video of this series before watching this video. https://youtu.be/02P_mrszvzY

Main Menu UI in Unity

For this lesson, we will begin by showing you how to set up your UI system for your main menu scene. You will have the main menu panel which will have one button for connecting to the Photon lobby. We will then need a Lobby panel which will have two input fields for creating new rooms. We will then need two buttons, one for creating a room and one for returning to the main menu panel. This panel will also need a container to hold all the room listing objects. The last panel we will need for this scene is the room panel. This panel will need two, once for starting the game and loading the multiplayer scene and one for leaving the currently connected room. This panel will also need another container for holding all the player listing objects.

Scripting the Lobby Controller

Next up we will open our scripts need for this custom matchmaking system. For the lobby controller script, we need to enable the lobby connect button once we have connected to the master photon servers. We then need to make a public function for this lobby connect button. in this function, we will connect the player to a photon lobby using PhotonNetwork.JoinLobby(); Once our player has connected to a photon lobby the OnRoomListUpdate callback function will begin sending updates for what rooms are in the lobby. We will use this callback function to instantiate room listing object into our container object. Next, we will create public functions to pair to the different UI object we have for creating new rooms.

Scripting the Room Controller

Next, we will switch over to the room controller script. In this script, we will first use the OnJoinedRoom callback function for updating all the player listings in the container of our room panel. We will also do the same for when other players join and leave the current room. To do this we will use the OnPlayerEnteredRoom and OnPlayerLeftRoom callback functions. The last thing we need to do for this script is creating public functions, one for the start game but and the second for the leave room button. The start game function should load all the players into the multiplayer scene using PhotonNetwork.LoadLevel(); via the master client.

Scripting the Room Button

The last script that we need for this tutorial is the script attached to the room listing prefab. This room button script needs to have a public function for updating the room name and room size. This function is called in the lobby controller when the room listing is instantiated on the room container object. This script also needs to have a public function for the on click on this button object. This function should have the players then join the room of this room listing object via the room name. This is done by using PhotonNetwork.JoinRoom( roomName );

Applying Code to Unity Scene

Back in Unity, we need to attach these script to their respective objects and then initialize all the need variables in the inspector. We will then need to set the OnClick and OnValueChanged functions for any necessary UI objects. This will require you to create the room listing prefab and player listing prefab objects. Once you have done all this you should then have a working custom matchmaking system for your next multiplayer game.

Posted on

Unity Multiplayer – Delay Start

For this lesson, I will teach you how to create a delay start matchmaking system using the Photon 2 plugin in Unity. In the last video, I showed you how to create a quick start matchmaking system which is where the players can click one button that will connect them to a photon room and load them into the multiplayer scene. This is mostly used in games where the players can join an ongoing game. A delay start system is where the players can click one button which will connect them to a photon room but instead of loading the multiplayer scene it will instead load a waiting room scene. Once the minimum players have joined a timer will start and once the timer runs out all the players in the room will be loaded into the multiplayer scene at the exact same time. This is a better system for games that need to be played from beginning to ends such as Rainbow Six Siege, PUBG, and Fortnite.

Menu Scene in Unity

To get started we will first create the menu scene in Unity. This scene needs to have three buttons. one for showing before we have connected to the Photon servers, one for starting the search for a room, and the last for canceling the search. Once you have created the menu scene we will then code two scripts for this scene. One will be the lobby controller and the other will be the room controller.

Coding the Delay Start Matchmaking in C#

In the lobby controller script, we need to automatically sync the scene loading. we will need to create a function to pair to the start button. This function will first try to join an existing room. If it fails to join we will then try to create our own room. If we fail to create a room we will then just keep trying to create a room. We will also need to create a function for our cancel button. In our room controller script, we will need to load the waiting room scene when the player successfully joins a photon room.

After this save your scripts and go back to Unity. Inside Unity and the menu scene, you will need to add these script to the scene. You will then need to set the variables of these scripts and pair the public functions to the different buttons in the scene.

Waiting Room Scene in Unity

The next thing we need to do is create the waiting room scene. This scene needs to have a cancel button, a player count text display, and a timer text display. Once you have created this scene we will then need to create one script for this scene which will be our waiting room controller.

Coding the Countdown and Waiting Room

In this script, we will first update the displays for the player count and the timer. We will then need to update the displays whenever a new player joins the room or when a player leaves the room. We will then need to count down the timer in the update function if there are enough players in the scene. if the timer gets down to zero then we will load the master client into the multiplayer scene which because of automatically syncing the scenes will in turn load all the other players into the same scene.

Once you have all this you can then save all your scripts and go back to Unity. Inside Unity, you need to add this script to your waiting room scene. You will then want to set the variables in the inspector and pair the function to the cancel button.

After this, the last thing you need to have is your multiplayer scene which we created in our quick start tutorial. If you followed along with this tutorial you should now have a working delay start matchmaking system for your multiplayer games.