jairogarcíarincón
29/07/2018
Contenidos
En esta fase crearemos los puntos de generación de los tanques (SpawnPoints) y un Canvas para los mensajes de información.
Además, introduciremos el uso de GameManagers y pondremos todo a funcionar.
- Crea 2 Empty GameObjects y llámalos SpawnPoint1 y SpawnPoint2
- Modifica el Transform de SpawnPoint1 para que sea Position -3, 0, 30 y Rotation 0, 180, 0
- Modifica el Transform de SpawnPoint2 para que sea Position 13, 0, -5 y Rotation 0, 0, 0
- EN el Inspector window, haz clic en el icono lado del nombre del objeto SpawnPoint1 y cámbialo al icono azul
- Para el SpawnPoint2, cámbialo al icono rojo
- Si pulsas la vista de escena 2D, deberías tener algo similar a esto:
- Crea un nuevo UI > Canvas y llámalo MessageCanvas
- Si habías vuelto a vista 3D, cambia a vista 2D en la escena
- Con el MessageCanvas seleccionado, pon el cursor sobre la escena y pulsa F para centrarnos en el objeto.
- Haz clic derecho en MessageCanvas de la Hierarchy window y crea un nuevo UI > Text.
- Cambia el RectTransform de Text para que los Anchors de los ejes X e Y sean Min 0.1 y Max 0.9
- Ajusta también Left, Right, Top, Bottom y Pos Z a 0.
- En el componente Text, escribe ¡TANQUES!
- Cambia la fuente con el círculo selector a BowlbyOne-Regular
- Cambia la alineación del texto vertical y horizontal a centrada.
- Habilita Best Fit
- Ajusta el tamaño de fuente máximo a 60
- Ajusta el color de fuente a White RGB 255, 255, 255.
- Haz clic en Add Component y añade un Shadow al Text
- Ajusta el Effect Color a marrón RGBA: 114, 71, 40, 128.
- Ajusta Effect Distance a -3, 3.
- Antes de quitar el modo 2D en la vista de escena, deberías ver algo como esto:
- Selecciona el objeto CameraRig y en su script Camera Control ajusta Targets a 0.
- Abre el script Camera Control y descomenta el texto de la línea 8 [HideInInspector]
- Guarda el script y vuelve a Unity
Crea un nuevo EmptyObject y llámalo GameManager
Arrastra desde Scripts > Managers el script GameManager al objeto GameManager
Abre el script GameManager, así como el script TankManager.
Modifícalos según los mostrados abajo e intenta entenderlos fijándote en los comentarios.
using System;
using UnityEngine;
[Serializable] //Hace que los atributos aparezcan en el inspector (si no los escondemos)
public class TankManager
{
//Esta clase gestiona la configuración del tanque junto con el GameManager
//gestiona el comportamiento de los tanques y si los jugadores tienen control sobre el tanque
//en los distintos momentos del juego
public Color m_PlayerColor; //Color para el tanque
public Transform m_SpawnPoint; //Posición y direción en la que se generará el tanque
[HideInInspector] public int m_PlayerNumber; //Especifica con qué jugador está actuando el Game Manager
[HideInInspector] public string m_ColoredPlayerText; //String que reprsenta el color del tanque
[HideInInspector] public GameObject m_Instance; //Refernecia a la instancia del tanque cuando se crea
[HideInInspector] public int m_Wins; //Número de victorias del jugador
private TankMovement m_Movement; //Referencia al script de movimiento del tanque. Utilizado para deshabilitar y habilitar el control
private TankShooting m_Shooting; //Referencia al script de disparo del tanque. Utilizado para deshabilitar y habilitar el control
private GameObject m_CanvasGameObject; //Utilizado para deshabilitar el UI del mundo durante als fases de inicio y fin de cada ronda
public void Setup()
{
//Cojo referencias de los componentes
m_Movement = m_Instance.GetComponent<TankMovement>();
m_Shooting = m_Instance.GetComponent<TankShooting>();
m_CanvasGameObject = m_Instance.GetComponentInChildren<Canvas>().gameObject;
//Ajusto los número de jugadores para que sean iguales en todos los scripts
m_Movement.m_PlayerNumber = m_PlayerNumber;
m_Shooting.m_PlayerNumber = m_PlayerNumber;
//Creo un string usando el color del tanque que diga PLAYER 1, etc.
m_ColoredPlayerText = "<color=#" + ColorUtility.ToHtmlStringRGB(m_PlayerColor) + ">PLAYER " + m_PlayerNumber + "</color>";
//Cojo todos los renderers del tanque
MeshRenderer[] renderers = m_Instance.GetComponentsInChildren<MeshRenderer>();
//Los recorro...
for (int i = 0; i < renderers.Length; i++)
{
//..y ajusto el color del material al del tanque
renderers[i].material.color = m_PlayerColor;
}
}
//Usado durante la fases del juego en las que el jugador no debe poder controlar el tanque
public void DisableControl()
{
m_Movement.enabled = false;
m_Shooting.enabled = false;
m_CanvasGameObject.SetActive(false);
}
//Usado durante la fases del juego en las que el jugador debe poder controlar el tanque
public void EnableControl()
{
m_Movement.enabled = true;
m_Shooting.enabled = true;
m_CanvasGameObject.SetActive(true);
}
//Usado al inicio de cada ronda para poner el tanque en su estado inicial
public void Reset()
{
m_Instance.transform.position = m_SpawnPoint.position;
m_Instance.transform.rotation = m_SpawnPoint.rotation;
m_Instance.SetActive(false);
m_Instance.SetActive(true);
}
}
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public int m_NumRoundsToWin = 5; //Número de rondas que un jugador debe ganar para ganar el juego
public float m_StartDelay = 3f; //Delay entre las fases de RoundStarting y RoundPlaying
public float m_EndDelay = 3f; //Delay entre las fases de RoundPlaying y RoundEnding
public CameraControl m_CameraControl; //Referencia al sccript de CameraControl
public Text m_MessageText; //Referencia al texto para mostrar mensajes
public GameObject m_TankPrefab; //Referencia al Prefab del Tanque
public TankManager[] m_Tanks; //Array de TankManagers para controlar cada tanque
private int m_RoundNumber; //Número de ronda
private WaitForSeconds m_StartWait; //Delay hasta que la ronda empieza
private WaitForSeconds m_EndWait; //Delay hasta que la ronda acaba
private TankManager m_RoundWinner; //Referencia al ganador de la ronda para anunciar quién ha ganado
private TankManager m_GameWinner; //Referencia al ganador del juego para anunciar quién ha ganado
private void Start()
{
//Creamos los delays para que solo se apliquen una vez
m_StartWait = new WaitForSeconds(m_StartDelay);
m_EndWait = new WaitForSeconds(m_EndDelay);
SpawnAllTanks(); //Generar tanques
SetCameraTargets(); //Ajustar cámara
StartCoroutine(GameLoop()); //Iniciar juego
}
private void SpawnAllTanks()
{
//Recorro los tanques...
for (int i = 0; i < m_Tanks.Length; i++)
{
//...los creo, ajusto el número de jugador y ls referencias necesarias para controlarlo
m_Tanks[i].m_Instance =
Instantiate(m_TankPrefab, m_Tanks[i].m_SpawnPoint.position, m_Tanks[i].m_SpawnPoint.rotation) as GameObject;
m_Tanks[i].m_PlayerNumber = i + 1;
m_Tanks[i].Setup();
}
}
private void SetCameraTargets()
{
//Creo un array de Transforms del mismo tamaño que el número de tanques
Transform[] targets = new Transform[m_Tanks.Length];
//Recorro los Transforms...
for (int i = 0; i < targets.Length; i++)
{
//...lo ajusto al transform del tanque apropiado
targets[i] = m_Tanks[i].m_Instance.transform;
}
//Estos son los targets que la cámara debe seguir
m_CameraControl.m_Targets = targets;
}
//llamado al principio y en cada fase del juego después de otra
private IEnumerator GameLoop()
{
//Empiezo con la corutina RoundStarting y no retorno hasta que finalice
yield return StartCoroutine(RoundStarting());
//Cuando finalice RoundStarting, empiezo con RoundPlaying y no retorno hasta que finalice
yield return StartCoroutine(RoundPlaying());
//Cuando finalice RoundPlaying, empiezo con RoundEnding y no retorno hasta que finalice
yield return StartCoroutine(RoundEnding());
//Si aún no ha ganado ninguno
if (m_GameWinner != null)
{
//Si hay un ganador, reinicio el nivel
SceneManager.LoadScene(0);
}
else
{
//Si no, reinicio lsa corutinas para que continúe el bucle
//En este caso sin yiend, de modo que esta versión del GameLoop finalizará siempre
StartCoroutine(GameLoop());
}
}
private IEnumerator RoundStarting()
{
// Cuando empiece la ronda reseteo los tanques e impido que se muevan.
ResetAllTanks ();
DisableTankControl ();
// Ajusto la cámara a los tanques resteteados.
m_CameraControl.SetStartPositionAndSize ();
// Incremento la ronda y muestro el texto informativo.
m_RoundNumber++;
m_MessageText.text = "ROUND " + m_RoundNumber;
// Espero a que pase el tiempo de espera antes de volver al bucle.
yield return m_StartWait;
}
private IEnumerator RoundPlaying()
{
// Cuando empiece la ronda dejo que los tanques se muevan.
EnableTankControl ();
// Borro el texto de la pantalla.
m_MessageText.text = string.Empty;
// Mientras haya más de un tanque...
while (!OneTankLeft())
{
// ... vuelvo al frame siguiente.
yield return null;
}
}
private IEnumerator RoundEnding()
{
// Deshabilito el movimiento de los tanques.
DisableTankControl ();
// Borro al ganador de la ronda anterior.
m_RoundWinner = null;
// Miro si hay un ganador de la ronda.
m_RoundWinner = GetRoundWinner ();
// Si lo hay, incremento su puntuación.
if (m_RoundWinner != null)
m_RoundWinner.m_Wins++;
// Compruebo si alguien ha ganado el juego.
m_GameWinner = GetGameWinner ();
// Genero el mensaje según si hay un gaandor del juego o no.
string message = EndMessage ();
m_MessageText.text = message;
// Espero a que pase el tiempo de espera antes de volver al bucle.
yield return m_EndWait;
}
// Usado para comprobar si queda más de un tanque.
private bool OneTankLeft()
{
// Contador de tanques.
int numTanksLeft = 0;
// recorro los tanques...
for (int i = 0; i < m_Tanks.Length; i++)
{
// ... si está activo, incremento el contador.
if (m_Tanks[i].m_Instance.activeSelf)
numTanksLeft++;
}
// Devuelvo true si queda 1 o menos, false si queda más de uno.
return numTanksLeft <= 1;
}
// Comprueba si algún tanque ha ganado la ronda (si queda un tanque o menos).
private TankManager GetRoundWinner()
{
// Recorro los tanques...
for (int i = 0; i < m_Tanks.Length; i++)
{
// ... si solo queda uno, es el ganador y lo devuelvo.
if (m_Tanks[i].m_Instance.activeSelf)
return m_Tanks[i];
}
// SI no hay ninguno activo es un empate, así que devuelvo null.
return null;
}
// Comprueba si hay algún ganador del juegoe.
private TankManager GetGameWinner()
{
// Recorro los tanques...
for (int i = 0; i < m_Tanks.Length; i++)
{
// ... si alguno tiene las rondas necesarias, ha ganado y lo devuelvo.
if (m_Tanks[i].m_Wins == m_NumRoundsToWin)
return m_Tanks[i];
}
// Si no, devuelvo null.
return null;
}
// Deveulve el texto del mensaje a mostrar al final de cada ronda.
private string EndMessage()
{
// Pordefecto no hya ganadores, así que es empate.
string message = "EMPATE!";
// Si hay un ganador de ronda cambio el mensaje.
if (m_RoundWinner != null)
message = m_RoundWinner.m_ColoredPlayerText + " GANA LA RONDA!";
// Retornos de carro.
message += "\n\n\n\n";
// Recorro los tanques y añado sus puntuaciones.
for (int i = 0; i < m_Tanks.Length; i++)
{
message += m_Tanks[i].m_ColoredPlayerText + ": " + m_Tanks[i].m_Wins + " GANA\n";
}
// Si hay un ganador del juego, cambio el mensaje entero para reflejarlo.
if (m_GameWinner != null)
message = m_GameWinner.m_ColoredPlayerText + " GANA EL JUEGO!";
return message;
}
// Para resetear los tanques (propiedaes, posiciones, etc.).
private void ResetAllTanks()
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].Reset();
}
}
//Habilita el control del tanque
private void EnableTankControl()
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].EnableControl();
}
}
//Deshabilita el control del tanque
private void DisableTankControl()
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].DisableControl();
}
}
}
Es el momento de asignar los objetos a las variables de los scripts:
- Arrastra el objeto CameraRig a la variable Camera Control del script GameManager
- Arrastra el Text del MessageText a la variable MessageText
- Arrastra el prefab Tank de la carpeta Prefabs a la variable Tank Prefab
- Expande el array de Tanks y ajusta Size a 2.
- Para Element 0, cambia Player Color a azul (RGB 42, 100, 178)
- Arrastra SpawnPoint1 a la variable SpawnPoint del Element 0.
- Para Element 1, cambia Player Color a rojo (RGB 229, 46, 40)
- Arrastra SpawnPoint2 a la variable SpawnPoint del Element 1.
Con esto ya deberías tener el juego funcionado para dos jugadores. El primero de ellos con AWSD y barra espaciadora para disparar, y el segundo con las flechas y Enter para disparar. ¡Busca un amigo y prueba!
Si has estudiado con detenimiento los scripts GameManager y TankManager, habrás comprendido lo siguiente:
- GameManager inicializa el juego (genera los tanques y ajusta los targets de cámara)
- GameManager ejecuta los estados del juego (empezar, jugar finalizar)
- Para cada tanque, GameManager usa un TankManager
- Cada TankManager tiene su control de disparo, movimiento y UI
- Las co-rutinas del GameManager nos permiten saltar entre estados del juego cuando se cumplan ciertas condiciones (yield)
- A modo de resumen, se muestra la siguiente captura extraída de las diapositivas del curso original:
Fuente: Unite 2015
Publicado el 30 de Enero de 2025
Unity2dortográfico