En este post me voy a centrar en ver cómo montar la base para crear un videojuego clásico del tipo ‘MataMarcianos’. Vamos a ver como se haría en el motor Unity 3D. Mi idea es probar varios motores que ofrecen características similares y crear la misma base o el mismo juego y comparar el desarrollo. Próximamente utilizaré el motor Wave Engine.

 

¿Qué es Unity 3D?

 

Unity es un motor de videojuegos multiplataforma creado por Unity Technologies y está disponible como plataforma de desarrollo para Microsoft Windows y OS X. La plataforma de desarrollo tiene soporte de compilación con diferentes tipos de plataformas.

 

Empecemos

Partimos de la base de que se conocen los conceptos básicos que ofrece este motor, tanto del editor, como la creación de objetos, asignación de Scripts o de componentes etc.

Creamos un nuevo proyecto desde la pantalla de Bienvenida o si ya tenemos abierto el programa, desde File -> New Project. En este caso el proyecto será creado con la opción 3D, también te ofrecerá la opción de crearlo en 2D. La diferencia entre estas opciones es la creación de la ‘MainCamera’, que en 2D tendrá la opción de Ortográfica para renderizar Sprites y los ejes en el editor te vendrán en 2D(x, y). Como nosotros lo hemos creado en 3D, la cámara vendrá en Perspectiva y el editor nos ofrecerá en este caso los ejes en 3D (x, y, z).

 

Creando nuestro Player

Vamos a comenzar a crear nuestro Player, para ello primero vamos a crear un nuevo objeto 3D, yo he elegido la figura del Cubo, aunque ésta puede ser cualquiera. Desde GameObject -> 3D Object -> Cube. Esto colocará un nuevo cubo en la escena. Lo renombramos y le ponemos el nombre de ‘Player’, para que lo tengamos localizado. Por defecto la posición puede ser que no sea la que queramos, así que lo movemos a nuestro gusto, en mi caso, lo voy a colocar en la parte inferior de la escena (Figura 1), en el centro, ya que los enemigos van a venir de arriba hacia abajo.

Figura 1

Para empezar a darle vida al Cubo, vamos a crear nuestro primer Script, que lo vamos a llamar ‘PlayerControl.cs’ y se lo asignamos a nuestro objeto Player.

 

Moviendo el Player

Dentro de nuestro Script recién creado vamos a ir añadiendo el código básico para poder mover al jugador por la escena, mediante el teclado. Como veremos Unity, al crear un nuevo script de C# nos crea una clase que hereda de MonoBehavior, y trae dos métodos por defecto Start() y Update(). Start() se ejecuta justo en el Frame cuando el Script se habilita, antes de cualquier otro método y se ejecuta una sola vez en la vida de este. Update(), por el contrario, se llama en cada frame durante toda la vida del Script.

Para detectar las entradas del teclado vamos a utilizar la clase ‘Input’ que nos ofrece UnityEngine.

Creamos un método privado para actualizar el movimiento del player, yo lo he llamado UpdateMovement(). En este vamos controlar las entradas del teclado y según que tecla vamos a darle una velocidad y una dirección. En mi juego el jugador solo se va a poder mover en el eje X. Por lo tanto la clase quedaría de la siguiente forma:

Con esto conseguimos que si pulsamos la letra ‘D’ o la tecla de la flecha derecha, nuestro Player se mueva hacia la derecha. Y haga lo contrario si pulsamos ‘A’ o la flecha izquierda. Estamos modificando la posición del Transform del Player, que es un Vector3 y añadiéndole velocidad en X. Usamos Time.DeltaTime que son los segundos que tardó el ultimo Frame en completarse. Speed es una variable pública del tipo ‘float’, la cual podremos modificar desde el editor, y ponerle el valor que más se acerque a nuestro gusto, en mi caso le pongo un valor de 20.

Si nos damos cuenta al movernos, el player desaparece de la visión de la cámara. Para mejorar esto vamos a añadir unos limites ficticios. Estos limites variaran según la configuración de los parámetros de la cámara, Field of View, Viewport Rect, etc. Para saber cuales son nuestros limites en la pantalla, desde el editor movemos nuestro personaje a izquierda y derecha y vemos cuando desaparece por ambos lados de la pantallas. Cogemos los valores de X y los apuntamos, en mi caso, seria -13 y 13. Vamos a hacer que el personaje desaparezca por un lado y aparezca por el otro, para que nunca se pierda por la escena. Añadimos el siguiente código a nuestro método UpdateMovement():

Así tenemos un movimiento lateral en el eje de las X sin que nuestro personaje se pierda de Vista. Y con esto finalizamos el manejo. Ahora vamos a ver cómo disparar.

 

Creando la Bala

El mismo proceso que para crear el Cubo del personaje, en este caso elijo la figura de la capsula que es lo más parecido a una Bala. La renombramos como Bullet, por ejemplo y le ponemos un tamaño acorde con nuestro player, ya que la capsula por defecto será más grande que el cubo. Y a diferencia de nuestro personaje, la bala no va a permanecer en escena siempre, y no será solo una. Así que el proceso que vamos a seguir será un poco diferente.

Para poder instanciar objetos en Unity que ya estén creados, debemos de tener una carpeta que se llame ‘Resources’ en nuestro proyecto, y meter ahí dentro todo lo que queramos que se vaya a instanciar por código, en este caso la bala, otros ejemplos serían, partículas, sonidos, etc. Podemos arrastrar directamente la capsula a la carpeta, con esto ya tenemos listo para buscarla por código., A continuación podemos borrarla de la escena, porque inicialmente no queremos que esté ahí.

Para controlar cuando disparamos, añadimos a nuestro Script un nuevo método que se llame UpdateFire():

Similar al control de movimiento vamos a esperar una entrada por teclado, en este caso he elegido la barra espaciadora. Con lo cual al pulsar esta tecla vamos disparar. Como veis he controlado la creación de balas por tiempo, para que no se creen infinitas balas consecutivas al mantener pulsado el ‘Espacio’, con un contador de tiempo hago que se creen cada cierto tiempo, esto puede ser configurado a gusto de cada uno, yo lo he puesto cada 0,2s. Entonces lanzamos el método Fire() que es donde instanciaremos las balas:

Llamamos al método Instantiate de la clase Object, que sirve para instanciar objetos, el primer parámetro es el objeto en si, en nuestro caso buscamos dentro de la carpeta ‘Resources’ por el nombre que le hayamos puesto a la bala, lo siguiente que nos pide es en la posición que queremos que se cree, en este caso para recrear un disparo que procede desde el player, le indicamos en el vector3 la posición del player en X y en Y. Por último nos pide la rotación, usamos Quaternion.identity, para que mantenga la rotación original del objeto.

Así tenemos nuestra clase PlayerControl terminada para nuestro objetivo, que sería mover y disparar. Si nos fijamos al disparar se instancian las balas en la posición del player, faltaría darles una velocidad y dirección. Pero esto lo haremos en un Script diferente que irá asociado al objeto Bala.

 

Moviendo la Bala

Creamos un nuevo Script llamado Bullet.cs, y lo asociamos al objeto Bullet que metimos en la carpeta Resources, con lo que conseguimos que cada bala que se instancie en la escena lleve asociado el comportamiento de este Script. Queremos que la bala en este juego salga disparada hacia arriba. Unicamente necesitamos añadir en el Update() la siguiente linea:

Aumentando la posicion en Y por el tiempo del Frame, le he dado la velocidad de 20. Con esto conseguimos que cada bala que se instancie cuando pulsamos el Espacio se mueva en el eje recreando así el disparo del player.

Para que cada bala creada no se quede hasta el infinito en la escena y estas saturen el rendimiento del juego en si, vamos a ir destruyendolas cuando salgan de la nuestra vista o colisionen con un enemigo, en este caso será en el futuro, cuando creemos a éstos. Añadimos para ello una comprobación en el Update y utilizamos un método que nos proporciona la clase Object, Destroy(Object), en este caso destruye por completo un objeto de la escena. Nos quedaría así:

Por la posición de mi cámara y los objetos en la escena, limito a 20 en el eje Y, así cuando pasen esa altura se borraran las balas de la escena.

Con esto tenemos ya nuestro personaje moviendose y disparando por la escena (Figura 2).

Figura 2.

Creando los Enemigos

El objetivo es que vayan cayendo enemigos desde arriba hacia abajo, para ello vamos a crear un Script que controle la creación aleatoria de estos enemigos, lo vamos a llamar EnemiesRespawn.cs. Y lo vamos a asignar a un objeto vacio en la escena, este no va a ser visible, únicamente servirá para que el Script se ejecute en nuestra escena. Lo creamos desde GameObject -> CreateEmpty, da igual la posición, lo renombramos como EnemiesRespawn, para tenerlo localizado y le asignamos el Script. Siguiendo las pautas de nuestro objetivo, vamos a tener el siguiente código:

Vamos a tener una posición inicial en Y para que los enemigos se empiece a crear por encima de la vista de la cámara, también tendremos un tiempo aleatorio para que se vayan creando y no haya saturación en pantalla, al igual que con las balas, esto lo conseguimos con un contador. Y en el método Respawn hacemos que el origen de X sea aleatorio, así caerán los enemigos en diferentes posiciones. En mi caso les doy -12 y 12, para que siempre entren dentro de la vista de la cámara. Al igual que hicimos con la bala, tendremos que hacer el mismo proceso de creación del objeto enemigo y añadirlo a Resources, para que a la hora de su creación los encuentre. Para los enemigos he vuelto a elegir un cubo como figura, podéis elegir el que queráis. Si os fijais ya se van creando de forma aleatoria los enemigos por encima de la cámara. pero se quedan estáticos.

 

Moviendo a los enemigos

Ahora vamos a hacer que los enemigos se muevan en la dirección del Player. Para ello creamos un nuevo Script que vamos a asignar al Objeto Enemy, yo lo he llamado Enemy.cs. Y para mover los enemigos añadimos el siguiente código:

Similar al Player añadimos una velocidad, pero para darle variedad lo hacemos de forma aleatoria, para que no bajen todos igual. Un toque especial también es que bajen rotando, da la sensación de que son como asteroides, así no quedan tan monótonos y parecidos al Player. Esto se hace con el método Rotate del Transform, que hace que gire el objeto según los valores que le apliques a su vector. Y por último para controlar que no se llena la escena de enemigos los vamos destruyendo cuando pasen el limite inferior de la cámara.

 

Colisiones entre objetos

Para controlar las colisiones necesitamos añadir un componente al enemigo, un Rigidbody, vamos al objeto dentro de la carpeta Resources y con la opción Add Component -> Physics -> Rigidbody (Figura3). Lo primero que haremos será desmarcar la opción Use Gravity del rigidbody para que no afecte al objeto, ya que el movimiento lo generamos por código, solo queremos el Rigidbody para controlar la colisión. El siguiente paso es añadir una Tag al enemigo que no viene por defecto, para saber que estamos colisionando con un enemigo y no con otro objeto. Dentro del Inspector del objeto, al igual que hemos añadido el Rigidbody podemos añadir Tags, aparecerá el tag ‘Untagged’ por defecto en todos los objetos (Figura 4). Si entramos dentro, nos dará la opción Add Tag… debajo de una lista por defecto en la que viene por ejemplo ‘Player’, pulsamos y creamos una con el nombre Enemy. Esto añadirá la opción la próxima vez que abramos la lista de Tags, así que se lo asignamos al objeto Enemy.

Figura 3.

 Figura 4.

Así queda nuiestro enemigo configurado para ser detectado por colisión.

A partir de aquí vamos a añadir código a las clases necesarias que ya tenemos creadas. Primero vamos a detectar la colisión entre la bala y el enemigo, para eso vamos a utilizar un evento que nos proporciona UnityEngine, OnCollisionEnter(Collision collision). ESte evento salta cuando un objeto colisiona con otro y nos da por parametros la collision, que nos ofrecerá datos sobre el objeto impactado, en este caso lo diferenciaremos por el Tag. Añadimos este evento a la clase Bullet, para controlar el impacto de la bala con otros objetos:

Una vez salte el evento, comprobamos que el objeto colisionado sea el Enemigo y si es así destruimos tanto la bala, como al enemigo, usando el método que ya vimos antes, Destroy(). Para destruir la bala basta con decir que se destruya el mismo gameObject, y para destruir exactamente el enemigo impactado, accedemos al gameObject que nos proporciona la clase Collision, así desaparecerán ambos al mismo tiempo cuando colisionen.

Vamos a hacer lo mismo en la clase Enemy para detectar cuando el Enemigo colisiona con el Player, para ello el Objeto Player debera llevar el Tag asignado ‘Player’, tal cual hicimos con el enemigo, aunque en este caso Unity te lo proporciona en la lista por defecto y no tendremos que crearlo, solo asignarlo. Añadimos el evento a la clase Enemy.cs:

La única diferencia es la comprobación del Tag.

Con esto ya tenemos la jugabilidad básica de un juego MataMarcianos. Enemigos cayendo, Un jugador disparando, y las colisiones entre objetos. Al colisionar con el Player un enemigo el juego terminaría y solo tendríamos opción de reiniciar el juego manualmente desde el editor. Para hacerlo un poco más completo, vamos a añadir la opción Restart cuando te matan. También vamos a añadir un contador de puntos y el feedback visual de Game Over.

 

Añadiendo UI a nuestro juego

Vamos a añadir los textos necesarios a la escena, desde GameObject -> UI -> Text. Esto va a generar un Canvas en la escena, donde a partir de esto, si añadimos más objetos de UI se irán incluyendo aquí. Y también un objeto EventSystem que serviría para añadir interacción con botones, etc. que en este caso vamos a ignorar. Vamos a renombrar nuestro texto por ‘Points’ y recolocarlo dentro del Canvas en la posición que más nos guste, en mi caso arriba a la izquierda con las opciones de la Figura 5. También podemos cambiar el color de la fuente, el tamaño etc.

Figura 5.

Vamos a añadir también el texto que queramos que salga cuando perdemos en este caso, lo centraremos en el Canvas. Yo he puesto ‘Game Over Press ‘R’ to Restart’, así se da feedback de que has acabado y tienes opción de reiniciar, que implementaremos a continuación. Con esto tenemos ya nuestra UI lista. Ahora vamos a ir actualizándola.

Creando un Game Manager

Para controlar los estados del juego, vamos a crear un nuevo script, que al igual que el respawn de enemigos ira asociado a un objeto vacío en la escena con el mismo nombre, GameManager.cs. Esta clase va a controlar, los puntos del jugador, si has muerto y si reinicias una vez muerto. Para ello vamos a utilizar el siguiente código:

Vamos a analizar un poco que hemos incluido en esta clase, lo primero que vemos son dos propiedades publicas de tipo Text, que serán las que acabamos de crear en el editor, para que se correspondan con estos textos exactamente, al ser publicas, podemos arrastrar los textos desde el editor directamente. Es una opción que viene muy bien en Unity, para no hacer la búsqueda por código de los objetos en escena, se los asignamos antes y ya no hay que hacerlo. Pero hay que tener cuidado muchas veces si creamos la variable y no la asignamos en el editor, estaremos tratando con objetos nulos, y esto hará que falle nuestro juego en muchos casos. Una vez asignados estos textos en el editor, ya podemos trabajar con ellos. También tendremos unas variables para controlar si hemos perdido y para llevar la puntuación.

En el Start() desactivamos el texto de Game Over porque no queremos que se vea desde el inicio. En el Update() controlamos si hemos perdido, para dar la opción de reiniciar el juego. Esto lo hacemos en el método Restart(), que simplemente vuelve a cargar la escena, en este caso yo la llamé Level1. Y nos quedan los métodos para sumar puntos y para indicar que hemos perdido. Son Públicos porque serán llamados desde otros Objetos.

 

Sumando Puntos

Para ir aumentando la puntuación, solo tenemos que hacer una llamada al método de GameManager, en este caso los puntos se sumarán cuando matemos un enemigo, con lo cual tendremos que crear en la clase Bullet.cs una referencia a este GameManager, y en el evento donde destruíamos la bala y el enemigo hacemos la llamada correspondiente:

Así tendremos acceso al GameManager de la escena.

Nos queda el evento finalmente destruyendo los objetos que colisionan y sumando los puntos al marcador.

Solo nos queda añadir la lógica cuando perdemos.

 

GAME OVER

Seguimos los mismos pasos que antes, añadimos una referencia de GameManager en la clase Enemy.cs, que es donde controlamos cuando el enemigo colisiona con nosotros. Y llamamos al método que nos proporciona la clase para hacer el GameOver.

Ahora si podemos dar por terminado nuestro juego. Es una base de la que podemos partir para hacerlo más completo, y aumentar las posibilidades. Se le pueden añadir más enemigos, tiempo, Menus, partículas para simular las colisiones, sonidos, etc. O simplemente dar nociones para crear otros tipos de juegos.

 

Conclusión

Hemos visto como de manera muy sencilla podemos montar la base de un juego clásico con Unity 3d, sin muchas complicaciones y con cierta facilidad. Aunque se podría profundizar mucho más, de hecho solo hemos tocado un 1% de lo que ofrece, pero quiero ver como con otros motores se podría hacer lo mismo y comparar las similitudes y diferencias, facilidades y complicaciones que proporcionan unos y otros.

 

@CarlosKlsOne