Comprende el lenguaje de serialización de Unity, YAML
¿Sabías que puedes editar cualquier tipo de activo sin la molestia de lidiar con lenguajes de serialización como XML o JSON en el Editor de Unity? Si bien esto funciona la mayor parte del tiempo, sin embargo, hay algunos casos en los que debe modificar sus archivos directamente. Piense en conflictos de fusión o archivos corruptos como ejemplos.
Es por eso que, en esta publicación de blog, desglosaremos aún más el sistema de serialización de Unity y compartiremos casos de uso de lo que se puede lograr modificando archivos de activos directamente.
Como siempre, haga una copia de seguridad de sus archivos e, idealmente, use el control de versiones para evitar la pérdida de datos. La modificación manual de los archivos de activos es una operación riesgosa y no es compatible con Unity. Los archivos de activos no están diseñados para modificarse manualmente y no generarán mensajes de error útiles para explicar qué sucedió si se producen errores y cuándo, lo que dificulta la corrección de errores. Al comprender mejor cómo funciona Unity y prepararse para resolver los conflictos de combinación, puede compensar las situaciones en las que la API de la base de datos de activos no es suficiente.
Estructura YAML
YAML, también conocido como "YAML no es un lenguaje de marcado", es parte de la familia de lenguajes de serialización de datos legibles por humanos como XML y JSON. Pero debido a que es liviano y relativamente sencillo en comparación con otros lenguajes comunes, se considera más fácil de leer.
Unity usa una biblioteca de serialización de alto rendimiento que implementa un subconjunto de la especificación YAML. Por ejemplo, las líneas en blanco, los comentarios y alguna otra sintaxis admitida en YAML no son compatibles con los archivos de Unity. En ciertos casos extremos, el formato de Unity difiere de la especificación YAML.
Exploremos esto mirando un fragmento de código YAML en un Cube Prefab. Primero, cree un cubo predeterminado en Unity, conviértalo en Prefab y abra el archivo Prefab en cualquier editor de texto. Como puede ver en la Figura 1, las dos primeras líneas son encabezados que no se repetirán más adelante. El primero define qué versión de YAML estás usando, mientras que el segundo crea una macro llamada “!u!” para el prefijo URI “tag:unity3d.com,2011:” (discutido a continuación).
Cada definición de objeto comienza con un encabezado de dos líneas, como el de nuestro ejemplo de la Figura 2: “--- !u!1 &7618609094792682308” sigue el formato “--- !u!{ID DE CLASE} &{ID DE ARCHIVO }” que se puede analizar en dos partes:
- !u!{ ID DE CLASE } :Esto le dice a Unity a qué clase pertenece el objeto. El “!u!” parte será reemplazada con la macro previamente definida, dejándonos con “tag:unity3d.com,2011:1” – el número 1 se refiere a la GameObject ID en este caso. Cada ID de clase se define en el código fuente de Unity, pero se puede encontrar una lista completa de ellos aquí .
- &{ID DE ARCHIVO} : esta parte define la ID del objeto en sí, que se utiliza para hacer referencia a objetos entre sí. Se llama ID de archivo porque representa la ID del objeto en un archivo específico. Siga leyendo para obtener más información sobre las referencias entre archivos más adelante en esta publicación.
La línea de encabezado del segundo objeto es el nombre del tipo de objeto (aquí, GameObject), que le permite identificarlo leyendo el archivo.
Después del encabezado del objeto, puede encontrar todas las propiedades serializadas. En nuestro ejemplo de GameObject anterior, la figura 2 proporciona detalles como su nombre (m_Name: Cube) y la capa (m_Layer: 0). En el caso de la serialización de MonoBehaviour, notará los campos públicos y los privados con el atributo SerializeField. Este formato se usa de manera similar para ScriptableObjects, Animations, Materials, etc. Tenga en cuenta que ScriptableObjects usa MonoBehaviour como su tipo de objeto, en lugar de definir el suyo propio. Esto se debe a que la misma clase MonoBehaviour interna también los aloja.
Refactorización rápida con YAML
Con lo que hemos cubierto hasta ahora, puede comenzar a aprovechar el poder de modificar YAML para fines como la refactorización de pistas de animación.
Los archivos de animación de Unity funcionan describiendo un conjunto de pistas o curvas de animación; uno para cada propiedad que desee animar. Como se muestra en la figura 4, una curva de animación identifica el objeto que necesita animar a través de la propiedad de la ruta, que contiene los nombres de los GameObjects secundarios hasta el específico. En este ejemplo, estamos animando un GameObject llamado "JumpingCharacter", un elemento secundario del GameObject "Shoulder", que es un elemento secundario del GameObject que tiene el componente Animator reproduciendo esta animación. Para aplicar la misma animación a diferentes objetos, el sistema de animación utiliza rutas basadas en cadenas en lugar de ID de GameObject.
Referencias locales
Como se mencionó anteriormente, cada objeto en un archivo YAML tiene una identificación conocida como "ID de archivo". Este ID es único para cada objeto dentro del archivo y sirve para resolver las referencias entre ellos. Piense en un GameObject y sus componentes, los componentes y su GameObject, o incluso referencias de secuencias de comandos, como una referencia de componente "Arma" a un GameObject "SpawnPoint" en el mismo Prefab.
El formato YAML para esto es "{fileID: FILE ID}" como el valor de la propiedad. En la Figura 6 se puede ver como este Transform pertenece a un GameObject con ID 4112328598445621100, dado que su propiedad “m_GameObject” lo referencia a través del File ID. También puede observar ejemplos de referencias nulas como "m_PrefabInstance" (dado que su ID de archivo es cero). Siga leyendo para obtener más información sobre las instancias prefabricadas.
Consideremos el caso de reparentar objetos dentro de un Prefab. Puede cambiar el Id. de archivo de la propiedad "m_Father" de una Transformación con el Id. de archivo de la nueva Transformación de destino, e incluso corregir el YAML de la Transformación principal anterior para eliminar este objeto de su matriz "m_Children" y agregarlo a la nueva propiedad padre “m_Children”.
Metaarchivos y referencias entre archivos
Cuando se hace referencia a objetos fuera de este archivo, como un script de "Arma" que hace referencia a un prefabricado "Bullet", las cosas se vuelven un poco más complejas. Recuerde que el ID de archivo es local para el archivo, lo que significa que puede repetirse en diferentes archivos. Para identificar de manera única un objeto en otro archivo, necesitamos una ID adicional o "GUID" que identifique todo el archivo en lugar de los objetos individuales dentro de él. Cada activo tiene esta propiedad GUID definida en su metaarchivo, que se puede encontrar en la misma carpeta que el archivo original, con exactamente el mismo nombre más una extensión ".meta" agregada.
- Tipo 2 : activos que el editor puede cargar directamente desde la carpeta de activos, como materiales y archivos .asset
- Tipo 3 : recursos que han sido procesados y escritos en la carpeta Biblioteca, y cargados desde allí por el Editor, como prefabricados, texturas y modelos 3D
Otro factor a destacar con respecto a la serialización de secuencias de comandos es que el tipo YAML es el mismo para todas las secuencias de comandos; solo MonoBehaviour. Se hace referencia al script real en la propiedad "m_Script", utilizando el GUID del metaarchivo del script. Con esto, puede observar cómo se trata cada script, solo como un activo.
Figura 11: MonoBehaviour YAML haciendo referencia a un recurso de script
Los casos de uso para este escenario incluyen, pero no se limitan a:
- Encontrar todos los usos de un activo buscando el GUID del activo en todos los demás activos
- Reemplazar todos los usos de ese activo con otro GUID de activo en todo el proyecto
- Reemplazar un activo con otro que tenga una extensión diferente (es decir, reemplazar un archivo MP3 con un archivo WAV) eliminando el activo original, nombrando el nuevo exactamente igual con la nueva extensión y renombrando el metaarchivo del activo original con la nueva extensión
- Corrección de referencias perdidas al eliminar y volver a agregar el mismo activo cambiando el GUID de la nueva versión con el GUID de la versión anterior
Instancias prefabricadas, prefabricadas anidadas y variantes
Cuando se usan instancias de Prefab en una escena, o Prefabs anidados dentro de otro Prefab, los GameObjects y los componentes de Prefab no se serializan en el Prefab que los usa, sino que se agrega un objeto PrefabInstance. Como puede ver en la Figura 12, PrefabInstance tiene dos propiedades clave: "m_SourcePrefab" y "m_Modifications".
Como habrá notado, "m_SourcePrefab" es una referencia al activo prefabricado anidado. Ahora, si busca su ID de archivo en el activo prefabricado anidado, no lo encontrará. En este caso, "100100000" es el Id. de archivo de un objeto creado durante la importación de Prefab, llamado Prefab Asset Handle, que no existirá en YAML.
Además, "m_Modifications" comprende un conjunto de modificaciones o "anulaciones" realizadas en el Prefab original. En la Figura 12, anulamos los ejes X, Y y Z de la posición local original de una Transformación dentro del prefabricado anidado, que se puede identificar a través de su ID de archivo en la propiedad de destino. Tenga en cuenta que la Figura 12 anterior se ha abreviado para facilitar la lectura. Una PrefabInstance real normalmente tendrá más entradas en la sección m_Modifications.
Ahora, es posible que se pregunte, si no tenemos los objetos prefabricados anidados en nuestro prefabricado externo, ¿cómo hacemos referencia a los objetos en los prefabricados anidados? Para tales escenarios, Unity crea un objeto de "marcador de posición" en el Prefab que hace referencia al objeto adecuado en el Prefab anidado. Estos objetos de marcador de posición están marcados con la etiqueta "despojada", lo que significa que se simplifican con solo las propiedades necesarias para actuar como objetos de marcador de posición.
La Figura 13 muestra de manera similar cómo tenemos un Transform marcado con la etiqueta "stripped", que no tiene las propiedades habituales de un Transform (como "m_LocalPosition"). En su lugar, tiene las propiedades "m_CorrespondingSourcePrefab" y "m_PrefabInstance" rellenadas de una manera que hace referencia al activo prefabricado anidado y al objeto PrefabInstance en el archivo al que pertenece. Encima, puede ver parte de otra transformación cuyo "m_Father" hace referencia a esta Transformación de marcador de posición, lo que convierte a GameObject en un elemento secundario del objeto Nested Prefab. A medida que comience a hacer referencia a más objetos en los prefabricados anidados, se agregarán más de estos objetos de marcador de posición a YAML.
Convenientemente, no hay diferencia cuando se trata de variantes prefabricadas. El Prefab base de una Variant es solo una PrefabInstance con una Transformación que no tiene padre, lo que significa que es el objeto raíz de la Variant. En la Figura 14, puede ver que la propiedad "m_TransformParent" de PrefabInstance hace referencia a "fileID: 0". Esto significa que no tiene padre, lo que lo convierte en el objeto raíz.
Si bien puede usar este conocimiento para reemplazar un prefabricado anidado o el prefabricado base de una variante por otro, este tipo de modificación puede ser riesgoso. Proceda con precaución y tenga una copia de seguridad por si acaso.
Comience reemplazando todas las referencias al GUID del Prefab base actual con el GUID del nuevo, tanto en el objeto PrefabInstance como en los objetos de marcador de posición. Asegúrese de tomar nota de los ID de archivo de los objetos de marcador de posición. Sus propiedades "m_CorrespondingSourceObject" no solo hacen referencia al activo, sino también a los objetos dentro de él a través de sus ID de archivo. Es muy probable que los ID de archivo de los objetos en el Prefab actual difieran de los del nuevo Prefab, y si no los corrige, perderá anulaciones, referencias, objetos y otros datos.
Como puede ver, cambiar una base o un prefabricado anidado no es tan sencillo como podría pensarse. Esa es una de las razones principales por las que no se admite de forma nativa en el Editor.
Referencias obsoletas
Hay varios escenarios en los que se pueden dejar objetos obsoletos y referencias en YAML; un caso clásico sería eliminar variables en scripts. Si agrega una secuencia de comandos de armas a Player Prefab, tendría que establecer la referencia de Bullet Prefab a una prefabricada existente y luego eliminar la variable Bullet Prefab de la secuencia de comandos de armas. A menos que cambie y guarde el Player Prefab nuevamente, volviendo a serializarlo en el proceso, la referencia de la viñeta permanecerá en YAML. Otro ejemplo se centra en los objetos de marcador de posición de Prefabs anidados que no se eliminan cuando el objeto se elimina del Prefab original, lo que, nuevamente, podría solucionarse cambiando y guardando el Prefab. Finalmente, la re-serialización de activos podría forzarse a través de secuencias de comandos con la API AssetDatabase.ForveReserializeAssets.
Pero, ¿por qué Unity no elimina automáticamente las referencias obsoletas en los escenarios enumerados anteriormente? Esto se debe principalmente al rendimiento; para evitar volver a serializar todos los activos cada vez que cambia un script o Prefab base. Otra razón es para evitar la pérdida de datos. Supongamos que elimina por error una propiedad de secuencia de comandos (como Bullet Prefab) y desea recuperarla. Solo necesita revertir el cambio en su secuencia de comandos. Siempre que tenga una variable con el mismo nombre que la eliminada, sus cambios no se perderán. Lo mismo sucedería si elimina el Bullet Prefab al que se hace referencia. Si recupera el Prefab exactamente como estaba, incluido el metaarchivo, se conservará la referencia.
Normalmente, esto no es un problema durante el tiempo de ejecución, dado que cuando Unity crea Player o Addressables, estos objetos obsoletos y referencias se borran. Pero incluso entonces, hay algunos casos en los que las referencias obsoletas pueden causar problemas, es decir, usar paquetes de activos puros. El cálculo de la dependencia del paquete de activos considera referencias obsoletas, lo que podría crear dependencias innecesarias entre paquetes, cargando más de lo necesario en tiempo de ejecución. Vale la pena pensar en esto al usar paquetes de activos. Cree o use cualquier herramienta existente para eliminar las referencias innecesarias.
Conclusión
Aunque puede ignorar completamente YAML la mayor parte del tiempo, entenderlo es útil para comprender el sistema de serialización de Unity. Si bien enfrentarse a grandes refactorizaciones y leer o modificar el YAML directamente con herramientas de procesamiento de activos puede ser rápido y efectivo, se recomienda buscar soluciones basadas en la API de la base de datos de activos de Unity siempre que sea posible. También es particularmente útil para resolver problemas de combinación en el control de versiones. Le recomendamos que explore la herramienta Smart Merge , que puede fusionar automáticamente Prefabs en conflicto, y lea más sobre YAML en nuestra documentación oficial .
Fuente: https://blog.unity.com/technology/understanding-unitys-serialization-language-yaml
No hay comentarios.