This document is about: PUN 1
SWITCH TO

PUN Classic (v1), PUN 2 and Bolt are in maintenance mode. PUN 2 will support Unity 2019 to 2022, but no new features will be added. Of course all your PUN & Bolt projects will continue to work and run with the known performance in the future. For any upcoming or new projects: please switch to Photon Fusion or Quantum.

4 - Game Manager & Levels

This section covers the addition of features to handle the various loading of levels based on the number of players currently playing in the room.

Loading Arena Routine

We've created 4 different rooms and we named them all with the convention that the last character is the number of players, so it's now very easy to bind the current number of players in the room and the related scene.
It's a very effective technique known as "convention over configuration", a "Configuration" based approached would have been, for example, maintaining a lookup table list of the scene name for a given number of players in a room.
Our scripts would have then looked in that list and be returned a scene in which the name doesn't matter at all.
"Configuration" requires more scripting in general, that's why we'll go for "Convention" here which lets us get to working code faster without polluting our code with unrelated features.

  1. Open GameManager script

  2. Let's add a new method within a new region dedicated to private methods we'll create for the occasion. Don't forget to save GameManager script.

    C#

    #region Private Methods
    
    void LoadArena()
    {
        if (!PhotonNetwork.isMasterClient) 
        {
            Debug.LogError("PhotonNetwork : Trying to Load a level but we are not the master Client");
        }
        Debug.Log("PhotonNetwork : Loading Level : " + PhotonNetwork.room.PlayerCount);
        PhotonNetwork.LoadLevel("Room for "+PhotonNetwork.room.playerCount);
    }
    
    #endregion
    
  3. Save GameManager script

When we'll call this method, we are going to load the appropriate room, based on the PlayerCount property of the room we are in.

There are two things to watch out for here, it's very important.

Now that we have our function to load the right level, let's bind this with players connection and disconnections.

Watching Players Connection

Currently, our GameManager Script is a regular MonoBehaviour, we've studied the various ways to get Photon callbacks in the previous part of the tutorial and now the GameManager needs to listen to players connection and disconnection.
Let's implement this.

  1. Open GameManager script

  2. Modify the base class from MonoBehaviour to Photon.PunBehaviour

    C#

    public class GameManager : Photon.PunBehaviour {
    
  3. Let's add the following Photon callbacks messages and save GameManager script

    C#

    #region Photon Messages
    
    public override void OnPhotonPlayerConnected(PhotonPlayer other)
    {
        Debug.Log("OnPhotonPlayerConnected() " + other.NickName); // not seen if you're the player connecting
    
        if (PhotonNetwork.isMasterClient) 
        {
            Debug.Log("OnPhotonPlayerConnected isMasterClient " + PhotonNetwork.isMasterClient); // called before OnPhotonPlayerDisconnected
    
            LoadArena();
        }
    }
    
    public override void OnPhotonPlayerDisconnected(PhotonPlayer other)
    {
        Debug.Log("OnPhotonPlayerDisconnected() " + other.NickName); // seen when other disconnects
    
        if (PhotonNetwork.isMasterClient) 
        {
            Debug.Log("OnPhotonPlayerDisonnected isMasterClient " + PhotonNetwork.isMasterClient); // called before OnPhotonPlayerDisconnected
    
            LoadArena();
        }
    }
    
    #endregion
    
  4. Modify the OnLeftRoom signature as follow, since it is now defined in Photon.PunBehaviour

    C#

            public override void OnLeftRoom()
    
  5. Save GameManager script

Now, we have a complete setup. Every time a player joins or leaves the room, we'll be informed and we'll call the LoadArena() method we've just built above.
However, we'll call LoadArena() ONLY if we are the master using PhotonNetwork.isMasterClient.

Let's now come back to the Lobby to finally be able to load the right scene when joining a room.

Loading Arena from the Lobby

  1. Edit the Script Launcher.

  2. Append the following to the OnJoinedRoom() method

    C#

    // #Critical: We only load if we are the first player, else we rely on  PhotonNetwork.automaticallySyncScene to sync our instance scene.
    if (PhotonNetwork.room.PlayerCount == 1)
    {
        Debug.Log("We load the 'Room for 1' ");
    
        // #Critical
        // Load the Room Level. 
        PhotonNetwork.LoadLevel("Room for 1");
    }
    
  3. Save the Script Launcher.

Let's test this, open the scene Launcher and run it. Click on "Play" and let the system connect and join a room.
That's it, we have now or lobby working. But if you leave the room, you'll notice that when coming back to the lobby, it automatically rejoins... oops, let's address this.

If you don't know why yet, "simply" analyze the logs.
I put simply in quotes because it takes practice and experience to acquire the automatism to overview an issue and know where to look and how to debug it.

Try yourself now and if you are still unable to find the source of the problem, let's do this together.

  1. Run the Launcher Scene
  2. Hit the "Play" button and wait until you've joined the room and that "Room for 1" is loaded
  3. Clear the Unity Console
  4. Hit "Leave Room"
  5. Study the Unity Console, notice that "DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN" is logged
  6. Stop the Launcher Scene
  7. Double Click on the log entry "DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN" the script will be loaded and point to the line of the debug call.
  8. Uhmmm... so, every time we get informed that we are connected, we automatically join a JoinRandomRoom. but that's not what we want.

To fix this, we need to be aware of the context. When the User clicks on the "Play" button, we should raise a flag to be aware that the connection procedure originated from the user.
Then we can check for this flag to act accordingly within the various Photon callbacks.

  1. Edit the script Launcher

  2. Create a new property within the Private Variables regions

    C#

        /// <summary>
        /// Keep track of the current process. Since connection is asynchronous and is based on several callbacks from Photon, 
        /// we need to keep track of this to properly adjust the behavior when we receive call back by Photon.
        /// Typically this is used for the OnConnectedToMaster() callback.
        /// </summary>
        bool isConnecting;
    
  3. Inside Connect() method set isConnecting to the return value of the PhotonNetwork.ConnectUsingSettings() method as follows:

    C#

        // keep track of the will to join a room, because when we come back from the game we will get a callback that we are connected, so we need to know what to do then
        isConnecting = PhotonNetwork.ConnectUsingSettings(_gameVersion);
    

    result:

    C#

        public void Connect()
        {
            progressLabel.SetActive(true);
            controlPanel.SetActive(false);
            if (PhotonNetwork.IsConnected)
            {
                PhotonNetwork.JoinRandomRoom();
            } 
            else 
            {
                isConnecting = PhotonNetwork.ConnectUsingSettings(_gameVersion);
            }
        }
    
  4. in OnConnectedToMaster() method, surround the PhotonNetwork.JoinRandomRoom() with an if statement as follow

    C#

        // we don't want to do anything if we are not attempting to join a room. 
        // this case where isConnecting is false is typically when you lost or quit the game, when this level is loaded, OnConnectedToMaster will be called, in that case
        // we don't want to do anything.
        if (isConnecting)
        {
            // #Critical: The first we try to do is to join a potential existing room. If there is, good, else, we'll be called back with OnPhotonRandomJoinFailed()
            PhotonNetwork.JoinRandomRoom();
            isConnecting = false;
        }
    
  5. in OnDisconnectedFromPhoton method, set isConnecting to false

  6. Save the script Launcher

Now if we test again and run the Launcher Scene and go back and forth between the Lobby and the Game, all is well :)
In order to test the automatic syncing of Scenes, you'll need to publish the application (publish for desktop, it's the quickest for running tests) and run it alongside Unity, so you have effectively two players that will connect and join a room.
If the Unity Editor creates the Room first, it will be the master client and you'll be able to verify in Unity Console that you get "PhotonNetwork: Loading Level: 1" and later "PhotonNetwork: Loading Level: 2" as you connect with the published instance.

Good! we have covered a lot, but this is only half of the job... :) we need to tackle the Player itself, let's do that in the next section.
Don't forget to take breaks away from the computer from time to time, to be more effective in absorbing the various concepts explained.

Don't hesitate to ask questions on the forum if you are in doubt of a particular feature or if you have trouble following the tutorial and hit an error or an issue not covered here, we'll be happy to help :)

Next Part.
Previous Part.

Back to top