Comunidad orientada al desarrollo de videojuegos

UNITY3D – Tareas asíncronas

En este post vamos a explicar cómo hacer tareas asíncronas, que se ejecutan en segundo plano y así evitar bloquear la interfaz con el efecto “extraño” que eso puede causar.

Un poco de UX

Hay gente que piensa que esto de la UX es una “milonga” que se ha puesto de moda gracias a Apple y a su buen hacer en temas de diseño, pero esta definición de tiempos viene de un libro llamado “Usability Engineering” de 1993, hace nada más y nada menos de 20 años.

Dentro del mundo de la UX (User eXperience) existen unos límites de tiempos en los que podemos pasar de una buena experiencia de usuario (la aplicación funciona bien, va de una forma fluida) a una mala experiencia de usuario (aunque una aplicación funcione correctamente, la imagen que da no es del todo correcta). En esta página describen el plazo de tiempo. Si no te apetece leer demasiado Lengua fuera hago un pequeño resumen.

  • 0.1 segundos: es el límite para que usuario note que el sistema reacciona de forma instantánea.
  • 1 segundo: siempre que no se exceda este segundo, no sería estrictamente necesario mostrar un indicador de progreso, es un tiempo que el usuario estará dispuesto a esperar, sin esperar un feedback.
  • 10 segundos: este es límite que podemos tener al usuario esperando, mostremos o no algún indicador de progreso es un tiempo. Pensemos que el usuario podría estar haciendo otras cosas más que esperar a que nuestro proceso termine. En este caso habría que mostrar una estimación de cuándo se va a terminar.

Esto…. ¿Si estoy en un blog de desarrollo de juegos esto en que me afecta?

No es extraño que a veces cuando estamos jugando y guardamos o cargamos alguna partida este sea un proceso costoso y se lleve una buena cantidad de segundos. En algunas (muchas) ocasiones la cosa está bien resuelta y la animación de cargando no se detiene nunca, otras veces el resultado no es tan bueno, y hasta la música del juego llega a verse afectada en esta carga.

Vamos a ver cómo hacerlo.

Creamos una escena con un cubo que da vueltas

Para la demo vamos a crear una escena con un cubo girando y vamos a ver cómo afecta hacer el mismo proceso de forma síncrona y de forma asíncrona.

No voy a contar como crear la escena y añadir un plano y un cubo, sólo voy a añadir el código del behaviour del cubo que gire (no es un script para nada complicado).

   1: using UnityEngine;

   2: using System.Collections;

   3:

   4: public class Rotating : MonoBehaviour {

   5:

   6:     public float RotateSpeed = 2f;

   7:

   8:     private Transform _myTransform;

   9:

  10:     // Use this for initialization    

  11:     void Start () {

  12:         _myTransform = transform;

  13:     }

  14:

  15:     // Update is called once per frame

  16:     void Update () {

  17:         float increase = RotateSpeed * Time.deltaTime;

  18:

  19:         _myTransform.Rotate (new Vector3(0,increase,0));

  20:

  21:     }

  22: }

Botones para guardar

El siguiente script añade los botones que utilizaremos para ver las diferencias entre hacerlos síncrono o asíncrono

   1: void OnGUI(){

   2:         if (GUI.Button(new Rect(10, 10, 150, 75), "Guardar sincrono")){

   3:             SaveSync();

   4:         }

   5:

   6:         if (GUI.Button (new Rect(10, 95, 150, 75), "Guardar asincrono")){

   7:             SaveAsync();

   8:         }

   9:     }

Operación síncrona

La siguiente función hace congela el hilo durante 5 segundos, como es una demo directamente paro el hilo, pero en el mundo real esto podría ser una operación de lectura o escritura en disco, una llamada a algún servicio online, etc…

   1: void SaveSync(){

   2:     Debug.Log("Empezando a guardar");

   3:

   4:     Thread.Sleep(5000);

   5:

   6:     Debug.Log ("Guardado completado");

   7: }

Si ejecutamos este código lo que ocurre es que durante 5 segundos la aplicación se congela y no responde a ninguna interacción. Una vez que ha terminado toda la operación, la aplicación ya responde.

Operación asíncrona

La siguiente función hace exactamente lo mismo, pero la gran diferencia es que esto se lanza desde un hilo en segundo plano. Este ejemplo sólo se encarga de lanzarlo, en otro post haré un ejemplo de sincronización entre hilos.

   1: void SaveAsync(){

   2:         Thread saveThread = new Thread(o =>{

   3:             Debug.Log ("Empezando a guardar");

   4:

   5:             Thread.Sleep (5000);

   6:

   7:             Debug.Log ("Guardado completo");

   8:         });

   9:

  10:         saveThread.Start ();

  11:     }

¿Qué tenemos en este código? Primero configuramos el hilo, definiendo la función que se llamará cuando el hilo se lance, y una vez preparado, lanzamos el hilo llamando a la función Start.

Si ahora ejecutamos el código el resultado es mas “vistoso” (dentro de lo que permite un cubo girando Guiño). Durante todo el tiempo que el hilo está bloqueado, nuestra interfaz no se congela en ningún momento.

En los próximos post hablaré de sincronización entre hilos, de la función StartCoroutine que también se usa para ejecutar tareas en segundo plano.

Saludos!!!!!

, , , ,

Leave a Reply