miércoles, 17 de enero de 2018

Unity3D Plugin: UniRx

Esta vez hablaré de otro plugin que, para mi, también es esencial, y si bien tiene una pequeña dificultad añadida al tener que cambiar el paradigma de programación, una vez te acostumbras, es mucho mas rápido e incluso mejora la eficiencia en muchos casos, especialmente en los proyectos grandes.

Ahora bien, para empezar, comencemos en explicar en que consiste este plugin, que ventajas ofrece usarlo y algunos ejemplos de uso variados ya que es todo un framework completo, no una utilidad como el anterior.

¿Para que sirve este plugin?

UniRx (Reactive Extensions for Unity, Extensiones reactivas para Unity en español) es una reimplementación de las extensiones reactivas de .NET. La implementación original de Rx es buena, pero no funciona con Unity y tiene problemas con la compatibilidad IL2CPP princpalmente. Esta librería corrige esos problemas y añade algunas utilidades específicas de Unity3D.

Plataformas soportadas:
  • PC
  • Mac
  • Android
  • iOS
  • WP8
  • WindowsStore
  • No lo especifica pero presumiblemente también para consolas
Esta librería es soportada desde la versión 4.6 de Unity3D

Enlace a la librería: http://u3d.as/content/neuecc/uni-rx-reactive-extensions-for-unity/7tT

¿Que es una extensión reactiva? 

La programación reactiva es un paradigma de programación que está orientado a los flujos de datos y más específicamente a la propagación de los cambios de los datos en dicho flujo. Dicho de esta manera puede parecer complejo, pero pongamos un ejemplo práctico en Unity3D ya que será mucho mas fácil de entender con un ejemplo que con la teoría:

Si queremos que, cuando pulsemos un botón nuestro personaje salte, de manera convencional, pensamos en estar comprobando el estado del botón, cuando cambie a estar pulsado, entonces ejecutamos la acción de saltar.

Bien, de manera reactiva, nuestra forma de pensar cambiaría y sería justo al revés, en la programación del salto, decimos que este método se ejecute cuando el botón de salto cambie a pulsado, de manera que, observamos el estado del botón y el botón al cambiar será quien nos avise de que ha cambiado, sin tener que estar comprobándolo constantemente.

Bien, dicho así suena muy bonito, no lo comprobamos nosotros si no que el mismo botón cuando cambie, será quien nos avise, nos ahorraremos comprobaciones repetitivas e innecesarias y el código quedará mas corto, legible y concentrado sin tener que enlazar un controlador para el input y otro para el salto en este caso, pero veamos algunas otras ventajas de usar este framework.

¿Por qué UniRx?

Normalmente, las operaciones de red en Unity, requieren de WWW y Corutinas. Usar corutinas no es una buena practica en las operaciones asincronas de seguimiento de datos (como esperar la respuesta de una petición web como estamos usando de ejemplo) por las siguientes razones:
  1. Las corutinas no puede devolver un valor, ya que una corutina lo que devuelve es un IEnumerator irremediablemente.
  2. Las corutinas no pueden gestionar excepciones ya que al usar yield return, no podemos usar try-catch y especialmente en un WWW es uno de los sitios mas importantes donde poder gestionar una excepción ya que puede no depender de nuestro código, si no de código externo.
Este tipo de falta de capacidad de compilación hace que las operaciones se acoplen estrechamente, lo que a menudo resulta en enormes IEnumerator bastante tediosos de gestionar y desde luego, poco elegantes.

Rx es una librería para gestionar operaciones asincronas, basada en eventos observables como si fuesen llamadas de LINQ.

El bucle de juego (Awake, Start, Update, OnCollisionEnter, etc), sensores (Kinect, Leap Motion, VR Input, etc.) todos son tipos de eventos. Rx representa los eventos como secuencias reactivas y las simplifica como llamadas de LINQ.

Unity generalmente se usa en un solo hilo, salvo que, gestionemos, a través de un plugin como vimos anteriormente o por nosotros mismos hilos externos, UniRx facilita el multithreading en sus llamadas.

UniRx ayuda a programar UI con uGUI. todos los events de UI (clicked, valuechanged, etc) pueden ser convertidos a eventos de UniRx.

Ejemplos de uso

Bien, una vez explicado esto, pasemos a ejemplos de uso que, son sencillos de entender y podremos ver todo su potencial.

Ejemplo para saber cuantos clicks hicimos

 

var clickStream = Observable.EveryUpdate() // En cada update
    .Where(_ => Input.GetMouseButtonDown(0)); // Cuando el botón izquierdo del ratón se pulse

clickStream.Buffer(clickStream.Throttle(TimeSpan.FromMilliseconds(250))) // En 250 milisegundos
    .Where(xs => xs.Count >= 2) // Si se han hecho al menos dos clicks
    .Subscribe(xs => Debug.Log("DoubleClick Detected! Count:" + xs.Count)); // Número de clicks!

Ejemplo para descargar una web con código de ok y de error


ObservableWWW.Get("http://google.com/") // Descargamos por WWW la web de google
    .Subscribe(
        x => Debug.Log(x.Substring(0, 100)), // Si se descargó correctamente ejecutamos este debug
        ex => Debug.LogException(ex)); // En caso de error mostramos este debug de excepción

Ejemplo para descargar multiples webs en paralelo


var parallel = Observable.WhenAll( // Pedimos en paralelo estas webs
    ObservableWWW.Get("http://google.com/"),
    ObservableWWW.Get("http://bing.com/"),
    ObservableWWW.Get("http://unity3d.com/"));

parallel.Subscribe(xs =>
{
    Debug.Log(xs[0].Substring(0, 100)); // google
    Debug.Log(xs[1].Substring(0, 100)); // bing
    Debug.Log(xs[2].Substring(0, 100)); // unity
});

Ejemplo para notificar cuando se descaga una web


var progressNotifier = new ScheduledNotifier();  // creamos un notificador
progressNotifier.Subscribe(x => Debug.Log(x)); // muestra el www.progress
ObservableWWW.Get("http://google.com/", progress: progressNotifier).Subscribe(); // Progreso

Ejemplo para controlar una excepción

 

ObservableWWW.Get("http://www.google.com/404")
    .CatchIgnore((WWWErrorException ex) => //En caso de excepción para WWW ejecutamos esto
    {
        Debug.Log(ex.RawErrorMessage);
        if (ex.HasResponse)
        {
            Debug.Log(ex.StatusCode);
        }
        foreach (var item in ex.ResponseHeaders)
        {
            Debug.Log(item.Key + ":" + item.Value);
        }
    })
    .Subscribe();

Ejemplo para arrastrar y soltar con el ratón

 

using UniRx; // Librerías necesarias
using UniRx.Triggers; 

public class DragAndDropOnce : MonoBehaviour
{
    void Start()
    {
        this.OnMouseDownAsObservable() // Observamos desde que pulse un botón del ratón...
            .SelectMany(_ => this.UpdateAsObservable()) // En cada update...
            .TakeUntil(this.OnMouseUpAsObservable()) // Hasta que suelte el botón del ratón...
            .Select(_ => Input.mousePosition) // Seleccionamos la posición del ratón...
            .Subscribe(x => Debug.Log(x)); // Mostramos la posición actual del ratón
    }
}
Hay muchos mas ejemplos tanto en en Asset Store como en su página de GitHub además de los diversos ejemplos que trae el mismo framework.

Como podrás observar, con muy póco código se puede hacer mucho con este plugin, si ya tienes un proyecto avanzado, es mala idea integrar cualquier tipo de framework avanzado, pero en caso de estar empezando, es muy aconsejable especialmente si quieres hacer algo grande, complejo o por qué no, por el simple hecho de aprender.

Para mi, este framework ha marcado un antes y un después en mi día a día ya que es mucho mas sencillo de trabajar con el sin dudas, quizás al principio pueda parecer complejo, pero con la práctica podremos aprender mucho con el.

1 comentario: