Metodtips för mesh-visualiseringsskript för nätverk
Översikt
I Mesh delas de flesta scenegenskaper som standard automatiskt mellan alla klienter som är anslutna till samma rum. Ett scenobjekts transformeringsposition och rotation, en komponents aktiverade tillstånd eller text i TextMeshPro.
Som tumregel delas komponentegenskaper och objektvariabler som har följande värdetyper automatiskt som standard:
- Booleskt värde, heltal, flyttal och sträng
- Färg
- Rect
- Vektor 2, vektor 3 och vektor 4
- Quaternion
- Matris 4x4
Samlingstyper (listor och uppsättningar) och scenobjektreferenser delas inte.
Visuella skriptnoder som kommer åt eller ändrar egenskaper i Mesh taggas med en etikett som anger om de är "Delade av alla klienter" eller "Lokal för den här klienten":
Objektvariabler delas också som standard om du har deklarerat dem med någon av de värdetyper som anges ovan:
Mesh har inte stöd för scenvariabler, men du kan använda fristående variabler i miljön för att förvara variabler som kan delas oberoende av en specifik komponent för skriptdatorn .
Om du inte vill dela egenskaper eller objektvariabler automatiskt kan du lägga till en komponent för lokalt skriptomfång i scenen. Detta gör alla scenegenskaper och skriptvariabler för det här spelobjektet och någon av dess underordnade lokala.
Tips: Du kan se flera exempel på hur komponenten Omfång för lokalt skript används i kapitel 3 i vår Mesh 101-självstudiekurs som fokuserar på visuella skript.
För lokala skriptvariabler som du bara använder i en enda skriptdator är det bäst att använda Graph-variabler som aldrig delas mellan klienter av Mesh.
Delning via Mesh Visual Scripting ger följande garantier:
Garanterad slutlig konsekvens: Alla klienter kommer så småningom till samma delade tillstånd.
Garanterad atomitet per komponent: Alla uppdateringar av egenskaperna för samma scenkomponent (eller samma variabelkomponent ) i samma uppdatering tillämpas atomiskt på varje klient.
Observera följande:
Ingen beställningsgaranti: Uppdateringar som tillämpas av en klient på flera olika scenkomponenter kan komma i olika beställningar på olika klienter.
Ingen tidsgaranti: Mesh gör sitt bästa för att replikera tillståndsändringar mellan klienter så snabbt som möjligt, men nätverksvillkoren kan fördröja ankomsten av en viss tillståndsuppdatering på vissa eller alla klienter.
Ingen kornighetsgaranti: Alla klienter kanske inte ser alla enskilda inkrementella uppdateringar av delat tillstånd. Detta kan inträffa när nätverksvillkoren tvingar Mesh-servern att hastighetsbegränsa uppdateringar. Det händer också när en klient ansluter till ett rum sent.
Tillståndet är delat – inte händelser
Du kan inte skicka eller ta emot explicita nätverksmeddelanden med Mesh Visual Scripting. Detta kan vara överraskande i början, men det hjälper till att upprätta ett nätverksparadigm som gör det enkelt att hantera körningsändringar på ett enhetligt sätt samt sen anslutning. I stället för meddelanden finns det delat tillstånd i scenegenskaper och skriptvariabler.
Dina skript kan svara på uppdateringar av delat tillstånd på ett enhetligt sätt oavsett om uppdateringarna gjordes av ett lokalt skript eller en annan användare, av en annan klient som delar upplevelsen i samma rum eller av andra klienter som redan var i rummet innan du ens gick med i det själv.
Att inte uttryckligen kunna skicka nätverksmeddelanden innebär att du måste börja tänka på delat tillstånd som hämtar uppdateringar i stället för delade händelser som orsakar tillståndsuppdateringar. Delade händelser är en följd av att delat tillstånd uppdateras, inte tvärtom.
Lyckligtvis gör Mesh Visual Scripting det enkelt för dina visuella skript att reagera på tillståndsuppdateringar. Använd händelsenoden Vid tillstånd ändrad och anslut indata på vänster sida med valfri skriptvariabel eller komponentegenskap som du vill observera för ändringar, och händelsenoden utlöser skriptet (anslutet till dess högra sida) när någon av variablerna eller egenskaperna som är anslutna till den ändrar värdet.
Detta fungerar både med delat tillstånd och lokalt tillstånd. Händelsen Ändrad vid tillstånd utlöses oavsett om variablerna eller egenskaperna som observeras har ändrats av den lokala klienten, av en fjärrklient eller till och med av en fjärrklient innan den lokala klienten ens anslöt till rummet.
Det är effektivt att använda Vid tillståndsändringar för att svara på tillståndsändringar: Det finns ingen inaktiv bandbredd eller prestandakostnad. Du kan låta valfritt antal visuella skript passivt lyssna efter tillståndsuppdateringar på det här sättet utan att påverka bildfrekvensen eller bandbreddsanvändningen i din miljö negativt.
Sen anslutning
Sen anslutning sker när en klient ansluter till ett rum som redan har andra klienter anslutna till det.
Vid sen anslutning tar Mesh emot rummets aktuella tillstånd från servern– till exempel vem som redan är i rummet och där deras avatarer för närvarande finns – och förbereder snabbt den anslutande klientens lokala version av den delade miljön så att den matchar tillståndet som delas av alla i rummet.
Till stor del gör Mesh Visual Scripting samma sak. Alla egenskaper för delade komponenter och visuella skriptvariabler som har ändrats i rummet innan klienten precis anslöt uppdateras lokalt för att matcha det delade tillståndet, och sedan utlöses eventuella händelsenoder som observerar dessa egenskaper eller variabler vid status.
Sena joiners spelar inte upp delade händelser – de får delat tillstånd.
Från den lokala klientens synvinkel utvecklas miljön alltid från det ursprungliga tillstånd som den hade precis efter att den scen som du har laddat upp till Mesh har lästs in. Vid sen anslutning kan den första tillståndsändringen vara större än vad som händer när den lokala användaren interagerar med rummet i en pågående session, men det är exakt samma sak i princip.
Allt detta händer när miljön läses in innan den ens tonar in från svart. Så snart användaren faktiskt kan se och interagera med miljön är sen anslutning redan klar.
Få det lokala tillståndet att följa det delade tillståndet
Mycket ofta är det "delade tillstånd" som en användare kan observera i en miljö faktiskt en kombination av tillstånd som delas direkt av Mesh och lokalt tillstånd som har upprättats av visuella skript som svar på en händelse som inträffade i rummet. När en användare till exempel vänder en växel i miljön (delat tillstånd) kan ett visuellt skript ändra färg på skybox-miljön (lokalt tillstånd). Du kan vara frestad att tillämpa den lokala ändringen (uppdatera skybox-färgen) direkt som svar på att användaren interagerar med växeln. Men även om interaktionshändelsen inträffar på alla klienter som för närvarande finns i rummet, kommer alla klienter som ansluter till rummet senare inte att få den händelsen bara för att de inte var där när det hände. I stället bör du få det lokala tillståndet att följa det delade tillståndet så här:
- När användaren interagerar (till exempel vänder växeln) gör du den här utlösaren till en lokal händelse som uppdaterar en delad variabel (till exempel på/av-tillståndet för växeln).
- Använd Vid tillstånd har ändrats för att observera den delade variabeln.
- När händelsen På tillstånd ändrat utlöses (eftersom den delade variabeln ändrade dess värde) tillämpar du alla lokala ändringar du vill ha (till exempel uppdatera färg på skybox-rutan).
På så sätt följer det lokala tillståndet (skybox-färgen) delat tillstånd (växelns tillstånd). Det som är trevligt med detta är att det fungerar utan ändring för den lokala klienten som vände växeln, för alla andra fjärrklienter som finns i rummet samtidigt och för framtida klienter som ansluter till rummet senare.
Gör så att det lokala tillståndet följer det delade tillståndet: Metodtips
Lokala händelser: Till exempel en händelsenod vid tillståndsändring som observerar egenskapen Är vald lokalt för en mesh-komponent för interaktionsbar brödtext :
- 🆗 Kan ändra lokalt tillstånd som är privat för en klient. Dessa tillståndsändringar förblir strikt på den lokala klienten och försvinner när klienten lämnar sessionen.
- 🆗 Kan ändra delat tillstånd.
- ❌Det går inte att ändra det lokala tillståndet så att det är konsekvent mellan klienter. En lokal händelse körs bara på en klient, så de uppdateringar som krävs för att hålla det lokala tillståndet konsekvent mellan klienter sker helt enkelt inte på någon annan klient.
Delade händelser: Till exempel en Vid utlösare Ange händelsenod kopplad till en delad fysikutlösare kolliderare:
- 🆗 Kan ändra lokalt tillstånd för momentära effekter: Till exempel en partikeleffekt eller en kort ljudeffekt. Endast klienter som finns i rummet när den delade händelsen inträffar kan se den lokala effekten. klienter som ansluter till rummet senare kommer inte att göra det.
- ❌Det går inte att ändra det lokala tillståndet så att det är konsekvent mellan klienter. En delad händelse körs bara på klienter som är närvarande vid den tidpunkt då den inträffar, men den spelas inte upp igen för klienter som ansluter till sessionen senare.
- ⛔ Får inte ändra delat tillstånd. Eftersom en delad händelse körs på alla klienter görs allt den gör av alla klienter mycket nära i tid. Beroende på typen av ändring kan den upprepas flera gånger (till exempel kan en poängräknare ökas med mer än en som svar på en enskild händelse).
På Tillstånd ändrade händelser som observerar delat tillstånd i delade variabler eller delade komponentegenskaper:
- 🆗 Kan ändra det lokala tillståndet så att det är konsekvent med delat tillstånd mellan klienter. För att detta ska fungera bra på ett repeterbart och konsekvent sätt för alla klienter måste du översätta alla möjliga nya värden för det observerade delade tillståndet till lokalt tillstånd, inte bara några körsbärsplockade tillståndsövergångar (till exempel Väljs blir sant).
Gör så att det lokala tillståndet följer det delade tillståndet: Exempel
I det här exemplet finns det två interaktiva knappar i den här miljön: en märkt med "Star", den andra märkt med "Svamp". Att välja någon av knapparna ska göra två saker:
- Lagra motsvarande etikett i en delad strängvariabel med namnet ObjectKind.
- Lagra referensen till ett motsvarande scenobjekt i en lokal GameObject-referensvariabel med namnet ObjectRef.
Här är de två skriptflödena, ett för varje knapp. Var och en lyssnar på den delade egenskapen Is Selected för en knapps Mesh Interactable Body-komponent och uppdaterar ObjectKind och ObjectRef beroende på vilken knapp som valdes:
Allt verkar fungera bra, men bara för användare som redan är i rummet när en av knapparna väljs. Alla användare som ansluter till sessionen senare hittar ett inkonsekvent tillstånd i sin lokala version av den delade miljön: Endast ObjectKind är korrekt inställt enligt den senast valda knappen, men ObjectRef förblir null.
Vad är det för fel med dessa två skriptflöden?
Observera först att dessa skriptflöden utlöses av en delad händelse eftersom båda lyssnar på varje knapps delade egenskap Är vald ändras. Detta verkar vettigt eftersom det är det enda sättet att få den lokala ObjectRef-variabeln att uppdateras på alla klienter.
Observera följande:
- Delade händelser får inte ändra delat tillstånd , men dessa skriptflöden uppdaterar variabeln shared ObjectKind .
- Delade händelser kan inte ändra det lokala tillståndet så att de är konsekventa mellan klienter , men dessa skriptflöden uppdaterar den lokala ObjectRef-variabeln , som vi tänker vara konsekventa för alla klienter, precis som ObjectKind.
Så som detta för närvarande är konfigurerat bör vi faktiskt inte göra något av de saker vi behöver knapparna för att göra.
Det enda uppenbara sättet att komma ur det här problemet är att göra de händelser som utlöser dessa flöden lokala. Det kan vi göra genom att göra så att händelsenoden Vid tillstånd ändrat observerar egenskapen Är vald lokalt i stället för Är vald.
Eftersom händelsen nu är lokal betyder det...
- Lokala händelser kan ändra delat tillstånd så att vi nu på ett säkert sätt kan uppdatera den delade ObjectKind-variabeln och dess värde delas automatiskt mellan klienter av Mesh Visual Scriptings inbyggda nätverk.
- Lokala händelser kan inte ändra det lokala tillståndet så att de är konsekventa mellan klienter , så vi kan fortfarande inte uppdatera den lokala ObjectRef-variabeln i dessa skriptflöden. Vi måste hitta ett annat sätt.
Så här ser de två skriptflödena ut efter dessa ändringar:
Vad kan vi göra för att ange den lokala ObjectRef-variabeln så att den förblir konsekvent med detta? Lyckligtvis etablerar dessa två skriptflöden redan ett delat tillstånd som vi kan följa: den delade ObjectKind-variabeln . Allt vi behöver göra är att använda en händelse som har ändrats vid tillstånd och som observerar den här variabeln och uppdaterar den lokala ObjectRef-variabeln beroende på dess värde:
Det här är ett bra sätt att göra det på eftersom händelser som observerar delat tillstånd kan ändra det lokala tillståndet så att de överensstämmer med det. Detta fungerar för klienten som tryckte på knappen, för alla andra klienter som finns i samma rum samtidigt och för alla klienter som kommer att ansluta till sessionen senare.
Nätverksgropar
Delade uppdateringar med hög frekvens
Nästan hela scentillståndet delas av Mesh Visual Scripting som standard. Det är bra för delning, men det kan också smyga in av misstag och orsaka onödig nätverksbelastning. Följande skriptflöde översvämmar till exempel nätverket med redundanta uppdateringar av transformeringens rotation. Men eftersom alla klienter kör den samtidigt kommer ingen av fjärruppdateringarna någonsin att ha någon verklig inverkan på någon klient lokalt:
I det här fallet bör du förmodligen använda ett lokalt skriptomfång för att göra transformeringskomponenten lokal för varje klient. Dessutom bör du förmodligen använda en Animator-komponent i stället för ett vid uppdateringsskriptflöde till att börja med.
Panelen Mesh Visual Scripting Diagnostics och Content Analizator performansi (CPA), från och med Mesh Toolkit 5.2411, visar en varning om delad uppdatering med hög frekvens för den här typen av konstruktion.
Vid Startkörningar på varje klient
Du kan vara frestad att se händelsen På start som något som körs vid start av sessionen, men den utlöses faktiskt på varje klient lokalt när de ansluter till sessionen. Det passar perfekt för att initiera lokalt tillstånd:
Men när du försöker använda På start för att initiera delat tillstånd upptäcker du att det delade tillståndet oavsiktligt initieras igen för alla när någon ansluter till sessionen:
Panelen Mesh Visual Scripting Diagnostics (från och med Mesh Toolkit 5.2410) och Content Analizator performansi (CPA) (från och med Mesh Toolkit 5.2411) visar en varning om delad uppdatering vid sessionsanslutning när de identifierar detta.
Delning är skrivet , men variabeltilldelning är inte
Av säkerhets- och säkerhetsskäl skrivs variabler för delade visuella skript starkt. Det innebär att den typ som du väljer i komponenten Variabler för de skriptvariabler som du har deklarerat definierar vilken exakt värdetyp som ska synkroniseras mellan klienter.
Tyvärr ignorerar Unity Visual Scripting helt en variabels deklarerade typ när du uppdaterar dess värde. Det är till exempel enkelt att oavsiktligt lagra ett float-typat värde i en variabel som deklarerades för typen Heltal. I den lokala klienten kommer dina visuella skript inte att märka det här felet eftersom Visual Scripting automatiskt konverterar den felaktiga floaten till det förväntade heltal där det behövs. Men när det gäller att synkronisera det här värdet mellan klienter kan mesh-visualisering inte ta samma friheter: "slutlig konsekvens"-garantin förhindrar alla värdekonverteringar under flygning, och säkerhets- och säkerhetsöverväganden gör det oläsligt att acceptera en annan värdetyp från en fjärrklient än vad som deklarerades för variabeln.
Överväg till exempel den här deklarationen av en delad variabel med namnet MyIntegerVar:
Här är ett skriptflöde som uppdaterar den här variabeln:
Vad kan gå fel? Skriptnoden Random | Range som används i det här exemplet finns tyvärr i två varianter: en som ger ett slumpmässigt heltalsvärde och en som ger ett slumpmässigt float-värde. Skillnaden mellan dessa två skriptnoder i nodväljarpanelen är diskret:
Om du av misstag väljer fel skriptnod för slumpmässigt | intervall där kan skriptet oavsiktligt lagra ett Float-värde i variabeln Heltalstyp, men det felaktiga Float-värdet replikeras inte till andra klienter.
Tänk på detta som en möjlig orsak till att en delad variabel som du har skapat kan ha slutat delas. Framtida versioner av Mesh Visual Scripting kan varna för den här typen av skriptfel när de kan identifiera det.