viernes, 31 de enero de 2014

Unity 3D Scripting: Corutinas

Este apartado a muchos les ha sido sumamente complicado por lo que he visto en internet pero es mucho, muchísimo mas sencillo de lo que a priori puede parecer.

Para empezar, decir que una corutina se comporta como un hilo, pero no lo es.

Para quien no sepa que es un hilo en programación, es como una ejecución aparte, un ejemplo sencillo que se me ocurre es que el navegador web desde el que ves este blog se ejecuta a través de un hilo y en cambio el unity3D se ejecuta en otro hilo aparte, son procesos independientes que si bien podrían comunicarse, durante la ejecución cada uno va por separado, eso tiene sus ventajas y sus desventajas que si bien podría explicar mas detalladamente esto, no lo haré aquí por una sencilla razón, las corutinas se comportan como hilos independientes pero no lo son.

Es decir, una corutina hará algo durante un tiempo determinado pero no puede por ejemplo tener un bucle infinito ya que haría que el resto de procesos se bloqueasen hasta que saliese de ese bucle ya que no es independiente.

Después de dejar claro eso empecemos con el ejemplo.



using UnityEngine;
using System.Collections;

public class EnemyMaker : MonoBehaviour {

    public float tiempo = 2;

    void Start()
    {
        StartCoroutine("corutina"tiempo);
    }

    IEnumerator corutina(float t)
    {
        while(true)
        {
            yield return new WaitForSeconds(tiempo);
            Debug.Log(Time.time);
        }
    }
}
Bien, vamos a explicar paso por paso que estamos haciendo aquí.

void Start()
{
    StartCoroutine("corutina"tiempo);
}
Empezamos a ejecutar nuestra corutina, simplemente le pasamos el nombre entre comillas y en caso de tener alguna variable, pues la variable que le queramos pasar, objeto o lo que fuese.

Ahora veamos que hace nuestra corutina:

IEnumerator corutina(float t)
{
    while(true)
    {
        yield return new WaitForSeconds(t);
        Debug.Log(Time.time);
    }
}
Toda corutina debe empezar de esta manera:

IEnumerator nombreCorutina(parametros)

En este caso una vez empieza, tenemos esto:

while(true){}
Una forma, a mi parecer elegante de decir siempre, y ahora pasemos a lo importante de las corutinas, como bien dije, si tenemos un bucle infinito de una corutina, al no ser un hilo diferente estaría el programa constantemente haciendo lo de dentro del bucle, pero si hay algo que tienen de especial es lo siguiente:

yield return new WaitForSeconds(t);
En este caso le estamos diciendo que pause la corutina durante un tiempo determinado, en este caso el tiempo que haya en la variable t, siendo 2 en el caso que tenemos de ejemplo.

¿Que pasa mientras está en espera? pues deja la corutina en segundo plano y sigue con el resto de procesos del videojuego, cuando terminen esos dos segundos volverá por donde iba en nuestra corutina, que en este caso sería:

Debug.Log(Time.time);
El cual nos devolverá el tiempo actual por consola.

Si una corutina termina, vease, tal que así:

IEnumerator corutina(float t)
{
    Debug.Log(Time.time);
}
la corutina finaliza como una función normal y corriente y sale de la memoria definitivamente.

Para finalizar, no solo tiene esa pausa por segundos, existen tres tipos de pausa.

yield return new WaitForSeconds(float tiempo); //espera un tiempo dado en segundos
yield return new WaitForEndOfFrame(); //espera a que termine el frame
yield return new WaitForFixedUpdate(); //espera a la función FixedUpdate()
Por si te lo preguntas no, no se pueden usar estas pausas en otras funciones, solo en corutinas.

3 comentarios: