Esta página describe problemas comunes de rendimiento y las mejores prácticas para mitigarlos.
Cálculo de script
Las operaciones costosas en el código 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 Luau se ejecuta de manera síncrona y bloquea el hilo principal hasta que encuentra una función que cede el hilo.
Problemas comunes
Operaciones intensivas en estructuras de tabla - Operaciones complejas como la serialización, deserialización y clonación profunda incurren en un alto costo de rendimiento, especialmente en estructuras de tabla grandes. Esto es particularmente cierto si estas operaciones son recursivas o implican la iteración 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 en cada fotograma, lo que a menudo resulta en un aumento innecesario en el tiempo de cálculo. Estos eventos incluyen:
Mitigación
- Invocar código en eventos de RunService con moderación, limitando su uso a casos donde la invocación de alta frecuencia es esencial (por ejemplo, actualizar la cámara). Puede ejecutar la mayoría del otro código en otros eventos o menos frecuentemente en un bucle.
- Divida tareas grandes o costosas utilizando task.wait() para distribuir el trabajo a través de múltiples fotogramas.
- Identifique y optimice operaciones innecesariamente costosas y use multitarea 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 de máquina en lugar de bytecode.
Ámbitos de MicroProfiler
| Ámbito | Cálculo asociado |
| RunService.PreRender | Código ejecutándose en el evento PreRender |
| RunService.PreSimulation | Código ejecutándose en el evento Stepped |
| RunService.PostSimulation | Código ejecutándose en el evento Heartbeat |
| RunService.Heartbeat | Código ejecutándose en el evento Heartbeat |
Para obtener más información sobre la depuración de scripts utilizando el MicroProfiler, consulte 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 API de Roblox llamados por scripts también tienen sus propias etiquetas asociadas en MicroProfiler que pueden proporcionar señales útiles.
Uso de memoria en scripts
Las fugas de memoria pueden ocurrir cuando escribe scripts que consumen memoria que el recolector de basura no puede liberar adecuadamente cuando ya no se usa. Las fugas son particularmente prevalentes 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 de Desarrolladores pueden indicar un problema que necesita una mayor investigación:
- LuaHeap - Un consumo alto o creciente sugiere una fuga de memoria.
- InstanceCount - Números de instancias en crecimiento constante sugieren que las referencias a algunas instancias en su código no están siendo recogidas por el recolector de basura.
- PlaceScriptMemory - Proporciona un desglose de uso de memoria script por script.
Problemas comunes
Dejar conexiones conectadas - El motor nunca recoge eventos conectados a una instancia y cualquier valor referenciado dentro del callback conectado. 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 memoria, incluso después de que se disparen los eventos.
Aunque los eventos se desconectan cuando la instancia a la que pertenecen se destruye, un error común es asumir que esto se aplica a los objetos Player. Después de que un usuario deja un juego, el motor no destruye automáticamente su objeto representativo de Player y su modelo de personaje, por lo que las conexiones al objeto Player y las instancias bajo el modelo de personaje, como CharacterAdded, aún consumen memoria si no se desconectan en sus 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 se necesitan causa un consumo innecesario de memoria, especialmente para tablas que rastrean datos de usuarios cuando se unen. Por ejemplo, el siguiente ejemplo de código crea una tabla añadiendo información del usuario cada vez que un usuario se une:
Ejemplolocal playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- alguna informaciónend)Si no elimina 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.
Mitigación
Para limpiar todos los valores utilizados para prevenir fugas de memoria:
Desconectar todas las conexiones - Revise su base de código y asegúrese 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 de jugadores y personajes después de salir - Habilitar Workspace.PlayerCharacterDestroyBehavior para destruir automáticamente los objetos de jugador y modelos de personaje después de que un usuario sale. Si lo prefiere, puede limpiarlos manualmente:
Ejemplo de limpieza de jugador y personajelocal 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 de aumento del tiempo de cálculo por fotograma, tanto en el servidor como en el cliente.
Problemas comunes
Frecuencia excesiva del paso de tiempo de física - Por defecto, el comportamiento de paso está en modo adaptativo, donde la física avanza a 60 Hz, 120 Hz o 240 Hz, dependiendo de la complejidad del mecanismo físico.
También hay un modo fijo con una precisión mejorada de la física, que obliga a todos los ensamblajes físicos a avanzar a 240 Hz (cuatro veces por fotograma). Esto resulta en un cálculo significativamente mayor en cada fotograma.
Número excesivo de complejidad de objetos simulados - Cuantos más ensamblajes 3D se simulan, más tiempo tardan los cálculos físicos en cada fotograma. A menudo, los juegos tendrán objetos simulados 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 alto y tarda más en ser calculado por el motor.
Mitigación
Anclar partes que no requieren simulación - Ancle todas las partes que no necesitan ser impulsadas por la física, como NPCs estáticos.
Utilizar un paso de física adaptativo - El paso adaptativo ajusta dinámicamente la tasa de cálculos físicos para los mecanismos físicos, permitiendo que las actualizaciones físicas se realicen con menos frecuencia en ciertos casos.
Reducir la complejidad del mecanismo
- Siempre que sea posible, minimice el número de restricciones o juntas de física en un ensamblaje.
- Reduzca la cantidad de auto-colisión dentro de un mecanismo, como aplicando límites o restricciones sin colisión a los miembros de un ragdoll para evitar que colisionen entre sí.
Reducir el uso de fidelidad de colisión precisa para mallas
Para objetos pequeños o no interactivos donde los usuarios rara vez notarían la diferencia, use fidelidad de caja.
Para objetos de tamaño pequeño a mediano, use fidelidades de caja o casco, dependiendo de la forma.
Para objetos grandes y muy complejos, construya colisiones personalizadas usando partes invisibles cuando sea posible.
Para objetos que no requieren colisiones, desactive las colisiones y use fidelidad de caja o casco, ya que la geometría de colisión todavía se almacena en la memoria.
Puede 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, puede aplicar el filtro CollisionFidelity=PreciseConvexDecomposition al Explorador que muestra un conteo de todas las partes de malla con la fidelidad precisa y le permite seleccionarlas fácilmente.
Para un recorrido en profundidad sobre cómo elegir una opción de fidelidad de colisión que equilibre sus requisitos de precisión y rendimiento, consulte Establecer parámetros de física y renderizado.
Ámbitos de MicroProfiler
| Ámbito | Cálculo asociado |
| physicsStepped | Cálculo global de física |
| worldStep | Pasos de física discretos tomados en cada fotograma |
Uso de memoria en 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 colisión por defecto y precisa consumen significativamente más memoria que los otros dos modos con formas de colisión de menor fidelidad.
Si ve niveles altos de consumo de memoria bajo PhysicsParts, es posible que deba explorar la reducción de la fidelidad de colisión de los objetos en su juego.
Cómo mitigar
Para reducir la memoria utilizada por la fidelidad de colisión:
- Para partes que no necesitan colisiones, desactive sus colisiones configurando BasePart.CanCollide, BasePart.CanTouch y BasePart.CanQuery en false.
- Reduzca la fidelidad de colisiones utilizando la configuración CollisionFidelity. Box tiene la sobrecarga de memoria más baja, y Default y Precise son generalmente más costosas.
- Generalmente es seguro establecer la fidelidad de colisión de cualquier parte anclada pequeña a Box.
- Para mallas grandes y muy complejas, es posible que desee construir su propia malla de colisión a partir de objetos más pequeños con fidelidad de colisión de caja.
Humanoides
Humanoid es una clase que proporciona una amplia gama de funcionalidades para personajes jugadores y no jugadores (NPCs). Aunque es poderosa, una Humanoid tiene un costo significativo de cálculo.
Problemas comunes
- Dejar habilitados todos los HumanoidStateTypes en NPCs - Hay un costo de rendimiento al dejar habilitados ciertos HumanoidStateTypes. Deshabilite cualquier estado que no sea necesario para sus NPCs. Por ejemplo, a menos que su NPC vaya a escalar escaleras, es seguro deshabilitar el estado Climbing.
- Instanciar, modificar y reaparecer modelos con Humanoids o MeshParts esqueleto frecuentemente
- Esto puede ser intensivo para que el motor lo procese, especialmente si estos modelos utilizan ropa en capas. Esto también puede ser particularmente problemático en juegos donde los avatares reaparecen a menudo.
- En el MicroProfiler, las largas etiquetas updateInvalidatedFastClusters (más de 4 ms) suelen ser una señal de que la instanciación/modificación del avatar está provocando invalidaciones excesivas.
- Usar Humanoides en casos donde no se requieren - Los 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 deben 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 la reconstrucción de FastCluster. Intente reducir esto durante la jugabilidad si experimenta problemas de rendimiento relacionados con FastCluster. De manera similar, otros cambios de propiedades también pueden causar la reconstrucción de FastCluster, por lo que en general debería reducir estos cambios tanto como sea posible.
Mitigación
- Reproducir animaciones de NPC en el cliente - En juegos con un gran número de NPCs, considere crear 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 permite optimizaciones adicionales (como reproducir animaciones solo para NPCs que están cerca del personaje).
- Usar alternativas que favorezcan el rendimiento en lugar de Humanoides - Los modelos de NPC no necesitan necesariamente contener un objeto humanoide.
- Para NPCs estáticos, use un simple AnimationController, porque no necesitan moverse, solo necesitan reproducir animaciones.
- Para NPCs en movimiento, considere implementar su propio controlador de movimiento y usar un AnimationController para animaciones, dependiendo de la complejidad de sus NPCs.
- Desactivar estados humanoides no utilizados - Utilice 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íe el NPC a un grupo de NPCs inactivos. De esta forma, cuando se requiera reaparecer un nuevo NPC, puede simplemente reactivar uno de los NPCs del grupo. Este proceso se llama agrupamiento, lo que minimiza el número de veces que los personajes deben ser instanciados.
- Solo generar NPCs cuando los usuarios estén cerca - No genere NPCs cuando los usuarios no estén dentro del rango, y cámbielos a un estado inactivo cuando los usuarios abandonen su rango.
- Evitar realizar cambios en la jerarquía del avatar después de que ha sido instanciada - Ciertas modificaciones en una jerarquía de avatar tienen implicaciones de rendimiento significativas. Algunas optimizaciones están disponibles:
- Para animaciones personalizadas procedimentales, no actualice las propiedades JointInstance.C0 y JointInstance.C1. En su lugar, actualice la propiedad Motor6D.Transform.
Ámbitos de MicroProfiler
| Ámbito | Cálculo asociado |
| stepHumanoid | Control humanoide y física |
| stepAnimation | Animación de humanoide y animador |
| updateInvalidatedFastClusters | Asociado con la instanciación o modificación de un avatar |
Renderización
Una parte significativa del tiempo que gasta el cliente en cada fotograma es en renderizar la escena en el fotograma actual. El servidor no realiza ninguna renderización, por lo que esta sección es exclusiva para el cliente.
Llamadas de dibujado
Una llamada de dibujado es un conjunto de instrucciones de la engine a la GPU para renderizar algo. Las llamadas de dibujado tienen una sobrecarga significativa. Generalmente, cuantas menos llamadas de dibujado haya por fotograma, menos tiempo computacional se gasta en renderizar un fotograma.
Puede ver cuántas llamadas de dibujado están ocurriendo actualmente con el elemento Estadísticas de Renderización ⟩ Temporización en Studio. Puede ver Estadísticas de Renderización en el cliente presionando ShiftF2.
Cuantos más objetos necesitan ser dibujados en su escena en un fotograma dado, más llamadas de dibujado se hacen a la GPU. Sin embargo, el Engine 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 dibujado. Específicamente, múltiples mallas con el mismo MeshContent se manejan en una sola llamada de dibujado cuando:
- SurfaceAppearances son idénticos si están presentes, de lo contrario, cuando TextureContents son idénticos.
- Los materiales son idénticos cuando tanto SurfaceAppearance como MeshPart.TextureID no existen.
Otros problemas comunes
Densidad excesiva de objetos - Si un gran número de objetos están concentrados con una alta densidad, renderizar esta área de la escena requiere más llamadas de dibujado. Si encuentra que su tasa de fotogramas disminuye al mirar un cierto parte del mapa, esta puede ser una buena señal de que la densidad de objetos en esta área es demasiado alta.
Objetos como decals, texturas y partículas no se agrupan bien y generan llamadas de dibujado adicionales. Preste atención especial 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 conducir a llamadas de dibujado innecesarias.
Una causa común de este problema es cuando una sola escena se importa de una 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 ayudarlo a identificar partes de malla con el mismo nombre que utilizan diferentes IDs de malla:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endendLa 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 su esquema de nombres, podrían indicar mallas duplicadas en su juego:
LargeRock, rbxassetid://106420009602747 (x144) -- buenoLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- todos posibles duplicadosComplejidad excesiva de objetos - Aunque no es tan importante como el número de llamadas de dibujado, el número de triángulos en una escena sí 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, así como las escenas con la propiedad MeshPart.RenderFidelity establecida en Precise en demasiadas mallas.
Sombra excesiva - Manejar sombras es un proceso costoso, y los 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.
Dibujado excesivo de transparencia - Colocar objetos con transparencia parcial cerca de otros obliga al motor a renderizar los píxeles superpuestos múltiples veces, lo que puede afectar el rendimiento. Para obtener más información sobre cómo identificar y solucionar este problema, consulte Eliminar transparencias en capas.
Movimiento innecesario de MeshPart en piel - Los MeshParts en piel que son parte de un modelo sin un Humanoid están agrupados utilizando Clusters Rápidos organizados espacialmente. Cuando estos MeshParts se mueven, deben añadirse y eliminarse continuamente de estos clústeres espaciales, lo que obliga a reconstruir los clústeres e impacta en el rendimiento.
- Una solución altamente efectiva es incrustar un Humanoid dentro del modelo. La presencia de un Humanoid anula el comportamiento de agrupamiento espacial predeterminado, exigiendo el uso de un único y unificado Cluster Rápido para todo el modelo. En consecuencia, las actualizaciones de posición ya no requieren reconstrucciones de clústeres, mitigando así el cuello de botella en el rendimiento. Esta técnica debe reservarse exclusivamente para MeshParts con movimiento esperado, ya que puede introducir sobrecarga de memoria y anular los beneficios de optimización espacial. Se recomienda siempre perfilar su juego después de realizar estos tipos de cambios. Para obtener información adicional, consulte Consejos de rendimiento de Humanoid.
Demasiadas partes en un Model - Demasiadas partes en un modelo podrían causar reconstrucciones más frecuentes debido a la posibilidad de que cambie una propiedad de una parte, requiriendo así una reconstrucción completa. Encuentre el equilibrio correcto de partes en un modelo cuando esté utilizando FastCluster.
Mitigación
Instanciación de mallas idénticas y reducción de la cantidad de mallas únicas - Si asegura que todas las mallas idénticas tengan los mismos IDs de activos subyacentes, el motor puede reconocerlas y renderizarlas en una sola llamada de dibujado. Asegúrese de solo cargar 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. Los Paquetes son un mecanismo útil para la reutilización de objetos.
Culling - Culling describe el proceso de eliminar llamadas de dibujado para objetos que no influyen en el fotograma renderizado final. Por defecto, el motor omite llamadas de dibujado para objetos fuera del campo de visión de la cámara (culling de frustum) y partes, mallas y terreno ocluidos de vista por otros objetos (culling por oclusión). En ciertos escenarios, como entornos interiores, puede implementar un sistema de habitación o portal y eliminar manualmente objetos para reducir aún más las llamadas de dibujado o la carga computacional general.
Reducir el nivel de detalle para modelos - Habilite streaming de instancias y ajuste la propiedad LevelOfDetail de sus modelos de mundo a SLIM para renderizar mallas SLIM optimizadas y ligeras a medida que aumenta la distancia de la cámara.
Reducir el nivel de detalle para avatares - Habilite el streaming de instancias y ajuste la propiedad Workspace.EnableSLIMAvatars para renderizar modelos de avatares de plataforma con cualquier número de accesorios o capas de ropa como representaciones ligeras de avatar optimizadas con soporte completo para animaciones a medida que aumenta la distancia de la cámara.
Reducir la fidelidad de renderizado - Ajuste 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, puede 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 puede hacer tanto en tiempo de edición como dinámicamente en tiempo de ejecución:
Use la propiedad BasePart.CastShadow para desactivar la proyección de sombras en partes pequeñas donde las sombras probablemente no sean visibles. Esta estrategia es particularmente efectiva cuando se aplica a partes que están lejos de la cámara del usuario.
Desactive las sombras en objetos en movimiento cuando sea posible.
Desactive Light.Shadows en instancias de luz donde el objeto no necesite proyectar sombras.
Limite el rango y el ángulo de las instancias de luz.
Use menos instancias de luz.
Considere desactivar luces que están fuera de un rango específico o sobre una base habitación por habitación en entornos interiores.
Ámbitos de MicroProfiler
| Ámbito | Cálculo asociado |
| Prepare and Perform | Renderizado global |
| Perform/Scene/computeLightingPerform | Actualizaciones de cuadrícula de luz y sombra |
| LightGridCPU | Actualizaciones de cuadrícula de luz de vóxel |
| ShadowMapSystem | Mapeo de sombras |
| Perform/Scene/UpdateView | Preparación para renderizado y actualizaciones de partículas |
| Perform/Scene/RenderView | Renderizado y procesamiento posterior |
Redes y replicación
Las redes y la replicación describen el proceso por el cual los datos se envían entre el servidor y los clientes conectados. La información se envía entre el cliente y el servidor en cada fotograma, pero grandes cantidades de información requieren más tiempo de procesamiento.
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 en cada fotograma. Errores comunes incluyen:
- Replicar datos en 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 compran 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 red.
Un culpable común aquí es el complejo conjunto de datos de animación guardados por plugins de Animation Editor en rigs. Si estos no se eliminan antes de que el juego sea publicado y el modelo animado se clone regularmente, se replicará una gran cantidad de datos innecesariamente.
TweenService del lado del servidor - Si se utiliza TweenService para tween un objeto del lado del servidor, la propiedad tweened se replica a cada cliente en cada fotograma. Esto no solo resulta en que el tween sea tembloroso a medida que la latencia de los clientes fluctúa, sino que también causa un gran tráfico innecesario en la red.
Mitigación
Puede emplear las siguientes tácticas para reducir la replicación innecesaria:
- Evite enviar grandes cantidades de datos a la vez a través de eventos remotos. En su lugar, envíe solo los datos necesarios a una frecuencia más baja. Por ejemplo, para el estado de un personaje, réplica cuando cambia en lugar de cada fotograma.
- Divida árboles de instancias complejos como mapas y cárguelos en piezas para distribuir el trabajo replicando estas en varios fotogramas.
- Limpie los metadatos de animación, especialmente el directorio de animación de rigs, después de la importación.
- Limite la replicación innecesaria de instancias, especialmente en casos donde el servidor no necesita conocer 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
| Ámbito | Cálculo asociado |
| ProcessPackets | Procesamiento de paquetes de red entrantes, como invocaciones de eventos y cambios de propiedades |
| Allocate Bandwidth and Run Senders | Eventos salientes relevantes en servidores |
Uso de memoria de activos
El mecanismo de impacto más alto 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 necesarias, lo que puede llevar a tiempos de carga considerablemente reducidos y aumentar la capacidad del cliente para prevenir fallos cuando está bajo presión de memoria.
Si está encontrando problemas de memoria y tiene deshabilitado el streaming de instancias, considere actualizar su juego para admitirlo, particularmente si su 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, puede aumentar su agresividad. Por ejemplo, considere:
- Reducir el uso de Enum.ModelStreamingMode.Persistent siempre que sea posible. Puede que necesite actualizar sus scripts si lo está utilizando como una medida de compatibilidad.
- Reducir el Workspace.StreamingMinRadius y el Workspace.StreamingTargetRadius.
Para obtener más información sobre las opciones de streaming y sus beneficios, consulte propiedades de streaming.
Otros problemas comunes
Duplicación de activos - Un error común es subir el mismo activo múltiples veces, lo que resulta en diferentes IDs de activos. Esto puede hacer 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 de 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 carga todos ellos en el cliente a la vez en lugar de solo cargar lo que necesita para una porción del juego. Para estrategias, vea 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 píxeles.
Las imágenes subidas a Roblox se transcodifican a un formato fijo, por lo que no hay beneficios 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 cargar o eliminar el canal alfa de imágenes que no lo necesiten puede disminuir el tamaño de la imagen en el disco, pero no mejora el uso de memoria.
A medida que se carga un juego, el motor comienza automáticamente con texturas de menor calidad y luego aumenta la calidad en función de la memoria disponible en el dispositivo, la distancia de la cámara, la cantidad de espacio en pantalla que ocupa la textura y otros factores. Aun así, dimensionar estratégicamente sus texturas puede mejorar el uso de memoria en su juego.
Mitigación
Solo suba activos una vez - Reutilice el mismo ID de activo en objetos y asegúrese de que los mismos activos, especialmente mallas e imágenes, no se suban múltiples veces por separado.
Busque y corrija activos duplicados - Busque partes de malla idénticas y texturas que se hayan subido múltiples veces con diferentes IDs.
- Aunque no hay una API para detectar similitudes de activos automáticamente, puede recopilar todos los IDs de activos de imagen en su lugar (ya sea manualmente o con un script), descargarlos y compararlos usando 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 duplicados manualmente.
- En lugar de usar texturas separadas para diferentes colores, suba una sola textura y use la propiedad SurfaceAppearance.Color para aplicar diversos tonos a ella.
Importar activos en el mapa por separado - En lugar de importar un mapa entero a la vez, importe y reconstruya los activos en el mapa individualmente. El Importador no realiza ninguna deduplicación de mallas, por lo que si importara un gran mapa con muchos azulejos de piso separados, cada uno de esos azulejos sería importado como un activo separado (incluso si son duplicados). Esto podría provocar problemas de rendimiento y memoria más adelante, ya que cada malla se trata de forma individual y ocupa memoria y llamadas de dibujado.
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 un máximo de 512x512 píxeles. La mayoría de las imágenes menores deben ser más pequeñas que 256x256 píxeles.
Usar hojas de recortes para asegurar la máxima reutilización de texturas en mapas 3D. Para pasos y ejemplos sobre cómo crear hojas de recortes, consulte Crear hojas de recortes.
También puede considerar usar hojas de sprites para cargar muchas imágenes de UI más pequeñas como una sola imagen. Luego puede usar ImageLabel.ImageRectOffset y ImageLabel.ImageRectSize para mostrar partes de la hoja.
Tiempos de carga
Muchos juegos implementan pantallas de carga personalizadas y utilizan el método ContentProvider:PreloadAsync() para solicitar activos de manera que imágenes, sonidos y mallas se descarguen en segundo plano.
La ventaja de este enfoque es que le permite asegurarse de que las partes importantes de su juego estén completamente cargadas sin que aparezcan de forma repentina. Sin embargo, un error común es sobreutilizar este método para pre-cargar más activos de los que realmente son necesarios.
Un ejemplo de una mala práctica es cargar todo el Workspace. Si bien esto podría evitar que las texturas aparezcan repentinamente, aumenta considerablemente 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 aumento significativo de los tiempos de carga, a la vez que es un método poco fiable debido a su naturaleza cambiante.
En su lugar, use ContentProvider:PreloadAsync() solo en situaciones necesarias, que incluyen:
- Imágenes en la pantalla de carga.
- Imágenes importantes en su menú de juego, como fondos de botones e iconos.
- Activos importantes en el área de inicio o generación.
Si necesita cargar una gran cantidad de activos, le recomendamos que proporcione un botón de Saltar Carga.