Mejorar el rendimiento

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Esta página describe problemas comunes de rendimiento y las mejores prácticas para mitigarlos.

Cálculo de scripts

Las operaciones costosas en el código de Luau tardan más en procesarse y, por lo tanto, pueden afectar la tasa de fotogramas. A menos que se ejecute en paralelo, el código de Luau se ejecuta de forma sincrónica y bloquea el hilo principal hasta que encuentra una función que cede el hilo.

Problemas comunes

  • Operaciones intensivas en estructuras de tablas - Operaciones complejas como la serialización, deserialización y clonación profunda incurren en un alto costo de rendimiento, especialmente en estructuras de tablas grandes. Esto es particularmente cierto si estas operaciones son recursivas o implican iterar sobre estructuras de datos muy grandes.
  • Eventos de alta frecuencia - Vincular operaciones costosas a eventos basados en fotogramas de RunService sin limitar la frecuencia significa que estas operaciones se repiten cada fotograma, lo que a menudo resulta en un aumento innecesario en el tiempo de cálculo. Estos eventos incluyen:

Mitigación

  • Invoca código en eventos de RunService con moderación, limitando el uso a casos donde la invocación de alta frecuencia es esencial (por ejemplo, actualizar la cámara). Puedes ejecutar la mayoría de los otros códigos en otros eventos o con menos frecuencia en un bucle.
  • Divide tareas grandes o costosas utilizando task.wait() para distribuir el trabajo a través de múltiples fotogramas.
  • Identifica y optimiza operaciones innecesariamente costosas y utiliza multihilo para tareas computacionales costosas que no necesitan acceder al modelo de datos.
  • Ciertos scripts del lado del servidor pueden beneficiarse de la generación de código nativo, una simple bandera que compila un script a código máquina en lugar de bytecode.
  • Si tus servidores aún se quedan sin CPU después de optimizar, puedes reservar más con Servicios Extendidos para Cómputo.

Ámbitos de MicroProfiler

ÁmbitoCálculo asociado
RunService.PreRenderCódigo ejecutándose en el evento PreRender
RunService.PreSimulationCódigo ejecutándose en el evento Stepped
RunService.PostSimulationCódigo ejecutándose en el evento Heartbeat
RunService.HeartbeatCódigo ejecutándose en el evento Heartbeat

Para más información sobre la depuración de scripts utilizando el MicroProfiler, consulta la biblioteca debug, que incluye funciones para etiquetar código específico y aumentar aún más la especificidad, como debug.profilebegin y debug.profileend. Muchos métodos de la API de Roblox llamados por scripts también tienen sus propias etiquetas asociadas de MicroProfiler que pueden proporcionar señales útiles.

Uso de memoria de scripts

Las fugas de memoria pueden ocurrir cuando escribes scripts que consumen memoria que el recolector de basura no puede liberar adecuadamente cuando ya no se utiliza. Las fugas son particularmente comunes en el servidor, porque pueden estar en línea continuamente durante muchos días, mientras que una sesión de cliente es mucho más corta.

Los siguientes valores de memoria en la Consola del Desarrollador pueden indicar un problema que necesita más investigación:

  • LuaHeap - Un consumo alto o creciente sugiere una fuga de memoria.
  • InstanceCount - Números de instancias que crecen de manera constante sugieren que las referencias a algunas instancias en tu código no están siendo recolectadas por el recolector de basura.
  • PlaceScriptMemory - Proporciona un desglose de uso de memoria script por script.

Problemas comunes

  • Dejar conexiones conectadas - El motor nunca recolecta eventos conectados a una instancia y cualquier valor referenciado dentro de la función de devolución de llamada conectada. Por lo tanto, las conexiones activas de eventos y el código dentro de las instancias conectadas, funciones conectadas y valores referenciados, están fuera del alcance del recolector de basura, incluso después de que se disparan los eventos.

    Aunque los eventos se desconectan cuando se destruye la instancia a la que pertenecen, un error común es asumir que esto se aplica a los objetos de Player. Después de que un usuario abandona un juego, el motor no destruye automáticamente su objeto representativo de Player y modelo de personaje, por lo que las conexiones al objeto Player y las instancias bajo el modelo de personaje, como CharacterAdded, siguen consumiendo memoria si no las desconectas en tus scripts. Esto puede resultar en fugas de memoria muy significativas con el tiempo en el servidor a medida que cientos de usuarios entran y salen del juego.

  • Tablas - Insertar objetos en tablas pero no eliminarlos cuando ya no son necesarios causa un consumo innecesario de memoria, especialmente para tablas que rastrean datos de usuario cuando se unen. Por ejemplo, el siguiente código crea una tabla añadiendo información del usuario cada vez que un usuario se une:

    Ejemplo
    local playerInfo = {}
    Players.PlayerAdded:Connect(function(player)
    playerInfo[player] = {} -- alguna información
    end)

    Si no eliminas estas entradas cuando ya no son necesarias, la tabla continúa creciendo en tamaño y consume más memoria a medida que más usuarios se unen a la sesión. Cualquier código que itere sobre esta tabla también se vuelve más costoso computacionalmente a medida que la tabla crece en tamaño.

Mitigación

Para limpiar todos los valores utilizados y prevenir fugas de memoria:

  • Desconectar todas las conexiones - Revisa tu base de código y asegúrate de que cada conexión se limpie a través de uno de los siguientes caminos:

    • Desconectando manualmente usando la función Disconnect().
    • Destruyendo la instancia a la que pertenece el evento con la función Destroy().
    • Destruyendo el objeto script al que la conexión se remonta.
  • Eliminar objetos y personajes de jugadores después de salir - Habilita Workspace.PlayerCharacterDestroyBehavior para destruir automáticamente objetos de jugadores y modelos de personajes después de que un usuario abandona. Si lo prefieres, puedes limpiarlos manualmente:

    Ejemplo de limpieza de jugador y personaje
    local Players = game:GetService("Players")
    Players.PlayerAdded:Connect(function(player)
    player.CharacterRemoving:Connect(function(character)
    task.defer(character.Destroy, character)
    end)
    end)
    Players.PlayerRemoving:Connect(function(player)
    task.defer(player.Destroy, player)
    end)

Cálculo de física

La simulación excesiva de física puede ser una causa clave del aumento del tiempo de cálculo por fotograma tanto en el servidor como en el cliente.

Problemas comunes

  • Frecuencia de paso de física excesiva - Por defecto, el comportamiento de paso está en modo adaptativo, donde la física se ejecuta a 60 Hz, 120 Hz o 240 Hz, dependiendo de la complejidad del mecanismo físico.

    También está disponible un modo fijo con una precisión mejorada de la física, que obliga a todos los ensamblajes de física a ejecutarse a 240 Hz (cuatro veces por fotograma). Esto resulta en un cálculo significativamente mayor en cada fotograma.

  • Número excesivo de objetos simulados o complejidad - Cuantos más ensamblajes 3D se simulen, más tiempo tardan los cálculos de física en cada fotograma. A menudo, los juegos tendrán objetos que se simulan que no necesitan serlo o tendrán mecanismos que tienen más restricciones y juntas de las que necesitan.

  • Detección de colisiones excesivamente precisa - Las partes de malla tienen una propiedad CollisionFidelity para detectar colisiones que ofrece una variedad de modos con diferentes niveles de impacto en el rendimiento. El modo de detección de colisiones precisa para partes de malla tiene el costo de rendimiento más caro y toma más tiempo para que el motor lo calcule.

Mitigación

  • Anclar partes que no requieren simulación - Ancla todas las partes que no necesitan ser impulsadas por la física, como NPCs estáticos.

  • Usar pasos de física adaptativos - El paso adaptativo ajusta dinámicamente la tasa de cálculos de física para mecanismos físicos, permitiendo que las actualizaciones de física se realicen con menos frecuencia en algunos casos.

  • Reducir la complejidad del mecanismo

    • Donde sea posible, minimiza el número de restricciones o juntas de física en un ensamblaje.
    • Reduce la cantidad de auto-colisión dentro de un mecanismo, como aplicando límites o restricciones de no-colisión a las extremidades de ragdoll para evitar que colisionen entre sí.
  • Reducir el uso de fidelidad de colisión precisa para mallas

    • Para objetos pequeños o no interactuables donde los usuarios rara vez notarían la diferencia, usa fidelidad de caja.

    • Para objetos de tamaño pequeño a mediano, usa fidelidades de caja o casco, dependiendo de la forma.

    • Para objetos grandes y muy complejos, construye colisiones personalizadas usando partes invisibles cuando sea posible.

    • Para objetos que no requieren colisiones, desactiva las colisiones y usa fidelidad de caja o casco, ya que la geometría de colisión aún se almacena en memoria.

    • Puedes renderizar la geometría de colisión para fines de depuración en Studio activando Fidelidad de colisión desde el widget de Opciones de Visualización en la esquina superior derecha del viewport 3D.

      Alternativamente, puedes aplicar el filtro CollisionFidelity=PreciseConvexDecomposition al Explorador que muestra un conteo de todas las partes de malla con la fidelidad precisa y te permite seleccionarlas fácilmente.

    • Para un recorrido detallado sobre cómo elegir una opción de fidelidad de colisión que equilibre tus requisitos de precisión y rendimiento, consulta Establecer parámetros de física y renderizado.

Ámbitos de MicroProfiler

ÁmbitoCálculo asociado
physicsSteppedCálculo general de física
worldStepPasos de física discretos tomados en cada fotograma

Uso de memoria de física

El movimiento de física y la detección de colisiones consumen memoria. Las partes de malla tienen una propiedad CollisionFidelity que determina el enfoque que se utiliza para evaluar los límites de colisión de la malla.

Problema común

Los modos de detección de colisiones por defecto y precisa consumen significativamente más memoria que los otros dos modos con formas de colisión de menor fidelidad.

Si ves altos niveles de consumo de memoria bajo PhysicsParts, es posible que necesites explorar la reducción de la fidelidad de colisión de los objetos en tu juego.

Cómo mitigar

Para reducir la memoria utilizada para la fidelidad de colisión:

  • Para partes que no necesitan colisiones, desactiva sus colisiones configurando BasePart.CanCollide, BasePart.CanTouch y BasePart.CanQuery a false.
  • Reduce la fidelidad de las colisiones utilizando la configuración CollisionFidelity. Box tiene la sobrecarga de memoria más baja, y Default y Precise son generalmente más costosos.
    • Generalmente es seguro establecer la fidelidad de colisión de cualquier parte pequeña anclada a Box.
    • Para mallas grandes y muy complejas, es posible que desees construir tu propia malla de colisión a partir de objetos más pequeños con fidelidad de colisión de caja.

Humanos

Humanoid es una clase que proporciona una amplia gama de funcionalidades a personajes jugadores y no jugadores (NPCs). Aunque es poderosa, un Humanoid viene con un costo de cálculo significativo.

Problemas comunes

  • Dejar todos los HumanoidStateTypes habilitados en NPCs - Hay un costo de rendimiento por dejar habilitados ciertos HumanoidStateTypes. Desactiva cualquier estado que no sea necesario para tus NPCs. Por ejemplo, a menos que tu NPC vaya a escalar escaleras, es seguro desactivar el estado Climbing.
  • Instanciar, modificar y reaparecer modelos con Humanoids o esqueletos MeshParts frecuentemente
    • Esto puede ser intensivo para que el motor lo procese, particularmente si estos modelos utilizan ropa en capas. Esto también puede ser problemático en juegos donde los avatares reaparecen a menudo.
    • En el MicroProfiler, las largas etiquetas updateInvalidatedFastClusters (más de 4 ms) son a menudo una señal de que la instanciación/modificación de avatares está provocando invalidaciones excesivas.
  • Usar Humanos en casos donde no son requeridos - NPCs estáticos que no se mueven generalmente no tienen necesidad de la clase Humanoid.
  • Reproducir animaciones en un gran número de NPCs desde el servidor - Las animaciones de NPC que se ejecutan en el servidor necesitan ser simuladas en el servidor y replicadas al cliente. Esto puede ser una sobrecarga innecesaria.
  • Realizar cambios innecesarios de tamaño y escala - Los cambios de tamaño/escala provocan que FastCluster se reconstruya. Intenta reducir esto durante el juego si estás viendo problemas de rendimiento relacionados con FastCluster. De manera similar, otros cambios de propiedades también pueden causar que FastCluster se reconstruya, así que en general reduce estos cambios tanto como sea posible.

Mitigación

  • Reproducir animaciones de NPC en el cliente - En juegos con un gran número de NPCs, considera crear el Animator en el cliente y ejecutar las animaciones localmente. Esto reduce la carga en el servidor y la necesidad de replicación innecesaria. También hace posibles optimizaciones adicionales (como reproducir animaciones solo para NPCs que están cerca del personaje).
  • Usar alternativas amigables con el rendimiento a los Humanos - Los modelos de NPC no necesitan necesariamente contener un objeto humanoide.
    • Para NPCs estáticos, usa un simple AnimationController, porque no necesitan moverse, solo necesitan reproducir animaciones.
    • Para NPCs en movimiento, considera implementar tu propio controlador de movimiento y usar un AnimationController para las animaciones, dependiendo de la complejidad de tus NPCs.
  • Desactivar estados humanoides no utilizados - Usa Humanoid:SetStateEnabled() para habilitar solo los estados necesarios para cada humanoide.
  • Agrupar modelos de NPC con reaparecimiento frecuente - En lugar de destruir un NPC completamente, envía el NPC a un grupo de NPCs inactivos. De esta manera, cuando se requiere un nuevo NPC para reaparecer, puedes simplemente reactivar uno de los NPCs del grupo. Este proceso se llama agrupamiento, lo que minimiza la cantidad de veces que los personajes necesitan ser instanciados.
  • Solo genera NPCs cuando los usuarios están cerca - No generes NPCs cuando los usuarios no están en rango, y elimínalos cuando los usuarios abandonan su rango.
  • Evita hacer cambios en la jerarquía del avatar después de que se haya instanciado - Ciertas modificaciones en una jerarquía de avatar tienen implicaciones significativas en el rendimiento. Algunas optimizaciones están disponibles:

Ámbitos de MicroProfiler

ÁmbitoCálculo asociado
stepHumanoidControl y física de humanoides
stepAnimationAnimación de humanoides y animadores
updateInvalidatedFastClustersAsociado con la instanciación o modificación de un avatar

Renderizado

Una parte significativa del tiempo que el cliente pasa en cada fotograma es en renderizar la escena en el fotograma actual. El servidor no realiza ningún renderizado, por lo que esta sección es exclusiva para el cliente.

Llamadas de dibujo

Una llamada de dibujo es un conjunto de instrucciones del motor a la GPU para renderizar algo. Las llamadas de dibujo tienen una sobrecarga significativa. Generalmente, cuantas menos llamadas de dibujo por fotograma, menos tiempo computacional se gasta en renderizar un fotograma.

Puedes ver cuántas llamadas de dibujo están ocurriendo actualmente con el elemento Estadísticas de Renderizado ⟩ Temporización en Studio. Puedes ver Estadísticas de Renderizado en el cliente presionando ShiftF2.

Cuantos más objetos necesiten ser dibujados en tu escena en un fotograma dado, más llamadas de dibujo se hacen a la GPU. Sin embargo, el Motor de Roblox utiliza un proceso llamado instanciación para colapsar mallas idénticas con las mismas características de textura en una sola llamada de dibujo. Específicamente, múltiples mallas con el mismo MeshContent se manejan en una sola llamada de dibujo cuando:

Otros problemas comunes

  • Densidad de objetos excesiva - Si un gran número de objetos están concentrados con una alta densidad, entonces renderizar esta área de la escena requiere más llamadas de dibujo. Si encuentras que tu tasa de fotogramas cae al mirar una cierta parte del mapa, esto puede ser una buena señal de que la densidad de objetos en esta área es demasiado alta.

    Objetos como calcomanías, texturas y partículas no se agrupan bien y introducen llamadas de dibujo adicionales. Presta especial atención a estos tipos de objetos en una escena. En particular, los cambios de propiedades en ParticleEmitters pueden tener un impacto dramático en el rendimiento.

  • Oportunidades de instanciación perdidas - A menudo, una escena incluirá la misma malla duplicada varias veces, pero cada copia de la malla tiene diferentes IDs de activos de malla o textura. Esto impide la instanciación y puede llevar a llamadas de dibujo innecesarias.

    Una causa común de este problema es cuando se importa toda una escena a la vez, en lugar de que los activos individuales se importen a Roblox y luego se dupliquen después de la importación para ensamblar la escena.

    Incluso un script simple como este puede ayudarte a identificar partes de malla con el mismo nombre que utilizan diferentes IDs de malla:

    for _,descendant in workspace:GetDescendants() do
    if descendant:IsA("MeshPart") then
    print(descendant.Name .. ", " .. descendant.MeshId)
    end
    end

    La salida (con Líneas de Pila habilitadas) podría verse algo así. Las líneas repetidas indican reutilización de la misma malla, lo cual es bueno. Las líneas únicas no son necesariamente malas, pero dependiendo de tu esquema de nombres, podrían indicar mallas duplicadas en tu juego:

    LargeRock, rbxassetid://106420009602747 (x144) -- bueno
    LargeRock, rbxassetid://120109824668127
    LargeRock, rbxassetid://134460273008628
    LargeRock, rbxassetid://139288987285823
    LargeRock, rbxassetid://71302144984955
    LargeRock, rbxassetid://90621205713698
    LargeRock, rbxassetid://113160939160788
    LargeRock, rbxassetid://135944592365226 -- todos posibles duplicados
  • Complejidad excesiva de objetos - Aunque no es tan importante como el número de llamadas de dibujo, el número de triángulos en una escena influye en cuánto tiempo tarda un fotograma en renderizarse. Las escenas con un número muy grande de mallas muy complejas son un problema común, al igual que las escenas con la propiedad MeshPart.RenderFidelity establecida en Precise en demasiadas mallas.

  • Sombra excesiva - Manejar sombras es un proceso costoso, y mapas que contienen un alto número y densidad de objetos de luz que proyectan sombras (o un alto número y densidad de partes pequeñas influenciadas por sombras) pueden tener problemas de rendimiento.

  • Sobreexposición de transparencia alta - Colocar objetos con transparencia parcial cerca unos de otros obliga al motor a renderizar los píxeles superpuestos múltiples veces, lo que puede afectar el rendimiento. Para más información sobre cómo identificar y solucionar este problema, consulta Eliminar transparencias en capas.

  • Movimiento innecesario de MeshPart esqueléticos - Los MeshParts esqueléticos que son parte de un Modelo sin un Humanoide se agrupan utilizando FastClusters organizados espacialmente. Cuando estos MeshParts se mueven, deben ser continuamente añadidos y eliminados de estos clústeres espaciales, forzando a que los clústeres se reconstruyan y afectando el rendimiento.

    • Una solución altamente efectiva es incrustar un Humanoide dentro del Modelo. La presencia de un Humanoide anula el comportamiento de agrupamiento espacial predeterminado, exigiendo el uso de un único FastCluster unificado para todo el Modelo. En consecuencia, las actualizaciones de posición ya no requieren reconstrucciones de clúster, mitigando así el cuello de botella de rendimiento. Esta técnica debe reservarse exclusivamente para MeshParts con movimiento esperado, ya que puede introducir sobrecarga de memoria y anular los beneficios de la optimización espacial. Recomendamos siempre perfilar tu juego después de realizar este tipo de cambios. Consulta Consejos de rendimiento de Humanoides para más información.
  • Demasiadas partes en un Model - Demasiadas partes en un Modelo podrían causar reconstrucciones más frecuentes debido a la posibilidad de que la propiedad de una parte cambie, lo que lleva a requerir una reconstrucción completa. Encuentra el equilibrio adecuado de partes en un Modelo cuando esté utilizando FastCluster.

Mitigación

  • Instanciar mallas idénticas y reducir la cantidad de mallas únicas - Si aseguras que todas las mallas idénticas tengan los mismos IDs de activos subyacentes, el motor puede reconocerlas y renderizarlas en una sola llamada de dibujo. Asegúrate de solo subir cada malla en un mapa una vez y luego duplicarlas en Studio para reutilizarlas en lugar de importar grandes mapas como un todo, lo que podría causar que mallas idénticas tengan IDs de contenido separados y sean reconocidas como activos únicos por el motor. Paquetes son un mecanismo útil para la reutilización de objetos.
  • Culling - Culling describe el proceso de eliminar llamadas de dibujo para objetos que no influyen en el fotograma renderizado final. Por defecto, el motor omite llamadas de dibujo para objetos fuera del campo de visión de la cámara (culling de frustum) y partes, mallas y terreno ocultos de la vista por otros objetos (culling de oclusión). En ciertos escenarios, como entornos interiores, podrías implementar un sistema de habitaciones o portal y eliminar manualmente objetos para reducir aún más las llamadas de dibujo o la carga computacional total.
  • Reducir el nivel de detalle para modelos - Habilita streaming de instancias y establece la propiedad LevelOfDetail de tus modelos del mundo a SLIM para renderizar mallas SLIM ligeras optimizadas a medida que aumenta la distancia de la cámara.
  • Reducir el nivel de detalle para avatares - Habilita el streaming de instancias y establece Workspace.EnableSLIMAvatars para renderizar avatares de plataforma como representaciones SLIM ligeras optimizadas con soporte completo de animación a medida que aumenta la distancia de la cámara.
  • Reducir la fidelidad de renderizado - Establece MeshPart.RenderFidelity a Automatic o Performance. Esto permite que las mallas retrocedan a alternativas menos complejas, lo que puede reducir el número de polígonos que necesitan ser dibujados.
  • Desactivar la proyección de sombras en partes y objetos de luz apropiados - El motor de Roblox degrada automáticamente la calidad de las sombras a medida que disminuye el nivel de calidad gráfica del cliente, desactivando eventualmente las sombras por completo en niveles de calidad por debajo de 4. Sin embargo, puedes desactivar selectivamente las propiedades de proyección de sombras en objetos de luz y partes para mejorar el rendimiento mientras las sombras están habilitadas y aumentar la probabilidad de que las sombras permanezcan habilitadas. Algunos ejemplos de optimizaciones que puedes hacer ya sea en tiempo de edición o dinámicamente en tiempo de ejecución:
    • Usa la propiedad BasePart.CastShadow para desactivar la proyección de sombras en partes pequeñas donde es poco probable que las sombras sean visibles. Esta estrategia es particularmente efectiva cuando se aplica a partes que están lejos de la cámara del usuario.

    • Desactiva las sombras en objetos en movimiento cuando sea posible.

    • Desactiva Light.Shadows en instancias de luz donde el objeto no necesita proyectar sombras.

    • Limita el rango y el ángulo de las instancias de luz.

    • Usa menos instancias de luz.

    • Considera desactivar luces que están fuera de un rango específico o en una base habitación por habitación para entornos interiores.

Ámbitos de MicroProfiler

ÁmbitoCálculo asociado
Prepare and PerformRenderizado general
Perform/Scene/computeLightingPerformActualizaciones de cuadrícula de luz y sombras
LightGridCPUActualizaciones de cuadrícula de luz voxel
ShadowMapSystemMapeo de sombras
Perform/Scene/UpdateViewPreparación para renderizado y actualizaciones de partículas
Perform/Scene/RenderViewRenderizado y post procesamiento

Redes y replicación

Las redes y la replicación describen el proceso por el cual se envían datos entre el servidor y los clientes conectados. La información se envía entre el cliente y el servidor cada fotograma, pero cantidades más grandes de información requieren más tiempo de cómputo.

Problemas comunes

  • Tráfico remoto excesivo - Enviar una gran cantidad de datos a través de objetos RemoteEvent o RemoteFunction o invocarlos con mucha frecuencia puede llevar a que se gaste una gran cantidad de tiempo de CPU procesando paquetes entrantes cada fotograma. Errores comunes incluyen:

    • Replicar datos cada fotograma que no necesitan ser replicados.
    • Replicar datos en la entrada del usuario sin ningún mecanismo para limitarlo.
    • Despachar más datos de los necesarios. Por ejemplo, enviar todo el inventario del jugador cuando compra un artículo en lugar de solo los detalles del artículo comprado.
  • Creación o eliminación de árboles de instancias complejos - Cuando se realiza un cambio en el modelo de datos en el servidor, se replica a los clientes conectados. Esto significa que crear y destruir grandes jerarquías de instancias como mapas en tiempo de ejecución puede ser muy intensivo en la red.

    Un culpable común aquí es la compleja información de animación guardada por los plugins de Animation Editor en los rigs. Si estos no se eliminan antes de que se publique el juego y el modelo animado se clona regularmente, se replicará una gran cantidad de datos innecesariamente.

  • TweenService del lado del servidor - Si TweenService se utiliza para tween un objeto del lado del servidor, la propiedad tweened se replica a cada cliente cada fotograma. Esto no solo resulta en que el tween sea inestable a medida que la latencia de los clientes fluctúa, sino que causa mucho tráfico de red innecesario.

Mitigación

Puedes emplear las siguientes tácticas para reducir la replicación innecesaria:

  • Evita enviar grandes cantidades de datos a la vez a través de eventos remotos. En su lugar, envía solo los datos necesarios a una frecuencia más baja. Por ejemplo, para el estado de un personaje, replicarlo cuando cambia en lugar de cada fotograma.
  • Dividir árboles de instancias complejos como mapas y cargarlos en piezas para distribuir el trabajo replicando estos a través de múltiples fotogramas.
  • Limpia los metadatos de animación, especialmente el directorio de animación de los rigs, después de importar.
  • Limitar la replicación innecesaria de instancias, especialmente en casos donde el servidor no necesita tener conocimiento de las instancias que se están creando. Esto incluye:
    • Efectos visuales como una explosión o un hechizo mágico. El servidor solo necesita conocer la ubicación para determinar el resultado, mientras que los clientes pueden crear visuales localmente.
    • Modelos de vista de ítems en primera persona.
    • Tween objetos en el cliente en lugar de en el servidor.

Ámbitos de MicroProfiler

ÁmbitoCálculo asociado
ProcessPacketsProcesamiento de paquetes de red entrantes, como invocaciones de eventos y cambios de propiedades
Allocate Bandwidth and Run SendersEventos salientes relevantes en servidores

Uso de memoria de activos

El mecanismo de mayor impacto disponible para los creadores para mejorar el uso de memoria del cliente es habilitar streaming de instancias.

Streaming de instancias

El streaming de instancias carga selectivamente partes del modelo de datos que no son requeridas, lo que puede llevar a tiempos de carga considerablemente reducidos y aumentar la capacidad del cliente para prevenir bloqueos cuando se encuentra bajo presión de memoria.

Si estás encontrando problemas de memoria y tienes el streaming de instancias deshabilitado, considera actualizar tu juego para soportarlo, particularmente si tu mundo 3D es grande. El streaming de instancias se basa en la distancia en el espacio 3D, por lo que los mundos más grandes se benefician naturalmente más de ello.

Si el streaming de instancias está habilitado, puedes aumentar su agresividad. Por ejemplo, considera:

  • Reducir el uso de Enum.ModelStreamingMode.Persistent donde sea posible. Es posible que necesites actualizar tus scripts si lo estás utilizando como medida de compatibilidad.
  • Reducir el Workspace.StreamingMinRadius y Workspace.StreamingTargetRadius.

Para más información sobre las opciones de streaming y sus beneficios, consulta propiedades de streaming.

Otros problemas comunes

  • Duplicación de activos - Un error común es subir el mismo activo múltiples veces resultando en diferentes IDs de activos. Esto puede llevar a que el mismo contenido se cargue en memoria múltiples veces.

  • Volumen excesivo de activos - Incluso cuando los activos no son idénticos, hay casos en los que se pierden oportunidades para reutilizar el mismo activo y ahorrar memoria.

  • Archivos de audio - Los archivos de audio pueden ser un contribuyente sorprendente al uso de memoria, particularmente si cargas todos ellos en el cliente a la vez en lugar de solo cargar lo que necesitas para una porción del juego. Para estrategias, consulta Tiempos de carga.

  • Texturas de alta resolución - El consumo de memoria gráfica para una textura no está relacionado con el tamaño de la textura en el disco; el número de píxeles en la textura determina el uso de memoria. Por ejemplo, una textura de 1024x1024 píxeles consume cuatro veces la memoria gráfica de una textura de 512x512.

    Las imágenes subidas a Roblox se transcodifican a un formato fijo, por lo que no hay beneficio de memoria al subir imágenes en un modelo de color asociado con menos bytes por píxel. De manera similar, comprimir imágenes antes de la subida o eliminar el canal alfa de imágenes que no lo necesitan puede disminuir el tamaño de la imagen en disco, pero no mejora el uso de memoria.

    A medida que un juego se carga, el motor comienza automáticamente con texturas de menor calidad y luego aumenta la calidad según la memoria disponible del dispositivo, la distancia desde la cámara, la cantidad de espacio en pantalla que ocupa la textura y otros factores. Aun así, dimensionar estratégicamente tus texturas puede mejorar el uso de memoria en tu juego.

Mitigación

  • Solo sube activos una vez - Reutiliza el mismo ID de activo en objetos y asegúrate de que los mismos activos, especialmente mallas e imágenes, no se suban por separado múltiples veces.

  • Encuentra y corrige activos duplicados - Busca partes de malla e texturas idénticas que se hayan subido múltiples veces con diferentes IDs.

    • Aunque no hay una API para detectar automáticamente la similitud de activos, puedes recopilar todos los IDs de activos de imagen en tu lugar (ya sea manualmente o con un script), descargarlos y compararlos utilizando herramientas de comparación externas.
    • Para partes de malla, la mejor estrategia es tomar IDs de malla únicos y organizarlos por tamaño para identificar manualmente duplicados.
    • En lugar de usar texturas separadas para diferentes colores, sube una sola textura y usa la propiedad SurfaceAppearance.Color para aplicar varios tonos a ella.
  • Importar activos en el mapa por separado - En lugar de importar un mapa entero a la vez, importa y reconstruye activos en el mapa individualmente y reconstruirlos. El Importador no realiza ninguna deduplicación de mallas, por lo que si importas un gran mapa con muchos azulejos de piso separados, cada uno de esos azulejos se importará como un activo separado (incluso si son duplicados). Esto puede llevar a problemas de rendimiento y memoria a largo plazo, ya que cada malla se trata como individual y ocupa memoria y llamadas de dibujo.

  • Limitar los píxeles de las imágenes a no más de la cantidad necesaria. A menos que una imagen esté ocupando una gran cantidad de espacio físico en la pantalla, generalmente necesita como máximo 512x512 píxeles. La mayoría de las imágenes menores deberían ser más pequeñas que 256x256 píxeles.

  • Usar hojas de recorte para asegurar la máxima reutilización de texturas en mapas 3D. Para pasos y ejemplos sobre cómo crear hojas de recorte, consulta Crear hojas de recorte.

    También podrías considerar usar hojas de sprites para cargar muchas imágenes de UI más pequeñas como una sola imagen. Luego puedes usar ImageLabel.ImageRectOffset y ImageLabel.ImageRectSize para mostrar porciones de la hoja.

Tiempos de carga

Antes de que un jugador pueda comenzar a jugar, el cliente debe recibir todo en ReplicatedFirst y ReplicatedStorage, el contenido inicial de Workspace, y cualquier activo que tus scripts precarguen. Cuanto más contenido tenga que descargar y procesar el cliente durante este tiempo, más tiempo esperan los jugadores. Para un desglose de cada paso en el proceso de unión, consulta Identificar problemas de rendimiento.

Problemas comunes

  • Streaming de instancias deshabilitado - Sin streaming de instancias, todo el Workspace debe replicarse al cliente antes de que el jugador pueda entrar, por lo que los juegos con muchas partes, modelos y activos tienen tiempos de carga proporcionalmente más largos.

  • Contenido excesivo en ReplicatedStorage - Todo en ReplicatedStorage se replica a cada cliente antes de que el jugador pueda ver o interactuar con algo, ya sea que el cliente lo necesite de inmediato o no. Los modelos gratuitos y los activos heredados pueden agregar silenciosamente un volumen significativo.

  • Contenido pesado de ReplicatedFirst - ReplicatedFirst se replica antes que cualquier otra cosa y existe principalmente para contener una pantalla de carga personalizada. Cada activo o script adicional en él retrasa el inicio de la fase principal de replicación.

  • Uso excesivo de PreloadAsync() - Muchos juegos implementan pantallas de carga personalizadas y utilizan ContentProvider:PreloadAsync() para solicitar activos para que imágenes, sonidos y mallas se descarguen en segundo plano. Este enfoque asegura que las partes importantes de tu juego estén completamente cargadas sin aparición repentina, pero un error común es precargar más activos de los que realmente se requieren.

    Un ejemplo de una mala práctica es cargar todo el Workspace. Si bien esto podría prevenir la aparición repentina de texturas, aumenta significativamente los tiempos de carga.

    Otra práctica similar es utilizar ContentProvider.RequestQueueSize para asegurarse de que todos los activos solicitados hayan terminado de cargarse. Sin embargo, esto presenta el mismo problema de tiempos de carga significativamente aumentados, además de ser un método poco confiable debido a su naturaleza fluctuante.

  • Scripts de inicio costosos - La ejecución pesada de scripts en LocalScripts que se ejecutan durante el proceso de unión, como cargar sincrónicamente grandes estructuras de datos, retrasa la transición de replicación a un estado jugable.

Mitigación

  • Habilitar el streaming de instancias - El streaming envía solo la porción de tu mundo cerca del punto de aparición del jugador en la unión inicial, luego transmite el resto progresivamente a medida que el jugador se mueve a través de tu juego. Para juegos que aún no lo utilizan, el streaming es una de las formas más efectivas de reducir los tiempos de carga. El streaming está habilitado por defecto para nuevos lugares. Para pasos de configuración, referencia de propiedades y orientación sobre migración, consulta Streaming de instancias.

  • Mantener ReplicatedStorage ligero - Solo almacena lo que los clientes necesitan en el momento en que se unen, como ModuleScripts compartidos que requieren LocalScripts. Mueve modelos grandes, audio y otros activos que no se necesitan de inmediato a ServerStorage, y muévelos al cliente solo cuando un jugador los necesite. Audita ReplicatedStorage regularmente para contenido que ya no utilizas.

  • Mantener ReplicatedFirst ligero - Limita ReplicatedFirst a lo que necesita tu pantalla de carga, como elementos básicos de UI en lugar de texturas grandes o modelos 3D, y evita cálculos pesados en scripts que se ejecutan desde él. Para un recorrido sobre cómo construir una pantalla de carga personalizada, incluyendo cuándo llamar a ReplicatedFirst:RemoveDefaultLoadingScreen(), consulta Pantallas de carga.

  • Solo precargar activos necesarios - Usa ContentProvider:PreloadAsync() solo en situaciones necesarias, que incluyen:

    • Imágenes en la pantalla de carga.
    • Imágenes importantes en tu menú de juego, como fondos de botones e íconos.
    • Activos importantes en el área de inicio o aparición.

    Si debes cargar un gran número de activos, te recomendamos que proporciones un botón de Saltar Carga.

  • Diferir el trabajo de inicio no esencial - Carga contenido que un jugador no ha alcanzado aún, como paneles de UI que no ha abierto, solo cuando se necesite por primera vez. Para el trabajo de inicialización que debe ocurrir temprano, distribúyelo a través de múltiples fotogramas en lugar de ejecutarlo todo a la vez. Para técnicas, consulta Cálculo de scripts.

©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.