Best practices voor mesh-visualscripts voor netwerken
Overzicht
In Mesh worden de meeste scène-eigenschappen standaard automatisch gedeeld voor alle clients die zijn verbonden met dezelfde ruimte. Bijvoorbeeld de positie en rotatie van een scèneobject, de ingeschakelde status van een onderdeel of de tekst van een TextMeshPro.
Als vuistregel worden onderdeeleigenschappen en objectvariabelen met de volgende waardetypen automatisch gedeeld:
- Booleaanse waarde, geheel getal, float en tekenreeks
- Kleur
- Rect
- Vector 2, Vector 3 en Vector 4
- Viertal
- Matrix 4x4
Verzamelingstypen (lijsten en sets) en scèneobjectverwijzingen worden niet gedeeld.
Visuele scriptknooppunten die eigenschappen in Mesh openen of wijzigen, worden gelabeld met een label dat aangeeft of ze 'Gedeeld door alle clients' of 'Lokaal voor deze client' zijn:
Objectvariabelen worden standaard ook gedeeld als u ze hebt gedeclareerd met een van de bovenstaande waardetypen:
Mesh biedt geen ondersteuning voor scènevariabelen, maar u kunt zelfstandige variabelen in de omgeving gebruiken om variabelen op te sluizen die onafhankelijk van een specifiek scriptmachineonderdeel kunnen worden gedeeld.
Als u het automatisch delen van eigenschappen of objectvariabelen niet wilt, kunt u een onderdeel Lokaal scriptbereik toevoegen aan uw scène. Hierdoor worden alle scène-eigenschappen en scriptvariabelen voor dit gameobject en alle onderliggende objecten lokaal.
Tip: U kunt verschillende voorbeelden zien van hoe het onderdeel Lokaal scriptbereik wordt gebruikt in hoofdstuk 3 van onze Mesh 101-zelfstudie die is gericht op visuele scripting.
Voor lokale scriptvariabelen die u alleen in één scriptmachine gebruikt, kunt u het beste Graph-variabelen gebruiken, die nooit worden gedeeld door clients door Mesh.
Delen via Mesh Visual Scripting biedt de volgende garanties:
Gegarandeerde uiteindelijke consistentie: alle clients komen uiteindelijk met dezelfde gedeelde status.
Gegarandeerd atomiciteit per onderdeel: alle updates voor eigenschappen van hetzelfde scèneonderdeel (of hetzelfde onderdeel Variabelen ) in dezelfde update worden atomisch toegepast op elke client.
Echter:
Geen bestelgarantie: updates die door één client op verschillende scèneonderdelen worden toegepast, kunnen in verschillende orders op verschillende clients binnenkomen.
Geen tijdige garantie: Mesh probeert zo snel mogelijk statuswijzigingen tussen clients te repliceren, maar netwerkvoorwaarden kunnen de komst van een bepaalde statusupdate op sommige of alle clients vertragen.
Geen granulariteitsgarantie: elke client ziet mogelijk niet alle afzonderlijke incrementele updates voor de gedeelde status. Dit kan gebeuren wanneer netwerkvoorwaarden ertoe dwingen dat de Mesh-server updates voor frequentielimieten bijwerkt. Het gebeurt ook wanneer een klant te laat lid wordt van een ruimte.
Status is gedeeld- niet-gebeurtenissen
U kunt geen expliciete netwerkberichten verzenden of ontvangen met Mesh Visual Scripting. Dit kan in eerste instantie verrassend zijn, maar het helpt bij het opzetten van een netwerkparadigma dat het eenvoudig maakt om runtimewijzigingen en late joins eenvoudig af te handelen. In plaats van berichten is er sprake van gedeelde status in scène-eigenschappen en scriptvariabelen.
Uw scripts kunnen op een uniforme manier reageren op gedeelde statusupdates, ongeacht of deze updates waren door een lokaal script of een lokale gebruiker, door een andere client die de ervaring in dezelfde ruimte deelt, of door andere clients die zich al in de ruimte bevonden voordat u er zelf lid van was.
Het niet expliciet verzenden van netwerkberichten betekent dat u moet nadenken over de gedeelde status die updates ontvangt in plaats van gedeelde gebeurtenissen die statusupdates veroorzaken. Gedeelde gebeurtenissen zijn een gevolg van gedeelde status die wordt bijgewerkt, niet het tegenovergestelde.
Gelukkig kunt u met Mesh Visual Scripting eenvoudig reageren op statusupdates voor uw visualscripts. Gebruik het gebeurtenisknooppunt On State Changed en verbind de invoer aan de linkerkant met een scriptvariabele of onderdeeleigenschap die u wilt bekijken voor wijzigingen. Het gebeurtenisknooppunt activeert uw script (verbonden met de rechterkant) wanneer een van de variabelen of eigenschappen die eraan zijn gekoppeld, de waarde ervan wijzigt.
Dit werkt met de gedeelde status en met de lokale status. De gebeurtenis On State Changed wordt geactiveerd, ongeacht of de variabelen of eigenschappen die worden waargenomen, zijn gewijzigd door de lokale client, door een externe client of zelfs door een externe client voordat de lokale client zelfs deelneemt aan de ruimte.
Het gebruik van On State Changed om te reageren op statuswijzigingen is efficiënt: er zijn geen niet-actieve bandbreedte- of prestatiekosten. U kunt op deze manier passief naar statusupdates luisteren zonder dat dit van invloed is op de framesnelheid of het bandbreedtegebruik van uw omgeving.
Late join
Late join vindt plaats wanneer een client deelneemt aan een ruimte waaraan al andere clients zijn verbonden.
Bij late deelname ontvangt Mesh de huidige status van de ruimte van de server, bijvoorbeeld wie zich al in de ruimte bevindt en waar hun avatars zich momenteel bevinden, en bereidt de lokale versie van de client van de gedeelde omgeving snel voor, zodat deze overeenkomt met de status die wordt gedeeld door iedereen in de ruimte.
Voor een groot deel doet Mesh Visual Scripting hetzelfde. Alle eigenschappen van gedeelde onderdelen en visuele scriptvariabelen die zijn gewijzigd in de ruimte voordat de client zojuist is toegevoegd, worden lokaal bijgewerkt zodat deze overeenkomen met de gedeelde status. Alle knooppunten die deze eigenschappen of variabelen observeren, worden vervolgens geactiveerd.
Late joiners spelen gedeelde gebeurtenissen niet opnieuw af. Ze krijgen de gedeelde status.
Vanuit het oogpunt van de lokale client ontwikkelt de omgeving zich altijd van de oorspronkelijke status die de omgeving had vlak na het laden van de scène die u naar Mesh hebt geüpload. In het geval van late deelname kan de eerste statuswijziging groter zijn dan wat er gebeurt terwijl de lokale gebruiker communiceert met de ruimte in een lopende sessie, maar het is in principe precies hetzelfde.
Dit alles gebeurt omdat de omgeving wordt geladen voordat het zelfs van zwart afvaagt. Zodra de gebruiker de omgeving daadwerkelijk kan zien en ermee kan werken, is late join al klaar.
Lokale status de gedeelde status volgen
Zeer vaak is de 'gedeelde status' die een gebruiker in een omgeving kan observeren, eigenlijk een combinatie van status die rechtstreeks wordt gedeeld door Mesh en de lokale status die is ingesteld door visuele scripts als reactie op een gebeurtenis die in de ruimte is opgetreden. Wanneer een gebruiker bijvoorbeeld een switch spiegelt in de omgeving (gedeelde status), kan een visueel script de kleur van de skybox (lokale status) wijzigen. Mogelijk bent u geneigd om de lokale wijziging toe te passen (werk de skybox-kleur bij) rechtstreeks in reactie op de interactie met de gebruiker met de switch. Zelfs als de interactiegebeurtenis zich voordoet op alle clients die zich momenteel in de ruimte bevinden, krijgt elke client die later deelneemt aan de ruimte deze gebeurtenis niet omdat ze er niet waren toen het gebeurde. In plaats daarvan moet u de lokale status als volgt laten volgen :
- Wanneer de gebruiker communiceert (bijvoorbeeld de schakeloptie spiegelt), zorgt u ervoor dat deze een lokale gebeurtenis activeert waarmee een gedeelde variabele wordt bijgewerkt (bijvoorbeeld de aan/uit-status van de switch).
- Gebruik On State Changed om de gedeelde variabele te observeren.
- Wanneer de gebeurtenis On State Changed wordt geactiveerd (omdat de gedeelde variabele de waarde heeft gewijzigd), past u de gewenste lokale wijziging toe (bijvoorbeeld de skybox-kleur bijwerken).
Op deze manier volgt de lokale status (de skyboxkleur) de gedeelde status (de status van de switch). Wat leuk aan dit is, is dat het werkt zonder verandering voor de lokale client die de switch heeft gespiegeld, voor alle andere externe clients die tegelijkertijd in de ruimte aanwezig zijn, en voor toekomstige clients die later aan de kamer zullen deelnemen.
Lokale status volgen de gedeelde status: Aanbevolen procedures
Lokale gebeurtenissen: bijvoorbeeld een gebeurtenisknooppunt On State Changed dat de eigenschap Lokaal is geselecteerd van een Mesh Interactable Body-onderdeel :
- 🆗 Kan de lokale status wijzigen die privé is voor een client. Deze statuswijzigingen blijven strikt op de lokale client staan en ze verdwijnen wanneer de client de sessie verlaat.
- 🆗 Kan de gedeelde status wijzigen.
- ❌Kan de lokale status niet wijzigen zodat deze consistent is tussen clients. Een lokale gebeurtenis wordt alleen uitgevoerd op één client, dus de updates die nodig zijn om de lokale status consistent te houden tussen clients, vinden gewoon niet plaats op een andere client.
Gedeelde gebeurtenissen: bijvoorbeeld een On Trigger Enter-gebeurtenisknooppunt dat is gekoppeld aan een gedeelde fysicatriggerbotser:
- 🆗 Kan de lokale status wijzigen voor tijdelijke effecten: bijvoorbeeld een deeltjeseffect of een kort audio-effect. Alleen clients die aanwezig zijn in de ruimte wanneer de gedeelde gebeurtenis plaatsvindt, kunnen het lokale effect zien; clients die later deelnemen aan de kamer, zullen dat niet doen.
- ❌Kan de lokale status niet wijzigen zodat deze consistent is tussen clients. Een gedeelde gebeurtenis wordt alleen uitgevoerd op clients die aanwezig zijn op het moment dat deze plaatsvindt, maar deze wordt later niet opnieuw afgespeeld voor clients die deelnemen aan de sessie.
- ⛔ De gedeelde status mag niet worden gewijzigd. Omdat een gedeelde gebeurtenis wordt uitgevoerd op alle clients, wordt alles wat het doet door alle clients zeer dicht in de tijd uitgevoerd. Afhankelijk van de aard van de wijziging kan deze meerdere keren worden herhaald (een scoreteller kan bijvoorbeeld worden verhoogd met meer dan één als reactie op één gebeurtenis).
Bij status gewijzigde gebeurtenissen die de gedeelde status in gedeelde variabelen of eigenschappen van gedeelde onderdelen observeren:
- 🆗 Kan de lokale status wijzigen zodat deze consistent is met de gedeelde status tussen clients. Om dit goed te laten werken op een herhaalbare en consistente manier voor alle clients, moet u elke mogelijke nieuwe waarde van de waargenomen gedeelde status omzetten in een lokale staat, niet alleen een paar door kersen gekozen statusovergangen (zoals Is geselecteerd worden waar).
Lokale status volgen de gedeelde status: voorbeeld
In dit voorbeeld zijn er twee interactieve knoppen in deze omgeving: één met 'Ster', de andere met 'Spons'. Als u een van de knoppen selecteert, moet u twee dingen doen:
- Sla het bijbehorende label op in een gedeelde tekenreeksvariabele met de naam ObjectKind.
- Sla de verwijzing op naar een bijbehorend scèneobjectobject in een lokale GameObject-referentievariabele met de naam ObjectRef.
Hier volgen de twee scriptstromen, één voor elke knop. Elk luistert naar de gedeelde eigenschap Is Geselecteerd van het mesh-component Interactable Body van één knop en werkt ObjectKind en ObjectRef bij, afhankelijk van welke knop is geselecteerd:
Alles werkt prima, maar alleen voor gebruikers die zich al in de ruimte bevinden wanneer een van de knoppen is geselecteerd. Elke gebruiker die deelneemt aan de sessie, vindt later een inconsistente status in de lokale versie van de gedeelde omgeving: Alleen ObjectKind is correct ingesteld op basis van de laatst geselecteerde knop, maar ObjectRef blijft null.
Wat is er mis met deze twee scriptstromen?
U ziet dat deze scriptstromen worden geactiveerd door een gedeelde gebeurtenis, omdat ze beide luisteren naar de gedeelde eigenschap Is geselecteerd van elke knop. Dit lijkt logisch omdat het de enige manier is om de lokale ObjectRef-variabele te laten bijwerken op alle clients.
Echter:
- Gedeelde gebeurtenissen mogen de gedeelde status niet wijzigen, maar deze scriptstromen werken de gedeelde ObjectKind-variabele bij.
- Gedeelde gebeurtenissen kunnen de lokale status niet wijzigen zodat ze consistent zijn voor clients , maar deze scriptstromen werken de lokale ObjectRef-variabele bij, die we willen consistent zijn voor alle clients, net zoals ObjectKind.
Dus de manier waarop dit momenteel is ingesteld, moeten we eigenlijk geen van de dingen doen die we nodig hebben om de knoppen te doen.
De enige voor de hand liggende manier om dit probleem te omzeilen, is door de gebeurtenissen te maken die deze stromen lokaal activeren. We kunnen dit doen door het gebeurtenisknooppunt On State Changed te laten zien dat de eigenschap Lokaal is geselecteerd in plaats van Is geselecteerd.
Nu de gebeurtenis lokaal is, betekent dit...
- Lokale gebeurtenissen kunnen de gedeelde status wijzigen, zodat we de gedeelde ObjectKind-variabele veilig kunnen bijwerken en de waarde ervan automatisch wordt gedeeld tussen clients door het ingebouwde netwerk van Mesh Visual Scripting.
- Lokale gebeurtenissen kunnen de lokale status niet wijzigen zodat deze consistent is voor clients , zodat we de lokale ObjectRef-variabele in deze scriptstromen nog steeds niet kunnen bijwerken. We moeten een andere manier vinden.
Dit is hoe de twee scriptstromen eruitzien na deze wijzigingen:
Wat kunnen we doen om de lokale ObjectRef-variabele in te stellen, zodat deze consistent blijft? Gelukkig stellen deze twee scriptstromen al een gedeelde status vast die we kunnen volgen: de variabele shared ObjectKind . U hoeft alleen maar een gebeurtenis On State Changed te gebruiken die deze variabele bekijkt en de lokale ObjectRef-variabele bijwerken, afhankelijk van de waarde:
Dit is een prima manier om dit te doen, omdat bij gebeurtenissen met statuswijziging die zien dat de gedeelde status de lokale status kan wijzigen zodat deze consistent is. Dit werkt voor de client die op de knop heeft gedrukt, voor alle andere clients die tegelijkertijd in dezelfde ruimte aanwezig zijn en voor alle clients die later aan de sessie deelnemen.
Netwerkstoringen
Gedeelde updates met hoge frequentie
Bijna de volledige scènestatus wordt standaard gedeeld door Mesh Visual Scripting. Dat is handig voor delen, maar het kan ook per ongeluk binnensluipen en onnodige netwerkbelasting veroorzaken. Met de volgende scriptstroom wordt het netwerk bijvoorbeeld overspoeld met redundante updates voor de rotatie van de transformatie. Omdat alle clients deze echter tegelijkertijd uitvoeren, heeft geen van de externe updates ooit een werkelijke invloed op elke client lokaal:
In dit geval moet u waarschijnlijk een lokaal scriptbereik gebruiken om het transformatieonderdeel lokaal te maken voor elke client. U moet waarschijnlijk ook een Resource-onderdeel gebruiken in plaats van een On Update-scriptstroom om mee te beginnen.
Het Mesh Visual Scripting Diagnostics-paneel en CPA (Content Performance Analyzer ), vanaf Mesh Toolkit 5.2411, geeft een waarschuwing voor een gedeelde update met hoge frequentie weer voor dit soort constructies.
Op Start wordt uitgevoerd op elke client
Het is misschien verleidelijk om de on-startgebeurtenis te zien als iets dat wordt uitgevoerd bij het opstarten van de sessie, maar het wordt daadwerkelijk geactiveerd op elke client, lokaal, wanneer ze deelnemen aan de sessie. Het is perfect geschikt voor het initialiseren van de lokale status:
Wanneer u echter aan de slag probeert te gaan om de gedeelde status te initialiseren, zult u merken dat de gedeelde status onbedoeld opnieuw wordt geïnitialiseerd voor iedereen wanneer iemand deelneemt aan de sessie:
In het deelvenster Mesh Visual Scripting Diagnostics (vanaf Mesh Toolkit 5.2410) en Content Performance Analyzer (CPA) (vanaf Mesh Toolkit 5.2411) wordt de waarschuwing 'Gedeelde update bij sessiedeelname' weergegeven wanneer ze dit detecteren.
Delen is getypt, maar variabele toewijzing is niet
Om veiligheids- en beveiligingsredenen worden gedeelde visualscriptvariabelen sterk getypt. Dit betekent dat het type dat u selecteert in het onderdeel Variabelen voor de scriptvariabelen die u hebt gedeclareerd definieert welk type exacte waarde tussen clients wordt gesynchroniseerd.
Helaas negeert Unity Visual Scripting het gedeclareerde type van een variabele volledig wanneer u de waarde bijwerkt. Het is bijvoorbeeld eenvoudig om per ongeluk een float-getypte waarde op te slaan in een variabele die is gedeclareerd voor het type Integer. In de lokale client zien uw visuele scripts deze fout niet, omdat visualscripting de fout automatisch converteert naar het verwachte gehele getal wanneer dat nodig is. Als het gaat om het synchroniseren van deze waarde tussen clients, kan Mesh Visual Scripting echter niet dezelfde vrijheden nemen: de 'uiteindelijke consistentie' garandeert dat elke waardeconversie tijdens de vlucht wordt uitgesloten, en veiligheids- en beveiligingsoverwegingen maken het ondenkbaar om een ander waardetype van een externe client te accepteren dan wat voor de variabele is gedeclareerd.
Denk bijvoorbeeld aan deze declaratie van een gedeelde variabele met de naam MyIntegerVar:
Hier volgt een scriptstroom waarmee deze variabele wordt bijgewerkt:
Wat kan er mis gaan? Helaas heeft het scriptknooppunt Random | Range dat in dit voorbeeld wordt gebruikt, twee varianten: een die een willekeurige integer-waarde produceert en een die een willekeurige float-waarde produceert. Het verschil tussen deze twee scriptknooppunten in het deelvenster knooppuntkiezer is subtiel:
Dus als u daar per ongeluk het verkeerde scriptknooppunt random | range selecteert, kan uw script onbedoeld een Float-waarde opslaan in de variabele Geheel getal, maar die foutieve Float-waarde wordt niet gerepliceerd naar andere clients.
Houd dit in gedachten als een mogelijke reden waarom een gedeelde variabele die u hebt gemaakt, misschien niet meer wordt gedeeld. Toekomstige releases van Mesh Visual Scripting kunnen waarschuwen voor dit soort scriptfouten wanneer ze deze kunnen detecteren.