Este guia descreve várias técnicas para usar o streaming de instâncias de forma eficiente e eficaz dentro do jogo. Embora não exista uma solução "tamanho único" para projetar um jogo de streaming ou converter um jogo não streaming em streaming, seguir estas etapas de alto nível o levará na maior parte do caminho.
Propriedades de streaming
Assim que StreamingEnabled estiver ativado para o objeto Workspace no Studio, defina suas propriedades relacionadas para os seguintes valores recomendados:
| Propriedade | Recomendação |
|---|---|
| EnableSLIMAvatars | Use Enabled para renderizar avatares de rig padrão como substitutos animados leves quando apropriado. Veja avatares SLIM para mais informações. |
| ModelStreamingBehavior | Use Improved para habilitar o streaming mais eficiente para Models com descendentes de BasePart. |
| StreamingIntegrityMode | Use PauseOutsideLoadedArea para equilibrar a integridade do jogo sem pausar desnecessariamente ou com frequência excessiva. |
| StreamingMinRadius | Use o padrão de 64 para maximizar quanto o motor pode escalonar o jogo para dispositivos de baixo desempenho. |
| StreamingTargetRadius | Use o padrão de 1024 para encontrar um bom equilíbrio entre visibilidade para jogadores em dispositivos de alto desempenho e uma pegada de memória razoável. |
| StreamOutBehavior | Use Opportunistic para permitir que o cliente colete conteúdo de forma agressiva, reduzindo significativamente o uso de memória e ajudando a prevenir falhas de falta de memória. |
Nível de detalhe do modelo
Model.LevelOfDetail ajuda a preencher conteúdo de Model não transmitido com malhas compostas ou impostoras leves, fazendo o mundo parecer visualmente completo. Malhas compostas SLIM (Modelo Leve Interativo Escalável) são particularmente eficazes, pois os jogadores muitas vezes não conseguem distinguir uma malha SLIM do original totalmente transmitido.
Para melhores resultados:
- Agrupe partes que estão espacial e logicamente relacionadas, por exemplo, todas as partes de um carro.
- Defina LevelOfDetail como SLIM em modelos que contêm malhas e partes estáticas. Modelos que contêm malhas enfeitadas, são modificados em tempo de execução ou reproduzem animações não são suportados.
- Mantenha a extensão espacial de cada modelo abaixo de ~64 studs cúbicos para aumentar a probabilidade de que o modelo real inteiro seja transmitido junto. Se um modelo tiver extensões muito grandes, divida-o em modelos modulares menores e aplique um LevelOfDetail apropriado a cada um.
Estrutura do modelo
Além de definir o nível de detalhe do modelo, a estrutura e configurações de seus Models têm um impacto significativo sobre o quão bem o streaming funciona. Ao desenvolver ou converter um jogo existente:
Use modelos atômicos para agrupamento lógico — Quando um script precisa acessar todas as partes dentro de um modelo, defina seu ModelStreamingMode como Atomic. Isso permite que scripts do lado do cliente acessem com segurança instâncias dentro do modelo sem o uso excessivo de WaitForChild() (embora tais scripts ainda devam usar WaitForChild() para o modelo atômico geral).
Minimize modelos persistentes — Modelos Persistentes são carregados após a entrada e nunca saem do streaming, ocupando permanentemente memória. Defina o ModelStreamingMode de um modelo como Persistent apenas se ele deve permanecer disponível e acessível a scripts em todos os momentos.
Decomponha modelos de contêiner — Um padrão comum não streaming é um único Model gigante que contém muitos NPCs, objetos ou agrupamentos similares. Sob streaming, modelos de contêiner diminuem a eficiência do streaming e não são ideais para o nível de detalhe do modelo que funciona melhor com instâncias agrupadas de forma próxima. Decomponha modelos de contêiner em modelos menores com partes fisicamente próximas ou logicamente relacionadas.
Achate hierarquias de modelos profundamente aninhadas — Aninhar um modelo persistente dentro de um modelo atômico efetivamente força o modelo atômico a se comportar como persistente. Hierarquias planas são mais fáceis de entender sob streaming.
Avatares SLIM
Avatares da plataforma fora da área atualmente transmitida não são visíveis por padrão, mas habilitar Workspace.EnableSLIMAvatars renderiza avatares de rig padrão como substitutos animados leves quando apropriado. De forma eficaz, o motor:
- Renderiza uma versão SLIM quando um modelo de avatar real é transmitido para fora.
- Alterna entre SLIM/real com base nos recursos disponíveis dentro do raio de streaming, uma vez que os modelos SLIM podem ser significativamente menos custosos para renderizar do que o modelo de alta resolução.
- Regula as animações SLIM pela importância da cena e largura de banda.
O sistema de nível de detalhe do avatar tem otimizações específicas para avatares. A seguir, descreve o que será ou não processado pelo SLIM:
Padrões de script
Os seguintes padrões de script são mais comumente afetados pelo streaming. A estratégia correta depende da intenção do código, portanto, cada padrão lista várias opções, quando apropriado.
Índice direto para descendentes
Indexar descendentes de Workspace com o operador . gera um erro se qualquer instância no caminho não estiver atualmente transmitida. O mesmo se aplica a FindFirstChild(), FindFirstChildWhichIsA() e FindFirstChildOfClass() que retornam nil se o filho não foi transmitido.
Consulta de Descendenteslocal house1 = workspace:FindFirstChild("House1") -- nil se "House1" não foi transmitidolocal door = workspace.House1.Door -- Quebrado se "House1" ou "Door" não foi transmitido
Um padrão semelhante é o acesso a descendentes de Humanoid ou outros descendentes de personagem diretamente dentro de uma conexão de Player.CharacterAdded. Sob streaming, o modelo do personagem é paiado a Workspace antes que todos os seus descendentes sejam replicados, portanto, a indexação direta falha.
Descendentes do Personagem
local Players = game:GetService("Players")
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character.Humanoid
end)
Se o script não puder avançar sem uma instância, espere por ela com WaitForChild():
Consulta de Descendenteslocal house1 = workspace:WaitForChild("House1")local door = house1:WaitForChild("Door")
Instâncias enviadas remotamente
Um sinal de RemoteEvent/RemoteFunction e a instância a que ele se refere viajam de forma independente, portanto, o sinal pode chegar ao cliente antes que a instância esteja presente — ou a instância pode nunca estar presente. Duas causas prováveis incluem:
Sob streaming, pode haver um pequeno atraso entre quando uma parte/modelo é criada no servidor e quando ela é replicada para os clientes. De forma eficaz, uma parte referenciada por um RemoteEvent/RemoteFunction pode simplesmente não existir ainda, mesmo dentro de uma área transmitida.
Enviar uma referência de parte/modelo do servidor para o cliente através de um RemoteEvent ou RemoteFunction exige que a instância seja replicada para o cliente que a recebe. Enviar um caminho de instância como uma string tem o mesmo problema, já que o caminho pode se resolver em um local inexistente no cliente:
Script do Clientelocal ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")remoteEvent.OnClientEvent:Connect(function(data)local checkpoint = data.checkpoint -- Erros se "checkpoint" não estiver transmitidolocal level = workspace.Levels[data.levelPath] -- Erros se o caminho não estiver transmitidoend)
Se o script do cliente precisa da instância para prosseguir, inclua WaitForChild() antes de usá-la. Observe que isso pode render um yield indefinido se a instância nunca for transmitida, então considere adicionar um tempo limite como o segundo parâmetro de WaitForChild().
Desincronização do cliente
A desincronização do lado do cliente deve ser tratada como uma exceção, não como um padrão de design. Introduzir cópias somente do cliente ou reapear instâncias localmente pode criar sérios problemas. Audite seu código em busca de lugares que dependem desses tipos de alterações persistindo no cliente.
Por exemplo, reapear uma instância localmente de ReplicatedStorage para Workspace pode tornar essa instância elegível para ser transmitida para fora. Da mesma forma, clonar uma instância localmente (Instance:Clone()) de ReplicatedStorage para Workspace cria uma cópia apenas do cliente que não faz mais parte do pipeline de replicação do servidor e não receberá atualizações de propriedades da instância de propriedade do servidor original.
O mesmo conceito se aplica ao chamar Instance:Destroy() no cliente para um objeto de propriedade do servidor. Isso remove a instância localmente, mas o servidor ainda a possui, então ela será transmitida novamente em seu estado original quando for elegível.
Streaming proativo
Quando o próximo destino de um jogador pode ser antecipado, faça chamadas do lado do servidor para Player:RequestStreamAroundAsync() para transmitir áreas transitórias para carregamento temporário, ou use Player:AddReplicationFocus() de forma limitada para áreas que devem permanecer carregadas até serem liberadas explicitamente.
Por exemplo, quando um personagem jogador está prestes a teleportar para a casa de outro jogador em uma localização distante, você pode pré-buscar a área do destino para minimizar o pop-in e proporcionar uma transição mais suave. O seguinte script mostra como um evento remoto de cliente para servidor pode ser disparado para mover um personagem jogador usando um método de pré-busca. Se a solicitação de pré-busca for bem-sucedida quando a função retornar, o raio mínimo em torno da localização alvo deve estar presente no cliente.
Script do Servidor - Teleportar Personagem do Jogador
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- Solicitar streaming ao redor da localização alvo
player:RequestStreamAroundAsync(teleportTarget)
-- Teleportar personagem
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- Chame a função de teleportar quando o cliente disparar o evento remoto
teleportEvent.OnServerEvent:Connect(teleportPlayer)
Leituras de propriedade de instância
Uma vez que uma instância é transmitida para fora, suas atualizações de propriedades não são mais replicadas para aquele cliente. Ler propriedades como BasePart.Position continua a ter sucesso, mas retorna o último valor replicado, que pode estar obsoleto.
local Players = game:GetService("Players")local player = Players.LocalPlayer-- A posição pode estar obsoleta se "target" foi transmitido para foralocal dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude
Mova a lógica para o servidor, pois scripts do lado do servidor veem todas as instâncias o tempo todo. Esta é geralmente a opção mais confiável para verificações de distância e outras lógicas sensíveis à posição.
Esperando no caminho crítico
Alguns jogos não streaming carregam seu mapa clonando-o de ReplicatedStorage para Workspace, e depois esperam por ele no cliente antes de dispensar uma tela de carregamento e sinalizar a prontidão. Sob streaming, isso pausa indefinidamente — o personagem do cliente ainda não foi gerado, portanto, não há foco de replicação, e a instância do mapa espacial nunca entra em streaming.
Mova a lógica da tela de carregamento para que ela não dependa de uma instância espacial específica estar presente, por exemplo, sinalizando a prontidão uma vez que o personagem tenha surgido e a área imediata esteja transmitida.
Manipulação de mudanças de sinal
Sinais como Instance.ChildAdded/Instance.ChildRemoved e sinais de CollectionService como GetInstanceAddedSignal() ou GetInstanceRemovedSignal() também são disparados em streaming para dentro/fora, indistinguíveis de verdadeiros surgimentos/remocões. Scripts que recebem não conseguem dizer a diferença apenas pelo sinal, portanto, lógica que assume que um sinal corresponde a um evento "real" precisa ser atualizada.
Audite scripts para qualquer ouvinte de sinal que possa quebrar ou mudar significativamente quando acionado por streaming para dentro e/ou streaming para fora. Por exemplo, se você reproduzir áudio ou efeitos visuais quando um NPC inimigo surge inicialmente no mundo, atribua a cada inimigo um atributo como Spawned no primeiro surgimento e pule a reprodução do mesmo áudio/efeitos em futuros streames de entrada do inimigo.
Rastreamento de Atributos
local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "Enemy"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- Defina o atributo "Spawned" no inimigo para o surgimento inicial
enemy:SetAttribute("Spawned", true)
-- Reproduza efeitos de áudio/vídeo para este surgimento inicial
playSpawnEffects(enemy)
end
end)
Iteração sobre coleções
Iterações de coleções do lado do cliente, como Instance:GetChildren() e Instance:GetDescendants(), retornam apenas o subconjunto de descendentes transmitidos. Isso se aplica mesmo quando o pai em si é sempre replicado, como uma Folder diretamente sob Workspace cujos descendentes espaciais entram e saem do streaming.
local Players = game:GetService("Players")local player = Players.LocalPlayer-- A pasta "Homes" é sempre replicada, mas seus filhos entram e saem do streaming-- Este loop pode perder casas que não estão atualmente transmitidasfor _, home in workspace.Homes:GetChildren() doif home.Settings.Owner.Value == player.Name thenreturn homeendend
Se uma enumeração completa for necessária, execute a varredura no servidor e passe o resultado para o jogador através de um RemoteEvent, se necessário.
Consultas espaciais
Consultas espaciais do lado do cliente, como WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox() e Model:GetBoundingBox(), refletem apenas o conteúdo transmitido. Se isso é um problema depende do que a consulta é usada.
Use o servidor para consultas cujo resultado deve refletir o mundo completo, por exemplo, um raycast que verifica se o jogador tem linha de visão para um alvo distante.
Outros padrões
Os seguintes padrões também podem se aplicar e devem ser cuidadosamente considerados:
Um Sound ou AudioPlayer paiado a um objeto 3D para quando aquele objeto sai do streaming. Para áudio ambiente que deve persistir independentemente do streaming, pai o emissor a um modelo persistente ou a um contêiner não streaming.
Objetos de UI no jogo, como BillboardGui ou SurfaceGui, bem como efeitos visuais como Beams ou Highlights, cujo adornee ou anexo sai de streaming, simplesmente param de renderizar. Este pode ser o comportamento pretendido, mas você deve verificar isso.
Eventos BasePart.Touched, ProximityPrompts, DragDetectors, e ClickDetectors não operam para jogadores cujo cliente não tem a parte/modelo associado transmitido. Se a interação deve ser possível a qualquer distância, o modelo precisa ser persistente ou a interação deve ter um mecanismo diferente.
Para PathfindingService e pathfinding do lado do cliente, o caminho só vê geometria transmitida no cliente e pode roteá-lo através de obstáculos que existem no servidor. Veja aqui para estratégias.
Condições de teste realistas
Uma vez que os scripts são atualizados, teste o jogo exaustivamente. Bugs de streaming muitas vezes se manifestam apenas nas bordas da área transmitida ou durante transições, portanto, testar apenas próximo ao spawn ou ao raio alvo não é suficiente.
Teste com Workspace.StreamingTargetRadius definido para seu valor mínimo (64). Alguns bugs de streaming só aparecem quando a área transmitida é pequena.
Jogue por todos os padrões de travessia do jogo, teleporte entre áreas distantes e revisite áreas após deixá-las. Estas são as situações que exercitam mais o streaming para dentro e para fora.
Use a sobreposição de depuração de streaming para monitorar as configurações de streaming ativas, regiões atualmente carregadas e o estado de streaming em tempo de execução.
Assista à janela Output e ao Console do Desenvolvedor em busca de erros, pois muitos dos padrões de script produzem erros em vez de comportamento silencioso inadequado. Preste especial atenção a erros da forma attempt to index nil with ... que muitas vezes indicam uma chamada ausente de WaitForChild().
Equipe e ative Tools, dispare armas e desencadeie diferentes interações do jogo.

