Compartilhar via


Práticas recomendadas de script visual de malha para rede

Visão geral

Na malha, a maioria das propriedades de cena é, por padrão, compartilhada automaticamente entre todos os clientes conectados à mesma sala. Por exemplo, a posição e a rotação de transformação de um objeto de cena, o estado ativado de um componente ou o texto de um TextMeshPro.

Como regra geral, as propriedades do componente e as variáveis de objeto que têm os seguintes tipos de valor são compartilhadas automaticamente por padrão:

Os tipos de coleção (listas e conjuntos) e as referências de objeto de cena não são compartilhados.

Os nós de script visual que acessam ou modificam propriedades no Mesh são marcados com um rótulo que indica se eles são "Compartilhados por todos os clientes" ou "Local para este cliente":

______________

As variáveis de objeto também são compartilhadas por padrão se você as tiver declarado com um dos tipos de valor listados acima:

______________

A malha não oferece suporte a variáveis de cena, mas você pode usar componentes de variáveis independentes no ambiente para ocultar variáveis que podem ser compartilhadas independentemente de qualquer componente específico da máquina de script.

Se você não quiser o compartilhamento automático de propriedades ou variáveis de objeto, poderá adicionar um componente Escopo de script local à sua cena. Isso tornará todas as propriedades de cena e variáveis de script neste objeto de jogo e em qualquer um de seus descendentes locais.

______________

Dica: Você pode ver vários exemplos de como o componente Local Script Scope é usado no Capítulo 3 do nosso tutorial Mesh 101, que se concentra em scripts visuais.

Para variáveis de script locais que você está usando apenas em uma única máquina de script, é melhor usar variáveis de gráfico, que nunca são compartilhadas entre clientes pelo Mesh.

O compartilhamento por meio do Script Visual de Malha oferece as seguintes garantias:

  • Consistência eventual garantida: todos os clientes eventualmente chegarão ao mesmo estado compartilhado.

  • Atomicidade garantida por componente: todas as atualizações nas propriedades do mesmo componente de cena (ou do mesmo componente Variáveis ) na mesma atualização serão aplicadas atomicamente em cada cliente.

No entanto:

  • Sem garantia de pedido: as atualizações aplicadas por um cliente a vários componentes de cena diferentes podem chegar em pedidos diferentes em clientes diferentes.

  • Sem garantia de pontualidade: o Mesh fará o possível para replicar as alterações de estado entre os clientes o mais rápido possível, mas as condições de rede podem atrasar a chegada de qualquer atualização de estado em alguns ou todos os clientes.

  • Sem garantia de granularidade: qualquer cliente pode não ver todas as atualizações incrementais individuais para o estado compartilhado. Isso pode acontecer quando as condições de rede forçam o servidor Mesh a limitar as atualizações de taxa. Também acontece quando um cliente entra tarde em uma sala.

O estado é compartilhado - não eventos

Você não pode enviar ou receber mensagens de rede explícitas com o Mesh Visual Scripting. Isso pode ser surpreendente no início, mas ajuda a estabelecer um paradigma de rede que facilita o tratamento uniforme de alterações de tempo de execução, bem como a junção tardia. Em vez de mensagens, há um estado compartilhado nas propriedades da cena e nas variáveis de script.

Seus scripts podem responder a atualizações de estado compartilhadas de maneira uniforme, independentemente de essas atualizações terem sido feitas por um script ou usuário local, por outro cliente que compartilha a experiência na mesma sala ou por outros clientes que já estavam na sala antes mesmo de você entrar nela.

Não ser capaz de enviar mensagens de rede explicitamente significa que você terá que começar a pensar no estado compartilhado que recebe atualizações em vez de eventos compartilhados que causam atualizações de estado. Eventos compartilhados são uma consequência da atualização do estado compartilhado, e não o contrário.

Felizmente, o Mesh Visual Scripting facilita a reação de seus scripts visuais a atualizações de estado. Use o nó de evento On State Changed e conecte suas entradas do lado esquerdo com qualquer variável de script ou propriedade de componente que você gostaria de observar para alterações, e o nó de evento acionará seu script (conectado ao seu lado direito) sempre que qualquer uma das variáveis ou propriedades conectadas a ele alterar seu valor.

______________

Isso funciona com o estado compartilhado, bem como com o estado local. O evento On State Changed será acionado independentemente de as variáveis ou propriedades que ele está observando terem sido alteradas pelo cliente local, por um cliente remoto ou até mesmo por um cliente remoto antes mesmo de o cliente local entrar na sala.

Usar On State Changed para responder a alterações de estado é eficiente: não há largura de banda ociosa ou custo de desempenho. Você pode fazer com que qualquer número de scripts visuais ouça passivamente as atualizações de estado dessa maneira sem afetar negativamente a taxa de quadros ou o uso da largura de banda do seu ambiente.

Adesão tardia

A junção tardia acontece quando um cliente entra em uma sala que já tem outros clientes conectados a ela.

Na junção tardia, o Mesh recebe o estado atual da sala do servidor - por exemplo, quem já está na sala e onde seus avatares estão localizados no momento - e prepara rapidamente a versão local do cliente de junção do ambiente compartilhado para que corresponda ao estado compartilhado por todos na sala.

Em grande parte, o Mesh Visual Scripting faz o mesmo. Todas as propriedades de componentes compartilhados e variáveis de script visual que foram alteradas na sala antes de o cliente recém-ingressado são atualizadas localmente para corresponder ao estado compartilhado e, em seguida, todos os nós de evento On State Changed que observam essas propriedades ou variáveis são acionados.

Os participantes atrasados não reproduzem eventos compartilhados - eles obtêm o estado compartilhado.

Do ponto de vista do cliente local, o ambiente sempre evolui de seu estado inicial logo após carregar a cena que você carregou no Mesh. No caso de junção tardia, a primeira alteração de estado pode ser maior do que o que acontece enquanto o usuário local está interagindo com a sala em uma sessão em andamento, mas é exatamente a mesma coisa em princípio.

Tudo isso acontece à medida que o ambiente carrega antes mesmo de desaparecer do preto. Assim que o usuário puder realmente ver e interagir com o ambiente, o late join já está feito.

Fazer com que o estado local siga o estado compartilhado

Muitas vezes, o "estado compartilhado" que um usuário pode observar em um ambiente é, na verdade, uma combinação de estado compartilhado diretamente pelo Mesh e estado local que foi estabelecido por scripts visuais em resposta a um evento que ocorreu na sala. Por exemplo, quando um usuário aciona uma chave no ambiente (estado compartilhado), um script visual pode alterar a cor da skybox (estado local). Você pode ficar tentado a aplicar a alteração local (atualizar a cor do skybox) diretamente em resposta ao usuário que interage com o switch. No entanto, mesmo que o evento de interação ocorra em todos os clientes atualmente na sala, qualquer cliente que entrar na sala mais tarde não receberá esse evento simplesmente porque não estava lá quando aconteceu. Em vez disso, você deve fazer com que o estado local siga o estado compartilhado assim:

  1. Quando o usuário interage (por exemplo, inverte a opção), torne esse gatilho um evento local que atualiza uma variável compartilhada (por exemplo, o estado ativado/desativado da opção).
  2. Use On State Changed para observar a variável compartilhada.
  3. Quando o evento On State Changed for acionado (porque a variável compartilhada alterou seu valor), aplique qualquer alteração local desejada (por exemplo, atualize a cor do skybox).

Dessa forma, o estado local (a cor do skybox) está seguindo o estado compartilhado (o estado do switch). O que é bom nisso é que funciona sem alterações para o cliente local que acionou o interruptor, para todos os outros clientes remotos que estão presentes na sala ao mesmo tempo e para quaisquer futuros clientes que entrarão na sala mais tarde.

Fazer com que o estado local siga o estado compartilhado: práticas recomendadas

Eventos locais: Por exemplo, um nó de evento On State Changed observando a propriedade Is Selected Locally de um componente Mesh Interactable Body :

  • 🆗 Pode alterar o estado local privado para um cliente. Essas alterações de estado permanecerão estritamente no cliente local e desaparecerão quando o cliente sair da sessão.
  • 🆗 Pode alterar o estado compartilhado.
  • Não é possível alterar o estado local para ser consistente entre os clientes. Um evento local é executado apenas em um cliente, portanto, as atualizações necessárias para manter o estado local consistente entre os clientes simplesmente não acontecerão em nenhum outro cliente.

Eventos compartilhados: por exemplo, um nó de evento On Trigger Enter anexado a um colisor de gatilho de física compartilhado:

  • 🆗 Pode alterar o estado local para efeitos momentâneos: por exemplo, um efeito de partícula ou um efeito de áudio curto. Somente os clientes presentes na sala quando o evento compartilhado ocorrer poderão ver o efeito local; qualquer cliente que entrar na sala mais tarde não o fará.
  • Não é possível alterar o estado local para ser consistente entre os clientes. Um evento compartilhado só é executado em clientes que estão presentes no momento em que ocorre, mas não será reproduzido para clientes que ingressarem na sessão posteriormente.
  • Não deve alterar o estado compartilhado. Como um evento compartilhado é executado em todos os clientes, tudo o que ele faz é feito por todos os clientes muito próximos no tempo. Dependendo da natureza da alteração, ela pode acabar sendo repetida várias vezes (por exemplo, um contador de pontuação pode ser incrementado em mais de um em resposta a um único evento).

Em eventos de estado alterado que observam o estado compartilhado em variáveis compartilhadas ou propriedades de componentes compartilhados:

  • 🆗 Pode alterar o estado local para ser consistente com o estado compartilhado entre os clientes. Para que isso funcione bem de maneira repetível e consistente para todos os clientes, você deve traduzir todos os novos valores possíveis do estado compartilhado observado em estado local, não apenas algumas transições de estado escolhidas a dedo (como Is Selected se tornando true).

Fazer com que o estado local siga o estado compartilhado: Exemplo

Neste exemplo, existem dois botões interativos neste ambiente: um rotulado com "Estrela", o outro rotulado com "Esponja". A seleção de qualquer um dos botões deve fazer duas coisas:

  • Armazene o rótulo correspondente em uma variável de cadeia de caracteres compartilhada chamada ObjectKind.
  • Armazene a referência a um objeto de cena correspondente em uma variável de referência GameObject local chamada ObjectRef.

Aqui estão os dois fluxos de script, um para cada botão. Cada um escuta a propriedade compartilhada Is Selected do componente Mesh Interaction Body de um botão e atualiza ObjectKind e ObjectRef dependendo de qual botão foi selecionado:

______________

Tudo parece funcionar bem, mas apenas para usuários que já estão na sala quando um dos botões é selecionado. Qualquer usuário que ingressa na sessão posteriormente encontra um estado inconsistente em sua versão local do ambiente compartilhado: Somente ObjectKind é definido corretamente de acordo com o botão selecionado mais recentemente, mas ObjectRef permanece nulo.

O que há de errado com esses dois fluxos de script?

Primeiro, observe que esses fluxos de script são disparados por um evento compartilhado porque ambos estão ouvindo a alteração da propriedade Is Selected compartilhada de cada botão. Isso parece fazer sentido porque é a única maneira de fazer com que a variável ObjectRef local seja atualizada em todos os clientes.

No entanto:

  • Os eventos compartilhados não devem alterar o estado compartilhado, mas esses fluxos de script estão atualizando a variável ObjectKind compartilhada.
  • Os eventos compartilhados não podem alterar o estado local para serem consistentes entre os clientes, mas esses fluxos de script estão atualizando a variável ObjectRef local, que pretendemos que seja consistente em todos os clientes, assim como ObjectKind.

Então, da maneira como isso está configurado atualmente, na verdade não deveríamos estar fazendo nenhuma das coisas que precisamos que os botões façam.

A única maneira óbvia de sair desse problema é tornar os eventos que acionam esses fluxos locais. Podemos fazer isso fazendo com que o nó de evento On State Changed observe a propriedade Is Selected Locally em vez de Is Selected.

Com o evento agora sendo local, isso significa...

  • Os eventos locais podem alterar o estado compartilhado para que agora possamos atualizar com segurança a variável ObjectKind compartilhada e seu valor será compartilhado automaticamente entre os clientes pela rede integrada do Mesh Visual Scripting.
  • Os eventos locais não podem alterar o estado local para serem consistentes entre os clientes, portanto, ainda não podemos atualizar a variável ObjectRef local nesses fluxos de script. Teremos que encontrar outra maneira.

É assim que os dois fluxos de script ficam após essas alterações:

______________

O que podemos fazer para definir a variável ObjectRef local para que ela permaneça consistente com isso? Felizmente, esses dois fluxos de script já estabelecem algum estado compartilhado que poderíamos seguir: a variável ObjectKind compartilhada. Tudo o que precisamos fazer é usar um evento On State Changed que observa essa variável e atualiza a variável ObjectRef local dependendo de seu valor:

______________

Essa é uma boa maneira de fazer isso porque os eventos On State Changed que observam o estado compartilhado podem alterar o estado local para serem consistentes com ele. Isso funcionará para o cliente que pressionou o botão, para todos os outros clientes presentes na mesma sala ao mesmo tempo e para todos os clientes que ingressarão na sessão posteriormente.

Armadilhas de rede

Atualizações compartilhadas de alta frequência

Quase todo o estado da cena é compartilhado pelo Script Visual de Malha por padrão. Isso é ótimo para compartilhar, mas também pode se infiltrar acidentalmente e causar carga de rede desnecessária. Por exemplo, o fluxo de script a seguir inundará a rede com atualizações redundantes para a rotação da Transformação. No entanto, como todos os clientes estão executando ao mesmo tempo, nenhuma das atualizações remotas terá um impacto real em qualquer cliente localmente:

______________

Nesse caso, você provavelmente deve usar um Escopo de Script Local para tornar o componente Transform local para cada cliente. Além disso, você provavelmente deve usar um componente Animator em vez de um fluxo de script On Update para começar.

O painel Mesh Visual Scripting Diagnostics e o Content Performance Analyzer (CPA), a partir do Mesh Toolkit 5.2411, mostram um aviso de "Atualização compartilhada de alta frequência" para esse tipo de construção.

On Start é executado em cada cliente

Você pode ficar tentado a pensar no evento On Start como algo que é executado na inicialização da sessão, mas na verdade ele é disparado em cada cliente, localmente, quando eles ingressam na sessão. É perfeitamente adequado para inicializar o estado local:

______________

No entanto, ao tentar usar Ao iniciar para inicializar o estado compartilhado, você descobrirá que o estado compartilhado será reinicializado involuntariamente para todos sempre que alguém ingressar na sessão:

______________

O painel Mesh Visual Scripting Diagnostics (a partir do Mesh Toolkit 5.2410) e o Content Performance Analyzer (CPA) (a partir do Mesh Toolkit 5.2411) mostram um aviso "Atualização compartilhada na entrada da sessão" quando detectam isso.

O compartilhamento é digitado, mas a atribuição de variável não é

Por motivos de segurança e proteção, as variáveis de script visual compartilhadas são fortemente tipadas. Isso significa que o tipo selecionado no componente Variáveis para as variáveis de script declaradas define qual tipo de valor exato será sincronizado entre os clientes.

Infelizmente, o Unity Visual Scripting ignora completamente o tipo declarado de uma variável quando você atualiza seu valor. Por exemplo, é fácil armazenar acidentalmente um valor do tipo Float em uma variável que foi declarada para o tipo Integer. No cliente local, seus scripts visuais não perceberão esse erro porque o Visual Scripting converterá automaticamente o Float incorreto no Integer esperado quando necessário. No entanto, quando se trata de sincronizar esse valor entre clientes, o Mesh Visual Scripting não pode tomar as mesmas liberdades: a garantia de "consistência eventual" impede qualquer conversão de valor em andamento, e as considerações de segurança e proteção tornam desaconselhável aceitar um tipo de valor diferente de um cliente remoto do que o que foi declarado para a variável.

Por exemplo, considere esta declaração de uma variável compartilhada chamada MyIntegerVar:

______________

Aqui está um fluxo de script que atualiza essa variável:

______________

O que poderia dar errado? Infelizmente, o nó de script Random | Range usado neste exemplo vem em dois tipos: um que produz um valor Integer aleatório e outro que produz um valor Float aleatório. A diferença entre esses dois nós de script no painel seletor de nós é sutil:

______________

Portanto, se você selecionar acidentalmente o nó de script Random | Range errado, seu script poderá acabar armazenando involuntariamente um valor Float em sua variável do tipo Integer, mas esse valor Float incorreto não será replicado para nenhum outro cliente.

Lembre-se disso como um possível motivo pelo qual uma variável compartilhada que você criou pode parecer ter parado de ser compartilhada. Versões futuras do Mesh Visual Scripting podem alertar sobre esse tipo de erro de script quando puderem detectá-lo.

Próximas etapas