Duck's Blog

Einstieg in den PUN2 `Teams Manager`

Autor: Duck

01.07.2021

Einstieg in den PUN2 `Teams Manager`

Viele Online-Spiele benötigen eine Art “Teams-System”, das die Clients in verschiedene Teams einteilen kann. Mit PUN 2 greifen Entwickler oftmals auf eigene Lösungen, was grundsätzlich auch nicht falsch ist, aber PUN 2 nimmt dir als Entwickler viel Arbeit ab. In PUN 2 ist nämlich ein solches System bereits integriert und sehr einfach zum Benutzen. Das ist der Teams Manager, den ich heute vorstellen werde.
Ausser der offiziellen API Referenz zum Photon Teams Manager existieren kaum Informationen über dieses äusserst hilfreiche System. Nachfolgend findest du eine aussführliche Erklärung über diese Funktion und eine Anleitung, wie man damit anfangs umzugehen hat.

Importieren des PUN 2 Assets

Die Reise beginnt ganz am Anfang beim Importieren des PUN 2 Assets. Dabei musst du darauf achten, dass du die nötigen Dateien importierst. Nebst all den anderen Dateien, die PUN 2 zwingend benötigt, gibt es auch einen Ordner namens “Utility Scripts”. Genau da findest du die benötigten Dateien für das Teams System. Es gibt natürlich auch andere “Utility Scripts”, die aber nicht zwingend nötig sind für den Zweck, den wir hier verfolgen, jedoch kann es nicht schaden, wenn man diese auch importiert, da man sie zu einem späteren Zeitpunkt eventuell brauchen könnte.

Import Teams Manager

Diese Dateiens sind sehr wichtig, wenn man den Teams Manager verwenden möchte.

Einrichten des Teams Managers

Zuerst brauchst du einen Photon Teams Manager in deiner Szene, welches ein C# Mono Behaviour Skript (eine Klasse, die von MonoBehaviour erbt) ist. Du solltest diese Komponente an ein Objekt hängen, das konstant in der Szene ist und nicht instanziiert oder zerstört wird, sondern immer da, aktiv und von Anfang an in der Szene ist.
Dann kannst du Teams erstellen. Du kannst jedem Team, das du erstellst, einen Namen und einen Code geben. Der Code kann von 0 bis 255 gehen. Also kannst du theoretisch 256 Teams haben.

So sähe dein Photon Teams Manager mit 2 Teams (Rot und Blau) aus:

Photon Teams Manager Komponente

Das allererste, was du in deinem Code, in welchem du das System irgendwie benutzen möchtest, machen solltest, ist, den Photon.Pun.UtilityScripts Namespace zu importieren. Da du höchstwahrscheinlich auch mit anderen PUN 2 Klassen und ihren Membern arbeiten wirst, empfiehlt es sich, auch den Photon.Pun Namespace zu benutzen. Aus Erfahrung kann ich sagen, dass dies praktisch immer erforderlich ist, wenn du mit PUN 2 arbeitest.
Dann brauchst du eine Referenz zur PhotonTeamsManager Komponente in der Szene. Wir werden ein “privates” (private) Feld vom Typ PhotonTeamsManager benutzen. Dazu verwenden wir noch das [SerializeField] Attribut, um es im Inspector anzeigen zu lassen.

[SerializeField] private PhotonTeamsManager teamsManager;

Danach solltest du die Referenz per “Drag and Drop” in das Feld ziehen, das in der Zwischenzeit im Inspector erschienen ist.
Nun kannst du Spielern Teams zuweisen; sowohl dir selbst als auch anderen. So ist der Master Client beispielsweise dazu in der Lage, allen anderen Clients ein Team aufgrund von beliebigen Kriterien zuzuweisen.

Tipps und Tricks

Zuerst gibt es ein paar Dinge klarzustellen. Es gibt einige Klassen, denen du hier oft begegnen wirst.

  • Einerseits ist es die bereits erwähnte PhotonTeamsManager Klasse, die, wie es der Name schon sagt, als eine Art zentraler Manager für die Teams fungiert.

  • Dann gibt es auch die PhotonTeam Klasse, die ein Team darstellt. Dabei enthält es Informationen, wie den Namen vom Team und den Code.

  • Zuletzt ist hierbei auch die Player Klasse ein wichtiger Faktor. Dies ist eine Klasse, die im Namespace Photon.Realtime enthalten ist. Diese Klasse stellt einen Client/“Spieler” dar und ist nicht spezifisch für das Teams System geschaffen worden, sondern existiert auch ausserhalb davon, wie man es von dessen Namespace erraten kann.

Hinweis

Alle Methoden, die mit Try beginnen, wie zum Beispiel TryGetTeamByName oder TryGetTeamMembers, tun genau das, was der Name auch verrät. Sie versuchen, etwas bestimmtes zu tun. Diese Methoden haben einen bool Rückgabewert. Somit kann man diese Methoden in einer if-Abfrage benutzen und wenn diese false zurückgibt, ist die Aktion gescheitert. Somit kann man den Code, der von einer erfolgreichen Aktion abhängt, innerhalb dieser if-Abfrage platzieren und nur ausführen, wenn die Methode erfolgreich ausgeführt wurde. Damit entgeht man Fehlermeldungen und Sachen, die den reibungslosen Ablauf des Programmes behindern.

PhotonTeam Instanz

Du kannst jedes Team in einer PhotonTeam Variable speichern, also jedes Team kann sozusagen durch eine PhotonTeam Instanz ausgedrückt werden.

Informationen über das Team

Du kannst den Namen oder den Code von jedem Team mit Name für den Namen und Code für den Code erhalten. Also kannst du so den Code oder Namen einer PhotonTeam Referenz bekommen.

Team eines Clients abfragen

player.GetPhotonTeam()

Du kannst das Team eines Spielers/Clients mit player.GetPhotonTeam() abfragen, was eine PhotonTeam Referenz zurückgeben wird. Jetzt kannst du zum Beispiel die Teams zweier Spieler/Clients vergleichen, den Code bekommen, irgendetwas anzeigen aufgrund des Teams oder sonst etwas. Das Team von sich selbst (dem lokalen Client), kannst du natürlich mit PhotonNetwork.LocalPlayer.GetPhotonTeam() kriegen. Es ist nur wichtig, dass du eine Player Referenz hast, von der du das Team abfragen kannst.

Team beitreten

player.JoinTeam(string teamName)
player.JoinTeam(byte teamCode)
player.JoinTeam(PhotonTeam team)

Du kannst jeden Spieler/Client einem Team zuordnen mit: player.JoinTeam(), dabei übergibst du entweder den Namen des Teams (als string), den Code als byte oder das Team als PhotonTeam Instanz als Parameter. Dabei muss player, wie in diesem Beispiel benutzt, eine Referenz zu einer Player (Photon.Realtime.Player) Instanz sein.
Hierzu noch einige Beispiele, wie man dies in der Praxis benutzt.

PhotonNetwork.LocalPlayer.JoinTeam("Blue"); // Der EIGENE Client tritt dem Team "Blue" bei, falls dieses vorhanden ist

PhotonNetwork.LocalPlayer.JoinTeam(1); // Der EIGENE Client tritt dem Team mit dem Code 1 bei, falls vorhanden

Nun werde ich noch ein praxisnahes Beispiel aufführen, das bei einer Kollision mit einem Objekt, das den Tag “Player” trägt, den eigenen Client dem selben Team, dem der Spieler, mit dem man kollidiert ist, zugehört, beitreten lässt. In diesem Beispiel ist wichtig zu wissen, dass sowohl PhotonNetwork.LocalPlayer als auch die nicht-statische Owner Eigenschaft der PhotonView Klasse eine Player Referenz zurückgeben. Daher solltest du dich durch die langen Teile davor nicht verwirren lassen, denn dieses Vorgehen funktioniert mit jeder Player Referenz genau gleich.

void OnCollisionEnter(Collision collision) // Wird beim Zusammenstoss mit einem anderen Collider aufgerufen (3D)
{
    Transform otherObject = collision.transform; // Referenz zur `Transform` Komponente des Objektes, mit dem man zusammengestossen ist
    if(otherObject.CompareTag("Player")) // Überprüfe, ob dieses Objekt den Tag "Player" besitzt
    {
        // Referenz zum Team des Clients (Zugriff auf Client durch Referenz zur `PhotonView` Komponente)
        PhotonTeam team = otherObject.GetComponent<PhotonView>().Owner.GetPhotonTeam(); 
        PhotonNetwork.LocalPlayer.JoinTeam(team); // Der EIGENE Client tritt dem Team des anderen Clients, mit dessen Objekt man zusammengestossen ist, bei
    }
}

Die Anzahl Clients in einem Team

teamsManager.GetTeamMembersCount(string teamName)
teamsManager.GetTeamMembersCount(byte teamCode)
teamsManager.GetTeamMembersCount(PhotonTeam team)

Du kannst die Anzahl Clients in einem Team mit teamsManager.GetTeamMembersCount() herausbekommen, wobei teamsManager die Referenz zur PhotonTeamsManager Komponente ist. Diese Methode erfordert wieder entweder einen string für den Namen, einen nummerischen byte Wert für den Code oder eine PhotonTeam Referenz des Teams, dessen Anzahl Clients du erfahren möchtest, als Parameter.

Alle verfügbaren Teams

teamsManager.GetAvailableTeams()

Du kannst einen Array mit allen Teams mithilfe von teamsManager.GetAvailableTeams() erhalten. Das ist dann ein PhotonTeam[] (PhotonTeam Array).

Team per Code erhalten

teamsManager.TryGetTeamByCode(byte teamCode, out PhotonTeam team)

Du kannst ein Team mit dem Code identifizieren. Dabei verwendet man die teamsManager.TryGetTeamByCode() Methode, während sie einen byte als ersten Parameter (für den Code, den du kennst) annimmt und eine PhotonTeam out-Referenz als zweiten Parameter. Denn was diese Methode buchstäblich macht, ist eine PhotonTeam Referenz herauszugeben.
Zum Beispiel:

teamsManager.TryGetTeamByCode(1, out PhotonTeam team)

Nun kannst du diese neu erstellte lokale team Variable verwenden.

Team per Name erhalten

teamsManager.TryGetTeamByName(string teamName, out PhotonTeam team)

Dasselbe mit dem Namen des Teams, nur mit teamsManager.TryGetTeamByName(), während die Methode einen string als ersten Parameter (für den Namen, den du kennst) annimmt und wieder eine PhotonTeam out-Referenz als zweiten Parameter. Denn was diese Methode buchstäblich macht, ist eine PhotonTeam Referenz herauszugeben. Dieser Satz sollte dir schon bekannt vorkommen.
Zum Beispiel:

teamsManager.TryGetTeamByName("Blue", out PhotonTeam team)

Alle Spieler eines Teams

teamsManager.TryGetTeamMembers(string teamName, out Player[] players)
teamsManager.TryGetTeamMembers(byte teamCode, out Player[] players)
teamsManager.TryGetTeamMembers(PhotonTeam team, out Player[] players)

Man kann ebenfalls alle Spieler eines bestimmten Teams als Player[] (Player Array) erhalten. Dies ist mit der teamsManager.TryGetTeamMembers() möglich. Diese nimmt als ersten Parameter entweder einen string für den Namen, einen byte für den Code oder eine PhotonTeam Referenz an und der zweite Parameter ist wieder eine out-Referenz, aber diesmal vom Typ Player[] (Player Array).
Beispiel:

teamsManager.TryGetTeamMembers(1, out Player[] players);

teamsManager.TryGetTeamMembers("Blue", out Player[] players);

teamsManager.TryGetTeamMembers(team, out Player[] players); // `team` ist hierbei eine `PhotonTeam` Instanz

Alle Mitspieler eines Spielers

teamsManager.TryGetTeamMatesOfPlayer(Player player, out Player[] teamMates)

Es gibt auch die Möglichkeit, alle Mitspieler eines Spielers/Clients zu erhalten, also alle Spieler, die neben diesem im selben Team sind. Dazu braucht man lediglich eine Player Instanz, die man als ersten Parameter der teamsManager.TryGetTeamMatesOfPlayer() Methode übergibt. Der zweite Parameter ist wieder eine out-Referenz und erneut vom Typ Player[] (Player Array).

teamsManager.TryGetTeamMatesOfPlayer(player, out Player[] mates);

Wie du siehst, ist der Teams Manager von PUN 2 überhaupt nicht kompliziert zum Benutzen und zudem auch sehr hilfreich. Es erleichtert einem Entwickler eine ganze Menge Arbeit und unterstützt diesen bei einer oft verwendeten Funktion in Online-Spielen.