Home | Clases | Unity: Videojuego para 2 jugadores en 3D ortográfico | Los gestores

Los gestores


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.


Creación de SpawnPoints

  • 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:





Creación de texto para los mensajes

  • 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:





Script de control de cámara

  • 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


Scripts de Game Manager y Tank Manager

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!


Explicación de la estructura de los GameManager

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

Fecha de publicación: 29/07/2018
Asignaturas: realización de proyectos multimedia interactivosdesarrollo de entornos interactivos multidispositivo
Temas: Unity 2d ortográfico
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