Esta guía describe varias técnicas para utilizar el streaming de instancias en el juego de manera eficiente y efectiva. Si bien no hay una solución "talla única" para diseñar un juego en streaming o convertir un juego que no sea de streaming a streaming, seguir estos pasos de alto nivel te llevará la mayor parte del camino.
Propiedades de streaming
Una vez que StreamingEnabled se activa para el objeto Workspace en Studio, establece sus propiedades relacionadas en los siguientes valores recomendados:
| Propiedad | Recomendación |
|---|---|
| EnableSLIMAvatars | Utiliza Enabled para renderizar avatares de rig estándar como versiones ligeras y animadas cuando sea apropiado. Consulta avatares SLIM para más información. |
| ModelStreamingBehavior | Utiliza Improved para habilitar el streaming más eficiente para Models con descendientes de BasePart. |
| StreamingIntegrityMode | Utiliza PauseOutsideLoadedArea para equilibrar la integridad del juego sin pausar innecesariamente o con demasiada frecuencia. |
| StreamingMinRadius | Utiliza el valor predeterminado de 64 para maximizar cuánto puede escalar el motor del juego para dispositivos de gama baja. |
| StreamingTargetRadius | Utiliza el valor predeterminado de 1024 para lograr un buen equilibrio entre visibilidad para jugadores en dispositivos de gama alta y un uso razonable de memoria. |
| StreamOutBehavior | Utiliza Opportunistic para permitir que el cliente recolecte agresivamente contenido no utilizado, reduciendo significativamente el uso de memoria y ayudando a prevenir bloqueos por falta de memoria. |
Nivel de detalle del modelo
Model.LevelOfDetail ayuda a completar el contenido de Model no transmitido con mallas compuestas ligeras o impostores, haciendo que el mundo se vea visualmente completo. Las mallas compuestas SLIM (Modelo Interactivo Escalable y Ligero) son particularmente efectivas, ya que los jugadores a menudo no pueden distinguir una malla SLIM del original totalmente transmitido.
Para obtener los mejores resultados:
- Agrupa partes que son espacial y lógicamente relacionadas, por ejemplo, todas las partes de un coche.
- Establece LevelOfDetail en SLIM en modelos que contienen mallas y partes estáticas. Los modelos que contienen mallas esculpidas, son modificados en tiempo de ejecución o reproducen animaciones no son compatibles.
- Mantén la extensión espacial de cada modelo por debajo de ~64 studs cúbicos para aumentar la probabilidad de que todo el modelo real se transmita junto. Si un modelo tiene extensiones muy grandes, divídelo en modelos modulares más pequeños y aplica un LevelOfDetail apropiado a cada uno.
Estructura del modelo
Más allá de establecer el nivel de detalle del modelo, la estructura y las configuraciones de tus Models tienen un impacto significativo en el rendimiento del streaming. A medida que desarrollas o conviertes un juego existente:
Usa modelos atómicos para agrupamiento lógico — Cuando un script necesita acceso a todas las partes dentro de un modelo, establece su ModelStreamingMode en Atomic. Esto permite que los scripts del lado del cliente accedan de manera segura a las instancias dentro del modelo sin un uso excesivo de WaitForChild() (aunque dichos scripts aún deben usar WaitForChild() para el modelo atómico en general).
Minimiza modelos persistentes — Los modelos persistentes se cargan después de unirse y nunca se transmiten, ocupando permanentemente memoria. Establece el ModelStreamingMode de un modelo en Persistent solo si debe permanecer disponible y accesible para scripts en todo momento.
Descompón modelos contenedores — Un patrón común en juegos que no utilizan streaming es un único gran Model que contiene muchos NPCs, objetos o agrupaciones similares. Bajo streaming, los modelos contenedores disminuyen la eficiencia del streaming y no son óptimos para el nivel de detalle del modelo que funciona mejor con instancias agrupadas de cerca. Descompón los modelos contenedores en modelos más pequeños con partes físicamente cercanas o lógicamente relacionadas.
Aplana jerarquías de modelos profundamente anidadas — Anidar un modelo persistente dentro de un modelo atómico efectivamente obliga al modelo atómico a comportarse como persistente. Las jerarquías planas son más fáciles de gestionar bajo streaming.
Avatares SLIM
Los avatares de plataforma fuera del área actualmente transmitida no son visibles por defecto, pero habilitar Workspace.EnableSLIMAvatars renderiza avatares de rig estándar como versiones ligeras y animadas cuando es apropiado. Efectivamente, el motor:
- Renderiza una versión SLIM cuando un modelo de avatar real se transmite fuera.
- Cambia entre SLIM/real según los recursos disponibles dentro del radio de streaming, ya que los modelos SLIM pueden costar significativamente menos para renderizar que el modelo de alta resolución.
- Ralentiza las animaciones SLIM según la importancia de la escena y el ancho de banda.
El sistema de nivel de detalle de avatares tiene optimizaciones específicas para avatares. Lo siguiente describe lo que será o no procesado por SLIM:
Patrones de scripts
Los siguientes patrones de script son los más comúnmente afectados por el streaming. La estrategia correcta depende de la intención del código, por lo que cada patrón enumera múltiples opciones cuando es apropiado.
Índice directo a descendientes
Indexar en los descendientes de Workspace con el operador . lanza un error si alguna instancia en la ruta no está actualmente transmitida. Lo mismo se aplica a FindFirstChild(), FindFirstChildWhichIsA(), y FindFirstChildOfClass() que devuelven nil si el hijo no ha sido transmitido.
Búsqueda de Descendienteslocal house1 = workspace:FindFirstChild("House1") -- nil si "House1" no ha sido transmitidolocal door = workspace.House1.Door -- Roto si "House1" o "Door" no han sido transmitidos
Un patrón similar es acceder a Humanoid u otros descendientes del personaje directamente dentro de una conexión Player.CharacterAdded. Bajo streaming, el modelo del personaje es padre del Workspace antes de que todos sus descendientes se hayan replicado, por lo que el indexado directo falla.
Descendientes del Personaje
local Players = game:GetService("Players")
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character.Humanoid
end)
Si el script no puede avanzar sin una instancia, espera a ella con WaitForChild():
Búsqueda de Descendienteslocal house1 = workspace:WaitForChild("House1")local door = house1:WaitForChild("Door")
Instancias enviadas de forma remota
Una señal RemoteEvent/RemoteFunction y la instancia a la que se refiere viajan de forma independiente, por lo que la señal puede llegar al cliente antes de que la instancia esté presente — o la instancia puede no estar presente en absoluto. Dos causas probables incluyen:
Bajo streaming, puede haber un ligero retraso entre la creación de una parte/modelo en el servidor y cuando se replica a los clientes. Efectivamente, una parte referida por un RemoteEvent/RemoteFunction puede simplemente no existir todavía, incluso dentro de un área transmitida.
Enviar una referencia de parte/modelo del servidor al cliente a través de un RemoteEvent o RemoteFunction requiere que la instancia se replique en el cliente receptor. Enviar una ruta de instancia como una cadena tiene el mismo problema, ya que la ruta puede resolver a una ubicación no existente en el cliente:
Script del Clientelocal ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")remoteEvent.OnClientEvent:Connect(function(data)local checkpoint = data.checkpoint -- Errores si "checkpoint" no está transmitidolocal level = workspace.Levels[data.levelPath] -- Errores si la ruta no está transmitidaend)
Si el script del cliente receptor necesita la instancia para continuar, incluye WaitForChild() antes de usarla. Ten en cuenta que esto puede esperar indefinidamente si la instancia nunca se transmite, así que considera añadir un tiempo de espera como segundo parámetro de WaitForChild().
Desincronización del cliente
La desincronización del lado del cliente debe tratarse como una excepción, no como un patrón de diseño estándar. Introducir copias solo del cliente o volver a parentar instancias localmente puede crear problemas graves. Audita tu código para lugares en los que se confíen en estos tipos de cambios para que persistan en el cliente.
Por ejemplo, volver a parentar una instancia localmente de ReplicatedStorage a Workspace puede hacer que esa instancia sea elegible para ser transmitida. Del mismo modo, clonar una instancia localmente (Instance:Clone()) de ReplicatedStorage en Workspace crea una copia solo del cliente que ya no forma parte del pipeline de replicación del servidor y no recibirá actualizaciones de propiedades de la instancia original del servidor.
El mismo concepto se aplica a la llamada Instance:Destroy() en el cliente para un objeto de propiedad del servidor. Esto elimina la instancia localmente, pero el servidor aún la tiene, por lo que se transmitirá de nuevo con su estado original cuando sea elegible.
Streaming proactivo
Cuando se puede anticipar el próximo destino de un jugador, realiza llamadas del lado del servidor a Player:RequestStreamAroundAsync() para transmitir áreas transitorias para la carga temporal, o usa Player:AddReplicationFocus() de manera limitada para áreas que deben permanecer cargadas hasta que se liberen explícitamente.
Por ejemplo, cuando un personaje de jugador está a punto de teletransportarse mediante un cambio de CFrame a la casa de otro jugador en una ubicación distante, puedes pre‑cargar el área de destino para minimizar la aparición repentina y proporcionar una transición más suave. El siguiente script muestra cómo un evento remoto de cliente a servidor puede ser activado para mover un personaje de jugador utilizando un método de pre-carga. Si la solicitud de pre-carga tiene éxito cuando la función devuelve, el radio mínimo alrededor de la ubicación objetivo debería estar presente en el cliente.
Script del Servidor - Teletransportar Personaje del Jugador
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- Solicitar streaming alrededor de la ubicación objetivo
player:RequestStreamAroundAsync(teleportTarget)
-- Teletransportar personaje
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- Llamar a la función de teletransporte cuando el cliente activa el evento remoto
teleportEvent.OnServerEvent:Connect(teleportPlayer)
Lecturas de propiedades de instancias
Una vez que una instancia se transmite fuera, sus actualizaciones de propiedad ya no se replican a ese cliente. Leer propiedades tales como BasePart.Position continúa siendo exitoso, pero devuelve el último valor replicado que puede estar obsoleto.
local Players = game:GetService("Players")local player = Players.LocalPlayer-- La posición puede estar obsoleta si "target" se ha transmitido fueralocal dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude
Mueve la lógica al servidor, ya que los scripts del lado del servidor ven todas las instancias en todo momento. Esta es generalmente la opción más confiable para controles de distancia y otra lógica sensible a la posición.
Esperando en la ruta crítica
Algunos juegos que no utilizan streaming cargan su mapa clonándolo desde ReplicatedStorage a Workspace, luego esperan que se cargue en el cliente antes de desestimar una pantalla de carga y señalizar su disponibilidad. Bajo streaming, esto se queda colgado indefinidamente — el personaje del cliente aún no ha aparecido, por lo que no hay foco de replicación, y la instancia espacial del mapa nunca se transmite.
Mueve la lógica de la pantalla de carga para que no dependa de una instancia espacial específica esté presente, por ejemplo, señalizando la disponibilidad una vez que el personaje ha aparecido y el área inmediata está transmitida.
Manejo de cambios de señal
Las señales como Instance.ChildAdded/Instance.ChildRemoved y las señales de CollectionService como GetInstanceAddedSignal() o GetInstanceRemovedSignal() también se activan en la transmisión de entrada/salida, indistinguibles de los verdaderos nacimientos/remociones. Los scripts receptores no pueden distinguir la diferencia solo por la señal, por lo que la lógica que asume que una señal corresponde a un evento "real" necesita ser actualizada.
Audita los scripts para cualquier escucha de señales que pueda romperse o cambiar significativamente cuando se activan por streaming. Por ejemplo, si reproduces efectos de audio o visuales cuando un NPC enemigo aparece inicialmente en el mundo, asigna a cada enemigo un atributo como Spawned en la primera aparición y omite reproducir el mismo audio/efectos en futuras transmisiones de entrada del enemigo.
Seguimiento de Atributos
local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "Enemy"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- Establecer el atributo "Spawned" en el enemigo para la aparición inicial
enemy:SetAttribute("Spawned", true)
-- Reproducir efectos de audio/visual para esta aparición inicial
playSpawnEffects(enemy)
end
end)
Iteración sobre colecciones
Las iteraciones sobre colecciones del lado del cliente como Instance:GetChildren() y Instance:GetDescendants() devuelven solo el subconjunto de descendientes transmitidos. Esto se aplica incluso cuando el padre mismo siempre se replica, como un Folder directamente bajo Workspace cuyos descendientes espaciales se transmiten dentro y fuera.
local Players = game:GetService("Players")local player = Players.LocalPlayer-- La carpeta "Homes" se replica siempre pero sus hijos se transmiten dentro y fuera-- Este bucle puede omitir casas que no están actualmente transmitidasfor _, home in workspace.Homes:GetChildren() doif home.Settings.Owner.Value == player.Name thenreturn homeendend
Si se requiere una enumeración completa, realiza el escaneo en el servidor y pasa el resultado al jugador a través de un RemoteEvent si es necesario.
Consultas espaciales
Las consultas espaciales del lado del cliente como WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox(), y Model:GetBoundingBox() solo reflejan contenido transmitido. Si eso es un problema depende de para qué se utilice la consulta.
Usa el servidor para consultas cuyo resultado debe reflejar el mundo completo, por ejemplo, un raycast que verifica si el jugador tiene línea de visión a un objetivo distante.
Otros patrones
Los siguientes patrones también pueden aplicarse y deben considerarse cuidadosamente:
Un Sound o AudioPlayer parentado a un objeto 3D se detiene cuando ese objeto se transmite fuera. Para audio ambiental que debe persistir independientemente del streaming, parenta el emisor a un modelo persistente o a un contenedor que no utilice streaming.
Los objetos de UI dentro del juego como BillboardGui o SurfaceGui así como los efectos visuales como Beams o Highlights cuyo sujeto o anclaje se transmite fuera simplemente dejan de renderizarse. Esto puede ser el comportamiento deseado, pero debes verificarlo.
Los eventos BasePart.Touched, ProximityPrompts, DragDetectors, y ClickDetectors no operan para los jugadores cuyo cliente no tiene la parte/modelo asociado transmitido. Si la interacción debe ser posible a cualquier rango, el modelo necesita ser persistente o la interacción necesita un mecanismo diferente.
Para PathfindingService y la búsqueda de rutas del lado del cliente, el buscador de rutas solo ve la geometría transmitida en el cliente y puede enrutar a través de obstáculos que existen en el servidor. Consulta aquí para ver estrategias.
Condiciones de prueba realistas
Una vez que los scripts se actualicen, prueba el juego exhaustivamente. Los errores de streaming a menudo solo se manifiestan en los bordes del área transmitida o durante las transiciones, así que solo probar cerca del punto de aparición o en el radio objetivo no es suficiente.
Prueba con Workspace.StreamingTargetRadius configurado en su valor mínimo (64). Algunos errores de streaming solo aparecen cuando el área transmitida es pequeña.
Juega a través de los patrones de travesía completos del juego, teletransportándote entre áreas distantes y revisitando áreas después de haberlas dejado. Estas son las situaciones que ejercen el streaming de entrada y salida más.
Usa la superposición de depuración de streaming para monitorear la configuración de streaming activa, las regiones actualmente cargadas y el estado de streaming en tiempo de ejecución.
Observa la ventana de Output y la Consola del Desarrollador en busca de errores, ya que muchos de los patrones de script generan errores en lugar de un comportamiento incorrecto silencioso. Presta especial atención a errores de la forma attempt to index nil with ... que a menudo indican una llamada faltante a WaitForChild().
Equipa y activa Tools, dispara armas y activa diferentes interacciones del juego.

