Home | Clases | Unity: Plataformas en 3D | Receta 4: Contadores de vida y tiempo

Receta 4: Contadores de vida y tiempo


Introducción

En esta receta, veremos cómo crear una interfaz de usuario UI que muestre contadores de coleccionables, vidas, y tiempo a nuestro juego.

Además, añadiremos la lógica necesaria para mostrar el tiempo transcurrido en el juego y mensajes a medida que se produzcan ciertos eventos.

Puedes comprobar el objetivo final haciendo clic AQUÍ.


Creación del escenario

Tomaremos como partida la receta 2, ya que en la próxima receta añadiremos los elementos y la lógica necesaria para recoger los coleccionables y premios de nuestro juego.

A partir de la receta 2, vamos a crear un Canvas con los siguientes elementos:

  • Un UI > Text con el nombre Texto Coleccionables
  • Un UI > Text con el nombre Texto Tiempo
  • Un UI > Text con el nombre Mensajes
  • Un UI > Text con el nombre Vidas

Utiliza los conocimientos adquiridos en las recetas anteriores para crear tu propia versión, pero inicialmente la interfaz de usuario se debería parecer a esto:




El tiempo transcurrido

Para mostrar el tiempo transcurrido, vamos a crear un script llamado Tiempo con el siguiente código:


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

public class Tiempo : MonoBehaviour {

//Variable para el tiempo transcurrido (pública porque la necesitaremos más adelante en Jugador Controller)
public float tiempo = 0.0f;

//Variable para asociar el objeto Texto Tiempo
public Text textoTiempo;

//Variables Jugador y JugadorController para obtener isTiempo
public GameObject jugador;
JugadorController jugadorController;

void Start () {

//Inicializo el texto del contador de tiempo
textoTiempo.text = "Tiempo: 00:00";

//Capturo el componente JugadorController de jugador
jugadorController = jugador.GetComponent<JugadorController>();

}

void Update () {

//Escribo tiempo transcurrido (si no se ha acabado el juego)
if (jugadorController.isTiempo){
textoTiempo.text = "Tiempo: " + formatearTiempo();
}

}

//Formatear tiempo (público porque la necesitaremos más adelante en Jugador Controller)
public string formatearTiempo(){

//Añado el intervalo transcurrido a la variable tiempo
if (jugadorController.isTiempo){
tiempo += Time.deltaTime;
}
//Fromateo minutos y segundos a dos dígitos
string minutos = Mathf.Floor(tiempo / 60).ToString("00");
string segundos = Mathf.Floor(tiempo % 60).ToString("00");
//Devuelvo el string formateado con : como separador
return minutos + ":" + segundos;

}
}


Para que funcione, solo debemos aplicar el script al Canvas y asociar el objeto Texto Tiempo a la variable Texto Tiempo

Por último, debemos añadir la variable isTIempo a JugadorController (después de la declaración de la variable salto, por ejemplo):


//Variable para comprobar si debo seguir incrementando el tiempo transcurrido
public bool isTiempo = true;



Las vidas

En esta receta, consideraremos que el Jugador comienza con 3 vidas y pierde una cada vez que se sale del Suelo. Esto ocurrirá, al menos en mi caso, aproximadamente cuando el jugador supere las posiciones 15 o -15 en los ejes X o Z (Para comprobarlo en tu caso, mueve el Jugador a las esquinas y comprueba los valores de Position).

Puesto que es el Jugador el que pierde vidas, añadiremos el código necesario en JugadorController:


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

public class JugadorController : MonoBehaviour {

//Declarlo la variable de tipo RigidBody que luego asociaremos a nuestro Jugador
private Rigidbody rb;

//Declaro la variable pública velocidad para poder modificarla desde la Inspector window
[Range(1,10)]
public float velocidad = 5;

//Declaro la variable pública salto para poder modificarla desde la Inspector window
[Range(1,10)]
public float salto = 7;

//Límites en X y Z del Suelo
int limiteSuperior = 15;
int limiteInferior = -15;

//Variable para el número de vidas
int vidas = 3;

//Variable para asociar el objeto Texto Vidas
public Text textoVidas;

//variable para la posición inicial del jugador
Vector3 posicionInicial;

void Start () {

//Capturo el rigidbody del jugador al iniciar el juego
rb = GetComponent<Rigidbody>();

//Inicializo el texto del contador de vidas
textoVidas.text = "Vidas: " + vidas;

//Capturo la posición inicial del jugador para cuando pierda una vida
posicionInicial = transform.position;

}

void FixedUpdate () {

//Capturo el movimiento en horizontal y vertical de nuestro teclado
float movimientoH = Input.GetAxis("Horizontal");
float movimientoV = Input.GetAxis("Vertical");

//Genero el vector de movimiento asociado, teniendo en cuenta la velocidad
Vector3 movimiento = new Vector3(movimientoH * velocidad, 0.0f, movimientoV * velocidad);

//Aplico ese movimiento al RigidBody del jugador
rb.AddForce(movimiento);

//Si pulsa el botón de saltar y está en el suelo
if (Input.GetButtonDown("Jump") && isSuelo()){
//Aplico el movimiento vertical con la potencia de salto
rb.velocity += Vector3.up * salto;
}

//Compruebo si se ha salido del suelo en X o Z
if (transform.position.x > limiteSuperior ||
transform.position.x < limiteInferior || 
transform.position.z > limiteSuperior ||
transform.position.z < limiteInferior){

//Quito una vida
quitarVida();

}

}

//Compruebo si el jugador está en el suelo antes de saltar (para que no vuele)
private bool isSuelo(){

//Genero el array de colisiones de la esfera/jugador pasando su centro y su radio
Collider[] colisiones = Physics.OverlapSphere(transform.position, 0.5f);
//Recorro ese array y si está colisionando con el suelo devuelvo true
foreach (Collider colision in colisiones){
if (colision.tag == "Suelo"){
return true;
}
}
return false;

}

//Quito una vida y muevo al jugador a la posición inicial
void quitarVida(){

//Resto una vida
vidas--;
//Actualizo el contador de vidas
textoVidas.text = "Vidas: " + vidas;
//Devuelvo el Jugador a su posición inicial y le quito la fuerza
transform.position = posicionInicial;
rb.velocity = Vector3.zero;
}
}


Para que funcione, lo único que debemos hacer es asociar el objeto Texto Vidas la variable Texto Vidas.


Mensajes

Como habrás podido apreciar, con el código anterior se van restando vidas al Jugador pero el juego no termina nunca. Además, el texto del Mensaje está siempre presente en la pantalla.

Para finalizar la receta, añadiremos la siguiente lógica:

Ocultar al inicio el texto de Mensajes
Comprobar que el Jugador tiene vidas disponibles.
Mostrar un mensaje de juego terminado si no tiene vidas, que desaparecerá al cabo de 5 segundos.
Impedir que el jugador se siga moviendo.
Parar el contador de Texto Tiempo.
Nota: Añadiremos la lógica del Texto Coleccionables en la próxima receta.

Para conseguir los 4 primeros objetivos, modificaremos el código de JugadorController. Observa que hemos incorporado el método moverJugador() y hemos trasladado a él toda la lógica relacionada con el movimiento del Jugador. Esto es para poder quitar el movimiento al Jugador cuando se quede sin vidas de una forma más optimizada:


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

public class JugadorController : MonoBehaviour {

//Declarlo la variable de tipo RigidBody que luego asociaremos a nuestro Jugador
private Rigidbody rb;

//Declaro la variable pública velocidad para poder modificarla desde la Inspector window
[Range(1,10)]
public float velocidad = 5;

//Declaro la variable pública salto para poder modificarla desde la Inspector window
[Range(1,10)]
public float salto = 7;

//Límites en X y Z del Suelo
int limiteSuperior = 15;
int limiteInferior = -15;

//Variable para el número de vidas
int vidas = 3;

//Variable para asociar el objeto Texto Vidas
public Text textoVidas;

//variable para la posición inicial del jugador
Vector3 posicionInicial;

//Variable para asociar el objeto Mensajes
public Text textoMensajes;

//Variable para comprobar si debo seguir incrementando el tiempo transcurrido
public bool isTiempo = true;

void Start () {

//Capturo el rigidbody del jugador al iniciar el juego
rb = GetComponent<Rigidbody>();

//Inicializo el texto del contador de vidas
textoVidas.text = "Vidas: " + vidas;

//Inicio el texto de mensajes a vacío
textoMensajes.text = "";

//Capturo la posición inicial del jugador para cuando pierda una vida
posicionInicial = transform.position;

}

void FixedUpdate () {

if (vidas > 0){
//Muevo el jugador (si tiene vidas)
moverJugador();
//Compruebo si se ha salido del suelo en X o Z
if (transform.position.x > limiteSuperior ||
transform.position.x < limiteInferior || 
transform.position.z > limiteSuperior ||
transform.position.z < limiteInferior){

//Quito una vida
quitarVida();
}
}
else{
//Muestro mensaje (si no tiene vidas)
textoMensajes.text = "Juego Terminado";
if (isTiempo)
{
StartCoroutine(FadeInOutText(textoMensajes, 0.5f, 5.0f, false));
}
//Pongo isTiempo a false para que deje de contar el script Tiempo
isTiempo = false;
}



}

//Compruebo si el jugador está en el suelo antes de saltar (para que no vuele)
private bool isSuelo(){

//Genero el array de colisiones de la esfera/jugador pasando su centro y su radio
Collider[] colisiones = Physics.OverlapSphere(transform.position, 0.5f);
//Recorro ese array y si está colisionando con el suelo devuelvo true
foreach (Collider colision in colisiones){
if (colision.tag == "Suelo"){
return true;
}
}
return false;

}

//Quito una vida y muevo al jugador a la posición inicial
void quitarVida(){

//Resto una vida
vidas--;
//Actualizo el contador de vidas
textoVidas.text = "Vidas: " + vidas;
//Devuelvo el Jugador a su posición inicial y le quito la fuerza
transform.position = posicionInicial;
rb.velocity = Vector3.zero;

}

//Lógica necesaria para mover al jugador
void moverJugador(){

//Capturo el movimiento en horizontal y vertical de nuestro teclado
float movimientoH = Input.GetAxis("Horizontal");
float movimientoV = Input.GetAxis("Vertical");

//Genero el vector de movimiento asociado, teniendo en cuenta la velocidad
Vector3 movimiento = new Vector3(movimientoH * velocidad, 0.0f, movimientoV * velocidad);

//Aplico ese movimiento al RigidBody del jugador
rb.AddForce(movimiento);

//Si pulsa el botón de saltar y está en el suelo
if (Input.GetButtonDown("Jump") && isSuelo()){
//Aplico el movimiento vertical con la potencia de salto
rb.velocity += Vector3.up * salto;
}

}

//Mostrar (y ocultar) texto con fade in/out
private IEnumerator FadeInOutText(Text texto, float duracionIn, float duracionOut, bool ocultar){

//Fade in
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, 0); //opacidad a 0
while (texto.color.a < 1.0f){
//Voy incrementando la opacidad hasta llegar a 1
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, texto.color.a + duracionIn * Time.deltaTime);
yield return null;
}

//Fade out
if (ocultar){
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, 1); //opacidad a 1
while (texto.color.a > 0.0f){
//Voy decrementando la opacidad hasta llegar a 0
texto.color = new Color(texto.color.r, texto.color.g, texto.color.b, texto.color.a - duracionOut * Time.deltaTime);
yield return null;
}
}

}

}


Para que funcione, lo único que falta es asociar el objeto Mensajes la variable Texto Mensajes.


Y para terminar la receta con el 5º objetivo, modificaremos el código del script Tiempo para que recoja la variable booleana isTiempo de JugadorController y compruebe si debe incrementar el tiempo:


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

public class Tiempo : MonoBehaviour {

//Variable para el tiempo transcurrido
public float tiempo = 0.0f;

//Variable para asociar el objeto Texto Tiempo
public Text textoTiempo;

//Variables Jugador y JugadorController para obtener isTiempo
public GameObject jugador;
JugadorController jugadorController;

void Start () {

//Inicializo el texto del contador de tiempo
textoTiempo.text = "Tiempo: 00:00";

//Capturo el componente JugadorController de jugador
jugadorController = jugador.GetComponent<JugadorController>();

}

void Update () {

//Escribo tiempo transcurrido (si no se ha acabado el juego)
if (jugadorController.isTiempo){
textoTiempo.text = "Tiempo: " + formatearTiempo();
}

}

//Formatear tiempo
public string formatearTiempo(){

//Añado el intervalo transcurrido a la variable tiempo
tiempo += Time.deltaTime;
//Fromateo minutos y segundos a dos dígitos
string minutos = Mathf.Floor(tiempo / 60).ToString("00");
string segundos = Mathf.Floor(tiempo % 60).ToString("00");
//Devuelvo el string formateado con : como separador
return minutos + ":" + segundos;

}
}


Para que funcione, lo único que falta es asociar el objeto Jugador a la variable Jugador (recuerda que el script Tiempo está asociado al Canvas).

Con esto estaría terminada la receta, espero que te haya resultado útil.

Puedes comprobar su funcionamiento haciendo clic AQUÍ.
Fecha de publicación: 19/09/2020
Asignaturas: desarrollo de entornos interactivos multidispositivo
Temas: unity
Utilizamos cookies propias y de terceros para mejorar su experiencia en la navegación. Al seguir navegando entendemos que acepta su uso.
Si lo desea, consulte nuestras políticas de privacidad y cookies
ENTENDIDO
[X] Cerrar

Contacta conmigo


[X] Cerrar

Acceso alumnos