PREPARANDO A NUESTRA NAVE PARA DISPARAR

Ahora que ya podemos mover a nuestra nave vamos a hacer que dispare. Para ello vamos a crear un bloque de código que detecte cuándo presionamos una tecla para disparar, por ejemplo, la tecla de ESPACIO. Así que vamos a agregar el siguiente bloque:

 

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

public class Hero : MonoBehaviour
{
    public float speed;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position += Vector3.right * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.position += Vector3.left * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.DownArrow))
        {
            transform.position += Vector3.down * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.position += Vector3.up * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.Space))
        {
            //SHOOT!!
        }
    }
}

 

PREPARANDO NUESTRA BULLET

Ahora la parte divertida: Preparar nuestra bullet. Para esto, lo primero que vamos a hacer es importar el gráfico de una bullet y tirarlo en la escena para configurarlo:

 

 

Antes de que podamos usar el sprite de esta bullet, debemos setearle un tamaño correcto y crear un prefabricado (prefab), para poder crear copias que tengan el mismo comportamiento. Pero… Qué es un PREFAB? Nada de otro mundo. Es simplemente un archivo que guarda un prefabricado ya configurado con todas los seteos que le pongamos. O sea… Si ponemos una imagen, con un color X, componentes X, escala y rotación X, y creamos el prefab con eso, cada vez que creemos una copia de ese prefabricado tendrá esos seteos.

Lo que vamos a necesitar es reescalar nuestra imagen y ponerle un script para que se mueva. Así que vamos a crear dicho script, por ahora con nada en especial.

 

 

El nombre que le pongamos a nuestros archivos es bastante importante aunque quizás a algunos les parezca poca cosa. En este caso le pongo “Bullet” porque esa bullet podría ser la de un héroe, pero también podría ser la de un enemigo. Si lo pensamos, lo único que cambia es quién la dispara, a quién hace daño, quizás, pero son muy pocas las cosas que varían entre el disparo de un personaje u otro. La gráfica podría ser diferente pero es sólo gráfica al fin y al cabo.

Vamos a ver que la estructura base del script es la misma que nuestro script anterior, ya que también es un componente. Así que lo que hacemos en el siguiente paso es arrastrar el script a la bullet.

 

 

Lo siguiente es seleccionar el bullet y arrastrarle nuestro script. De esta manera podemos empezar a programar su comportamiento.

Ahora que nuestro bullet tiene un script, vamos a corregir el tamaño, para que quede más acorde a la nave que lo va a disparar. Esto lo podemos hacer modificando la escala de la imagen o su tamaño. La diferencia entre escala y tamaño es que el tamaño representa las medidas reales de la imagen, y la escala es por cuánto va a multiplicar esa medida real.

 

 

Lo que quedaría ahora es crear el prefab del que hablábamos. Para eso, basta con que lo arrastremos hasta la ventana de “Project“. Esto genera un archivo que va a representar al prefabricado de la bullet. Vamos a ponerlo en una carpeta llamada “Prefabs” que hay que crear. Esto no es algo que Unity pida hacer. Sólo es para que quede todo ordenado.

 

 

Noten que en la pestaña de “Hierarchy” ahora el bullet aparece en color azul. Esto significa que ESE GameObject es un prefab, o está instanciado a partir de uno. Noten también que si ahora borramos el archivo de la escena, y lo volvemos a arrastrar desde la carpeta prefab nuevamente en la escena, este volverá a aparecer de la misma manera en la que lo habíamos guardado. Y esto es conceptualmente un prefab. Nos va a servir para armar el diseño de un objeto determinado de nuestro juego, y luego poder crear copias con los mismos seteos.

 

MOVIENDO NUESTRA BULLET

Vamos a hacer ahora que la bullet pueda trasladarse a lo largo de la escena. En principio, el movimiento va a ser el mismo que hacíamos con nuestro personaje, con la diferencia de que la bullet no se basa en las teclas que presiona el usuario, sino que siempre va hacia arriba o en la dirección que la hayamos disparado, según sea el caso. Hagamos ahora una implementación básica de esto, y después la vamos ajustando de a poco. Pongamosle una velocidad para poder ir modificándola y que se mueva más rápido o más lento según corresponda.

 

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

public class Bullet : MonoBehaviour
{
    public float speed;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.up * speed * Time.deltaTime;
    }
}

 

 

Como verán, el código es bastante similar. Sólo hace que la bullet se mueva hacia arriba con una velocidad determinada, que podemos setear seleccionando el prefab de la bullet. Hagan click en el botón “Play” para probar el comportamiento.

 

¿Se acordaron de setear la velocidad de la bullet? Seleccionen la bullet desde la ventana “Hierarchy” o desde la ventana de “Project” y cambien el valor de la variable “speed”. 

IMPORTANTE!!

No es lo mismo cambiar el valor del prefab que cambiar el valor del GameObject creado en la escena. La diferencia está en que si lo modificamos desde la pestaña “Hierarchy” SOLAMENTE estaríamos modificando ESA COPIA de la bullet. Si modificamos desde la ventana de “Project” estaríamos modificando todas las copias, presentes y futuras.

 

Así que… Si les funciona todo bien, lo que van a ver es a la bullet moviéndose. Mientras está el juego en “Play“, prueben arrastrar a la escena otras bullets (prefabs), y vean qué pasa. Si todo salió bien, verán que cada bullet que vayan tirando a la escena se va a mover sola, de la misma manera. Esto es porque programamos una, y todas las copias tienen el mismo comportamiento.

 

DISPARANDO NUESTRAS BULLETS

Volvamos ahora al script de nuestra nave, y vamos a crear una variable para almacenar el prefab de nuestra bullet. El tipo de dato de los prefabs es GameObject, así que si creamos una variable de tipo GameObject en el script de la nave, podríamos guardar en dicha variable a nuestro prefab. Veamos cómo.

 

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

public class Hero : MonoBehaviour
{
    public float speed;
    public GameObject prefab;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position += Vector3.right * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.position += Vector3.left * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.DownArrow))
        {
            transform.position += Vector3.down * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.position += Vector3.up * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.Space))
        {
            //SHOOT!!
        }
    }
}

 

Si volvemos a Unity, luego de guardar el script, vamos a ver que si seleccionamos a nuestra nave, tiene una variable ahora llamada “prefab“. Lo que hacemos ahora es simplemente arrastrar nuestra prefab de la bullet a esa variable. Vamos a ver que nos permite depositarla ahí. Ahora, la variable “prefab” tiene un valor, y ese valor es nuestro prefab.

 

 

Ahora estamos listos para hacer que nuestra bullet aparezca!! Volvamos al script de la nave. Ahora vamos a programar sobre el bloque que habíamos preparado antes, y vamos a escribir el código para crear una copia del prefab de nuestra bullet, y que aparezca en la escena:

 

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

public class Hero : MonoBehaviour
{
    public float speed;
    public GameObject prefab;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position += Vector3.right * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.position += Vector3.left * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.DownArrow))
        {
            transform.position += Vector3.down * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.position += Vector3.up * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.Space))
        {
            GameObject.Instantiate(prefab);
        }
    }
}

 

De más está decir, que si no borraron la bullet que estaba en la escena, pueden hacerlo ahora. Guarden el script y presionen el botón “Play”. Van a ver cómo cada vez que apretamos la tecla “Space” se va a crear una copia de nuestra bullet. Aunque… ¿Qué pasa cuando mantienen presionada la tecla? Para “fixear” eso, vamos a hacer un pequeño cambio en el if:

 

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

public class Hero : MonoBehaviour
{
    public float speed;
    public GameObject prefab;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position += Vector3.right * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.position += Vector3.left * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.DownArrow))
        {
            transform.position += Vector3.down * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.position += Vector3.up * speed * Time.deltaTime;
        }
        
        if (Input.GetKeyDown(KeyCode.Space))
        {
            GameObject.Instantiate(prefab);
        }
    }
}

 

El método Input.GetKeyDown(KeyCode.Space) lo que va a hacer es solamente devolver VERDADERO cuando se presione la tecla “Space” pero ÚNICAMENTE durante un frame. En el siguiente frame, vuelve a devolver FALSO hasta que se suelte la tecla y se vuelva a presionar. Esto nos permite hacer que si mantenemos apretada la tecla, no esté disparando constantemente.

Ahora… Siguiente problema: ¿Cómo hacemos que dispare desde la nave? Bien… Vamos a empezar guardando a nuestra bullet recién copiada en una variable temporal. Esto lo vamos a hacer para luego de creada poder cambiarle la posición.

 

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

public class Hero : MonoBehaviour
{
    public float speed;
    public GameObject prefab;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position += Vector3.right * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.position += Vector3.left * speed * Time.deltaTime;
        }
        
        if (Input.GetKey(KeyCode.DownArrow))
        {
            transform.position += Vector3.down * speed * Time.deltaTime;
        }else if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.position += Vector3.up * speed * Time.deltaTime;
        }
        
        if (Input.GetKeyDown(KeyCode.Space))
        {
            GameObject myNewBullet = GameObject.Instantiate(prefab);
            myNewBullet.transform.position = transform.position;
        }
    }
}

 

Lo que hacemos en el script es asignarle a la bullet la misma posición que la nave. De esta manera, cada vez que copiemos una bullet, esta va a aparecer sobre nuestra nave, creando así la “simulación” de que la nave está disparando.

 

DESTRUIR LAS BULLETS QUE SALEN DE LA ESCENA

Por supuesto, las bullets que salen de la escena no se están destruyendo. Lo malo de esto es que esas bullets, que dejan de servir, siguen ocupando memoria y procesamiento. Lo lógico es que cada una que vaya saliendo, se vaya destruyendo. Hay muchas formas de definir qué sería “salir del escenario”. La que voy a mostrarles es: “Sale del escenario cuando la cámara deja de verla“. Para esto, Unity ejecuta, si es que existe, una función llamada OnBecameInvisible. Dicho método vamos a crearlo en el script “Bullet“.

 

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

public class Bullet : MonoBehaviour
{
    public float speed;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.up * speed * Time.deltaTime;
    }
    
    void OnBecameInvisible()
    {
        Debug.Log("Salí de la escena!!");
    }
}

 

Esta función se ejecuta cuando TODAS LAS CÁMARAS dejan de ver al personaje. Hago esta aclaración porque la ventana “Scene” cuenta como una cámara, y si hicieran zoom out para mostrar más contenido, verán que el mensaje sale realmente cuando la bullet sale del rango visual de todas las cámaras.

Ahora… ¿Cómo la destruimos? Así como Unity nos da una herramienta para crear copias de un prefab, también nos da otra para destruir los objetos que creamos:

 

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

public class Bullet : MonoBehaviour
{
    public float speed;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.up * speed * Time.deltaTime;
    }
    
    void OnBecameInvisible()
    {
        GameObject.Destroy(this);
    }
}

 

Guarden el script, presionen “Play” y vean lo que pasa.

 

 

Como verán… Una vez que las bullets salen de la visual de las cámaras el script “Bullet” se destruye. Pero… ¿Por qué sólo el script y no el GameObject entero? Pues… La orden que le dimos fue que destruya “this“, y “this” es nuestro script, no el GameObject. Si le decimos que destruya el GameObject, debería destruir todo lo que este contiene, o sea, su componente Transform, SpriteRenderer y, por supuesto, nuestro script. O sea:

 

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

public class Bullet : MonoBehaviour
{
    public float speed;
    
    // Use this for initialization
    void Start ()
    {
        
    }
    
    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.up * speed * Time.deltaTime;
    }
    
    void OnBecameInvisible()
    {
        GameObject.Destroy(gameObject);
    }
}

 

 

Ahora sí!! Cuando disparen y todos los disparos salgan de la vista de todas las cámaras, la bullet se destruiría con todos sus componentes.