En este video te explicamos en detalle todo el proceso para publicar una Aplicación Android…
Creación de videojuego en Unity 3D (II): scripts
6.
Creación de videojuego en Unity 3D (II): scripts
Vamos a completar el desarrollo del videojuego FPS en Unity 3D, que comenzamos explicando en el tutorial anterior, describiendo el código fuente de los scripts que vamos a utilizar.
Estos scripts nos permiten establecer la lógica de funcionamiento asociada tanto a las colisiones que se producen en el videojuego, como al modelo de puntuación utilizado, además de controlar el daño que recibe el personaje, mostrando de manera gráfica la vida restante a través de una barra de vida.
Scripts: si no estás familiarizado con los programas que puedes crear para tus juegos, consulta nuestros anteriores tutoriales sobre scripts en Unity 3D.
Los scripts desarrollados para este videojuego han sido programados en lenguaje C# por ser el lenguaje de referencia en Unity, tal como comentamos en esas publicaciones, aparte de la similitud de su sintaxis al lenguaje Java, y por extensión a Android.
En la siguiente publicación de esta serie podrás descargar el proyecto de este videojuego y su APK para Android. También veremos explicado todo este proyecto en un video.
Colisión enemigo con jugador (decremento de la variable energiaPersonaje)
Vemos el diagrama UML de las dos clases AtaqueEnemigo y BarraVidaPersonaje con sus variables (campos) y métodos:
AtaqueEnemigo.cs
Script encargado de controlar las colisiones que se produzcan entre el objeto Jugador, y el objeto golem, y en caso de que se produzca dicha colisión, se invocará al método SendMessage , pasándole como argumento un string con el nombre del método a ejecutar:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class AtaqueEnemigo : MonoBehaviour { void OnCollisionEnter(Collision collision) { GameObject jugador = GameObject.Find("Jugador"); if (collision.gameObject.name == "golem") { jugador.SendMessage("golpeEnemigo"); } } } |
Nota: Es necesario recordar que toda variable que se declare con el modificador de acceso «public», podrá ser inicializada desde el editor de Unity, para un mayor control de la configuración del juego.
BarraVidaPersonaje.cs
Script donde se define el evento OnGUI , que permite controlar tanto la pausa del videojuego, como la construcción de la barra de vida en la parte superior izquierda de la pantalla:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
void OnGUI() { if (GUI.Button (new Rect (Screen.width/3, 10, 150, 80), tx2Dpausa)) { if (Time.timeScale == 1) { Time.timeScale = 0; camaraPausa.SetActive(true); } else if (Time.timeScale == 0) { Time.timeScale = 1; camaraPausa.SetActive(false); } } GUI.BeginGroup (new Rect (10, 10, 400, 64)); GUI.Box (new Rect (0, 0, 400, 64), tx2DcolorVidaUsada, guiBarraPersonaje); GUI.BeginGroup (new Rect (0, 0, energiaPersonaje, 64)); GUI.Box (new Rect (0, 0, 400, 64), tx2DcolorVidaRestante, guiBarraPersonaje); GUI.EndGroup (); } void golpeEnemigo () { energiaPersonaje -= 5.0f; jugador = GameObject.Find("Jugador"); jugador.transform.position = new Vector3 (jugador.transform.position.x + 0.30f,jugador.transform.position.y + 0.60f,jugador.transform.position.z); audio.PlayOneShot(vidaPersonaje); if (energiaPersonaje < 0.0f) { Destroy(this.gameObject); camaraGameOver.SetActive(true); } } |
Colisión proyectil con enemigo (incremento de la variable contadorEnemigo)
AtaqueJugador.cs
Al igual que el script comentado anteriormente, controlará las colisiones que se produzcan, en este caso, entre el objeto golem y el objeto bala (prefab), invocando al método SendMessage en caso de que se produzca dicha colisión.
Como característica principal, el evento OnCollisionEnter , recibe como parámetro de entrada la clase Collision, que permitirá controlar, además de los puntos donde se produzcan colisiones, la velocidad de impacto. Para que este evento sea lanzado, será necesario añadir un componente de tipo Rigidbody al GameObject que contenga este script:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class AtaqueJugador : MonoBehaviour { void OnCollisionEnter(Collision collision) { GameObject golem = GameObject.Find("golem"); if (collision.gameObject.name == "bala") { golem.SendMessage("golpeJugador"); } } } |
BarraVidaEnemigo.cs
Método que permite contabilizar el número de colisiones que se han producido entre el objeto golem y los proyectiles disparados por el jugador.
Se establece la condición de que el número de impactos sea múltiplo de un valor asignado, para además de instanciar un objeto de vida, que permita al jugador recuperar salud y aumentar la dificultad del videojuego, incrementando los parámetros asociados a la velocidad del enemigo.
Otra de las condiciones controladas desde este script, es la finalización del juego al superar al enemigo, manteniendo en todo momento actualizado el marcador de puntos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public void golpeJugador () { golem = GameObject.Find("golem"); contadorEnemigo ++; int multiploEnemigo = contadorEnemigo % valorMultiplo; audio.PlayOneShot(muerteEnemigo); if (multiploEnemigo == 0) { LogicaEnemigo.velocidadMov += 5.0f; LogicaEnemigo.velocidadRot += 2.0f; instanciarVida = Instantiate(vida, new Vector3(transform.position.x,transform.position.y + 5.0f, transform.position.z), transform.rotation) as GameObject; Destroy(instanciarVida, tiempoEliminarVida); } if (contadorEnemigo >= finalizarJuego) { golem.animation.Play("death"); Destroy(vida); Destroy(instanciarVida); Destroy(golem, tiempoEliminarEnemigo); Destroy(jugador); camaraNivelSuperado.SetActive(true); } puntosGameOver.text = (contadorEnemigo * 2).ToString(); puntosNivelSuperado.text = (contadorEnemigo * 2).ToString(); puntosPantalla.text = (contadorEnemigo * 2).ToString(); } |
Colisión de jugador con objeto de vida (aumento de la variable energiaPersonaje)
RecogerVida.cs
Script que implementa el evento OnTriggerEnter , que permite invocar al método aumentarVida() , al producirse la colisión entre el objeto Jugador y el objeto instanciado en tiempo de ejecución «Energía» (prefab, que no es más que un tipo de asset, formado por un GameObject con sus componentes y propiedades ya definidas):
1 2 3 4 5 6 7 8 9 10 11 12 |
public class RecogerVida : MonoBehaviour { public GameObject energiaPersonaje; void OnTriggerEnter(Collider colision) { if (colision.gameObject.name == "Jugador") { energiaPersonaje.SendMessage("aumentarVida"); } } } |
AumentarSalud.cs
Script encargado de reproducir un sonido al colisionar el objeto jugador con el objeto de vida (se declara de tipo public, para posteriormente desde el editor de Unity asignarle la pista de audio a reproducir), además de aumentar la vida del jugador. Una vez se produzcan dichas acciones, se destruirá el objeto que contenga este script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class AumentarSalud : MonoBehaviour { public AudioClip recogerEnergia; public void aumentarVida() { if (BarraVidaPersonaje.energiaPersonaje < 400) { audio.PlayOneShot(recogerEnergia); BarraVidaPersonaje.energiaPersonaje += 20.0f; Destroy(this.gameObject,0.5f); } } } |
DestruirBala.cs
Su funcionalidad radica en la destrucción de los objetos creados al lanzar proyectiles por parte del personaje que maneja el usuario:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public float tiempoDestruir; public float aceleracion; public Transform colisionBala; void Start () { Destroy(this.gameObject, tiempoDestruir); } void OnCollisionEnter(Collision colision){ Transform nuevaBala; nuevaBala = Instantiate(colisionBala, transform.position, transform.rotation) as Transform; Destroy(gameObject,tiempoDestruir); } |
Mostrar punto de mira en el centro de la pantalla
Diana.cs
Script que controla la visualización del punto de mira del arma del personaje cuando este se encuentra en movimiento:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public Texture2D diana; void OnGUI() { if (Input.GetButton("Horizontal") || Input.GetButton("Vertical")) { int posicionAncho = diana.width / 2; int posicionAlto = diana.height / 2; Rect position = new Rect ((Screen.width - posicionAncho) / 2 + 60, (Screen.height - posicionAlto) / 2 + 20, posicionAncho, posicionAlto); GUI.DrawTexture (position, diana); } } |
Disparos.cs
Permite al usuario disparar proyectiles al realizar una segunda pulsación en la pantalla. Como se puede apreciar en el código que se muestra a continuación, se instancia un componente Rigidbody, al que se asigna el nombre de un prefab, que contiene todas las propiedades necesarias para detectar colisiones con el enemigo:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public Rigidbody proyectil; public int velocidad = 20; void Update () { if(Input.GetButtonDown("Fire2")) { Rigidbody instantiatedProjectile = Instantiate(proyectil, transform.position, transform.rotation) as Rigidbody; instantiatedProjectile.velocity = transform.TransformDirection(new Vector3(0, 0, velocidad)); instantiatedProjectile.name = "bala"; Physics.IgnoreCollision(instantiatedProjectile.collider,transform.root.collider); } } |
LogicaEnemigo.cs
Desde este script se controlará los diferentes estados del enemigo, reproduciendo las distintas animaciones, dependiendo del estado en el que se encuentre:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
void Start () { animation[animacionReposo.name].speed = 1; animation[animacionReposo.name].wrapMode = WrapMode.Loop; animation[animacionCorrer.name].speed = 1; animation[animacionCorrer.name].wrapMode = WrapMode.Loop; animation[animacionDefensa.name].speed = 1; animation[animacionDefensa.name].wrapMode = WrapMode.Loop; animation[animacionAtaque.name].speed = 1; animation[animacionAtaque.name].wrapMode = WrapMode.Once; animation[animacionMuerte.name].speed = 1; animation[animacionMuerte.name].wrapMode = WrapMode.ClampForever; rotInic = transform.rotation; animation.Play(animacionReposo.name); if (objetivo == null) { objetivo = GameObject.FindGameObjectWithTag("Player"); } } void Update () { CharacterController controller = GetComponent<CharacterController>(); if (estado == 0) { } /*Estado en el que el enemigo persigue al personaje del usuario.*/ if (estado == 1) { transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(objetivo.transform.position - transform.position), velocidadRot * Time.deltaTime); controller.Move(transform.forward * velocidadMov * Time.deltaTime); controller.Move(transform.up * -gravedad * Time.deltaTime); distanciaPersonaje = Vector3.Distance(objetivo.transform.position, transform.position); if (distanciaPersonaje <= distanciaEnemigo) { estado = 2; animation.CrossFade(animacionDefensa.name); } } /*Estado en el que el enemigo se proteje de los ataques del personaje.*/ if (estado == 2) { distanciaPersonaje = Vector3.Distance(objetivo.transform.position, transform.position); if (distanciaPersonaje > distanciaEnemigo) { estado = 1; //Pasa al estado de perseguir. animation.CrossFade(animacionCorrer.name); }else{ estado = 3;//Pasa al estado de Atacar. contador = Time.time + (animation[animacionAtaque.name].clip.length * 1.2); animation.Play(animacionAtaque.name); } } /*Estado en el que el enemigo ataca al jugador.*/ if (estado == 3) { if (Time.time > contador) { estado = 2; animation.CrossFade(animacionDefensa.name, 2.0f); } } } void OnTriggerEnter (Collider other) { DoActivateTrigger (); } void muerteEnemigo () { estado = 9; } void DoActivateTrigger () { estado = 1; animation.Play(animacionCorrer.name); } void DoDesactivateTrigger () { estado = 0; animation.Play(animacionReposo.name); } |
SalirJuego.cs
Este script permitirá salir del juego desde la pantalla de pausa, con tan solo seleccionar el componente TextMesh con el título «Salir del juego»:
1 2 3 4 5 |
void OnTouchDown() { Application.Quit(); } |
NuevaPartida.cs
Este script permite volver a comenzar una partida, declarando e inicializando en el método Start() el máximo de vida para el personaje. También se implementa el evento OnTouchDown() cuya funcionalidad será la de cargar el nivel construido (se indica como parámetro de entrada un string con el nombre de la escena):
1 2 3 4 5 6 7 8 9 10 |
void Start () { BarraVidaPersonaje.energiaPersonaje = 400.0f; } void OnTouchDown() { Application.LoadLevel ("AcademiaAndroid_FPS"); } |