Incorporemos niveles a nuestro juego.

Ir avanzando por el juego, superando niveles, asumiendo nuevos retos y descubriendo nuevos mundos, es una de las sensaciones que mas les gustan a los jugadores. Da lo mismo el tipo de juego que estemos haciendo, tanto si es un Infinite Runner, un RPG, o un Arcade, los niveles esta ahí. Pueden estar camuflados en forma de nuevos peligros, nuevas misiones, o nuevos enemigos finales. No importa, en cualquier juego el jugador tiene que sentir que avanza, que aprende y que llega a nuevos niveles.

Este post forma parte del tutorial de desarrollo de Unity. En los post anteriores hemos creado un arcade completo que permite recolectar monedas, y existen unos enemigos que acaban con nosotros. Lo único que le falta para tener todos los básicos de un Buen Arcade es incorporar niveles, así que vamos a ello! Podéis obtener el proyecto de Unity con todas las modificaciones anteriores en este link: megafastball-gameover.

Creando el nuevo nivel.

Lo mejor es empezar creando un nuevo nivel, y haciendo que sea más complicado, pero no imposible. No tenemos que empezar desde 0, ya tenemos nuestro Level1 creado, por lo que ya partimos con una buena Base. Para aprovecharlo lo mejor es duplicar nuestro nivel: Level1. Lo seleccionamos en la sección Project y pulsamos Cmd+D o Ctrl+D en Windows. Estos nos creará un Level2.

Los dos niveles del juego en la sección Project y Hierarchy.
Hemos duplicado el Level1 y nos ha creado el Level2.

Arrastramos el nuevo nivel a la sección Hierarchy, ahora tendremos cargados dos niveles. Vemos que en nuestra pantalla donde editamos el nivel visualmente se produce un efecto raro, esto es porque intenta mostrar los dos niveles a la vez. Tenemos que seleccionar el primer nivel y seleccionar Unload Sccene en el menú contextual.

Menu contextual de las escenas
Tenemos que descargar la primera escena.

Ya con la primera escena fuera del editor podemos empezar a trabajar con nuestra segunda escena, creando mas muros, mas monedas, y mas enemigos. Para hacerlo, seleccionamos los objetos que queremos duplicar en la ventana de escenario, o en la lista de hyerarchy, y los duplicamos. Podemos jugar en la ventana scene con los controles para mover o cambiar el tamaños de los objetos duplicados.

Mi Level 2 con muchas paredes, monedas y enemigos.

En la parte superior izquierda de la imagen están los botones que os permitirán seleccionar las herramientas para mover o cambiar de tamaño los diferentes objetos. Para la creación del segundo nivel he creado mas muros, y he intentado crear un mini laberinto. He duplicado las monedas, ahora tenemos muchas mas y he aumentado los enemigos a seis. Es en resumen lo mismo que el primer nivel pero con mas objetos. Podríamos dejarlo así y retrasar la introducción de sorpresas para los niveles siguientes.

Crear un Canvas nuevo para cambiar de nivel

De la misma forma que creamos un Canvas para mostrar nuestro GameOver ahora tenemos que crear uno para mostrar el cambio de nivel. Felicitaremos al usuario y le mostraremos un botón para que al pulsarlo pase al siguiente nivel.

Para ello lo mas sencillo es duplicar el Canvas del GameOver. Con la escena Level1 cargada, seleccionamos el GameOverCanvas y pulsando Cmd+D o Ctrl+D en Windows conseguiremos un duplicado, cambiamos su nombre y sus elementos.

Al nuevo Canvas podemos llamarlo PasaNivelCanvas. Cambiaremos también el nombre y el contenido de su campo texto que se llamará PasaNivelText y ponemos el mensaje que queremos mostrar al Jugador cuando supera el nivel.

También modificamos el nombre del botón, para llamarlo PasaNivel. Para que cargue el nivel dos tenemos que editar en el Inspector el script que tenemos asociado al texto del botón y indicarle el nombre del segundo nivel.

Editamos los scripts.

Ahora ya tenemos todo lo necesarios para mostrar la pantalla intermedia entre los niveles, solo nos falta que los scripts gestionen el paso de un nivel a otro. Por eso lo primero que tenemos que plantearnos es: ¿Cuando va a pasar de nivel el jugador? En mi caso he decidido que sea cuando recoja todas las monedas, sin importar la puntuación realizada.

El primer script a modificar es el GameManager. Vamos a ver por partes las modificaciones introducidas.

...
public GameObject pasarNivelCanvas; 
public int coins2Pass; 
private bool bPasarNivel = false; 
...

Hempos definido tres nuevas variables. En pasarNivelCanvas almacenaremos el Canvas a mostrar cuando el jugador consigue pasar de nivel. En coins2Pass guardamos el numero de monedas que el usuario tiene que recoger para dar el nivel como terminado. La ultima variable, bPasarNivel, es una variable privada, en la que el script guarda si se ha informado o no el Canvas del siguiente nivel, en caso de no estar informado asume que no existe la posibilidad de pasar de nivel.

Cada nivel tiene su propio GameManager. Podemos configurar un numero diferente de monedas a recoger en cada uno de ellos. Aunque el script sea único la parametrización es única por nivel.

void Start () { 
.......
if (pasarNivelCanvas != null) {
pasarNivelCanvas.SetActive (false);
bPasarNivel = true;
}
.....
//Activamos el MainCanvas
mainCanvas.SetActive(true);
}

En la función Start incorporamos una comprobación para saber si nos han informado del Canvas pasarNivelCanvas. En caso afirmativo lo desactivamos y indicamos al GameManager que va a poder pasar de nivel poniendo un true en la variable bPasarNivel. Por último he incorporado una llamada a la activación del Canvas mainCanvas, ya que se quedaba desactivado al pasar de nivel.

void Update () {
switch (gameState)
{
case gameStates.Playing:
.....
if (bPasarNivel && coins2Pass == 0) {
gameState = gameStates.BeatLevel; //Cambiamos el estado del juego a GameOver
mainCanvas.SetActive (false); //Quitamos el mainCanvas. 
pasarNivelCanvas.SetActive (true); //Activamos el pasarNivelCVanvas Canvas. 
} 
....
case gameStates.BeatLevel:
// en el caso de que tengamos un GameOver no hacemos nada!
break;
}
}

En la función update, hemos incorporado la comprobación para saber si se ha pasado de nivel o no. Primero se mira si bPasarNivel es true, y en caso afirmativo se comprueba si coin2Pass ya es 0. Esta variable será 0 cuando el jugador haya recogido todas las monedas, ya que cada moneda que recoge le resta una al valor que le hemos dado en el editor a esta variable. En caso de que el usuario haya pasado el nivel, cambiamos el state del gameManager, desactivamos el mainCanvas y activamos el pasarNivelCanvas.

public void Collect(int amount) {
coins2Pass -= amount;
}

Hemos creado una funcion nueva, llamada Collect. Esta es la función que tendremos que llamar siempre que queramos restar algún valor a coins2Pass.
El excript entero queda así:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameManager : MonoBehaviour {
public static GameManager gm; 
public GameObject player;
public enum gameStates {Playing, Death, GameOver, BeatLevel};
public gameStates gameState = gameStates.Playing;
public GameObject mainCanvas;
public Text textScoreMainCanvas;
public GameObject gameOverCanvas;
public Text textScoreGameOver;
public GameObject pasarNivelCanvas; 
public int coins2Pass; 
private bool bPasarNivel = false; 
private isLive live; 
//Se ejecuta una vez, al iniciar el script. 
void Start () { 
if (player == null) { //Si no se ha informado el Player busca un objeto con el Tag Playe. 
player = GameObject.FindWithTag("Player");
}
if (pasarNivelCanvas != null) {
pasarNivelCanvas.SetActive (false);
bPasarNivel = true;
}
live = player.GetComponent<isLive> (); //Recuperamos el script isLive del player y lo guardamos en una variable. 
gameState = gameStates.Playing; //Cambiamos el estado a Playing. 
// Desactivamos el Canvas gameOver, just in case. 
gameOverCanvas.SetActive (false);
//Activamos el MainCanvas
mainCanvas.SetActive(true);
}
//Se ejecuta en cada frame, varias veces por segundo. 
void Update () {
switch (gameState)
{
case gameStates.Playing:
if (live.live == false) { // si la variable live es false el jugador ha muerto. 
gameState = gameStates.GameOver; //Cambiamos el estado del juego a GameOver
mainCanvas.SetActive (false); //Quitamos el mainCanvas. 
gameOverCanvas.SetActive (true); //Activamos el gameOver Canvas. 
textScoreGameOver.text = textScoreMainCanvas.text; //Copiamos el texto con la puntuación del MainCanvas al GameOver. 
} 
if (bPasarNivel && coins2Pass == 0) {
gameState = gameStates.BeatLevel; //Cambiamos el estado del juego a GameOver
mainCanvas.SetActive (false); //Quitamos el mainCanvas. 
pasarNivelCanvas.SetActive (true); //Activamos el pasarNivelCVanvas Canvas. 
}
break;
case gameStates.GameOver:
// en el caso de que tengamos un GameOver no hacemos nada!
break;
case gameStates.BeatLevel:
// en el caso de que tengamos un GameOver no hacemos nada!
break;
}
}
public void Collect(int amount) {
coins2Pass -= amount;
}
}

Para llamar a la función collect hemos utilizado el script Explosion, ya lo creamos en el post de explosiones, y esta en todos los objetos que explotan. Por ahora tan solo hemos configurado las monedas para que reduzcan la variable coins2Pass, pero en el futuro seria muy sencillo configurar cualquier objeto que tenga una explosión para que reduzca esa variable y permita a nuestro jugador pasar antes de nivel.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class explosion : MonoBehaviour {
public GameObject explosionObject; //Para que se pueda indicar la explosion a ejecutar. 
private isLive live;
private GameObject gm; 
private GameManager gManager;
public int valueCollect = 0;//Variable que indicara cuanto tiene que restar al juagador para que consiga su objetivo de pasaer de nivel.
void Start(){
live = GetComponent<isLive> ();
gm = GameObject.Find("GameManager");
if (gm != null) {
gManager = gm.GetComponent<GameManager>(); //
}
}
void OnDestroy(){ //Esta funciona se llama cuando el objeto va a ser destruido. 
if (explosionObject!=null && gManager.gameState == GameManager.gameStates.Playing) { //Comprobamos de que se ha informado la Explosion. 
Instantiate (explosionObject, transform.position, Quaternion.identity); //Se ejecuta la explosion
}
if (live != null) {
live.live = false;
}
if (valueCollect > 0) {
gManager.Collect (1); //Llamamos a GameManager para que reduzca la cantidad de monedas a recoger. 
}
}
}

Hemos incorporado una variable pública (valueCollect) para que se le pueda dar valor desde el editor. Contiene el valor a restar del total que necesita nuestro jugador para pasar de nivel. Es decir, si le damos el valor de 1 a la moneda, restara esa cantidad a la cantidad de monedas que nuestro jugador necesita recolectar. Pero podríamos crear un objeto nuevo: saco de monedas, y darle un valor de 5, para que el jugador se sienta tentado a recoger objetos nuevos.

Ya lo tenemos todo, ahora solo hace falta configurar los scripts en los objetos que lo usan.

Configuración del GameManager en el nivel 1

Como veis se ha incorporado el nombre del pasarNivelCanvas y le hemos asignado un valor de 9 a la variable Coins2Pass.

En el GameManager del Level2 no hemos informado las variables necesarias para pasar de Nivel.

En el GameManager del segundo nivel no hemos informado las variables de pasarCanvas ni coins2Pass, por lo que este nivel es el último, no llega a ningún sitio. Cosa que tampoco es del todo correcta. No costaría nada darle un final y permitir al usuario volver a cargar el primer nivel para empezar de nuevo.

En el Prefab informamos las variables Collect

Veamos el resultado final del juego:

Si queréis obtener el proyecto entero una vez finalizado el tutorial lo podéis obtener en este link: megafastball-final.

Se acaba el tutorial

Nuestro juego ya tiene todo lo que tiene que tener, bueno le falta un final, pero lo mas importante es que nosotros ya tenemos lo que tenemos que tener!!!! El juego se tiene y se puede mejorar, pero ya tenemos los conocimientos suficientes como para mejorarlo, y mucho!
Cosa que podriamos hace:

  • Añadir una pantalla de Felicitaciones cuando el usuario acabe el segundo nivel.
  • Añadir mas niveles, con sorpresas!!!
  • Objetos que se puedan recolectar y valgan mas que las monedas
  • Zonas seguras en las que la bola pueda entrar, pero los enemigos no
  • Objetos recolectables que cambien a nuestro jugador: multiplicando su tamaño, cambiando su material…

Todas estas mejoras las podemos hacer con los conocimientos que hemos obtenido en el tutorial de desarrollo de juegos en Unity. Aún nos queda mucho por aprender y mucho por descubrir. Un tema al que le tengo especiales ganas… es a la Realidad Aumentada, pero no nos engañemos, antes tendríamos que tener un cierto conocimiento de Unity.

Comentarios.