Preguntas y respuestas ampliadas: Optimización de la memoria y el tamaño de compilación con Addressables

En febrero, como parte de mi función como consultor senior de desarrollo de software para Unity Accelerate Solutions, dirigí un seminario web técnico sobre el sistema de activos direccionables. Durante la sesión en vivo, demostré varias herramientas de creación de perfiles que puedes usar para optimizar la memoria en tiempo de ejecución y el tamaño de compilación de un proyecto. El seminario web finalizó con una sesión de preguntas y respuestas, y nuestro equipo recibió más preguntas de las que tuvimos tiempo de responder.
Lo que sigue es una extensión de esa sesión de preguntas y respuestas de cierre, para que podamos responder más preguntas.
P: ¿Es necesario el sistema Addressables para juegos livianos (como juegos casuales, arcade o de rompecabezas) si no tengo problemas de memoria?
A: Quizás no, pero es bueno tener en cuenta que el sistema Addressables no solo mejora el rendimiento de la memoria. Tener la posibilidad de elegir cuándo cargar el contenido puede mejorar los tiempos de carga. La creación de contenido en Addressables le permite tener compilaciones iterativas que no toman tanto tiempo. Por ejemplo, si realiza un pequeño cambio en el script, es posible que no tenga que reconstruir todos sus paquetes.
P: ¿Los activos cargados se liberan cuando cambia la escena?
A: Potencialmente. Los activos cargados desde Addressables que están listos para ser liberados porque tienen un recuento de referencia de cero podrían descargarse de la memoria durante una transición de escena. Al realizar la transición entre escenas de forma no aditiva, llame a Resources.UnloadUnusedAssets(). Esto es costoso para la CPU, pero le permite descargar parcialmente AssetBundles.
P: ¿La agrupación de objetos y los direccionables funcionan bien juntos?
A: Sí. Puede cargar su objeto una vez desde Addressables y luego crear varias copias del mismo para crear su grupo. Cuando haya terminado con el grupo, destruya todos los objetos y libere el AsyncOperationHandle que se utilizó para cargar el activo.
P: ¿Los grupos y paquetes se cargan en la memoria todos a la vez?
A: Los grupos direccionables son un concepto exclusivo del editor. En tiempo de ejecución, solo se trabaja con paquetes. Los paquetes se cargan en la memoria solo cuando son necesarios y solo se carga el contenido deseado.
Ejemplo: Tienes un paquete con 10 personajes. Le pide a Addressables que cargue tres caracteres. Se cargarán los metadatos del paquete y los tres personajes.
P: Si quiero liberar un activo, ¿necesito conservar AsyncOperationHandle o AssetReference?
A: Te recomendamos conservar el identificador y usarlo, ya que eres responsable de publicar el contenido cuando termines de usarlo.
Por ejemplo, los miembros de nuestro equipo a menudo optan por la ruta del identificador para evitar llamar a Instantiate/Release directamente en AssetReference.
P: ¿Cuáles son las desventajas de muchos paquetes pequeños?
A: Esta documentación enumera varias desventajas de tener demasiados paquetes.
P: Cuando se necesita un activo de un paquete, ¿qué gastos generales tienen los demás activos del mismo paquete? Si se trata de un paquete remoto, debe descargarse, pero ¿realmente no hay sobrecarga de memoria debido a activos no utilizados en el paquete?
A: Correcto, un paquete remoto se descargará completamente antes de poder usarlo.
Los activos descargados en un paquete de activos cargados tienen una sobrecarga mínima en tiempo de ejecución. Cada vez que cargue activos desde un paquete, deberá cargar los metadatos del paquete. Parte de estos metadatos incluye una tabla de contenidos que enumera todos los activos del paquete. Más activos en un paquete equivalen a metadatos más grandes.
Puedes ver esta memoria de arriba tomando una captura con Unity Memory Profiler. En la pestaña “Toda la memoria”, hay una lista de todos los objetos “SerializedFile” en la memoria, uno para cada paquete. Estos objetos son los metadatos de sus paquetes.
Obtenga más información sobre estos metadatos en nuestra documentación.
P: Al trabajar en un entorno de mundo abierto, ¿qué estrategias de agrupamiento puedo usar para descargar activos individuales sin descargar la mitad de un paquete y confiar en Resources.UnloadUnusedAssets() para limpiarlo, sin la sobrecarga de tener cada activo en su propio paquete?
A: Lo más importante que hay que recordar es que el contenido debe estar agrupado si se espera descargarlo al mismo tiempo. Si el mundo de tu juego tiene contenido “estático”, como árboles y rocas para un bioma determinado que el jugador no moverá, ese contenido debe agruparse. Cualquier contenido “dinámico”, como los objetos que el jugador puede recoger, debe empaquetarse por separado.
Esta publicación de blog y el repositorio de GitHub vinculado cubren la división de paquetes para un juego de mundo abierto. También incluye una forma de deduplicar paquetes para reducir la sobrecarga de memoria de cada paquete. Las etapas 4 y 5 son especialmente relevantes para los mundos abiertos.
P: ¿Cuándo debo dejar habilitado “AssetBundle CRC”?
A: La práctica recomendada es tener esto habilitado, excluyendo los AssetBundles almacenados en caché para los grupos remotos, y deshabilitado para los grupos locales. La comprobación solo sirve para garantizar que los datos no se hayan dañado durante la descarga. Prácticamente no hay motivos para comprobar los AssetBundles locales.
P: ¿Cuándo no vale la pena utilizar Addressables debido a problemas de rendimiento de la CPU al cargar y descargar activos?
A: El sistema Addressables tiene un impacto positivo en el rendimiento de carga de la CPU debido a que no es necesario cargar todo el contenido por adelantado.
Si no utiliza Addressables al cargar una escena, tendrá que cargar todo el contenido y las referencias. Si mueve el contenido a Addressables, puede elegir cuándo cargar qué contenido.
Por ejemplo, supongamos que tiene un Administrador de inventario en una escena que tiene una referencia a 1000 elementos de inventario. Si no usa Addressables, tendrá que cargar cada malla, textura, clip de audio, etc., para todos estos elementos del inventario. Si esperas a cargar este contenido, la carga de la escena será más rápida.
P: ¿Todas las dependencias de un activo direccionable también deben ser direccionables, o eso solo es necesario si se comparten?
A: No es necesario marcar las dependencias como direccionables. Las dependencias se incorporarán a Addressables durante el proceso de compilación si es necesario.
Por ejemplo, si haces que un reproductor sea prefabricado y direccionable, no tienes que marcar manualmente la malla, las texturas o el audio del reproductor como direccionables también. Cuando se crea el paquete, todas las dependencias que aún no existen en Addressables se incluirán automáticamente en el paquete de prefabricados del reproductor.
P: Si olvidé publicar un activo y cambiar de escena, ¿qué sucede con este activo?
A: Cambiar de escena no implica necesariamente una mala interacción con los controladores. Pero si carga un activo y olvida liberar su identificador, el activo persistirá en la memoria.
Addressables tiene un sistema interno de conteo de referencias. Los identificadores son el modo en que interactuamos con este sistema. Al cargar un activo se incrementa el recuento de referencia, y al liberarlo se disminuye.
Los creadores son responsables de mantener este recuento de referencias actualizado. El activo estará en la memoria mientras el recuento de referencias sea mayor que uno.
P: En relación con el ejemplo del seminario web, supongamos que estoy creando un juego de mundo abierto. El jefe está presente en algún lugar del mundo abierto. Cuando el jugador se dirige al jefe, ¿cómo uso los direccionables aquí? ¿Envío el comando para cargar la espada de forma asincrónica, a través de un disparador, a una cierta distancia del enemigo, o algo más?
A: Puede ser una línea muy fina elegir cuándo cargar y descargar contenido. Quieres estar seguro de que el jefe esté listo cuando el jugador necesite verlo, pero quizás no quieras cargarlo demasiado pronto cuando el jugador aún puede darse la vuelta y evitarlo.
Lo bueno es que puedes iterar sobre cuándo cargar y descargar contenido: no es necesario optimizarlo perfectamente en el primer intento.
Para comenzar, sugerimos cargar todo el contenido de una “zona” particular cuando el jugador se acerca (por ejemplo, el jugador se acerca a la entrada de una mazmorra, lo que hace que todo dentro de la mazmorra se cargue). Si esto genera una presión de memoria innecesaria, puedes agregar carga y descarga más detalladas.
Si la espada no se carga lo suficientemente pronto, considere mover el disparador de carga para que comience antes, mejorando el tiempo de carga de los activos de la espada usando el módulo de CPU de Unity Profiler para ver qué se está cargando o usando Addressables de manera sincrónica para garantizar que la carga finalice.
Esta documentación incluye más detalles y un fragmento de código para direccionables sincrónicos.
P: Si cargo un objeto direccionable cuando comienza una escena, ¿necesito tener una pantalla de carga para ello?
A: La carga desde Addressables normalmente se realiza de forma asincrónica, como con Addressables.LoadAssetAsync().
Es posible que haya algún contenido que no desees cargar antes de salir de una pantalla de carga. Puede recopilar estos AsyncOperationHandles y esperar a que se completen los necesarios antes de salir.
P: ¿Cuál es la huella de memoria de los metadatos direccionables en tiempo de ejecución (antes de cargar cualquiera de sus datos)?
A: Durante la inicialización de Addressables, se carga el archivo de catálogo para que Addressables sepa cómo asignar etiquetas y direcciones a activos en el disco o en ubicaciones remotas. Un catálogo más grande equivale a una mayor sobrecarga de memoria en tiempo de ejecución.
El tamaño del catálogo se puede reducir eliminando datos innecesarios, como no incluir etiquetas o GUID en grupos que no los necesitan, o reduciendo el tamaño de los datos existentes. Por ejemplo, configurando el modo de nombres de activos internos de un grupo en GUID en lugar del nombre de archivo o la ruta completa (que puede ser más larga). Puede ver el tamaño de la memoria en tiempo de ejecución del catálogo en Unity Memory Profiler.
P: ¿Qué hace el Editor de Unity en el tiempo que dedica a crear objetos direccionables?
A: Se genera un registro de informe de compilación en la carpeta /Library. Este registro muestra cada paso del proceso de compilación. Para agregar detalles adicionales al registro, siga esta ruta para seleccionar “Usar registro de compilación detallado”: Habilitar Editar > Preferencias > Canal de compilación programable > Usar registro de compilación detallado.
Consulta imágenes y documentación sobre cómo ver el registro.
P: ¿Resources.Load() también tiene un problema de duplicación?
A: Sí. Puede ser útil pensar en el contenido de Addressables y el de Recursos como “mundos” diferentes. Si tiene una textura en /Recursos, se incluye una copia de esa textura en el archivo Recursos. Si los paquetes en Addressables dependen de esa textura, cada paquete incluye una copia implícita de ella. Terminarás con múltiples copias de la textura en el disco y potencialmente múltiples copias en la memoria.
Para evitar esta duplicación, mueva la textura fuera de /Recursos y agréguela a un grupo Addressables.
P: ¿Obtienes problemas de tamaño similares en el disco que se resuelven eliminando paquetes duplicados cuando no usas direccionables?
A: Sí. En el seminario web y las diapositivas mostramos cómo la eliminación de duplicados de las dos escenas de carreras acuáticas redujo significativamente el tamaño de la compilación.
P: ¿Cómo puedo evitar variantes duplicadas del sombreador?
A: Los sombreadores se pueden deduplicar en el mismo proceso que cualquier otro activo: declárelos explícitamente en un grupo.
Si un activo se declara explícitamente en un grupo de Addressables que va a su compilación, ese activo no se duplicará en múltiples paquetes.
En el caso específico de los sombreadores, es una práctica común que los proyectos utilicen un grupo de “Sombreadores compartidos” para contener los sombreadores que espera necesitar en la memoria durante la vida útil de su aplicación y que se comparten entre muchos activos.
P: ¿Dos escenas de Unity que comparten el mismo prefabricado duplican el tamaño de construcción?
A: Esto depende de si el prefab del que dependen las escenas se ha incluido explícitamente en Addressables y si las escenas están en el mismo paquete o en paquetes diferentes.
Vea la explicación visual de cómo ocurre la duplicación en las diapositivas del seminario web y en esta publicación del blog en la Etapa 4.
La clave para recordar es que todo el contenido que ingresa a un paquete debe poder acceder a todas sus dependencias. Si coloca una escena en un paquete, todas sus dependencias deben ser:
- Incluido explícitamente en algún lugar de Addressables
- Incluido implícitamente en el mismo paquete
P: ¿Es posible comparar duplicados en grupos determinados para evitar tener todos los recursos del juego empaquetados juntos en un grupo aislado?
A: Sí. Puede ejecutar la regla de deduplicación incorporada y luego ordenar los activos en la ventana Grupos de dispositivos direccionables en mejores agrupaciones.
O bien, un enfoque más escalable es escribir sus propias reglas de análisis direccionables, que aparecerán en la ventana Analizar. Las reglas integradas se entregan como C# en el paquete Addressables y pueden servir como base.
Por ejemplo, es posible que desees encontrar todos los duplicados en todos tus grupos que comiencen con "Carácter-". Cualquier duplicado implícito se puede colocar en un grupo de “Caracteres compartidos”.
P: ¿Vas a cubrir compilaciones remotas y rutas locales?
A: No cubrimos las rutas remotas y locales, que se denominan “Perfiles direccionables” en el seminario web. Sin embargo, en esta documentacióndescribimos qué son los perfiles direccionables y cómo utilizarlos.
P: ¿Cómo funciona Addressables con Cloud Content Delivery (CCD)?
A: La integración de CCD se analiza en esta documentación.
P: ¿Puede brindarnos sugerencias sobre las mejores prácticas para implementar variaciones de direccionables de baja y alta resolución?
A: Puede encontrar un ejemplo en la muestra de Addressables en GitHub.
P: ¿Qué pasa si el contenido del paquete está encriptado? ¿UnityDataTool también descifra el contenido?
A: No. Los datos deberán descifrarse antes de que UnityDataTool pueda analizar el contenido.
P: ¿Es un caso de uso compatible crear paquetes a partir de un proyecto de Unity y cargar los paquetes en tiempo de ejecución desde una aplicación creada a partir de un proyecto diferente?
A: Sí. Esto se soluciona utilizando varios catálogos al mismo tiempo.
P: ¿Existen inconvenientes en el uso de InstantiateAsync o situaciones en las que es mejor utilizar LoadAsync + Instantiate manual?
A: Se recomienda utilizar Addressables.LoadAssetAsync() y llamar a Object.Instantiate(). Addressables.InstantiateAsync() tiene un costo de rendimiento mayor.
P: Tengo muchos ScriptableObjects con al menos 1 o 2 sprites referenciados como variables. Si quiero cambiar los sprites a Addressables, ¿tengo que cambiar las referencias a Addressables una por una, o hay algún truco para hacer esto?
A: Un script del editor es probablemente el camino a seguir para convertir estas referencias.
Puedes agregar los campos AssetReference a tu ScriptableObject (y conservar temporalmente los campos Sprite). Luego, puede escribir un script de Editor que itere a través de sus ScriptableObjects, busque el activo Sprite en Addressables para encontrar el AddressableAssetEntry asociado y almacene la dirección o cree un AssetReference para almacenar en el ScriptableObject.
Por último, puedes eliminar las referencias directas de Sprite e intercambiar cualquier código relacionado para usar AssetReference.
P: ¿Puedo utilizar direccionables para juegos WebGL? Si es así, ¿hay cosas específicas que se deben tener en cuenta?
A: Sí, y sí. Dos cosas a tener en cuenta: En primer lugar, WebGL no admite subprocesos, por lo que no utilice tareas. En segundo lugar, el almacenamiento en caché funciona de manera diferente en WebGL: ya hemos visto problemas con el almacenamiento en caché de AssetBundles remotos anteriormente.
P: Si uso Shader.Find(“ShaderName”), ¿esto proviene de la compilación o de Addressables?
A: Estos provienen de la compilación de Unity Player, no son direccionables. Shader.Find() no devuelve resultados de AssetBundles.
P: ¿Cómo puedo organizar la ventana Grupos direccionables cuando tengo muchos grupos con nombres similares?
A: Para organizar la interfaz de usuario de grupos de direcciones, puede habilitar la jerarquía de grupos con guiones. Esto agrupará los grupos con nombres similares. Por ejemplo, “Personaje-persona” y “Personaje-persona2” aparecerán en la interfaz de usuario bajo la agrupación “Personaje”.
Esto no afecta la forma en que se crean los paquetes. Este es solo un cambio organizativo de la interfaz de usuario.
Comparte tus comentarios con nosotros en el foro Addressables . Asegúrese de estar atento a los nuevos blogs técnicos de otros desarrolladores de Unity como parte del trabajo en curso. Serie Tecnología de las Trincheras .
