Photon Network (Classic) Einsteigerhandbuch

Photon Network ist ein Dienst für Unity, der es Entwicklern ermöglicht, Echtzeit-Multiplayer-Spiele zu erstellen.

Es bietet eine leistungsstarke und benutzerfreundliche API, die es auch für unerfahrene Entwickler perfekt macht.

In diesem Beitrag werden wir die erforderlichen Dateien herunterladen, Photon AppID einrichten und ein einfaches Multiplayer-Beispiel programmieren.

Teil 1: Einrichten eines Photonennetzwerks

Der erste Schritt besteht darin, das Photon Network-Paket vom Asset Store herunterzuladen. Es enthält alle notwendigen Skripte und Dateien für die Multiplayer-Integration.

  • Öffnen Sie Ihr Unity-Projekt und gehen Sie dann zu Asset Store: (Fenster -> Allgemein -> AssetStore) oder drücken Sie Strg+9
  • Suchen Sie nach „Photon Unity Networking Classic – Free“ und klicken Sie dann auf das erste Ergebnis oder klicken Sie hier
  • Importieren Sie das Photon-Paket, nachdem der Download abgeschlossen ist

  • Nachdem das Paket importiert wurde, müssen Sie eine Photon-App-ID erstellen. Dies geschieht auf deren Website: https://www.photonengine.com/
  • Erstellen Sie ein neues Konto (oder melden Sie sich bei Ihrem bestehenden Konto an)
  • Gehen Sie zur Seite „Anwendungen“, indem Sie auf das Profilsymbol und dann auf "Your Applications" klicken oder diesem Link folgen: https://dashboard.photonengine.com/en-US/PublicCloud
  • Klicken Sie auf der Seite „Anwendungen“ auf "Create new app"

  • Wählen Sie auf der Erstellungsseite als Photonentyp "Photon Realtime" aus, geben Sie als Name einen beliebigen Namen ein und klicken Sie dann "Create"

Wie Sie sehen können, verwendet die Anwendung standardmäßig den kostenlosen Plan. Weitere Informationen zu den Preisplänen finden Sie hier

  • Sobald die Anwendung erstellt ist, kopieren Sie die App-ID, die sich unter dem App-Namen befindet

  • Gehen Sie zurück zu Ihrem Unity-Projekt und gehen Sie dann zu Fenster -> Photon Unity-Netzwerk -> PUN-Assistent
  • Klicken Sie im PUN-Assistenten auf "Setup Project", fügen Sie Ihre App-ID ein und klicken Sie dann "Setup Project"
  • Das Photonennetzwerk ist jetzt fertig

Teil 2: Erstellen eines Multiplayer-Spiels

Kommen wir nun zu dem Teil, in dem wir tatsächlich ein Multiplayer-Spiel erstellen.

Der Mehrspielermodus in Photon wird wie folgt gehandhabt:

  • Zuerst stellen wir eine Verbindung zur Photon-Region her (z. B. USA Ost, Europa, Asien usw.), die auch als Lobby bekannt ist.
  • Sobald wir in der Lobby sind, fordern wir alle Räume an, die in der Region erstellt werden, und können dann entweder einem der Räume beitreten oder unseren eigenen Raum erstellen.
  • Nachdem wir dem Raum beigetreten sind, fordern wir eine Liste der mit dem Raum verbundenen Spieler an und instanziieren ihre Player-Instanzen, die dann über PhotonView mit ihren lokalen Instanzen synchronisiert werden.
  • Wenn jemand den Raum verlässt, wird seine Instanz zerstört und er wird aus der Spielerliste entfernt.

1. Eine Lobby einrichten

Beginnen wir mit der Erstellung eines MainMenu, das eine Lobby-Logik enthält (Vorhandene Räume durchsuchen, neue Räume erstellen usw.).

  • Erstellen Sie eine neue Szene und nennen Sie sie "MainMenu"
  • Erstellen ein neues C#-Skript und nennen Sie es GameLobby
  • Erstellen Sie in der MainMenu-Szene ein neues GameObject. Nennen Sie es "_GameLobby" und hängen Sie das GameLobby-Skript daran an

Öffnen Sie nun das GameLobby-Skript.

Erstellen wir zunächst alle notwendigen Variablen:

    //Our player name
    string playerName = "Player 1";
    //This client's version number. Users are separated from each other by gameversion (which allows you to make breaking changes).
    string gameVersion = "0.9";
    //The list of created rooms
    RoomInfo[] createdRooms = new RoomInfo[0];
    //Use this name when creating a Room
    string roomName = "Room 1";
    Vector2 roomListScroll = Vector2.zero;
    bool joiningRoom = false;

Als nächstes müssen wir „Auto-Join Lobby“ und „Lobby Stats“ aktivieren, damit wir die Raumliste erhalten können. Dies geschieht im void Start().

Außerdem aktivieren wir „AutomaticSyncScene“, sodass die Szene automatisch synchronisiert wird, sobald wir dem Raum beitreten.

Und schließlich rufen wir PhotonNetwork.ConnectUsingSettings auf, um eine Verbindung herzustellen.

    // Use this for initialization
    void Start()
    {
        //Automatically join Lobby after we connect to Photon Region
        PhotonNetwork.PhotonServerSettings.JoinLobby = true;
        //Enable Lobby Stats to receive the list of Created rooms
        PhotonNetwork.PhotonServerSettings.EnableLobbyStatistics = true;
        //This makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
        PhotonNetwork.automaticallySyncScene = true;

        if (!PhotonNetwork.connected)
        {
            // Connect to the photon master-server. We use the settings saved in PhotonServerSettings (a .asset file in this project)
            PhotonNetwork.ConnectUsingSettings(gameVersion);
        }
    }

Um zu wissen, ob eine Verbindung zur Photon Cloud erfolgreich war, müssen wir diese beiden Rückrufe implementieren: OnReceivedRoomListUpdate() und OnFailedToConnectToPhoton(objectparameter).

    void OnFailedToConnectToPhoton(object parameters)
    {
        Debug.Log("OnFailedToConnectToPhoton. StatusCode: " + parameters + " ServerAddress: " + PhotonNetwork.ServerAddress);
        //Try to connect again
        PhotonNetwork.ConnectUsingSettings(gameVersion);
    }

    void OnReceivedRoomListUpdate()
    {
        Debug.Log("We have received the Room list");
        //After this callback, PhotonNetwork.GetRoomList() becomes available
        createdRooms = PhotonNetwork.GetRoomList();
    }

Als nächstes folgt der UI-Teil, in dem das Durchsuchen und Erstellen von Räumen durchgeführt wird:

Photon Network-Lobby

Und schließlich implementieren wir noch 4 Rückrufe: OnPhotonCreateRoomFailed(), OnPhotonJoinRoomFailed(object[] Cause), OnCreatedRoom() und OnJoinedRoom().

Diese Rückrufe werden verwendet, um festzustellen, ob wir dem Raum beigetreten/erstellt sind oder ob es während der Verbindung Probleme gab.

    void OnPhotonCreateRoomFailed()
    {
        Debug.Log("OnPhotonCreateRoomFailed got called. This can happen if the room exists (even if not visible). Try another room name.");
        joiningRoom = false;
    }

    void OnPhotonJoinRoomFailed(object[] cause)
    {
        Debug.Log("OnPhotonJoinRoomFailed got called. This can happen if the room is not existing or full or closed.");
        joiningRoom = false;
    }

    void OnCreatedRoom()
    {
        Debug.Log("OnCreatedRoom");
        //Set our player name
        PhotonNetwork.playerName = playerName;
        //Load the Scene called GameLevel (Make sure it's added to build settings)
        PhotonNetwork.LoadLevel("GameLevel");
    }

    void OnJoinedRoom()
    {
        Debug.Log("OnJoinedRoom");
    }

Und hier ist das endgültige GameLobby.cs-Skript:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameLobby : MonoBehaviour
{
    //Our player name
    string playerName = "Player 1";
    //This client's version number. Users are separated from each other by gameversion (which allows you to make breaking changes).
    string gameVersion = "0.9";
    //The list of created rooms
    RoomInfo[] createdRooms = new RoomInfo[0];
    //Use this name when creating a Room
    string roomName = "Room 1";
    Vector2 roomListScroll = Vector2.zero;
    bool joiningRoom = false;

    // Use this for initialization
    void Start()
    {
        //Automatically join Lobby after we connect to Photon Region
        PhotonNetwork.PhotonServerSettings.JoinLobby = true;
        //Enable Lobby Stats to receive the list of Created rooms
        PhotonNetwork.PhotonServerSettings.EnableLobbyStatistics = true;
        //This makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
        PhotonNetwork.automaticallySyncScene = true;

        if (!PhotonNetwork.connected)
        {
            // Connect to the photon master-server. We use the settings saved in PhotonServerSettings (a .asset file in this project)
            PhotonNetwork.ConnectUsingSettings(gameVersion);
        }
    }

    void OnFailedToConnectToPhoton(object parameters)
    {
        Debug.Log("OnFailedToConnectToPhoton. StatusCode: " + parameters + " ServerAddress: " + PhotonNetwork.ServerAddress);
        //Try to connect again
        PhotonNetwork.ConnectUsingSettings(gameVersion);
    }

    void OnReceivedRoomListUpdate()
    {
        Debug.Log("We have received the Room list");
        //After this callback, PhotonNetwork.GetRoomList() becomes available
        createdRooms = PhotonNetwork.GetRoomList();
    }

    void OnGUI()
    {
        GUI.Window(0, new Rect(Screen.width/2 - 450, Screen.height/2 - 200, 900, 400), LobbyWindow, "Lobby");
    }

    void LobbyWindow(int index)
    {
        //Connection Status and Room creation Button
        GUILayout.BeginHorizontal();

            GUILayout.Label("Status: " + PhotonNetwork.connectionStateDetailed);

            if(joiningRoom || !PhotonNetwork.connected)
            {
                GUI.enabled = false;
            }

            GUILayout.FlexibleSpace();

            //Room name text field
            roomName = GUILayout.TextField(roomName, GUILayout.Width(250));

            if (GUILayout.Button("Create Room", GUILayout.Width(125)))
            {
                if (roomName != "")
                {
                    joiningRoom = true;

                    RoomOptions roomOptions = new RoomOptions();
                    roomOptions.IsOpen = true;
                    roomOptions.IsVisible = true;
                    roomOptions.MaxPlayers = (byte)10; //Set any number

                    PhotonNetwork.JoinOrCreateRoom(roomName, roomOptions, TypedLobby.Default);
                }
            }

        GUILayout.EndHorizontal();

        //Scroll through available rooms
        roomListScroll = GUILayout.BeginScrollView(roomListScroll, true, true);

            if(createdRooms.Length == 0)
            {
                GUILayout.Label("No Rooms were created yet...");
            }
            else
            {
                for(int i = 0; i < createdRooms.Length; i++)
                {
                    GUILayout.BeginHorizontal("box");
                    GUILayout.Label(createdRooms[i].Name, GUILayout.Width(400));
                    GUILayout.Label(createdRooms[i].PlayerCount + "/" + createdRooms[i].MaxPlayers);

                    GUILayout.FlexibleSpace();
                
                    if (GUILayout.Button("Join Room"))
                    {
                        joiningRoom = true;

                        //Set our Player name
                        PhotonNetwork.playerName = playerName;

                        //Join the Room
                        PhotonNetwork.JoinRoom(createdRooms[i].Name);
                    }
                    GUILayout.EndHorizontal();
                }
            }

        GUILayout.EndScrollView();

        //Set player name and Refresh Room button
        GUILayout.BeginHorizontal();

            GUILayout.Label("Player Name: ", GUILayout.Width(85));
            //Player name text field
            playerName = GUILayout.TextField(playerName, GUILayout.Width(250));

            GUILayout.FlexibleSpace();

            GUI.enabled = PhotonNetwork.connectionState != ConnectionState.Connecting && !joiningRoom;
            if (GUILayout.Button("Refresh", GUILayout.Width(100)))
            {
                if (PhotonNetwork.connected)
                {
                    //We are already connected, simply update the Room list
                    createdRooms = PhotonNetwork.GetRoomList();
                }
                else
                {
                    //We are not connected, estabilish a new connection
                    PhotonNetwork.ConnectUsingSettings(gameVersion);
                }
            }

        GUILayout.EndHorizontal();

        if (joiningRoom)
        {
            GUI.enabled = true;
            GUI.Label(new Rect(900/2 - 50, 400/2 - 10, 100, 20), "Connecting...");
        }
    }

    void OnPhotonCreateRoomFailed()
    {
        Debug.Log("OnPhotonCreateRoomFailed got called. This can happen if the room exists (even if not visible). Try another room name.");
        joiningRoom = false;
    }

    void OnPhotonJoinRoomFailed(object[] cause)
    {
        Debug.Log("OnPhotonJoinRoomFailed got called. This can happen if the room is not existing or full or closed.");
        joiningRoom = false;
    }

    void OnCreatedRoom()
    {
        Debug.Log("OnCreatedRoom");
        //Set our player name
        PhotonNetwork.playerName = playerName;
        //Load the Scene called GameLevel (Make sure it's added to build settings)
        PhotonNetwork.LoadLevel("GameLevel");
    }

    void OnJoinedRoom()
    {
        Debug.Log("OnJoinedRoom");
    }
}

2. Erstellen eines Player-Fertigteils

In Multiplayer-Spielen hat die Player-Instanz zwei Seiten: Lokal und Remote.

Eine lokale Instanz wird lokal (von uns) gesteuert.

Die Remote-Instanz hingegen ist eine lokale Darstellung dessen, was der andere Spieler tut. Es sollte von unserer Eingabe unberührt bleiben.

Um festzustellen, ob die Instanz lokal oder remote ist, verwenden wir eine PhotonView-Komponente.

PhotonView fungiert als Messenger, der die zu synchronisierenden Werte empfängt und sendet, beispielsweise Position und Rotation.

Beginnen wir also mit der Erstellung der Player-Instanz (Wenn Sie Ihre Player-Instanz bereits bereit haben, können Sie diesen Schritt überspringen).

In meinem Fall ist die Player-Instanz ein einfacher Würfel, der mit den Tasten W und S bewegt und mit den Tasten A und D gedreht wird.

Photon Network Player-Instanz

Und hier ist ein einfaches Controller-Skript:

PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        //Move Front/Back
        if (Input.GetKey(KeyCode.W))
        {
            transform.Translate(transform.forward * Time.deltaTime * 2.45f, Space.World);
        }
        else if (Input.GetKey(KeyCode.S))
        {
            transform.Translate(-transform.forward * Time.deltaTime * 2.45f, Space.World);
        }

        //Rotate Left/Right
        if (Input.GetKey(KeyCode.A))
        {
            transform.Rotate(new Vector3(0, -14, 0) * Time.deltaTime * 4.5f, Space.Self);
        }
        else if (Input.GetKey(KeyCode.D))
        {
            transform.Rotate(new Vector3(0, 14, 0) * Time.deltaTime * 4.5f, Space.Self);
        }
    }
}

Der nächste Schritt besteht darin, eine PhotonView-Komponente hinzuzufügen.

  • Fügen Sie eine PhotonView-Komponente zur Player-Instanz hinzu
  • Erstellen ein neues C#-Skript, nennen Sie es PlayerNetworkSync und öffnen Sie es (dieses Skript wird für die Kommunikation über PhotonView verwendet).

Als erstes müssen wir MonoBehaviour durch Photon.MonoBehaviour ersetzen. Dieser Schritt ist notwendig, um die zwischengespeicherte photonView-Variable anstelle von GetComponent<PhotonView>() verwenden zu können.

public class PlayerNetworkSync : Photon.MonoBehaviour

Danach können wir alle notwendigen Variablen erstellen:

    //List of the scripts that should only be active for the local player (ex. PlayerController, MouseLook etc.)
    public MonoBehaviour[] localScripts;
    //List of the GameObjects that should only be active for the local player (ex. Camera, AudioListener etc.)
    public GameObject[] localObjects;
    //Values that will be synced over network
    Vector3 latestPos;
    Quaternion latestRot;

Dann prüfen wir in der Lücke Start(), ob der Player lokal oder remote ist, indem wir photonView.isMine verwenden:

    // Use this for initialization
    void Start()
    {
        if (photonView.isMine)
        {
            //Player is local
        }
        else
        {
            //Player is Remote
            for(int i = 0; i < localScripts.Length; i++)
            {
                localScripts[i].enabled = false;
            }
            for (int i = 0; i < localObjects.Length; i++)
            {
                localObjects[i].SetActive(false);
            }
        }
    }

Die eigentliche Synchronisierung erfolgt über den PhotonView-Callback: OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info):

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            //We own this player: send the others our data
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            //Network player, receive data
            latestPos = (Vector3)stream.ReceiveNext();
            latestRot = (Quaternion)stream.ReceiveNext();
        }
    }

In diesem Fall senden wir nur die Position und Rotation des Spielers, aber Sie können das obige Beispiel verwenden, um jeden Wert, der synchronisiert werden muss, mit hoher Frequenz über das Netzwerk zu senden.

Empfangene Werte werden dann im void Update() übernommen:

    // Update is called once per frame
    void Update()
    {
        if (!photonView.isMine)
        {
            //Update remote player (smooth this, this looks good, at the cost of some accuracy)
            transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
            transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);
        }
    }

Hier ist das endgültige PlayerNetworkSync.cs-Skript:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerNetworkSync : Photon.MonoBehaviour
{
    //List of the scripts that should only be active for the local player (ex. PlayerController, MouseLook etc.)
    public MonoBehaviour[] localScripts;
    //List of the GameObjects that should only be active for the local player (ex. Camera, AudioListener etc.)
    public GameObject[] localObject;
    //Values that will be synced over network
    Vector3 latestPos;
    Quaternion latestRot;

    // Use this for initialization
    void Start()
    {
        if (photonView.isMine)
        {
            //Player is local
        }
        else
        {
            //Player is Remote
            for(int i = 0; i < localScripts.Length; i++)
            {
                localScripts[i].enabled = false;
            }
            for (int i = 0; i < localObject.Length; i++)
            {
                localObject[i].SetActive(false);
            }
        }
    }

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            //We own this player: send the others our data
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            //Network player, receive data
            latestPos = (Vector3)stream.ReceiveNext();
            latestRot = (Quaternion)stream.ReceiveNext();
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (!photonView.isMine)
        {
            //Update remote player (smooth this, this looks good, at the cost of some accuracy)
            transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
            transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);
        }
    }
}

  • Speichern Sie die PlayerInstance in Prefab und verschieben Sie sie in den Ordner „Ressourcen“ (Falls kein solcher Ordner vorhanden ist, erstellen Sie einen). Dieser Schritt ist notwendig, um Multiplayer-Objekte über das Netzwerk erzeugen zu können.

3. Erstellen eines Spiellevels

GameLevel ist eine Szene, die nach dem Betreten des Raums geladen wird und in der die gesamte Aktion stattfindet.

  • Erstellen Sie eine neue Szene und nennen Sie sie "GameLevel" (Oder wenn Sie einen anderen Namen beibehalten möchten, stellen Sie sicher, dass Sie den Namen in dieser Zeile PhotonNetwork.LoadLevel("GameLevel"); in GameLobby.cs ändern).

In meinem Fall verwende ich eine einfache Szene mit einem Flugzeug:

  • Erstellen Sie nun ein neues Skript und nennen Sie es RoomController. Dieses Skript kümmert sich um die Logik im Raum (z. B. das Spawnen der Spieler, das Anzeigen der Spielerliste usw.).

Beginnen wir mit der Definition der notwendigen Variablen:

    //Player instance prefab, must be located in the Resources folder
    public GameObject playerPrefab;
    //Player spawn point
    public Transform spawnPoint;

Um das Player-Prefab zu instanziieren, verwenden wir PhotonNetwork.Instantiate:

    // Use this for initialization
    void Start()
    {
        //In case we started this demo with the wrong scene being active, simply load the menu scene
        if (!PhotonNetwork.connected)
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
            return;
        }

        //We're in a room. spawn a character for the local player. it gets synced by using PhotonNetwork.Instantiate
        PhotonNetwork.Instantiate(playerPrefab.name, spawnPoint.position, Quaternion.identity, 0);
    }

Und eine einfache Benutzeroberfläche mit einer "Leave Room"-Schaltfläche und einigen zusätzlichen Elementen wie dem Raumnamen und der Liste der verbundenen Spieler:

    void OnGUI()
    {
        if (PhotonNetwork.room == null)
            return;

        //Leave this Room
        if (GUI.Button(new Rect(5, 5, 125, 25), "Leave Room"))
        {
            PhotonNetwork.LeaveRoom();
        }

        //Show the Room name
        GUI.Label(new Rect(135, 5, 200, 25), PhotonNetwork.room.Name);

        //Show the list of the players connected to this Room
        for (int i = 0; i < PhotonNetwork.playerList.Length; i++)
        {
            //Show if this player is a Master Client. There can only be one Master Client per Room so use this to define the authoritative logic etc.)
            string isMasterClient = (PhotonNetwork.playerList[i].IsMasterClient ? ": MasterClient" : "");
            GUI.Label(new Rect(5, 35 + 30 * i, 200, 25), PhotonNetwork.playerList[i].NickName + isMasterClient);
        }
    }

Und schließlich implementieren wir einen weiteren PhotonNetwork-Rückruf namens OnLeftRoom(), der aufgerufen wird, wenn wir den Raum verlassen:

    void OnLeftRoom()
    {
        //We have left the Room, return to the MainMenu
        UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
    }

Und hier ist das endgültige RoomController.cs-Skript:

using UnityEngine;

public class RoomController : MonoBehaviour
{
    //Player instance prefab, must be located in the Resources folder
    public GameObject playerPrefab;
    //Player spawn point
    public Transform spawnPoint;

    // Use this for initialization
    void Start()
    {
        //In case we started this demo with the wrong scene being active, simply load the menu scene
        if (!PhotonNetwork.connected)
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
            return;
        }

        //We're in a room. spawn a character for the local player. it gets synced by using PhotonNetwork.Instantiate
        PhotonNetwork.Instantiate(playerPrefab.name, spawnPoint.position, Quaternion.identity, 0);
    }

    void OnGUI()
    {
        if (PhotonNetwork.room == null)
            return;

        //Leave this Room
        if (GUI.Button(new Rect(5, 5, 125, 25), "Leave Room"))
        {
            PhotonNetwork.LeaveRoom();
        }

        //Show the Room name
        GUI.Label(new Rect(135, 5, 200, 25), PhotonNetwork.room.Name);

        //Show the list of the players connected to this Room
        for (int i = 0; i < PhotonNetwork.playerList.Length; i++)
        {
            //Show if this player is a Master Client. There can only be one Master Client per Room so use this to define the authoritative logic etc.)
            string isMasterClient = (PhotonNetwork.playerList[i].IsMasterClient ? ": MasterClient" : "");
            GUI.Label(new Rect(5, 35 + 30 * i, 200, 25), PhotonNetwork.playerList[i].NickName + isMasterClient);
        }
    }

    void OnLeftRoom()
    {
        //We have left the Room, return to the MainMenu
        UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
    }
}
  • Erstellen Sie abschließend ein neues GameObject in der GameLevel-Szene und nennen Sie es "_RoomController"
  • Hängen Sie das RoomController-Skript an das _RoomController-Objekt an
  • Weisen Sie ihm das PlayerInstance-Prefab und eine SpawnPoint-Transformation zu und speichern Sie dann die Szene
  • Fügen Sie sowohl MainMenu als auch GameLevel zu den Build-Einstellungen hinzu.

4. Erstellen eines Test-Builds

Jetzt ist es an der Zeit, einen Build zu erstellen und ihn zu testen:

Sharp Coder Videoplayer

Alles funktioniert wie erwartet!

Bonus

RPC

Im Photon Network steht RPC für Remote Procedure Call und wird verwendet, um eine Funktion auf Remote-Clients aufzurufen, die sich im selben Raum befinden (Sie können hier mehr darüber lesen).

RPCs haben viele Verwendungsmöglichkeiten. Nehmen wir zum Beispiel an, Sie müssen eine Chat-Nachricht an alle Spieler im Raum senden. Mit RPCs ist das einfach.

[PunRPC]
void ChatMessage(string senderName, string messageText)
{
    Debug.Log(string.Format("{0}: {1}", senderName, messageText));
}

Beachten Sie [PunRPC] vor der Funktion. Dieses Attribut ist erforderlich, wenn Sie planen, die Funktion über RPCs aufzurufen.

Um die als RPC gekennzeichneten Funktionen aufzurufen, benötigen Sie ein PhotonView. Beispielaufruf:

PhotonView photonView = PhotonView.Get(this);
photonView.RPC("ChatMessage", PhotonTargets.All, PhotonNetwork.playerName, "Some message");

Profi-Tipp: Wenn Ihr Skript ein Photon.MonoBehaviour oder Photon.PunBehaviour ist, können Sie Folgendes verwenden: this.photonView.RPC().

Benutzerdefinierte Eigenschaften

In Photon Network handelt es sich bei Custom Properties um eine Hashtabelle, die dem Player oder dem Raum zugewiesen werden kann.

Dies ist nützlich, wenn Sie dauerhafte Daten festlegen müssen, die nicht häufig geändert werden müssen (z. B. Name des Spielerteams, Raumspielmodus usw.).

Zuerst müssen Sie eine Hashtabelle definieren, was durch das Hinzufügen der folgenden Zeile am Anfang des Skripts erreicht wird:

//Replace default Hashtables with Photon hashtables
using Hashtable = ExitGames.Client.Photon.Hashtable; 

Das folgende Beispiel legt die Raumeigenschaften mit den Namen "GameMode" und "AnotherProperty" fest:

        //Set Room properties (Only Master Client is allowed to set Room properties)
        if (PhotonNetwork.isMasterClient)
        {
            Hashtable setRoomProperties = new Hashtable();
            setRoomProperties.Add("GameMode", "FFA");
            setRoomProperties.Add("AnotherProperty", "Test");
            PhotonNetwork.room.SetCustomProperties(setRoomProperties);
        }

        //Will print "FFA"
        print((string)PhotonNetwork.room.CustomProperties["GameMode"]);
        //Will print "Test"
        print((string)PhotonNetwork.room.CustomProperties["AnotherProperty"]);

Spielereigenschaften werden auf ähnliche Weise festgelegt:

        //Set our Player's property
        Hashtable setPlayerProperties = new Hashtable();
        setPlayerProperties.Add("PlayerHP", (float)100);
        PhotonNetwork.player.SetCustomProperties(setPlayerProperties);

        //Will print "100"
        print((float)PhotonNetwork.player.CustomProperties["PlayerHP"]);

Um eine bestimmte Eigenschaft zu entfernen, setzen Sie einfach ihren Wert auf Null.

        //Remove property called "PlayerHP" from Player properties
        Hashtable setPlayerProperties = new Hashtable();
        setPlayerProperties.Add("PlayerHP", null);
        PhotonNetwork.player.SetCustomProperties(setPlayerProperties);