Cuando usted llama a una función, esta se ejecuta en su totalidad antes de retornar. Esto significa efectivamente que cualquier acción tomando lugar en una función debe suceder en una sola actualización de frame (cuadro); un llamado a una función no puede ser usado para contener una animación procedimental o una secuencia de eventos en el tiempo. Como un ejemplo, considere la tarea de reducir gradualmente el valor alfa (alpha) de un objeto (opacidad) hasta que se convierta completamente invisible.
void Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
}
}
Tal como está, la función Fade (Desvanecer) no tendrá el efecto que usted espera. Para que el desvanecimiento sea visible, el alfa debe ser reducido sobre una secuencia de frames (cuadros) para mostrar los valores intermedios siendo renderizados. Sin embargo, la función se ejecutará en su totalidad en una sola actualización de cuadro. Los valores intermedios nunca se verán y el objeto desaparecerá instantáneamente.
Es posible manejar situaciones como estas agregando código a la función Update que ejecuta el desvanecer en una base cuadro-a-cuadro (frame-by-frame). Sin embargo, es usualmente mas conveniente usar una coroutine (corrutina) para este tipo de tarea.
Una corrutina es una función que tiene la habilidad de pausar su ejecución y devolver el control a Unity para luego continuar donde lo dejó en el siguiente frame. En C#, una corrutina es declarada así:
IEnumerator Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
yield return null;
}
}
Es esencialmente una función declarada con un tipo de retorno IEnumerator y con una instrucción de retorno yield incluida en algún lugar de su cuerpo. La linea con la instrucción de retorno yield es el punto en el cual la ejecución se pausará y reanudará en el siguiente frame. Para establecer una corrutina en ejecución, necesitas usar la función StartCoroutine
void Update()
{
if (Input.GetKeyDown("f"))
{
StartCoroutine("Fade");
}
}
Notará que el contador en el bucle de la función Fade mantiene su valor correcto durante el tiempo de vida de la corrutina. De hecho, cualquier variable o parametro será preservado correctamente entre los yields.
Por defecto, una corrutina es reanudada en el siguiente frame después de haberse interrumpido (yield), pero también es posible introducir un tiempo de retardo usando WaitForSeconds:-
IEnumerator Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
yield return new WaitForSeconds(.1f);
}
}
Esto se puede utilizar como una forma de difundir un efecto durante un período de tiempo, pero también es una optimización útil. Muchas tareas en un juego deben llevarse a cabo periódicamente y la forma más obvia de hacerlo es incluirlas en la función Update. Sin embargo, esta función generalmente se llamará muchas veces por segundo. Cuando una tarea no necesita repetirse con tanta frecuencia, puede ponerla en una coroutine para obtener una actualización regularmente, pero no en todos los frames. Un ejemplo de esto podría ser una alarma que le avisa al jugador si hay un enemigo cerca. El código podría verse más o menos así:
function ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
return true;
}
}
return false;
}
Si hay muchos enemigos, entonces llamar a esta función en cada frame podría introducir una sobrecarga significativa. Sin embargo, podría usar una corrutina para llamarla cada décima de segundo:
IEnumerator DoCheck()
{
for(;;)
{
ProximityCheck;
yield return new WaitForSeconds(.1f);
}
}
Esto reducirá en gran cantidad el número de comprobaciones llevados a cabo sin ningún efecto notable en la experiencia de juego.
Nota: las Coroutines no se detienen cuando un MonoBehaviour esté desactivado, pero solo cuando se destruye definitivamente. Puede detener una Coroutine usando MonoBehaviour.StopCoroutine y MonoBehaviour.StopAllCoroutines. Las Coroutines también se detienen cuando se destruye el MonoBehaviour.