Sunday, August 16, 2015

Unity 5 Observables Reactions Rx

Why not use Reaction in Unity game development. It as such powerful technics.
Discussion is based on great lib UniRx. I couldn't be more grateful and happy that people like neuecc exist and share.






I wanted to have more visual control and not recreate wheel, more in Unity way so I've create Unity Observables. UnityObservables reuse UnityEvent drawing and UnityAction serialization logic.

#if UNI_RX
using UnityEngine.Events;
using System;
using System.Collections.Generic;
using System.Reflection;
using UniRx;
using UniRx.InternalUtil;



using UnityEngine;

namespace ws.winx.unity.core{

   using UnityEngine.Events;
   using System;
   using System.Collections.Generic;
   using UniRx;
   using UniRx.InternalUtil;
   using UnityEngine;
   using System.Reflection;
   using UniRx.Triggers;
   
   
   public static class  ObservableExtensionsEx{
      public static IDisposable DisposeWith(this IDisposable disposable, GameObject gameObject)
      {
         return gameObject.OnDestroyAsObservable().First().Subscribe(p => disposable.Dispose());
      }
      
      public static IDisposable AddListener (this IObservable<GameObject> source,UnityAction call)
      {
         return source.Where(tgt=>tgt==null || tgt==(call.Target as MonoBehaviour).gameObject).Subscribe<GameObject> (x => call ());
         //return ((UnityObservable)source).Subscribe (call);                        }
      
      public static IDisposable AddListener<T1> (this IObservable<Tuple<T1,GameObject>> source,UnityAction<T1> call)
      {
         
         return source.Where(tgt => tgt.Item2 == null || tgt.Item2 == (call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,GameObject>> (x => call (x.Item1));
         //return ((UnityObservable<T1>)source).Subscribe (call);               }

      public static IDisposable AddListener<T1,T2> (this IObservable<Tuple<T1,T2,GameObject>> source,UnityAction<T1,T2> call)
      {
         return source.Where (typle => typle.Item3 == null || typle.Item3 == (call.Target as MonoBehaviour).gameObject).Subscribe ((typle) => call (typle.Item1, typle.Item2));
         //return ((UnityObservable<T1,T2>)source).Subscribe (call);      }
      

      
      public static IDisposable AddListener<T1,T2,T3> (this IObservable<Tuple<T1,T2,T3,GameObject>> source,UnityAction<T1,T2,T3> call)
      {
         return source.Where(tgt=>tgt.Item4==null || tgt.Item4==(call.Target as MonoBehaviour).gameObject).Subscribe (typle => call (typle.Item1,typle.Item2,typle.Item3));
         //return ((UnityObservable<T1,T2,T3>)source).Subscribe (call);               }
      
      
      public static IDisposable AddListener<T1,T2,T3,T4> (this IObservable<Tuple<T1,T2,T3,T4,GameObject>> source,UnityAction<T1,T2,T3,T4> call)
      {
         return source.Where(tgt=>tgt.Item5==null || tgt.Item5==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,T3,T4,GameObject>> (x => call (x.Item1,x.Item2,x.Item3,x.Item4));

         //return ((UnityObservable<T1,T2,T3,T4>)source).Subscribe (call);               }
      
      
   }
   
   [Serializable]
   public class UnityObservable:UnityEvent,IObservable<GameObject>
   {
      
      private Dictionary<UnityAction,IDisposable> __disposables;
      private Subject<GameObject> subject;
      private bool persistentTargetsAreSubscribed;
      private CompositeDisposable __disposer;
      
      
      
      
      //      // Constructors      //      public UnityObservable ():base()
      {
         
         __disposables = new Dictionary<UnityAction, IDisposable> ();
         
         subject = new Subject<GameObject> ();
         
         __disposer = new CompositeDisposable ();
         
         //num of persistent is 0 here, so why SubscibeToPersistent is called in Invoke      }
      
      public new void AddListener (UnityAction call)
      {
         Subscribe (call);
      }
      
      void SubscibeToPersistent ()
      {
         //check is some listners are subscribed thru editor         if (!persistentTargetsAreSubscribed && this.GetPersistentEventCount () > 0) {
            //subscribe persistent obeservers to observer            int persistentTargetsNumber = this.GetPersistentEventCount ();
            MethodInfo methodInfo;
            UnityEngine.Object persistentTarget;
            
            
            
            for (int i=0; i<persistentTargetsNumber; i++) {
               
               persistentTarget = this.GetPersistentTarget (i);
               methodInfo = persistentTarget.GetType ().GetMethod (this.GetPersistentMethodName (i));
               UnityAction call= System.Delegate.CreateDelegate (typeof(UnityAction), persistentTarget, methodInfo) as UnityAction;
               
               this.AddListener(call);
               //subject.Where(trg=>trg==null || trg==(call.Target as MonoBehaviour).gameObject).Subscribe<GameObject> (x => call ())                           }
            
            persistentTargetsAreSubscribed = true;
         }
         
         
         
      }
      
      public void InvokeOnTarget (GameObject target)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            subject.OnNext (target);
         }
         
      }
      
      public new void Invoke ()
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            
            subject.OnNext(null);
         }
         
      }
      
      public new void RemoveListener (UnityAction call)
      {
         if (__disposables.ContainsKey (call)) {
            IDisposable disposable=__disposables [call];
            __disposables.Remove(call);
            
            disposable.Dispose();
            
            Debug.Log("Observer "+call.Method.Name+" on target "+call.Target+" Removed");
            
         }
         
         
      }
      
      public new void RemoveAllListeners ()
      {
         foreach (var d in __disposables)
            d.Value.Dispose ();
         
         __disposables.Clear ();
         
         __disposer.Dispose ();
      }

      
      
      public IDisposable Subscribe (UnityAction call)
      {
         IDisposable disposable = subject.Where(tgt=>tgt==null || tgt==(call.Target as MonoBehaviour).gameObject).Subscribe<GameObject> (x => call ());
         __disposables [call] = disposable;
         return disposable;
      }
      
      #region IObservable implementation      public IDisposable Subscribe (IObserver<GameObject> observer)
      {
         //Debug.LogWarning ("Use Subscribe (UnityAction call)");         return subject.Subscribe<GameObject> (observer.OnNext).AddTo(__disposer);
      }
      #endregion                                                    }
   
   
   
   [Serializable]
   public abstract class UnityObservable<T1,T2,T3,T4>:UnityEvent<T1,T2,T3,T4>,IObservable<Tuple<T1,T2,T3,T4,GameObject>>
   {
      private Dictionary<UnityAction<T1,T2,T3,T4>,IDisposable> __disposables;
      private Subject<Tuple<T1,T2,T3,T4,GameObject>> subject;
      private bool persistentTargetsAreSubscribed;
      private CompositeDisposable __disposer;
      
      
      
      //      // Constructors      //      public UnityObservable ():base()
      {
         
         __disposables = new Dictionary<UnityAction<T1,T2,T3,T4>, IDisposable> ();
         
         subject = new Subject<Tuple<T1,T2,T3,T4,GameObject>> ();
         
         __disposer = new CompositeDisposable ();
         
         //num of persistent is 0 here, so why SubscibeToPersistent is called in Invoke      }
      
      public new void AddListener (UnityAction<T1,T2,T3,T4> call)
      {
         Subscribe (call);
      }
      
      
      
      
      void SubscibeToPersistent ()
      {
         //check is some listners are subscribed thru editor         if (!persistentTargetsAreSubscribed && this.GetPersistentEventCount () > 0) {
            //subscribe persistent obeservers to observer            int persistentTargetsNumber = this.GetPersistentEventCount ();
            MethodInfo methodInfo;
            UnityEngine.Object persistentTarget;
            
            
            
            for (int i=0; i<persistentTargetsNumber; i++) {
               
               persistentTarget = this.GetPersistentTarget (i);
               methodInfo = persistentTarget.GetType ().GetMethod (this.GetPersistentMethodName (i));
               UnityAction<T1,T2,T3,T4> call= System.Delegate.CreateDelegate (typeof(UnityAction<T1,T2,T3,T4>), persistentTarget, methodInfo) as UnityAction<T1,T2,T3,T4>;
               
               this.AddListener(call);
               //subject.Where(trg=>trg.Item5==null || trg.Item5==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,T3,T4,GameObject>> (x => call (x.Item1,x.Item2,x.Item3,x.Item4));                           }
            
            persistentTargetsAreSubscribed = true;
         }
         
         
         
      }
      
      public void InvokeOnTarget (GameObject target, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            subject.OnNext (Tuple.Create (arg1, arg2, arg3, arg4, target));
         }
         
      }
      
      public new void Invoke (T1 arg1, T2 arg2, T3 arg3, T4 arg4)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            
            subject.OnNext (Tuple.Create (arg1, arg2, arg3, arg4,(GameObject)null));
         }
         
      }
      
      public new void RemoveListener (UnityAction<T1,T2,T3,T4> call)
      {
         if (__disposables.ContainsKey (call)) {
            IDisposable disposable=__disposables [call];
            __disposables.Remove(call);
            
            disposable.Dispose();
            
            Debug.Log("Observer "+call.Method.Name+" on target "+call.Target+" Removed");
            
         }
         
         
      }
      
      public new void RemoveAllListeners ()
      {
         
         foreach (var d in __disposables)
            d.Value.Dispose ();
         
         
         __disposables.Clear ();
         
      }
      
      public IDisposable Subscribe (UnityAction<T1,T2,T3,T4> call)
      {
         //convert UnityAction<T1,T2,T3,T4> to Action<T1,T2,T3,T4>         IDisposable disposable = subject.Where(tgt=>tgt.Item5==null || tgt.Item5==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,T3,T4,GameObject>> (x => call (x.Item1,x.Item2,x.Item3,x.Item4));
         
         __disposables [call] = disposable;
         
         return disposable;
         
      }
      
      
      #region IObservable implementation      public IDisposable Subscribe (IObserver<Tuple<T1,T2,T3,T4,GameObject>> observer)
      {
         //Debug.LogWarning ("Use Subscribe (UnityAction<T1,T2,T3,T4> call)");         //Action<Tuple<T1,T2,T3,T4,GameObject>> action = observer.OnNext as Action<Tuple<T1,T2,T3,T4,GameObject>>;                           //UnityAction<Tuple<T1,T2,T3,T4,GameObject>> call=Delegate.CreateDelegate(typeof(UnityAction<Tuple<T1,T2,T3,T4,GameObject>>),action.Target,action.Method) as UnityAction<Tuple<T1,T2,T3,T4,GameObject>>;                  return subject.Subscribe(observer.OnNext).AddTo(__disposer);
      }
      #endregion                                                    }
   
   [Serializable]
   public abstract class UnityObservable<T1,T2,T3>:UnityEvent<T1,T2,T3>,IObservable<Tuple<T1,T2,T3,GameObject>>
   {
      
      private Dictionary<UnityAction<T1,T2,T3>,IDisposable> __disposables;
      private Subject<Tuple<T1,T2,T3,GameObject>> subject;
      private bool persistentTargetsAreSubscribed;
      private CompositeDisposable __disposer;
      
      
      
      //      // Constructors      //      public UnityObservable ():base()
      {
         
         __disposables = new Dictionary<UnityAction<T1,T2,T3>, IDisposable> ();
         
         subject = new Subject<Tuple<T1,T2,T3,GameObject>> ();
         
         __disposer=new CompositeDisposable();
         
         //num of persistent is 0 here, so why SubscibeToPersistent is called in Invoke      }
      
      public new void AddListener (UnityAction<T1,T2,T3> call)
      {
         Subscribe (call);
      }
      
      void SubscibeToPersistent ()
      {
         //check is some listners are subscribed thru editor         if (!persistentTargetsAreSubscribed && this.GetPersistentEventCount () > 0) {
            //subscribe persistent obeservers to observer            int persistentTargetsNumber = this.GetPersistentEventCount ();
            MethodInfo methodInfo;
            UnityEngine.Object persistentTarget;
            
            
            
            for (int i=0; i<persistentTargetsNumber; i++) {
               
               persistentTarget = this.GetPersistentTarget (i);
               methodInfo = persistentTarget.GetType ().GetMethod (this.GetPersistentMethodName (i));
               UnityAction<T1,T2,T3> call= System.Delegate.CreateDelegate (typeof(UnityAction<T1,T2,T3>), persistentTarget, methodInfo) as UnityAction<T1,T2,T3>;
               //subject.Where(trg=>trg.Item4==null || trg.Item4==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,T3,GameObject>> (x => call (x.Item1,x.Item2,x.Item3));               AddListener(call);
            }
            
            persistentTargetsAreSubscribed = true;
         }
         
         
         
      }
      
      public void InvokeOnTarget (GameObject target, T1 arg1, T2 arg2, T3 arg3)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            subject.OnNext (Tuple.Create (arg1, arg2, arg3, target));
         }
         
      }
      
      public new void Invoke (T1 arg1, T2 arg2, T3 arg3)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            
            subject.OnNext (Tuple.Create (arg1, arg2, arg3, (GameObject)null));
         }
         
      }
      
      public new void RemoveListener (UnityAction<T1,T2,T3> call)
      {
         if (__disposables.ContainsKey (call)) {
            IDisposable disposable=__disposables [call];
            __disposables.Remove(call);
            
            disposable.Dispose();
            
            Debug.Log("Observer "+call.Method.Name+" on target "+call.Target+" Removed");
            
         }
         
         
      }
      
      public new void RemoveAllListeners ()
      {
         
         foreach (var d in __disposables)
            d.Value.Dispose ();
         
         __disposables.Clear ();
         
      }
      
      public IDisposable Subscribe (UnityAction<T1,T2,T3> call)
      {
         
         //convert UnityAction<T1,T2,T3> to Action<T1,T2,T3>         IDisposable disposable = subject.Where(tgt=>tgt.Item4==null || tgt.Item4==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,T3,GameObject>> (x => call (x.Item1,x.Item2,x.Item3));
         
         __disposables [call] = disposable;
         
         return disposable;
         
      }
      
      
      #region IObservable implementation      public IDisposable Subscribe (IObserver<Tuple<T1,T2,T3,GameObject>> observer)
      {
         //Debug.LogWarning ("Use Subscribe (UnityAction<T1,T2,T3> call)");         return subject.Subscribe<Tuple<T1,T2,T3,GameObject>> (observer.OnNext).AddTo(__disposer);
      }
      #endregion                                                    }
   
   [Serializable]
   public abstract class UnityObservable<T1,T2>:UnityEvent<T1,T2>,IObservable<Tuple<T1,T2,GameObject>>
   {
      
      
      
      
      private Dictionary<UnityAction<T1,T2>,IDisposable> __disposables;
      
      
      private Subject<Tuple<T1,T2,GameObject>> subject;
      private bool persistentTargetsAreSubscribed;
      private CompositeDisposable __disposer;
      
      
      
      //      // Constructors      //      public UnityObservable ():base()
      {
         
         __disposables = new Dictionary<UnityAction<T1,T2>, IDisposable> ();
         
         subject = new Subject<Tuple<T1,T2,GameObject>> ();
         
         __disposer = new CompositeDisposable ();
         
         //num of persistent is 0 here, so why SubscibeToPersistent is called in Invoke      }
      
      public new void AddListener (UnityAction<T1,T2> call)
      {
         Subscribe (call);
      }
      
      void SubscibeToPersistent ()
      {
         //check is some listners are subscribed thru editor         if (!persistentTargetsAreSubscribed && this.GetPersistentEventCount () > 0) {
            //subscribe persistent obeservers to observer            int persistentTargetsNumber = this.GetPersistentEventCount ();
            MethodInfo methodInfo;
            UnityEngine.Object persistentTarget;
            
            
            
            for (int i=0; i<persistentTargetsNumber; i++) {
               
               persistentTarget = this.GetPersistentTarget (i);
               methodInfo = persistentTarget.GetType ().GetMethod (this.GetPersistentMethodName (i));
               UnityAction<T1,T2> call= System.Delegate.CreateDelegate (typeof(UnityAction<T1,T2>), persistentTarget, methodInfo) as UnityAction<T1,T2>;
               //subject.Where(trg=>trg.Item3==null || trg.Item3==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,GameObject>> (x => call (x.Item1,x.Item2));               AddListener(call);
            }
            
            persistentTargetsAreSubscribed = true;
         }
         
         
         
      }
      
      public void InvokeOnTarget (GameObject target, T1 arg1, T2 arg2)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            subject.OnNext (Tuple.Create (arg1, arg2, target));
         }
         
      }
      
      public new void Invoke (T1 arg1, T2 arg2)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            
            subject.OnNext (Tuple.Create (arg1, arg2, (GameObject)null));
         }
         
      }
      
      public new void RemoveListener (UnityAction<T1,T2> call)
      {
         if (__disposables.ContainsKey (call)) {
            IDisposable disposable=__disposables [call];
            __disposables.Remove(call);
            
            disposable.Dispose();
            
            Debug.Log("Observer "+call.Method.Name+" on target "+call.Target+" Removed");
            
         }
         
         
      }
      
      public new void RemoveAllListeners ()
      {
         
         foreach (var d in __disposables)
            d.Value.Dispose ();
         
         __disposables.Clear ();
         
         __disposer.Dispose ();
         
      }
      
      public IDisposable Subscribe (UnityAction<T1,T2> call)
      {
         
         IDisposable disposable= subject.Where(tgt=>tgt.Item3==null || tgt.Item3==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,T2,GameObject>> (x => call (x.Item1,x.Item2));
         
         __disposables [call] = disposable;
         
         return disposable;
         
      }
      
      
      #region IObservable implementation      public IDisposable Subscribe (IObserver<Tuple<T1,T2,GameObject>> observer)
      {
         //Debug.LogWarning ("Use Subscribe (UnityAction<T1,T2> call)");         return subject.Subscribe<Tuple<T1,T2,GameObject>> (observer.OnNext).AddTo(__disposer);
      }
      #endregion                                                    }
   
   
   
   [Serializable]
   public abstract class UnityObservable<T1>:UnityEvent<T1>,IObservable<Tuple<T1,GameObject>>
   {
      
      
      
      
      private Dictionary<UnityAction<T1>,IDisposable> __disposables;
      
      
      private Subject<Tuple<T1,GameObject>> subject;
      private bool persistentTargetsAreSubscribed;
      
      private CompositeDisposable __disposer;
      
      
      //      // Constructors      //      public UnityObservable ():base()
      {
         
         __disposables = new Dictionary<UnityAction<T1>, IDisposable> ();
         
         subject = new Subject<Tuple<T1,GameObject>> ();
         
         __disposer = new CompositeDisposable ();
         
         //num of persistent is 0 here, so why SubscibeToPersistent is called in Invoke      }
      
      public new void AddListener (UnityAction<T1> call)
      {
         Subscribe (call);
      }
      
      void SubscibeToPersistent ()
      {
         //check is some listners are subscribed thru editor         if (!persistentTargetsAreSubscribed && this.GetPersistentEventCount () > 0) {
            //subscribe persistent obeservers to observer            int persistentTargetsNumber = this.GetPersistentEventCount ();
            MethodInfo methodInfo;
            UnityEngine.Object persistentTarget;
            
            
            
            for (int i=0; i<persistentTargetsNumber; i++) {
               
               persistentTarget = this.GetPersistentTarget (i);
               methodInfo = persistentTarget.GetType ().GetMethod (this.GetPersistentMethodName (i));
               UnityAction<T1> call= System.Delegate.CreateDelegate (typeof(UnityAction<T1>), persistentTarget, methodInfo) as UnityAction<T1>;
               //subject.Where(trg=>trg.Item2==null || trg.Item2==(call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,GameObject>> (x => call (x.Item1));               AddListener(call);
            }
            
            persistentTargetsAreSubscribed = true;
         }
         
         
         
      }
      
      public void InvokeOnTarget (GameObject target, T1 arg1)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            subject.OnNext (Tuple.Create (arg1, target));
         }
         
      }
      
      public new void Invoke (T1 arg1)
      {
         
         SubscibeToPersistent ();
         
         if (subject.HasObservers) {
            
            subject.OnNext (Tuple.Create (arg1,(GameObject)null));
         }
         
      }
      
      public new void RemoveListener (UnityAction<T1> call)
      {
         if (__disposables.ContainsKey (call)) {
            IDisposable disposable=__disposables [call];
            __disposables.Remove(call);
            
            disposable.Dispose();
            
            Debug.Log("Observer "+call.Method.Name+" on target "+call.Target+" Removed");
            
         }
         
         
      }
      
      public new void RemoveAllListeners ()
      {
         foreach (var d in __disposables)
            d.Value.Dispose ();
         
         __disposables.Clear ();
         
         //I hope disposer check if something was manually disposed before         __disposer.Dispose ();
         
      }
      
      public IDisposable Subscribe (UnityAction<T1> call)
      {
         
         IDisposable disposable = subject.Where (tgt => tgt.Item2 == null || tgt.Item2 == (call.Target as MonoBehaviour).gameObject).Subscribe<Tuple<T1,GameObject>> (x => call (x.Item1));
         
         __disposables[call] = disposable;
         
         return disposable;
         
      }
      
      #region IObservable implementation      public IDisposable Subscribe (IObserver<Tuple<T1,GameObject>> observer)
      {
         //Debug.LogWarning ("Use Subscribe (UnityAction<T1> call)");         return subject.Subscribe<Tuple<T1,GameObject>> (observer.OnNext).AddTo(__disposer);
      }
      #endregion                                                    }
   
   
   
   
   
   
   





}
#endif

No comments:

Post a Comment