Arquitectura Unity para jams II

Granularidad

Granularidad se refiere a separar cosas, separar scripts, se trata de entender cómo los objetos y scripts de unity funcionan entre ellos para crear una simbiosis más eficiente.

El mejor ejemplo de lo que esta MAL, es tener un script llamado Player, donde tenemos TODO sobre nuestro personaje. El manejo de input, el manejo de colisiones, la vida, las balas, el puntaje, etc. Perfectamente podemos hacer scripts para cada una de esas cosas y de hecho es la forma correcta de hacerlo.

Instintivamente creemos que tener todo en un mismo archivo nos ahorra tiempo, la realidad es que nos cuesta tiempo. Literalmente hacer un archivo y nombrarlo lleva dos segundos, referenciar los scripts usando GetComponent<>() o haciendo una variable pública y asignándolo desde el editor, nos lleva otros 2 segundos. Sin embargo, tener todo en un mismo archivo es la receta del desastre, si nosotros consolidamos muchas mecánicas dentro de un mismo comportamiento, estamos haciendo un archivo demasiado importante y por ende muy fácil de romper. El escenario más común de esto es cuando un programador queriendo implementar una mecánica de recarga de balas, lo hizo cambiando la clase Player, otro programador estaba trabajando en la vida que también estaba en la clase Player. Hay conflictos al consolidar los cambios, algo se rompe, el juego no funciona.

Cuando todos estamos metiendo muchas manos en un archivo, siempre es “algo se rompió “, si trabajamos con scripts separados y chicos, la frase cambia a “tal componente se rompió”. Lo bueno de que un componente se rompa es que es fácil de identificar, fácil de arreglar y no rompe todo el juego.

Desacoplamiento

Este concepto es parte del paradigma funcional, se trata de reducir la cantidad de Start y Updates que tenemos en el código en general. El mejor ejemplo es Input vs Movimiento.

El escenario es bastante común, se programa el movimiento en el mismo script que el Input. ¿Tiene sentido? Si nos guiamos por el concepto de granularidad, NO. Input y movimiento por mas de que sean cosas que van de la mano, son sumamente diferentes. De hecho, tan diferentes que ni siquiera deberían estar bajo el mismo elemento de la jerarquía.

Si lo analizamos bien, el que se mueve es el Personaje, y el que manda inputs es el Jugador, dos conceptos que muchas veces van de la mano, excepto por ejemplo en juegos de estrategia. La realidad es que son comportamientos separados y por ende deberían codearse separados. Otro motivo es la inteligencia artificial, digamos que las mismas reglas de movimiento se aplican para los jugadores como para la IA, claramente no deberíamos repetir todo el código de movimiento, sino hacer que el código de movimiento para todas las entidades, sea exactamente el mismo componente, pero que exista otro componente que controle al movimiento.

Acá es donde entra el desacoplamiento, idealmente el movimiento no debería usar ni Start, ni Update, debería exponer métodos públicos como por ejemplo, MoveRight(), MoveLeft(), Jump(), etc. El Update estará en el controlador, sea IA o Input de jugador, que va a tener una referencia al script de movimiento de la entidad que controla, y cuando leamos un input de moverse a la derecha vamos a llamar a ese movement.MoveHorizontal(xAxis)

 

Ejemplo practico

Hagamos, en 10 minutos, una demo técnica de como separar los comportamientos de movimiento e input y veamos como haciendo unos pequeños cambios, podemos hacer que el jugador mueva 3 personajes diferentes alternando entre ellos (Al estilo Lost Vikings).

Primero creemos nuestra escena, con un elemento vacío Player y un elemento vacío Character. Esta es una de las formas más comunes de empezar una idea, con elementos vacíos en una escena vacía. Dentro de Character podemos crear un cubo 3D como hijo de él.

Una de las mejores prácticas de Unity es separar las partes visuales de las lógicas dentro de la jerarquía, evitar tener un objeto con lógica y representación gráfica es muy útil para escalar luego.

Recordemos el desacoplamiento, el objetivo de esta demo es tener el input separado del movimiento. Empecemos con el movimiento, dentro de Character hagamos un nuevo script, CubeMovement, bien trivial.

2018-08-21 22_04_56-Window

Noten que no hay Update en un script de movimiento. Ahora continuemos con el input, para esto vamos a ir al objeto Player y vamos a crear un script de input, seamos bien específicos por el bien de nuestros compañeros de equipo, PlayerKeyboardInput.

2018-08-21 22_11_19-Window

Ahora si vemos el Update nuevamente, la prolijidad es clave, leyendo ese código no hay dudas, cuando actualizó el frame, movemos al personaje en el vector de Input. Si la lógica de movimiento cambia, solo tengo que cambiar en la propiedad de MoveAxis, no en el Update.

¡Lo único que falta es asignar nuestro Character al campo adecuado de Player usando el editor y listo! Tenemos un personaje móvil.

Ahora para la segunda parte, agreguemos más personajes que puedan ser controlados por el jugador de la misma manera. La condición es que no se pueden mover simultáneamente, el jugador puede controlar solo uno a la vez. Y para hacerlo interesante, hagamos que los personajes tengan diferente velocidad entre ellos.

Lo primero que vamos a necesitar va a ser mas personajes, así que hagamos 2 cubos mas con sus respectivos nombres. Luego, vamos a necesitar algo que sepa de su existencia, esto puede ser el jugador mismo o un contenedor en la escena, no es necesario que los personajes sean hijos de dicho contenedor, pero es mas conveniente para quien este trabajando en la escena. Todo tiene su lugar y cuanto mas ordenado, mas fácil de mantener y escalar.

2018-08-21 22_03_21-Window

Bien, con esto terminado, necesitamos agregar que el jugador pueda cambiar de personaje, pero el script de PlayerKeyboardInput SÓLO maneja input y eso no debe cambiar. Así que vamos a necesitar algo mas, aprovechando nuestro objeto Units podemos crear un script nuevo, PlayerUnits. Este nuevo script va a tener la única responsabilidad de enviarle al jugador, cual es el próximo personaje cuando el jugador quiera cambiar.

2018-08-21 22_05_17-Window
PlayerUnits expone una propiedad llamada NextUnit que sin pedir nada, devuelve el próximo personaje en la lista

Ahora dentro de nuestro input tenemos que manejar este cambio, ahora no vamos a asignarle públicamente un personaje, sino que le vamos a asignar un proveedor de personajes.

2018-08-21 22_05_04-Window
El movimiento pasa a ser privado ya que no se asigna desde afuera. Solo el proveedor de personajes es asignado.

Por último, hay que vincular todo lo necesario en el editor. Player ahora va a conocer a PlayerUnits, y PlayerUnits debe conocer a los personajes.

Finalmente, esto nos provee con una solución escalable y fácil de mantener para mover varios personajes desde un jugador. ¿Por qué es escalable?  porque es fácil y seguro implementar mas mecánicas encima de esto. Los scripts están desacoplados de manera que funcionan sin tener mil dependencias obligatorias y las responsabilidades están bien definidas dentro de cada comportamiento.

cubes

One thought on “Arquitectura Unity para jams II

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 )

Google photo

Estás comentando usando tu cuenta de Google. 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 )

Conectando a %s