Bewährte Methoden für das Visuelle Skripting von Gittern für Netzwerke
Übersicht
In Mesh werden die meisten Szeneneigenschaften standardmäßig automatisch für alle Clients freigegeben, die mit demselben Raum verbunden sind. Beispielsweise die Transform-Position und -Drehung eines Szenenobjekts, der aktivierte Zustand einer Komponente oder textMeshPro.For example, a scene object's Transform position and rotation, a component's enabled state, or a TextMeshPro's text.
Als Faustregel werden Komponenteneigenschaften und Objektvariablen, die über die folgenden Werttypen verfügen, standardmäßig automatisch freigegeben:
- Boolescher Wert, Ganze Zahl, Float und Zeichenfolge
- Farbe
- Rechteck
- Vektor 2, Vektor 3 und Vector 4
- Quaternion
- Matrix 4x4
Auflistungstypen (Listen und Sätze) und Szenenobjektverweise werden nicht freigegeben.
Visuelle Skriptknoten, die auf Eigenschaften in Mesh zugreifen oder ändern, werden mit einer Bezeichnung markiert, die angibt, ob sie "Von allen Clients freigegeben" oder "Lokal für diesen Client" sind:
Objektvariablen werden standardmäßig gemeinsam verwendet, wenn Sie sie mit einem der oben aufgeführten Werttypen deklariert haben:
Mesh unterstützt keine Szenenvariablen, Aber Sie können eigenständige Variablenkomponenten in der Umgebung verwenden, um Variablen zu stashen, die unabhängig von einer bestimmten Skriptcomputerkomponente freigegeben werden können.
Wenn Sie die automatische Freigabe von Eigenschaften oder Objektvariablen nicht wünschen, können Sie Ihrer Szene eine Komponente des lokalen Skriptbereichs hinzufügen. Dadurch werden alle Szeneneigenschaften und Skriptvariablen für dieses Spielobjekt und alle untergeordneten Elemente lokal festgelegt.
Tipp: Sie können mehrere Beispiele dafür sehen, wie die Komponente "Lokaler Skriptbereich" in Kapitel 3 unseres Lernprogramms "Mesh 101" verwendet wird, das sich auf die visuelle Skripterstellung konzentriert.
Für lokale Skriptvariablen, die Sie nur in einem einzelnen Skriptcomputer verwenden, empfiehlt es sich, Graph-Variablen zu verwenden, die niemals von Mesh über Clients hinweg freigegeben werden.
Die Freigabe über visuelles Mesh-Skripting bietet die folgenden Garantien:
Garantierte Konsistenz: Alle Clients werden schließlich am gleichen freigegebenen Zustand ankommen.
Garantierte Atomität pro Komponente: Alle Aktualisierungen von Eigenschaften derselben Szenenkomponente (oder derselben Variablenkomponente ) im selben Update werden auf jedem Client atomar angewendet.
Allerdings:
Keine Bestellgarantie: Updates, die von einem Client auf mehrere verschiedene Szenenkomponenten angewendet werden, können in unterschiedlichen Bestellungen auf verschiedenen Clients eingehen.
Keine Zeitachsen garantieren: Mesh versucht so schnell wie möglich, Zustandsänderungen über Clients hinweg zu replizieren, aber Netzwerkbedingungen können die Ankunft eines bestimmten Zustandsupdates für einige oder alle Clients verzögern.
Keine Granularitätsgarantie: Jeder Client sieht möglicherweise nicht alle einzelnen inkrementellen Updates für den freigegebenen Zustand. Dies kann passieren, wenn Netzwerkbedingungen erzwingen, dass der Mesh-Server Aktualisierungen mit der Geschwindigkeitsbegrenzung einschränkt. Dies geschieht auch, wenn ein Client zu einem Chatroom zu einem späteren Zeitpunkt beitritt.
Status ist freigegeben – keine Ereignisse
Sie können keine expliziten Netzwerknachrichten mit visuellem Mesh-Skripting senden oder empfangen. Dies kann zunächst überraschend sein, aber es hilft, ein Netzwerkparadigma zu etablieren, das die einheitliche Behandlung von Laufzeitänderungen sowie die späte Verknüpfung erleichtert. Anstelle von Nachrichten gibt es den freigegebenen Zustand in Szeneneigenschaften und Skriptvariablen.
Ihre Skripts können auf gemeinsam genutzte Zustandsupdates einheitlich reagieren, unabhängig davon, ob diese Updates von einem lokalen Skript oder Benutzer, von einem anderen Client, der die Erfahrung im selben Raum teilt, oder von anderen Clients, die sich bereits im Chatroom befanden, bevor Sie sich selbst beigetreten sind.
Das explizite Senden von Netzwerknachrichten bedeutet, dass Sie anfangen müssen, über den freigegebenen Zustand nachzudenken, der Updates erhält, anstatt freigegebene Ereignisse, die Zustandsaktualisierungen verursachen. Freigegebene Ereignisse sind eine Folge des Aktualisierten freigegebenen Zustands, nicht das Gegenteil.
Glücklicherweise erleichtert mesh Visual Scripting Ihre visuellen Skripts, auf Zustandsupdates zu reagieren. Verwenden Sie den Ereignisknoten "On State Changed ", und verbinden Sie die linksseitigen Eingaben mit einer beliebigen Skriptvariable oder Komponenteneigenschaft, die Sie für Änderungen beobachten möchten, und der Ereignisknoten löst Ihr Skript (verbunden mit der rechten Seite) aus, wenn eine der Variablen oder Eigenschaften, die mit ihr verbunden sind, ihren Wert ändert.
Dies funktioniert sowohl mit dem freigegebenen Zustand als auch mit dem lokalen Zustand. Das On State Changed-Ereignis wird unabhängig davon ausgelöst, ob die Variablen oder Eigenschaften, die sie beobachten, vom lokalen Client, von einem Remoteclient oder sogar von einem Remoteclient geändert wurden, bevor der lokale Client sogar dem Raum beigetreten ist.
Die Verwendung des Geänderten Zustands , um auf Zustandsänderungen zu reagieren, ist effizient: Es gibt keine Leerlaufbandbreite oder Leistungskosten. Sie können beliebig viele visuelle Skripts passiv auf Zustandsupdates lauschen lassen, ohne die Framerate oder die Bandbreitennutzung Ihrer Umgebung negativ zu beeinflussen.
Verspäteter Beitritt
Der späte Beitritt erfolgt, wenn ein Client einem Chatroom beitritt, der bereits mit anderen Clients verbunden ist.
Am späten Beitritt empfängt Mesh den aktuellen Status des Chatrooms vom Server, z. B. wer sich bereits im Raum befindet und wo sich seine Avatare derzeit befinden – und bereitet schnell die lokale Version des Beitrittsclients der freigegebenen Umgebung vor, sodass er mit dem Zustand übereinstimmt, den jeder im Raum freigegeben hat.
Für einen großen Teil funktioniert das visuelle Skripting von Gittern identisch. Alle freigegebenen Komponenteneigenschaften und visuelle Skriptvariablen, die im Raum geändert wurden, bevor der Client gerade verbunden ist, werden lokal aktualisiert, um dem freigegebenen Zustand zu entsprechen, und dann werden alle On State Changed-Ereignisknoten ausgelöst, die diese Eigenschaften oder Variablen beobachten.
Verspätete Joiner geben keine freigegebenen Ereignisse wieder – sie erhalten den freigegebenen Zustand.
Aus der Sicht des lokalen Clients entwickelt sich die Umgebung immer aus dem Anfangszustand, den sie hatte, unmittelbar nach dem Laden der Szene, die Sie in Mesh hochgeladen haben. Im Falle einer verspäteten Teilnahme kann die erste Zustandsänderung größer sein als das, was passiert, während der lokale Benutzer in einer laufenden Sitzung mit dem Raum interagiert, aber es ist dasselbe im Prinzip.
All dies geschieht, wenn die Umgebung geladen wird, bevor sie sogar von Schwarz eingeblendet wird. Sobald der Benutzer die Umgebung tatsächlich sehen und mit dieser interagieren kann, ist der späte Beitritt bereits abgeschlossen.
Festlegen des lokalen Zustands nach freigegebenem Zustand
Sehr oft kann der "freigegebene Zustand" eines Benutzers in einer Umgebung beobachten, tatsächlich eine Kombination aus Zustand, der direkt von Mesh und lokalem Zustand freigegeben wurde, der durch visuelle Skripts als Reaktion auf ein Ereignis im Raum erstellt wurde. Wenn ein Benutzer beispielsweise einen Schalter in der Umgebung (gemeinsam genutzter Zustand) kippt, kann ein visuelles Skript die Farbe des Skybox -Elements (lokaler Zustand) ändern. Möglicherweise sind Sie versucht, die lokale Änderung (die Skybox-Farbe aktualisieren) direkt als Reaktion auf den Benutzer anzuwenden, der mit dem Schalter interagiert. Auch wenn das Interaktionsereignis auf allen Clients auftritt, die sich derzeit im Raum befinden, erhält jeder Client, der dem Chatroom später beitritt, dieses Ereignis nicht einfach, weil er nicht dort war, wenn es passiert ist. Stattdessen sollten Sie den freigegebenen Zustand wie folgt festlegen:
- Wenn der Benutzer interagiert (z. B. den Schalter kippt), löst dies ein lokales Ereignis aus, das eine freigegebene Variable aktualisiert (z. B. den Ein-/Aus-Zustand des Schalters).
- Verwenden Sie "On State Changed ", um die freigegebene Variable zu beobachten.
- Wenn das On State Changed-Ereignis ausgelöst wird (da die freigegebene Variable ihren Wert geändert hat), wenden Sie alle gewünschten lokalen Änderungen an (z. B. aktualisieren Sie die Skybox-Farbe).
Auf diese Weise folgt der lokale Zustand (die Skybox-Farbe) dem freigegebenen Zustand (dem Zustand des Schalters). Das ist schön, dass es ohne Änderung für den lokalen Client funktioniert, der den Switch gekippt hat, für alle anderen Remoteclients, die gleichzeitig im Raum vorhanden sind, und für alle zukünftigen Clients, die später am Raum teilnehmen werden.
Festlegen des lokalen Zustands nach freigegebenem Zustand: Bewährte Methoden
Lokale Ereignisse: Ein On State Changed-Ereignisknoten , der die "Is Selected Local "-Eigenschaft einer interagierbaren Gittertextkomponente beobachtet:
- 🆗 Kann den lokalen Zustand ändern, der für einen Client privat ist. Diese Zustandsänderungen bleiben streng auf dem lokalen Client, und sie werden verschwinden, wenn der Client die Sitzung verlässt.
- 🆗 Kann den freigegebenen Zustand ändern.
- ❌Der lokale Zustand kann nicht so geändert werden, dass er für alle Clients konsistent ist. Ein lokales Ereignis wird nur auf einem Client ausgeführt, sodass die Updates, die erforderlich sind, um den lokalen Zustand über Clients hinweg konsistent zu halten, einfach nicht auf einem anderen Client stattfinden.
Freigegebene Ereignisse: Ein On Trigger Enter-Ereignisknoten, der an einen gemeinsam genutzten Physiktrigger angefügt ist:
- 🆗 Kann den lokalen Zustand für momentäre Effekte ändern: Beispielsweise ein Partikeleffekt oder ein kurzer Audioeffekt. Nur Clients, die im Raum vorhanden sind, wenn das freigegebene Ereignis auftritt, können den lokalen Effekt sehen. Alle Kunden, die später am Chatroom teilnehmen, werden nicht mehr verwendet.
- ❌Der lokale Zustand kann nicht so geändert werden, dass er für alle Clients konsistent ist. Ein freigegebenes Ereignis wird nur auf Clients ausgeführt, die zum Zeitpunkt des Auftretens vorhanden sind, aber es wird nicht für Clients wiedergegeben, die später an der Sitzung teilnehmen.
- ⛔ Der freigegebene Zustand darf nicht geändert werden. Da ein freigegebenes Ereignis auf allen Clients ausgeführt wird, wird alles, was es tut, von allen Clients sehr nah in der Zeit ausgeführt. Je nach Art der Änderung kann sie mehrmals wiederholt werden (z. B. kann ein Scorezähler als Reaktion auf ein einzelnes Ereignis um mehrere erhöht werden).
On State Changed events that observe shared state in shared variables or shared component properties:
- 🆗 Kann den lokalen Zustand so ändern, dass er mit dem freigegebenen Zustand für clients konsistent ist. Damit dies für alle Kunden auf wiederholbare und konsistente Weise funktioniert, müssen Sie jeden möglichen neuen Wert des beobachteten geteilten Zustands in den lokalen Zustand übersetzen, nicht nur ein paar Kirschauswahl-Zustandsübergänge (z . B. ist ausgewählt wird wahr).
Festlegen des lokalen Zustands nach freigegebenem Zustand: Beispiel
In diesem Beispiel gibt es zwei interaktive Schaltflächen in dieser Umgebung: eine mit "Stern", die andere mit "Schwamm" bezeichnet. Das Auswählen einer der Schaltflächen sollte zwei Aktionen ausführen:
- Speichern Sie die entsprechende Bezeichnung in einer freigegebenen Zeichenfolgenvariable namens ObjectKind.
- Speichern Sie den Verweis auf ein entsprechendes Szenenobjekt in einer lokalen GameObject-Referenzvariable namens ObjectRef.
Hier sind die beiden Skriptflüsse, eine für jede Schaltfläche. Jede lauscht auf die freigegebene Is Selected-Eigenschaft der Komponente "Mesh Interactable Body" einer Schaltfläche und aktualisiert ObjectKind und ObjectRef, je nachdem, welche Schaltfläche ausgewählt wurde:
Alles scheint einwandfrei zu funktionieren, aber nur für Benutzer, die sich bereits im Raum befinden, wenn eine der Schaltflächen ausgewählt ist. Jeder Benutzer, der der Sitzung beitritt, findet später einen inkonsistenten Zustand in seiner lokalen Version der freigegebenen Umgebung: Nur ObjectKind wird gemäß der zuletzt ausgewählten Schaltfläche richtig festgelegt, aber ObjectRef bleibt null.
Was ist mit diesen beiden Skriptflüssen falsch?
Beachten Sie zunächst, dass diese Skriptflüsse durch ein freigegebenes Ereignis ausgelöst werden, da sie beide auf die freigegebene Is Selected-Eigenschaft jeder Schaltfläche lauschen. Dies scheint sinnvoll zu sein, da es die einzige Möglichkeit ist, die lokale ObjectRef-Variable auf allen Clients zu aktualisieren.
Allerdings:
- Freigegebene Ereignisse dürfen den freigegebenen Zustand nicht ändern, aber diese Skriptflüsse aktualisieren die freigegebene ObjectKind-Variable .
- Freigegebene Ereignisse können den lokalen Zustand nicht so ändern, dass er über Clients hinweg konsistent ist, aber diese Skriptflüsse aktualisieren die lokale ObjectRef-Variable , die wir für alle Clients konsistent sein möchten, genau wie ObjectKind.
So wie dies derzeit eingerichtet ist, sollten wir eigentlich keines der Dinge tun, die wir für die Schaltflächen benötigen.
Die einzige offensichtliche Möglichkeit, dieses Problem zu vermeiden, besteht darin, die Ereignisse, die diese Flüsse auslösen, lokal zu machen. Dies ist möglich, indem wir den Ereignisknoten "On State Changed" anstelle von "Is Selected" als "Is Selected" beobachten.
Wenn das Ereignis jetzt lokal ist, bedeutet dies...
- Lokale Ereignisse können den freigegebenen Zustand ändern, sodass wir jetzt die freigegebene ObjectKind-Variable sicher aktualisieren können, und ihr Wert wird automatisch für clients durch das integrierte Netzwerk von Mesh Visual Scripting freigegeben.
- Lokale Ereignisse können den lokalen Zustand nicht so ändern, dass er für alle Clients konsistent ist, sodass die lokale ObjectRef-Variable in diesen Skriptflüssen immer noch nicht aktualisiert werden kann. Wir müssen einen anderen Weg finden.
So sehen die beiden Skriptflüsse nach den folgenden Änderungen aus:
Was können wir tun, um die lokale ObjectRef-Variable festzulegen, damit sie mit dieser Konstante konsistent bleibt? Glücklicherweise richten diese beiden Skriptflüsse bereits einen freigegebenen Zustand ein, dem wir folgen könnten: die freigegebene ObjectKind-Variable . Wir müssen nur ein On State Changed-Ereignis verwenden, das diese Variable beobachtet und die lokale ObjectRef-Variable abhängig vom Wert aktualisiert:
Dies ist eine gute Möglichkeit, dies zu tun, da Ereignisse , die den freigegebenen Zustand beobachten, den lokalen Zustand ändern können, damit sie konsistent sind. Dies funktioniert für den Client, der die Schaltfläche gedrückt hat, für alle anderen Clients, die gleichzeitig im selben Raum vorhanden sind, und für alle Clients, die später an der Sitzung teilnehmen werden.
Netzwerk-Fallstricke
Gemeinsam genutzte Updates mit hoher Häufigkeit
Fast der gesamte Szenenzustand wird standardmäßig von Mesh Visual Scripting freigegeben. Das eignet sich hervorragend für die Freigabe, kann aber auch versehentlich einsen und unnötige Netzwerklasten verursachen. Beispielsweise überflutet der folgende Skriptfluss das Netzwerk mit redundanten Aktualisierungen der Transformationsdrehung. Da alle Clients sie jedoch gleichzeitig ausführen, hat keiner der Remoteupdates eine tatsächliche Auswirkung auf jeden Client lokal:
In diesem Fall sollten Sie wahrscheinlich einen lokalen Skriptbereich verwenden, um die Transformationskomponente für jeden Client lokal zu machen. Außerdem sollten Sie wahrscheinlich eine Animatorkomponente anstelle eines On Update-Skriptflusses verwenden, um mit diesem zu beginnen.
Im Bereich "Mesh Visual Scripting Diagnostics" und "Content Leistungsanalyse (CPA) von Mesh Toolkit 5.2411" wird für diese Art von Konstrukt eine Warnung mit hoher Häufigkeit angezeigt.
Beim Start wird auf jedem Client ausgeführt
Möglicherweise möchten Sie sich das On Start-Ereignis als etwas vorstellen, das beim Starten der Sitzung ausgeführt wird, aber es wird tatsächlich auf jedem Client lokal ausgelöst, wenn sie der Sitzung beitreten. Sie eignet sich perfekt für die Initialisierung des lokalen Zustands:
Wenn Sie jedoch versuchen, den freigegebenen Zustand zu initialisieren, werden Sie feststellen, dass der freigegebene Zustand unbeabsichtigt für jeden initialisiert wird, wenn jeder der Sitzung beitritt:
Im Bereich "Mesh Visual Scripting Diagnostics" (ab Mesh Toolkit 5.2410) und "Content Leistungsanalyse (CPA) (ab Mesh Toolkit 5.2411) wird eine Warnung "Shared update on session join" angezeigt, wenn sie dies erkennen.
Die Freigabe ist typiert, aber die variable Zuweisung ist nicht
Aus Sicherheits- und Sicherheitsgründen werden freigegebene visuelle Skriptvariablen stark typiert. Dies bedeutet, dass der Typ, den Sie in der Variablenkomponente für die von Ihnen deklarierten Skriptvariablen auswählen, definiert, welcher genaue Werttyp zwischen Clients synchronisiert wird.
Leider ignoriert Unity Visual Scripting den deklarierten Typ einer Variablen beim Aktualisieren des Werts vollständig. Beispielsweise ist es einfach, versehentlich einen Float-Typwert in einer Variablen zu speichern, die für den Typ Integer deklariert wurde. Innerhalb des lokalen Clients bemerken Ihre visuellen Skripts diesen Fehler nicht, da Visual Scripting den fehlerhaften Float-Wert bei Bedarf automatisch in die erwartete ganze Zahl konvertiert. Wenn es jedoch darum geht, diesen Wert über Clients hinweg zu synchronisieren, kann Mesh Visual Scripting nicht die gleichen Freiheiten übernehmen: Die "eventuale Konsistenz" garantiert jede Wertkonvertierung in Flight, und Sicherheitsüberlegungen machen es nicht möglich, einen anderen Werttyp von einem Remoteclient zu akzeptieren als das, was für die Variable deklariert wurde.
Betrachten Sie beispielsweise diese Deklaration einer freigegebenen Variablen namens "MyIntegerVar":
Hier ist ein Skriptfluss, der diese Variable aktualisiert:
Was kann möglicherweise schief gehen? Leider kommt der in diesem Beispiel verwendete Skriptknoten random | range in zwei Varianten: einer, der einen zufälligen Ganzzahlwert erzeugt, und einer, der einen zufälligen Float-Wert erzeugt. Der Unterschied zwischen diesen beiden Skriptknoten im Knotenauswahlbereich ist subtil:
Wenn Sie also versehentlich den falschen Random | Range-Skriptknoten dort auswählen, speichert Ihr Skript möglicherweise unbeabsichtigt einen Float-Wert in ihrer Variablen vom Typ Integer, aber dieser fehlerhafte Float-Wert wird nicht auf andere Clients repliziert.
Beachten Sie dies als potenziellen Grund, warum eine von Ihnen erstellte freigegebene Variable möglicherweise nicht mehr freigegeben wurde. Zukünftige Versionen von Mesh Visual Scripting können diese Art von Skriptfehler warnen, wenn sie sie erkennen können.