Jump to content
Unity Insider Forum

Austausch Integer zwischen zwei Clients


straesso

Recommended Posts

Hallo,

ich habe ein (vermeintlich) einfaches Problem, welches ich gerne mit UNET lösen möchte, aber nicht so recht weiss, wie.

Genau zwei Clients sollen in einem Spiel-Setting existieren. Nun möchte ich erreichen, dass ein Client in der Lage ist ein Integer an den anderen Client zu senden und umgekehrt. Die Clients haben allerdings kein gemeinsames Spielfeld. Client 1 sieht nicht, was Client 2 macht und umgekehrt.

Es geht lediglich darum, dass ein Client dem Anderen einen einfachen Wert sendet, den dieser dann verarbeitet - nur für sich. Umgekehrt soll der zweite Client ebenfalls in der Lage sein, dem ersten eine Integer zu senden, die dann lediglich von diesem verarbeitet wird. Ich habe gedacht, das Problem mit zwei SyncVars zu lösen - bekomme das aber nicht auf die Reihe.

Ich komme einfach nicht weiter. Würde mich über Hilfe freuen.

Viele Grüße, Olaf

Link zu diesem Kommentar
Auf anderen Seiten teilen

Benutzt du wirklich UNet oder Mirror (nachfolger)? UNet ist nämlich veraltet. Würde empfehlen Mirror zu benutzen, weil es wirklich gut verbessert wurde. Daher kann nun sein, dass ich paar Dinge sage, die vllt bei UNet nicht gibt.

UNet ist so aufgebaut, dass jedes Client seine eigene Variable verändern kann, allerdings über den Server. Diese Variablen werden über das Netzwerk synchronisiert (also auch andere Clients können das mitbekommen, wenn so eingestellt ist). Das wäre eine Möglichkeit um damit zu arbeiten. Der andere Spieler könnte nun einfach diesen Wert lesen. 

Möglichkeit zwei. Wenn es nur zwei Clients sein sollen, dann ist das leichter. Du führst ein Command aus. Beispiel CmdSendIntegerToOtherPlayer(int intValue)

In der Funktion wird nun der Server den ändern Spieler (ich sage bewusst Spieler und nicht client) finden sein Wert aktualisieren. Wenn es syncvar ist, dann geht es automatisch und wenn normale Variable ist, dann kann man zusätzlich TargetRpc benutzen. Damit kann man gezielt an ein Spieler senden.

Es gibt auch kompliziertere Wege mit dem MessageSystem.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Vielen Dank für Deine Antwort MayZy.

Ja, ich verwende in der Tat UNet, weil ich dafür ein recht gutes Tutorial habe und das Spiel nur für den Privatgebrauch gedacht ist. Die Zukunftsfähigkeit ist da nicht so entscheidend. Ob Mirror nach dem Neudesign der Netzwerkfähigkeiten von Unity noch funktioniert, scheint mir auch nicht sicher zu sein.

Vermutlich habe ich grundsätzlich noch ein Verständnisproblem, weil ja eigentlich alle Clients denselben Code verwenden. Irgendwie komme ich damit durcheinander. Jeder der beiden Clients muss ja genau wissen, welche Variable nur für ihn gedacht ist...

Ich lasse mir Deine Antworten mal in Ruhe durch den Kopf gehen und überlege, wie ich das tatsächlich mit Code auch realisieren könnte...

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Umstellung zu Mirror könnte ein paar Fehler anzeigen, aber das Upgraden wird gut erklärt und relative schnell lösbar. Das Problem bei normalen UNet ist, dass es sehr viele Probleme hatte. Einer der Gründe, warum UNet auch aufgegeben wurde. Parallel in der Entwicklung hat sich einer Gedacht "warum nehme ich das Projekt nicht und fixe das und mache eigene Version". Somit entstand Mirror. Heute wird Mirror von mehreren Leuten entwickelt und es gibt sogar viele Donations, so dass sie auch gut Zeit investieren können.

Falls es bei dir nur um was simples geht und du Herausforderung magst, kann man auch Low Level Lösungen nehmen. Z.B. DarkRift 2, Telepathy, Lidgren fallen mir da spontan ein. Aber wie gesagt: Low-Level bedeutet, dass man Grundaufbau etwas selber bauen muss. Z.B: Components zu Unity findet man da eher nicht so und muss man selber machen. Hab mit Telepathy selber mal kompletten eigenen Multiplayer System geschrieben, welches zwar nicht optimiert war aber für sowas auf jeden Fall reicht.

Am 28.7.2020 um 17:07 schrieb straesso:

Vermutlich habe ich grundsätzlich noch ein Verständnisproblem, weil ja eigentlich alle Clients denselben Code verwenden. Irgendwie komme ich damit durcheinander. Jeder der beiden Clients muss ja genau wissen, welche Variable nur für ihn gedacht ist...

Das ist falsch. Alle Werte sind auf dem Server beim Spielerobject und nicht beim Client bzw. der Client kann das nicht ändern, aber erfährt über den Wert. Du muss mit dem Gedanken vorangehen, dass es Objekte vom Spieler sind, aber die Werte durch den Server verändert werden können. Das heißt, wenn beim Client z.B: health = 1 ist und health = 10 macht, wird es nicht funktionieren und auch nicht synchronisieren, da man den Server ermitteln muss "Hey setze bitte mein Health auf 10", und der Server wird das tun und jeden anderen Clients sagen "Dieses Spielerobjekt (und Component) hat nun Healthwert von 10".

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 7 months later...

Hey,

 

ich würde hier gerne nochmal anknüpfen. Bin neu was C# und Mirror betrifft. Hatte schon paar Tutorials gemacht, um das ganze besser zu verstehen. ich bekomme aber meinen Konkreten Fall nicht hin. Ich möchte ein rundenbasiertes Spiel programmieren und dachte mir ich fange erst mal vorne an, bevor ich irgendwie Interface mache.

Ich habe in meinem Canvas eine Textbox und einen Button. Jeder Spieler sieht in seinem Bildschirm also diese beiden Elemente. Die Textbox zeigt den Spielzug an. Mit dem Button beendet man seinen Zug und der Spielzug wird somit um 1 erhöht. Wenn man den Button drückt, um seinen Spielzug zu beenden, soll der Button unsichtbar werden.

Ohne Mirror geht das easy, aber mit dem Neztwerk bekomme ich das nicht hin.

Grobe Struktur im Canvas

TEXT(GameObject) = Anzeige Spielzug (müsste ja keine Netzwerk-ID haben, das könnte wohl jeder Client selbst steuern)

BUTTON(GameObject) = Spielzug beenden und ausblenden (müsste auch keine Netzwrk-ID haben, könnte sich selbst steuern, wenn er immer guckt, welcher Spielzug gerade ansteht). Hier hängt ein Script drin, sagen wir mal mit dem Namen "Rise_token.cs"

NetworkManager inkl. HUD (GameObject)

MASTER (GameObject) = Dieses Objekt, soll eigentlich ein Script "Control.cs" haben, in dem dann diese Variable "token" [SyncVar] von jedem Spieler erhöht werden kann mit dem BUTTON und alle Clienten sollen sich aufgrund der Variable hier drin aktualisieren, ich nehme an, dass das mit dem [ClientRpc] geschieht.

 

Jetzt die Frage:

Wie muss der Code für das Erhöhen des Spielzuges denn genau aussehen? Vielleicht habe ich auch nicht dieses Server/Client Prinzip begriffen? Vielleicht bin ich auch schon nah dran und der Groschen ist noch nicht gefallen? Oder fehlt mir noch etwas? Ich habe in Tutorials gesehen, dass man auch ein ziemlich großes Skript für das PlayerPrefab baut, gehört evtl. etwas rein?

Das ist das MASTER-Objekt

public class Control : NetworkBehavior
{
	[SnycVar]
	public int token;

}

 

Das ist im BUTTON-Objekt

public class rise_token : Networkbehavior
{
	public void IncreaseToken()
    {
		??
	}
	
	
}

 

Danke schonmal für die Mühen und Antworten, nach einigen Tagen suchen, habe ich irgendwie kein ähnliches Beispiel finden können, wo man sowas erklärt. Meist programmieren die Amis in den Tutorials ohne groß etwas zu erklären.

 

Gruß

Andy

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn du mit NetworkBehaviour arbeitest, dann kannst du [Command] und [ClientRpc] benutzen. Allerdings sind die eigentlich nur für die Playerobjekte gedacht. Das heißt, wenn man z.B.

[ClientRpc]
void RpcHealEffect()
{
    HealEffect();
}

Wenn der Server RpcKill ausführt, wird die Funktion bei jedem Spieler ausgeführt.

[Command]
void CmdHealAll()
{
  // server checkt, ob er überhaupt lebt und mana dafür hat
  if( IsAlive && IsMana )
  {
	// loop und heile alle Spieler
	...
   	// Macht, dass alle Spieler diesen HealEffekt sehen
	RpcHealEffect();
  }
}

Der Client wiederum sendet Command an den Server. 

Jetzt bei Button ist das natürlich etwas doof, wenn es eigenen NetworkBehaviour hat auf dem Server. buttonScript.ButtonClick() würde z.b. nur bedingt funktionieren, weil Mirror sagt beim Command, dass der Client aus seinem Spielerobjekt (Script) ein Command sendet. In anderen Worten: Ein Client kann nicht ein Script eines anderen Clients manipulieren. Da müsste man komplizierter denken. Man kann aber mittlerweile bei Command(requireAuthority = false) machen um doch andere Scripts anzusprechen. Das könntest du machen. Bevor ich das tue empfehle ich dir eher was anders (vllt auch etwas komplizierter).

Ich würde da eher Network Messages verwenden. https://mirror-networking.gitbook.io/docs/guides/communications/network-messages

Wenn es nur darum geht, dass jeder den Token um eins erhöhen kann, dann einfach in dein Control-Script IncreaseTokenMessage machen ungefähr wie in dem Beispiel im Link. Später im ButtonScript musst du nur NetworkClient.Send machen. Wichtig ist nur hier zu achten, dass im Text RegisterHandler steht. Das geht nur einmal im gesamten Spiel (also für den Server einmal, und für jedes Spieler auch einmal). In anderen Worten, zwei mal RegisterHandler geht nicht.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Hey,

ich habe jetzt echt längere Zeit mal nichts gemacht, dann wieder was ausprobiert, noch ein paar Ami-Videos angeschaut und ich habe nun folgende Lösung für diese Situation hinbekommen. Leider kann ich nicht beurteilen, ob diese Vorgehensweise noch Probleme mit sich bringt, deswegen wäre ich für ein paar weitere Ratschläge dankbar, wenn es noch welche gibt oder Hinweise, was ich beachten sollte. Denn um ehrlich zu sein, habe ich einen Teil noch nicht ganz begriffen und zwar den, wenn die NetworkIdentity-Zeilen durchgeführt werden.

Naja ich habe das ganze mal visualisiert. Hier noch ein paar Erklärungen:

- In der Hierarchie gibt es das GameObject für den NetworkManager. Dieser Spawned die Spieler --> PLAYER mit dem Skript PlayerManager.cs

- Der Button wird als Objekt genutzt, um den Spielzug zu zählen, darin ist die [SyncVar] TOKE enthalten. Der Button projeziert den Spielzug auf ein Textfeld: Spielzuganzeige

- Die Skripte habe ich in ein namespace "SPIELZUG" gepackt. Wenn das Spiel größer wird, müsste ich das wahrscheinlich umbenennen

- Ich habe nur die beiden Skripte, für diese Aufgabe denke ich ist das vollkommen in Ordnung

 

Below the Code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using UnityEngine.UI;

namespace SPIELZUG
{
    public class PlayerManager : NetworkBehaviour
    {

        // Der Server führt diese Funktion aus, um im Button die SyncVar (TOKE) zu erhöhen
        [Command]
        public void CmdNaechsterSpielzug(GameObject but)
        {
            but.GetComponent<Count>().TOKE++;
        }

    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Mirror;

namespace SPIELZUG
{
    public class Count : NetworkBehaviour
    {
        public PlayerManager PlayerManager;

        // Variable, die immer hochgezählt wird, SyncVar ist notwendig, wenn ein Spieler später dazukommt, sieht er den aktuellen    
        // Spielzug
        [SyncVar]
        public int TOKE = 0;

        // Verweis auf die Anzeige, Im Inspector verlinkt
        public GameObject SpielzugAnzeige;

        // Erzeugt/Verlegt hier eine Berechtigung, um beim Spieler eine Serverfunktion aufzurufen, die diesen TOKEN erhöht
        public void Click()
        {
            NetworkIdentity networkIdentity = NetworkClient.connection.identity;
            PlayerManager = networkIdentity.GetComponent<PlayerManager>();
            PlayerManager.CmdNaechsterSpielzug(gameObject);
        }

        // Aktualisiert die Anzeige laufend
        private void Update()
        {
            SpielzugAnzeige.GetComponent<Text>().text = TOKE.ToString();
        }
    }
}

 

Ich wäre für Anregungen sehr dankbar.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...