Procedure consigliate per la creazione di script visivi mesh per la rete
Panoramica
In Mesh la maggior parte delle proprietà della scena viene condivisa automaticamente per impostazione predefinita tra tutti i client connessi alla stessa stanza. Ad esempio, la posizione e la rotazione di un oggetto scena, lo stato abilitato di un componente o il testo di TextMeshPro.
Come regola generale, le proprietà dei componenti e le variabili Object con i tipi di valore seguenti vengono condivise automaticamente per impostazione predefinita:
- Boolean, Integer, Float e String
- Color
- Rect
- Vettore 2, Vettore 3 e Vettore 4
- Quaternione
- Matrice 4x4
I tipi di raccolta (elenchi e set) e i riferimenti agli oggetti scena non vengono condivisi.
I nodi di script visivi che accedono o modificano le proprietà in Mesh vengono contrassegnati con un'etichetta che indica se sono "Condivisi da tutti i client" o "Local to this client":
Le variabili oggetto vengono condivise anche per impostazione predefinita se sono state dichiarate con uno dei tipi di valore elencati in precedenza:
Mesh non supporta le variabili scene, ma è possibile usare componenti variabili autonome nell'ambiente per sfalsare le variabili che possono essere condivise in modo indipendente da qualsiasi componente specifico del computer di script.
Se non si vuole condividere automaticamente le proprietà o le variabili Object, è possibile aggiungere un componente Ambito script locale alla scena. In questo modo tutte le proprietà della scena e le variabili di script su questo oggetto gioco e su uno dei relativi discendenti locali.
Suggerimento: è possibile vedere diversi esempi di come viene usato il componente Ambito script locale nel capitolo 3 dell'esercitazione mesh 101 incentrata sullo scripting visivo.
Per le variabili di script locali usate solo in un singolo computer di script, è consigliabile usare le variabili graph, che non vengono mai condivise tra i client da Mesh.
La condivisione tramite script di oggetti visivi mesh offre le garanzie seguenti:
Coerenza finale garantita: tutti i client arriveranno allo stesso stato condiviso.
Atomicità garantita per componente: tutti gli aggiornamenti alle proprietà dello stesso componente della scena (o dello stesso componente Variabili) nello stesso aggiornamento verranno applicati in modo atomico in ogni client.
Tuttavia:
Nessuna garanzia di ordinamento: gli aggiornamenti applicati da un client a diversi componenti della scena possono arrivare in ordini diversi su client diversi.
Nessuna garanzia di tempestività: Mesh tenterà di replicare le modifiche di stato tra i client il più rapidamente possibile, ma le condizioni di rete possono ritardare l'arrivo di un determinato aggiornamento dello stato in alcuni o tutti i client.
Nessuna garanzia di granularità: qualsiasi client potrebbe non visualizzare tutti i singoli aggiornamenti incrementali allo stato condiviso. Ciò può verificarsi quando le condizioni di rete forzano il server Mesh a limitare la frequenza degli aggiornamenti. Si verifica anche quando un cliente si unisce in ritardo a una stanza.
Lo stato è eventi non condivisi
Non è possibile inviare o ricevere messaggi di rete espliciti con gli script visivi mesh. Questo potrebbe essere sorprendente in un primo momento, ma consente di stabilire un paradigma di rete che semplifica la gestione uniforme delle modifiche di runtime e del join tardivo. Invece dei messaggi, lo stato condiviso è nelle proprietà della scena e nelle variabili di script.
Gli script possono rispondere agli aggiornamenti dello stato condiviso in modo uniforme indipendentemente dal fatto che tali aggiornamenti fossero stati da uno script locale o da un utente, da un altro client che condivide l'esperienza nella stessa stanza o da altri client già presenti nella stanza prima di aggiungerli a se stessi.
Non essere in grado di inviare in modo esplicito messaggi di rete significa che è necessario iniziare a pensare allo stato condiviso che ottiene gli aggiornamenti anziché gli eventi condivisi che causano aggiornamenti dello stato. Gli eventi condivisi sono una conseguenza dell'aggiornamento dello stato condiviso, non dell'opposto.
Fortunatamente, lo scripting degli oggetti visivi mesh semplifica la reazione degli script visivi agli aggiornamenti dello stato. Usare il nodo evento On State Changed e connettere gli input sul lato sinistro con qualsiasi variabile di script o proprietà del componente che si vuole osservare per le modifiche e il nodo evento attiverà lo script (connesso al lato destro) ogni volta che una delle variabili o delle proprietà connesse cambia il valore.
Funziona con lo stato condiviso e con lo stato locale. L'evento On State Changed viene attivato indipendentemente dal fatto che le variabili o le proprietà che osservano siano state modificate dal client locale, da un client remoto o anche da un client remoto prima che il client locale si aggiunse anche alla sala.
L'uso di On State Changed per rispondere alle modifiche dello stato è efficiente: non è previsto alcun costo di larghezza di banda o prestazioni inattive. È possibile avere un numero qualsiasi di script visivi in ascolto passivo degli aggiornamenti dello stato in questo modo senza influire negativamente sulla frequenza dei fotogrammi o sull'uso della larghezza di banda dell'ambiente.
Join in ritardo
Il join in ritardo si verifica quando un client si unisce a una sala che dispone già di altri client connessi.
Al momento del join, Mesh riceve lo stato corrente della stanza dal server, ad esempio chi è già nella stanza e dove si trovano i loro avatar e prepara rapidamente la versione locale del client di join dell'ambiente condiviso in modo che corrisponda allo stato condiviso da tutti nella stanza.
Per una parte importante, lo scripting degli oggetti visivi mesh funziona allo stesso modo. Tutte le proprietà del componente condiviso e le variabili dello script visivo modificate nella stanza prima che il client appena aggiunto vengano aggiornate localmente in modo che corrispondano allo stato condiviso e quindi vengono attivati tutti i nodi eventi On State Changed che osservano queste proprietà o variabili.
I join in ritardo non replayno eventi condivisi, ma ottengono lo stato condiviso.
Dal punto di vista del client locale, l'ambiente si evolve sempre dal suo stato iniziale appena dopo il caricamento della scena caricata in Mesh. Nel caso di join in ritardo, il primo cambiamento di stato può essere maggiore di quello che accade mentre l'utente locale interagisce con la stanza in una sessione in corso, ma è la stessa cosa in linea di principio.
Tutto questo accade quando l'ambiente si carica prima che si dissolve anche dal nero. Non appena l'utente può effettivamente vedere e interagire con l'ambiente, l'aggiunta tardiva è già stata eseguita.
Rendere lo stato locale seguire lo stato condiviso
Molto spesso, lo "stato condiviso" che un utente può osservare in un ambiente è in realtà una combinazione di stato condiviso direttamente da Mesh e stato locale stabilito dagli script visivi in risposta a un evento che si è verificato nella stanza. Ad esempio, quando un utente capovolge un commutatore nell'ambiente (stato condiviso), uno script visivo potrebbe modificare il colore del skybox (stato locale). Si potrebbe essere tentati di applicare la modifica locale (aggiornare il colore skybox) direttamente in risposta all'utente che interagisce con il commutatore. Tuttavia, anche se l'evento di interazione si verifica su tutti i client attualmente presenti nella sala, qualsiasi client che si unisce alla sala in un secondo momento non otterrà l'evento semplicemente perché non erano presenti quando si è verificato. È invece consigliabile impostare lo stato locale come segue:
- Quando l'utente interagisce (ad esempio, capovolge l'opzione), imposta questo trigger su un evento locale che aggiorna una variabile condivisa ,ad esempio lo stato on/off dell'opzione.
- Usare On State Changed per osservare la variabile condivisa.
- Quando l'evento On State Changed viene attivato (perché la variabile condivisa ne ha modificato il valore), applicare qualsiasi modifica locale desiderata (ad esempio, aggiornare il colore skybox).
In questo modo, lo stato locale (colore skybox) segue lo stato condiviso (lo stato dell'opzione). Ciò che è bello di questo è che funziona senza modifiche per il client locale che ha capovolto il commutatore, per tutti gli altri client remoti presenti nella stanza contemporaneamente, e per tutti i futuri clienti che si uniranno alla sala in un secondo momento.
Rendere lo stato locale seguire lo stato condiviso: Procedure consigliate
Eventi locali: ad esempio, un nodo di evento On State Changed che osserva la proprietà Is Selected Local di un componente Corpo interagendo mesh:
- 🆗 Può modificare lo stato locale privato in un client. Queste modifiche di stato rimarranno rigorosamente sul client locale e svaniranno quando il client lascia la sessione.
- 🆗 Può modificare lo stato condiviso.
- ❌Impossibile modificare lo stato locale in modo che sia coerente tra i client. Un evento locale viene eseguito solo in un client, quindi gli aggiornamenti necessari per mantenere coerente lo stato locale tra i client non si verificheranno semplicemente in nessun altro client.
Eventi condivisi: ad esempio, un nodo evento On Trigger Enter collegato a un collisore di trigger di fisica condivisa:
- 🆗 Può cambiare lo stato locale per gli effetti momentanei: ad esempio, un effetto particella o un breve effetto audio. Solo i client presenti nella sala quando si verifica l'evento condiviso potranno visualizzare l'effetto locale; tutti i clienti che si uniscono alla stanza in un secondo momento non lo faranno.
- ❌Impossibile modificare lo stato locale in modo che sia coerente tra i client. Un evento condiviso viene eseguito solo nei client presenti al momento in cui si verifica, ma non verrà riprodotto per i client che accedono alla sessione in un secondo momento.
- ⛔ Non deve modificare lo stato condiviso. Poiché un evento condiviso viene eseguito in tutti i client, tutto ciò che fa viene eseguito da tutti i client molto vicini nel tempo. A seconda della natura della modifica, potrebbe venire ripetuto più volte (ad esempio, un contatore dei punteggi potrebbe essere incrementato di più di uno in risposta a un singolo evento).
Negli eventi State Changed che osservano lo stato condiviso nelle variabili condivise o nelle proprietà del componente condiviso:
- 🆗 Può modificare lo stato locale in modo che sia coerente con lo stato condiviso tra i client. Affinché questo funzioni bene in modo ripetibile e coerente per tutti i client, è necessario tradurre ogni possibile nuovo valore dello stato condiviso osservato nello stato locale, non solo in alcune transizioni di stato selezionata ciliegia (ad esempio è selezionato diventando true).
Impostare lo stato locale seguendo lo stato condiviso: Esempio
In questo esempio sono presenti due pulsanti interattivi in questo ambiente: uno con etichetta "Star", l'altro con etichetta "Sponge". La selezione di uno dei pulsanti dovrebbe eseguire due operazioni:
- Archiviare l'etichetta corrispondente in una variabile stringa condivisa denominata ObjectKind.
- Archiviare il riferimento a un oggetto scena corrispondente in una variabile di riferimento GameObject locale denominata ObjectRef.
Ecco i due flussi di script, uno per ogni pulsante. Ogni oggetto è in ascolto della proprietà Shared Is Selected del componente Mesh Interactable Body di un pulsante e aggiorna ObjectKind e ObjectRef a seconda del pulsante selezionato:
Tutto sembra funzionare bene, ma solo per gli utenti che si trovano già nella stanza quando viene selezionato uno dei pulsanti. Qualsiasi utente che partecipa alla sessione rileva in seguito uno stato incoerente nella versione locale dell'ambiente condiviso: Solo ObjectKind è impostato correttamente in base al pulsante selezionato più di recente, ma ObjectRef rimane Null.
Cosa c'è di sbagliato con questi due flussi di script?
Prima di tutto, si noti che questi flussi di script vengono attivati da un evento condiviso perché entrambi sono in ascolto della modifica della proprietà Is Selected condivisa di ogni pulsante. Questo sembra avere senso perché è l'unico modo per aggiornare la variabile ObjectRef locale in tutti i client.
Tuttavia:
- Gli eventi condivisi non devono modificare lo stato condiviso, ma questi flussi di script aggiornano la variabile ObjectKind condivisa.
- Gli eventi condivisi non possono modificare lo stato locale in modo che siano coerenti tra i client, ma questi flussi di script aggiornano la variabile ObjectRef locale, che intende essere coerente in tutti i client, proprio come ObjectKind.
Quindi il modo in cui è attualmente configurato, in realtà non dovremmo fare una delle cose che abbiamo bisogno dei pulsanti da fare.
L'unico modo ovvio per uscire da questo problema consiste nel rendere locali gli eventi che attivano questi flussi. A tale scopo, è possibile fare in modo che il nodo evento On State Changed osservi la proprietà Is Selected Local anziché Is Selected.
Con l'evento che ora è locale, questo significa...
- Gli eventi locali possono modificare lo stato condiviso in modo da poter aggiornare in modo sicuro la variabile ObjectKind condivisa e il relativo valore verrà condiviso automaticamente tra i client tramite la rete predefinita di Mesh Visual Scripting.
- Gli eventi locali non possono modificare lo stato locale in modo che siano coerenti tra i client, quindi non è comunque possibile aggiornare la variabile ObjectRef locale in questi flussi di script. Dobbiamo trovare un altro modo.
Questo è il modo in cui i due flussi di script vengono esaminati dopo queste modifiche:
Cosa è possibile fare per impostare la variabile ObjectRef locale in modo che rimanga coerente con questa impostazione? Fortunatamente, questi due flussi di script stabiliscono già uno stato condiviso che è possibile seguire: la variabile ObjectKind condivisa. Tutto ciò che dobbiamo fare è usare un evento On State Changed che osserva questa variabile e aggiorna la variabile ObjectRef locale a seconda del relativo valore:
Questo è un modo ottimale per farlo perché gli eventi On State Changed che osservano lo stato condiviso possono modificare lo stato locale in modo che sia coerente con esso. Questo funzionerà per il client che ha premuto il pulsante, per tutti gli altri client presenti nella stessa sala contemporaneamente e per tutti i client che si uniranno alla sessione in un secondo momento.
Problemi di rete
Aggiornamenti condivisi ad alta frequenza
Quasi l'intero stato della scena è condiviso da Mesh Visual Scripting per impostazione predefinita. È ideale per la condivisione, ma può anche intrufolarsi per errore e causare carico di rete non necessario. Ad esempio, il flusso di script seguente inonda la rete con aggiornamenti ridondanti alla rotazione della trasformazione. Tuttavia, poiché tutti i client lo eseguono contemporaneamente, nessuno degli aggiornamenti remoti avrà mai un impatto effettivo su qualsiasi client in locale:
In questo caso, è consigliabile usare probabilmente un ambito script locale per rendere il componente Transform locale in ogni client. Inoltre, è consigliabile usare un componente Animator anziché un flusso di script On Update per iniziare con.
Il pannello Mesh Visual Scripting Diagnostics (CPA) e content analizzatore prestazioni (CPA), a partire da Mesh Toolkit 5.2411, mostra un avviso "Aggiornamento condiviso ad alta frequenza" per questo tipo di costrutto.
All'avvio viene eseguito in ogni client
Si potrebbe essere tentati di considerare l'evento On Start come qualcosa che viene eseguito all'avvio della sessione, ma viene effettivamente attivato in ogni client, localmente, quando si uniscono alla sessione. È perfettamente adatto per l'inizializzazione dello stato locale:
Tuttavia, quando si tenta di usare On Start per inizializzare lo stato condiviso, si scopre che lo stato condiviso verrà involontariamente inizializzato per tutti ogni volta che chiunque partecipa alla sessione:
Il pannello Mesh Visual Scripting Diagnostics (a partire da Mesh Toolkit 5.2410) e Content analizzatore prestazioni (CPA) (a partire da Mesh Toolkit 5.2411) mostra un avviso "Aggiornamento condiviso al join di sessione" quando rilevano questo errore.
La condivisione è tipizzata, ma l'assegnazione di variabili non è
Per motivi di sicurezza e sicurezza, le variabili di script visivi condivisi sono fortemente tipate. Ciò significa che il tipo selezionato nel componente Variabili per le variabili di script dichiarate definisce quale tipo di valore esatto verrà sincronizzato tra i client.
Sfortunatamente, Visual Scripting di Unity ignora completamente il tipo dichiarato di una variabile quando si aggiorna il relativo valore. Ad esempio, è facile archiviare accidentalmente un valore tipizzato Float in una variabile dichiarata per il tipo Integer. All'interno del client locale, gli script visivi non noteranno questo errore perché Visual Scripting convertirà automaticamente il valore Float errato nell'intero previsto, se necessario. Tuttavia, quando si tratta di sincronizzare questo valore tra i client, gli script visivi mesh non possono accettare le stesse libertà: la garanzia di "coerenza finale" impedisce qualsiasi conversione di valori in anteprima e le considerazioni sulla sicurezza e la sicurezza rendono impossibile accettare un tipo di valore diverso da un client remoto rispetto a quello dichiarato per la variabile.
Si consideri ad esempio questa dichiarazione di una variabile condivisa denominata MyIntegerVar:
Ecco un flusso di script che aggiorna questa variabile:
Cosa potrebbe andare storto? Sfortunatamente, il nodo di script Intervallo casuale | usato in questo esempio include due versioni: una che produce un valore Integer casuale e una che produce un valore Float casuale. La differenza tra questi due nodi di script nel pannello del selettore di nodi è sottile:
Pertanto, se si seleziona accidentalmente il nodo di script Casual | Range errato, lo script potrebbe finire per archiviare involontariamente un valore Float nella variabile di tipo Integer, ma tale valore Float errato non verrà replicato in altri client.
Tenere presente questo aspetto come potenziale motivo per cui una variabile condivisa creata potrebbe aver smesso di essere condivisa. Le versioni future di Mesh Visual Scripting potrebbero segnalare questo tipo di errore di script quando possono rilevarlo.