Consiga un mejor flujo de trabajo de escena con ScriptableObjects

AMEL NEGRA / UNITY TECHNOLOGIESContributor
Jul 1, 2020|9 minutos
Consiga un mejor flujo de trabajo de escena con ScriptableObjects
Para tu comodidad, tradujimos esta página mediante traducción automática. No podemos garantizar la precisión ni la confiabilidad del contenido traducido. Si tienes alguna duda sobre la precisión del contenido traducido, consulta la versión oficial en inglés de la página web.

Administrar múltiples escenas en Unity puede ser un desafío, y mejorar este flujo de trabajo es crucial tanto para el rendimiento del juego como para la productividad de tu equipo. Aquí compartimos algunos consejos para configurar sus flujos de trabajo de Scene de manera que se adapten a proyectos más grandes.

La mayoría de los juegos implican múltiples niveles y los niveles a menudo contienen más de una escena. En los juegos donde las escenas son relativamente pequeñas, puedes dividirlas en diferentes secciones usando Prefabs. Sin embargo, para habilitarlos o instanciarlos durante el juego es necesario hacer referencia a todos estos Prefabs. Esto significa que a medida que el juego se hace más grande y esas referencias ocupan más espacio en la memoria, resulta más eficiente utilizar escenas.

Puedes dividir tus niveles en una o varias escenas de Unity. Encontrar la forma óptima de gestionarlos todos se vuelve clave. Puede abrir varias escenas en el Editor y en tiempo de ejecución utilizando la edición de múltiples escenas. Dividir los niveles en varias escenas también tiene la ventaja de facilitar el trabajo en equipo, ya que evita conflictos de fusión en herramientas de colaboración como Git, SVN, Unity Collaborate y similares.

Gestión de múltiples escenas para componer un nivel

En el vídeo a continuación, mostramos cómo cargar un nivel de manera más eficiente al dividir la lógica del juego y las diferentes partes del nivel en varias escenas de Unity distintas. Luego, al utilizar el modo de carga de escena aditiva al cargar estas escenas, cargamos y descargamos las partes necesarias junto con la lógica del juego, que es persistente. Usamos Prefabs para que actúen como “anclajes” para las Escenas, lo que también ofrece mucha flexibilidad cuando se trabaja en equipo, ya que cada Escena representa una parte del nivel y se puede editar por separado.

Aún puedes cargar estas escenas mientras estás en el modo de edición y presionar Reproducir en cualquier momento, para que puedas visualizarlas todas juntas al crear el diseño del nivel.

Mostramos dos métodos diferentes para cargar esas escenas. El primero se basa en la distancia, lo que resulta muy adecuado para niveles no interiores como un mundo abierto. Esta técnica también es útil para algunos efectos visuales (como la niebla, por ejemplo) para ocultar el proceso de carga y descarga.

La segunda técnica utiliza un disparador para verificar qué escenas cargar, lo que es más eficiente cuando se trabaja con interiores.

Ahora que todo está administrado dentro del nivel, puedes agregar una capa encima para administrar mejor los niveles.

Gestión de múltiples niveles dentro de un juego mediante ScriptableObjects

Queremos realizar un seguimiento de las diferentes escenas de cada nivel, así como de todos los niveles durante toda la duración del juego. Una forma posible de hacer esto es utilizar variables estáticas y el patrón singleton en los scripts de MonoBehaviour, pero esta solución presenta algunos problemas. El uso del patrón singleton permite conexiones rígidas entre sus sistemas, por lo que no es estrictamente modular. Los sistemas no pueden existir por separado y siempre dependerán unos de otros.

Otro problema tiene que ver con el uso de variables estáticas. Como no puedes verlos en el Inspector, necesitas cambiar el código para configurarlos, lo que dificulta que los artistas o diseñadores de niveles prueben el juego fácilmente. Cuando necesitas compartir datos entre las diferentes escenas, utiliza variables estáticas combinadas con DontDestroyOnLoad, pero esto último debe evitarse siempre que sea posible.

Para almacenar información sobre las diferentes escenas, puedes usar ScriptableObject, que es una clase serializable utilizada principalmente para almacenar datos. A diferencia de los scripts MonoBehaviour, que se utilizan como componentes adjuntos a GameObjects, los ScriptableObjects no están adjuntos a ningún GameObject y, por lo tanto, pueden compartirse entre las diferentes escenas de todo el proyecto.

Quieres poder usar esta estructura para los niveles pero también para las escenas del menú en tu juego. Para ello, cree una clase GameScene que contenga las diferentes propiedades comunes entre niveles y menús.

Tipo de bloque desconocido "codeBlock", especifique un serializador para él en la propiedad `serializers.types`

Tenga en cuenta que la clase hereda de ScriptableObject y no de MonoBehaviour. Puedes agregar tantas propiedades como necesites para tu juego. Después de este paso, puedes crear clases Level y Menu que heredan de la clase GameScene que se acaba de crear, por lo que también son ScriptableObjects.

Tipo de bloque desconocido "codeBlock", especifique un serializador para él en la propiedad `serializers.types`

Agregar el atributo CreateAssetMenu en la parte superior le permitirá crear un nuevo nivel desde el menú Activos en Unity. Puedes hacer lo mismo para la clase Menú. También puedes incluir una enumeración para poder elegir el tipo de menú desde el Inspector.

Tipo de bloque desconocido "codeBlock", especifique un serializador para él en la propiedad `serializers.types`


Ahora que puede crear niveles y menús, agreguemos una base de datos que enumere los niveles y menús para una fácil referencia. También puedes agregar un índice para realizar un seguimiento del nivel actual del jugador. Luego, puedes agregar métodos para cargar un nuevo juego (en este caso se cargará el primer nivel), para repetir el nivel actual y para pasar al siguiente nivel. Tenga en cuenta que solo cambia el índice entre estos tres métodos, por lo que puede crear un método que cargue el nivel con un índice para usarlo varias veces.

Tipo de bloque desconocido "codeBlock", especifique un serializador para él en la propiedad `serializers.types`


También hay métodos para los menús, y puedes usar el tipo de enumeración que creaste antes para cargar el menú específico que deseas; solo asegúrate de que el orden en la enumeración y el orden en la lista de menús sean los mismos.

Ahora finalmente puedes crear un ScriptableObject de nivel, menú o base de datos desde el menú Activos haciendo clic derecho en la ventana Proyecto.

Imagen

A partir de ahí, simplemente sigue agregando los niveles y menús que necesites, ajustando la configuración y luego agregándolos a la base de datos de Escenas. El siguiente ejemplo muestra cómo se ven los datos de Nivel 1, Menú principal y Escenas.

Imagen

Es hora de llamar a esos métodos. En este ejemplo, el botón Siguiente Nivel en la interfaz de usuario (UI) que aparece cuando un jugador llega al final del nivel llama al método NextLevel. Para adjuntar el método al botón, haga clic en el botón más del evento On Click del componente Botón para agregar un nuevo evento, luego arrastre y suelte Scenes Data ScriptableObject en el campo de objeto y elija el método NextLevel de ScenesData, como se muestra a continuación.

Imagen

Ahora puedes realizar el mismo proceso para los demás botones: para repetir el nivel o ir al menú principal, etc. También puedes hacer referencia a ScriptableObject desde cualquier otro script para acceder a las diferentes propiedades, como AudioClip para la música de fondo o el perfil de posprocesamiento, y usarlos en el nivel.

Consejos para evitar errores en sus procesos
  • Minimizar la carga/descarga

En el script ScenePartLoader que se muestra en el video, se puede ver que un jugador puede entrar y salir del colisionador varias veces, lo que desencadena la carga y descarga repetida de una escena. Para evitar esto, puedes agregar una corrutina antes de llamar a los métodos de carga y descarga de la Escena en el script, y detener la corrutina si el jugador abandona el disparador.

  • Convenciones de nombres

Otro consejo general es utilizar convenciones de nomenclatura sólidas en el proyecto. El equipo debe acordar de antemano cómo nombrar los diferentes tipos de activos, desde guiones y escenas hasta materiales y otras cosas del proyecto. Esto hará que sea más fácil no sólo para usted sino también para sus compañeros de equipo trabajar en el proyecto y mantenerlo. Esta es siempre una buena idea, pero es crucial para la gestión de escenas con ScriptableObjects en este caso particular. Nuestro ejemplo utilizó un enfoque sencillo basado en el nombre de la escena, pero hay muchas soluciones diferentes que dependen menos del nombre de la escena. Debes evitar el enfoque basado en cadenas porque si cambias el nombre de una escena de Unity en un contexto determinado, en otra parte del juego esa escena no se cargará.

  • Herramientas personalizadas

Una forma de evitar la dependencia de nombres en todo el juego es configurar el script para que haga referencia a las Escenas como tipo de Objeto . Esto le permite arrastrar y soltar un activo de escena en un Inspector y luego obtener de forma segura su nombre en un script. Sin embargo, dado que es una clase Editor, no tienes acceso a la clase AssetDatabase en tiempo de ejecución, por lo que necesitas combinar ambos datos para obtener una solución que funcione en el Editor, evite errores humanos y aún funcione en tiempo de ejecución. Puede consultar la interfaz ISerializationCallbackReceiver para obtener un ejemplo de cómo implementar un objeto que, tras la serialización, puede extraer la ruta de la cadena del activo de Escena y almacenarla para usarla en tiempo de ejecución.

Además, también puedes crear un Inspector personalizado para que sea más fácil agregar rápidamente escenas a las configuraciones de compilación usando botones, en lugar de tener que agregarlas manualmente a través de ese menú y tener que mantenerlas sincronizadas.

Como ejemplo de este tipo de herramienta, consulte esta excelente implementación de código abierto del desarrollador JohannesMP (este no es un recurso oficial de Unity).

Déjanos saber lo que piensas

Esta publicación muestra solo una forma en que ScriptableObjects puede mejorar su flujo de trabajo al trabajar con múltiples escenas combinadas con prefabricados. Cada juego tiene formas muy diferentes de gestionar las escenas: no existe una única solución que funcione para todas las estructuras de juego. Tiene mucho sentido implementar sus propias herramientas personalizadas para adaptarse a la organización de su proyecto.

Esperamos que esta información pueda ayudarte en tu proyecto o tal vez inspirarte a crear tus propias herramientas de gestión de escenas.

Déjanos saber en los comentarios si tienes alguna pregunta. Nos encantaría saber qué métodos utilizas para administrar las escenas en tu juego. Y siéntete libre de sugerir otros casos de uso que te gustaría que cubramos en futuras publicaciones del blog.