Consejos y buenas prácticas en Unity

Muchas veces, a la hora de desarrollar un juego, solemos pecar de ciertos malos hábitos que, ya sea por prisas o por pereza, con el tiempo hacen que nuestro proyecto se convierta en un monstruo inmantenible.

Sin duda, acostumbrarnos a cuidar los detalles relacionados con la organización de nuestro proyecto desde el inicio nos ayudará a desarrollar de una manera mucho más controlada y a crear productos mantenibles. Vamos a hablar de algunas buenas prácticas y recomendaciones útiles para mejorar nuestro trabajo diario.

Organización del proyecto

Organiza los directorios de tu proyecto

A pesar de que Unity nos ofrece total libertad para estructurar las carpetas de nuestro proyecto (pestaña “Project“), esto hace que en la mayoría de los casos acabemos teniéndolo todo desordenado, haciendo que nos cueste bastante encontrar cualquier recurso. Por ello, en la siguiente imagen, voy a proponer mi estructura de directorios ideal para cualquier tipo de proyecto:

Pestaña “Project” en Unity

Consejos:

  1. Intenta no guardar ningún asset en el directorio raíz del proyecto.
  2. Usa nombres lógicos e identificables para tus assets.
  3. Sigue siempre una misma convención a la hora de nombrar tus assets (por ejemplo usando CamelCase).
  4. Cuando descargues algo de la Asset Store, cópialo a la carpeta “AssetStore”, pero respeta la estructura de directorios que traiga el asset en sí. Esto será conveniente para mantener todo lo relacionado con dicho asset encapsulado bajo el mismo contexto.
  5. Dentro de la carpeta “Models”, es probable que cada modelo pueda contener sus propias carpetas de texturas, materiales, etc.. Es aconsejable hacerlo así por el mismo motivo del punto 4: mantener el contexto.

Organiza la estructura de tus escenas

Ahora hablemos de la organización de nuestras escenas (pestaña “Hierarchy“). Al igual que he comentado antes sobre la ordenación de las carpetas del proyecto, es muy común que la organización de nuestra escena también acabe siendo bastante caótica, sobre todo cuando la complejidad de nuestro proyecto va aumentando. Aunque aquí se puede ser más flexible en cuanto a organización y nomenclatura debido a que la composición de cada escena va a depender del tipo de juego, sí que creo que hay algunos elementos comunes a casi todas. He aquí mi organización ideal a seguir:

Pestaña “Hierarchy” en Unity

Consejos:

  1. Posiciona todos los objetos vacíos que crees en la posición (0, 0, 0) y con rotación (0, 0, 0).
  2. Para objetos vacíos que solo contengan scripts asignados (por ejemplo un objeto de tipo GameManager) usa el caracter @ delante de su nombre. Esto hará que sean fácilmente identificables.
  3. Aparte de esta plantilla que propongo para organizar nuestras escenas, dependiendo del tipo de juego que estemos desarrollando, es común que surjan algunos elementos que no correspondan a ninguna de las carpetas propuestas (por ejemplo items, enemigos, etc.). En estos casos quedará al criterio personal de cada desarrollador el organizar de la manera más efectiva dichos elementos.

Usa Prefabs para todo

Usar prefabs en Unity es, sin duda, la mejor forma de compartir objetos pre-configurados en tu proyecto. La razón principal por la que siempre deberías usar prefabs es que cuando cambiemos cualquier propiedad de estos, todas las instancias que tengas en cualquiera de tus escenas serán cambiadas automáticamente. Por ejemplo, ¿tenemos 50 escenas y queremos añadir un nuevo componente a nuestro player?, pues sin problema, si nuestro player es un prefab, simplemente le añadimos el nuevo componente y será cambiado directamente en las 50 instancias. Útil, ¿verdad?

El consejo en este caso no podría ser más claro: prefabrica todo lo que añadas a tu escena. De hecho, deberías llegar al punto en que seas capaz de crear un nuevo nivel partiendo de una escena vacía y arrastrando uno o más prefabs a ella.

Ejemplo de un Prefab

Puedes consultar la documentación oficial sobre Prefabs en Unity aquí.

Control de versiones (VCS)

Quizás te suenen conceptos como Git, Subversion, Unity Collab, etc. Todos ellos son sistemas de control de versiones (VCS), pero… ¿para qué sirven y por qué debería utilizarlos en mis proyectos?

Muchos desarrolladores, sobre todo programadores, hacen uso de este tipo de herramientas y sin embargo no son conscientes de todo su potencial, utilizando tan solo su función de “backup” para el código fuente, pero la verdad es que ofrecen muchas funcionalidades más que si sabemos aprovechar conseguiremos tener más control sobre nuestro código y, sin duda, ser más eficientes en nuestro trabajo.

Estas son algunas de las funcionalidades más útiles de cualquier sistema de control de versiones:

  • Sincronización de proyectos dentro de un equipo de trabajo

Un sistema de control de versiones es la herramienta idónea para que varios desarrolladores trabajen sobre un mismo proyecto y que ninguno de ellos sobreescriba o elimine el código de otros. Para ello, el control de versiones provee un sistema de sincronización (Servidor – Clientes) con el cual puedes mezclar (en la jerga de los VCS, hacer “merge“) diferentes versiones locales de un mismo código y aunarlo todo en una sola versión final. Aquí he de comentar que a veces hay que tener mucho cuidado a la hora de hacer estos “merges” de un mismo código fuente, pero que si nos acostumbramos a ello y somos cautos, conseguiremos agilizar muchísimo el trabajo dentro de un equipo.

  • Historial de diferentes versiones de tus assets

Una gran ventaja de cualquier VCS es que nos permite ir subiendo (hacer “commits“) diferentes versiones de nuestro código al servidor de VCS de manera que en cualquier momento podemos volver a una versión anterior sin problema. La filosofía con la que se suele trabajar es: desarrollo algo – lo pruebo – arreglo posibles errores – me aseguro de que esté todo OK 100% – hago “commit” – se crea una nueva versión de ese código. De este modo, siempre podré hacer futuras modificaciones en ese código sin miedo a estropear algo de lo que ya hice anteriormente y que estaba estable, ya que siempre tendré disponible las versiones anteriores (y estables) a las que puedo volver cuando quiera.

  • Sistema de backup

Esta funcionalidad tiene que ver con la del punto anterior y se trata simplemente de que cada una de las versiones que vamos guardando de nuestro código en el servidor del VCS nos sirve como backup de nuestro proyecto.

  • Creación de ramas (branches)

Esta funcionalidad es muy interesante cuando queremos llevar en paralelo el desarrollo de diferentes versiones de nuestro proyecto. Por defecto solemos tener una rama principal (“main branch“) y al crear una segunda rama lo que hacemos es simplemente clonar la rama principal. A partir de ahí, todos los cambios que hagamos sobre cada una de las ramas afectará solamente a ellas mismas. La utilidad de trabajar así es, sobre todo en equipos de desarrollo grandes, ofrecer la posibilidad de que cada subgrupo del equipo pueda centrarse en desarrollos largos e independientes entre sí, de manera que no exista ningún conflicto entre ellos. Y al final, para unir el desarrollo realizado en ambas ramas, lo que se hace es un “merge” éstas de forma que consigamos de nuevo unirlo todo en una sola. Al igual que comenté en el primer punto, hay que tener mucho cuidado con el “merge“, pero controlar este sistema de trabajo beneficiará a todo el equipo.

Espero que, después de leer sobre las bondades de un sistema de control de versiones, te animes a utilizarlo… ¡merece la pena! La elección de cuál usar depende de ti y de las necesidades de tu desarrollo/proyecto/equipo. Yo personalmente, para el desarrollo con Unity, suelo usar Bitbucket para proyectos privados y GitHub para proyectos públicos, ambos basados en el sistema Git, pero debes saber que incluso Unity ofrece, de manera gratuita, su propio sistema de control de versiones llamado Collab y que está totalmente integrado con el editor de Unity. Te animo a que leas documentación sobre ellos y que los uses en tus proyectos, ya que para empezar a usarlos no hace falta demasiado conocimiento y solo te aportará ventajas.

Optimizar nuestro trabajo programando

Usa métodos de extensión en tu código

Ya hablé sobre los métodos de extensión en un antiguo post de mi blog. Te recomiendo que le eches un vistazo ya que en él explico detalladamente tanto su gran utilidad como la manera de trabajar con ellos.

Personaliza las plantillas de script por defecto

Cuando creamos un nuevo script desde Unity (Create -> C# Script), automáticamente nos lo crea de la siguiente manera:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestScript : MonoBehaviour {

   // Use this for initialization
   void Start () {
   
   }

   // Update is called once per frame
   void Update () {
   
   }

}

Puede que esta plantilla por defecto no te resulte demasiado útil debido a que no siempre vayas a usar los eventos Start() y Update() en todos tus scripts, que te interese añadir código adicional o simplemente que no te guste cómo están colocadas las llaves en las funciones (sí, los programadores solemos ser bastante maniáticos con nuestra forma de programar). Y al final, una vez creamos un nuevo script, siempre acabamos borrando todo o parte de lo que nos ha generado la plantilla y empezamos a programar nuestro propio código. No es que sea algo que nos quite demasiado tiempo pero a la larga todo cuenta.

Bien pues existe una manera de acceder y modificar esta plantilla por defecto a nuestro antojo. Tan fácil como irnos a la siguiente ruta de nuestra máquina:

ruta_donde_tengamos_instalado_Unity/Editor/Data/Resources/ScriptTemplates

Allí encontraremos algunos ficheros como estos:

Todos ellos son plantillas por defecto que Unity usa para crear nuevo contenido. Entre ellos vemos uno que se llama “81-C# Script-NewBehaviourScript.cs.txt” que es el referente a la plantilla por defecto para crear nuevos scripts con C# y que podremos modificar a nuestra conveniencia.

Optimizar nuestro trabajo en el editor

Aprende a crear Editor Scripts

Una de las muchas cosas buenas que ofrece Unity es la posibilidad de extender su editor de modo que podamos crear nuestras propias herramientas (accesibles desde los propios menús e inspectores de Unity) que nos sirvan para realizar tareas útiles. Por ello es muy recomendable invertir algo de tiempo en aprender a crear Editor Scripts, que son scripts que añaden nuevas funcionalidades (extensiones) al editor. No hace falta que seamos expertos en ello ni que creemos interfaces bonitas, tan solo pequeñas funcionalidades que nos resulten útiles y/o que automaticen tareas que solemos hacer.

Ejemplo de una ventana personalizada en Unity

Puedes obtener más información sobre Editor Scripts en su documentación oficial.

Cambia el color del editor en “Play Mode”

¿Cuántas veces te ha pasado que, después de haber estado modificando características en tu escena, te has dado cuenta de que estabas en modo Play y al parar la ejecución has perdido todos esos cambios? Seguro que sabes de lo que hablo… Pues bien, una manera fácil de solucionarlo sería la de cambiar el color del editor cuando esté en Play Mode.

Es tan simple como irte a Edit -> Preferences -> Colors y cambiar la propiedad “Playmode tint” a un color llamativo que te alerte de cuando estás en Play Mode.

Color personalizado en “Play Mode”

Configura varios Layouts

Como sabrás, Unity te permite colocar cada una de sus ventanas (Project, Hierarchy, Inspector, Scene, Game…) en la parte de la pantalla que tú quieras, pudiendo así personalizar tu entorno de trabajo. Un uso interesante de esta característica es la de poder guardar varias de esas configuraciones personalizadas (Layouts) para poder usarlas en cualquier momento en función de tus necesidades. Por ejemplo, si trabajas con un ordenador portátil, es muy posible que a veces se te haya ocurrido la idea de extender tu escritorio mediante alguna pantalla externa y, al tener más espacio, cambiar la distribución de tus ventanas. Para casos así es muy útil tener varios Layouts pre-configurados para cada una de las situaciones en que trabajemos. Podríamos tener, por ejemplo, los siguientes Layouts: “Casa“, “Oficina“, “Montaje de Niveles“, “Animación“, etc.. Cada uno de ellos iría destinado a un entorno de trabajo o incluso a un tipo de tarea.

Para guardar un Layout, una vez posicionadas todas las ventanas como queremos, basta con abrir, en la parte superior derecha de Unity, el desplegable “Layout“:

Y seleccionar “Save Layout…“. Aquí ponemos el nombre de nuestro Layout, salvamos y ya está, ¡listo para usarlo!

Edita varias escenas a la vez

¿Sabías que Unity te permite abrir en su editor varias escenas a la vez? Pues sí, y es tan fácil como arrastrar las escenas que quieras desde la ventana “Project” a la de “Hierarchy”. Verás algo como esto:

Edición de 2 escenas simultáneamente

Aunque debemos ser cautos a la hora de abrir muchas escenas a la vez, sobre todo si estas tienen bastante carga gráfica y tu máquina no es excesivamente potente, esta funcionalidad es realmente útil cuando queremos modificar ciertos GameObjects que se repiten en varias de nuestras escenas y queremos aplicarle una misma configuración (posición, rotación, escala, etc.).

Duplica y bloquea pestañas

Hay veces en que nos puede resultar útil tener, por ejemplo, 2 pestañas “Project” abiertas a la vez mostrando assets de diferentes rutas. Esto podemos hacerlo, y además con cualquiera de las pestañas de Unity, del siguiente modo:

  1. Hacemos click con el segundo botón en la pestaña.
  2. Seleccionamos “Add Tab”.
  3. Elegimos la ventana que queremos añadir.
  4. Una vez añadida, podemos arrastrarla al lugar que queramos dentro de Unity.

Otra opción interesante de las pestañas en Unity es la de poder bloquearlas para así mantener en ellas, de manera fija, la información que queramos. Pongamos, por ejemplo, que queremos visualizar en el inspector de Unity las propiedades de 2 objetos de nuestra escena a la vez. Esto, a priori, parece imposible debido a que, cada vez que seleccionamos un objeto, Unity actualiza instantáneamente la información en el inspector. Pero, ¿qué pasa si duplicamos la pestaña “Inspector” y una de ellas la bloqueamos? Tendremos justo lo que queremos 🙂 Lo haremos del siguiente modo:

  1. Duplicamos la pestaña “Inspector” tal y como hemos explicado antes.
  2. Seleccionamos la nueva pestaña “Inspector” duplicada.
  3. Seleccionamos un GameObject para que muestre sus propiedades en el inspector.
  4. Pinchamos en el icono con forma de candado en la parte superior de la pestaña para bloquearla.
  5. Una vez bloqueada, podemos arrastrarla al lugar que queramos para una mejor visualización.
Propiedades de los objetos “Main Camera” y “Directional Light” mostrados a la vez

¡Y esto ha sido todo por hoy! Espero publicar más posts de este tipo en el futuro y espero que todas estas buenas prácticas que hemos visto te resulten útiles a la hora de trabajar día a día con Unity. A veces cuesta aplicar muchas de ellas por culpa de nuestra prisa por hacer las cosas o por la falta de costumbre, además ninguna de ellas es estrictamente necesaria para poder crear un videojuego, pero sí que son muy recomendables y una vez que las pongas en práctica varias veces y te acostumbres a ello te darás cuenta de que todo será un poco menos caótico, tu rendimiento diario aumentará y, más importante aún, tú habrás mejorado como profesional 😉

Anuncios

5 comentarios en “Consejos y buenas prácticas en Unity

  1. “Cuando instancies un objeto en tiempo de ejecución, asegúrate de meterlo en “Runtime” para así poder identificarlos rápidamente.”

    No es una buena práctica. Unity re-calcula el componente Transform de cada GameObject respecto a su padre, por lo que instanciar objetos en runtime dentro de otros, comporta el doble de trabajo.

    Ejemplo: Si instancias 200 proyectiles dentro de “Runtime” y se están moviendo cada frame, Unity hace el doble de trabajo, ya que re-calcula la posición en la escena y en la escena respecto a su padre “Runtime”.

    Los GameObjects NUNCA se deben usar como carpetas ni organizadores.

    “Siempre que sea posible, es aconsejable acceder a los componentes (ya sean componentes asociados a objetos externos o asociados al propio objeto) desde código, en tiempo de ejecución, desde el evento Start()”

    No es exactamente así, se debe acceder a los componentes que necesites cuando lo necesites. Aunque, si la referencia es estática (no se modifica ni desaparece en todo el juego) es muy recomendable acceder a ella en el Awake() que se ejecuta antes del primer frame del juego.

    Si se intenta acceder a ellas en el Start(), no podrás acceder ni modificarla ni en el Awake() ni en el OnEnable().

    Le gusta a 1 persona

    1. Hola Miguel! Gracias por tus aclaraciones, la verdad es que desconocía lo que comentas sobre instanciar objetos en runtime dentro de un objeto vacío. He eliminado el consejo referente a ello.

      Respecto a la referencia a componentes también he hecho una pequeña modificación respecto a lo del evento Start() ya que es más óptimo hacerlo en el Awake(), y también he aclarado que este tipo de cacheo en runtime es recomendable hacerlo sobre todo para componentes, como dices, estáticos en la escena.

      Gracias por aportar y ayudarme a mejorar el artículo! 🙂

      Me gusta

  2. Acabas de recomendar que se haga un “FIND” antes que una referencia por inspector????
    (notese la cantidad de ‘?’.. es por el asombro)

    Debes saber que GetComponent era una función que hasta hace poco necesitaba muchos recursos. Actualmente (si no me equivoco desde Unity 5, o 4.X) no requiere tanto, pero sigue siendo determinante para el performance (especialmente si usas muchos por frame).
    Pero mucho (Pero que MUCHO) mas costoso que GetComponent es: GameObject.Find().

    Si tu código tiene un solo “GameObject.Find()” estas haciendo algo mal.

    Tu solución a el hecho de que referencias externas al prefab se rompe al instanciar un prefab es completamente ineficiente!
    Si quieres acceder desde tu “Player” (que es un prefab) a un Manager (cualquier manager!) deberías usar referencias estaticas, es decir, Singletons….

    Échales un ojo aquí: https://mecze-entertainment.com/2016/08/06/singletons/

    La referencia estática es, probablemente, la referencia mas eficiente (o una de las mas eficientes) del mercado.

    La referencia por inspector, aunque es fea para el programador / diseñador, también es muy eficiente Es cuestión de organizar bien el Inspector, con Scripts de Editor:
    – Un script de editor con un botoncito que haga los “GetComponent” en tiempo de editor y los cachee en variables SERIALIZADAS y escondidas al inspector([SerializeField] [HideInInspector]). Si es en tiempo de editor hasta puede usar un “GameObject.Find()”, la ralentización la sufrirás tu, pero no tus jugadores.

    o sencillamente usando [Header(“Apartado de Referencias”)] para organizar el inspector.

    Le gusta a 1 persona

    1. Hola Mese, la verdad es que llevo unos días leyendo más documentación sobre este tema y, después de leer vuestros comentarios, es cierto que no he abordado el tema del “acceso a componentes” de la manera más adecuada. Creo que lo mejor será que corrija dicha sección del artículo.

      Muchas gracias por vuestros comentarios… creo que rectificar es de sabios y más si con ellos estoy aprendiendo 😉

      Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s