UniRx - Reactive Extensions for Unity

UniRx(Reactive Extensions for Unity) is re-implementation of .NET Reactive Extensions.
Welcome to the reactive game architecture!
This library is free and opensource on GitHub.

Now available on the Asset Store(FREE)

Presentation(Why? What? How?)

Source and Issue tracking.
https://github.com/neuecc/UniRx/

Please ask me any questions in this thread.

11 Likes

This looks really good! Thanks!

Thank you for your wonderful solution.

But, when I examined to run uniRX sample App on iOS,
ExecutionEngineException error occurred.
Maybe, I think it is originated from Schedule function of CurrentThreadScheduler.

Does it means that uniRX is not compatible with iOS totally?

Thank you for trying.

sorry, Iā€™m unchecked iOS on latest version.
Iā€™ll check and fix itā€™s problmen as soon as possible.

Until then, if problem is only CurrentThreadScheduler,
modify CurrentThreadScheduler.cs
line:11 public static readonly IScheduler CurrentThread=new CurrentThreadScheduler();
to
public static readonly IScheduler CurrentThread=new ImmediateScheduler();

I resolved ExecutionEngineException on iOS AOT.
There are fixed code.
https://github.com/neuecc/UniRx/pull/5

This update will be available this week.
Please wait a moment.

Thanks.

This is brilliant! Only thing Iā€™m afraid of is performance hit. Which version of Rx are you basing this on?

Btw, Iā€™m using it to build an MVVM framework, kinda like ReactiveUI, just for Unity. Will open source when done!

Thank you.

UniRx is basically based on Rx1.1.
But I have plan backport performance improvements of 2.0.

Thatā€™s great news! <MVVM Framework
FYI, I made Rx, UI and MVVM library for .NET before.
https://reactiveproperty.codeplex.com/
Itā€™s another approach of ReactiveUI.
I hope this will help.

1 Like

ā€˜UniRx - Reactive Extensions for Unity - 4.3ā€™ has been accepted!

changed log:

Fix iOS AOT Safe totally
MainThreadScheduleā€™s schedule(dueTime) acquired time accuracy
MainThreadDispatcher avoid deadlock at recursive call
Add Observable.Buffer(count, skip)
Change OfType, Cast definition
Change IScheduler definition
Add AotSafe Utilities(AsSafeEnumerable, WrapValueToClass)
Change Unit, TimeInterval and Timestamped to class(for iOS AOT)
Add Examples/Sample7_OrchestratIEnumerator.cs

wellā€¦ hereā€™s a hidden gem!

[edit]
going through the source, and all I can say is WOW. I was looking for a way to implement the observer design pattern, and this has so much more than I was initially thinkingā€¦ very impressed.

Iā€™m curious about your example, the behavior and ability to get a data stream back to the main thread (directly modifying New Sprite text property) is interesting. However, Iā€™m having trouble wrapping my head around how I may change that so the ā€œNew Spriteā€ could be an Observer of the ā€œClickerā€ (NewBehaviourScript) Observableā€¦ so that when it has completed itsā€™ get of the data stream, it is pushed to ā€œNew Spriteā€ and it does whatever it wants to with it.

I think once I get that concept better, I would be able to begin using Rx to solve some of my more complex ideas.

Do you say about UniRx/Assets/ObjectTest/NewBehaviourScript.cs at 2936d268171a78280d2d27485df5541ecc5060e7 Ā· neuecc/UniRx Ā· GitHub ?
Sorry, I canā€™t understand your question.
If attach ObservableMonoBehaviour then all monobehavoiour events can subscribe.
Observable.EveryUpdate/EveryFixedUpdate is environment monobehaviour events.
Itā€™s useful for catch Input Events.

yes, that is what I was referencing. I think I understand, Iā€™ll have to start working with the library to learn moreā€¦ I think I have done all the reading I can to get a grasp, now it is just practice.

I do have one concern, related to lines:

        var parallel = Observable.WhenAll(
                ObservableWWW.Get("http://google.com/"),
                ObservableWWW.Get("http://bing.com/"),
                ObservableWWW.Get("http://yahoo.com/"));

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

It seems the mainthread gets blocked causing the entire app to freeze until it completes LogStringToConsole. I enabled the code to permit the quad to spin in Update(), and it is really noticeable. Here is capture of performance:

The ā€œLogStringToConsoleā€ takes 1.2 seconds to complete with a GC of 4.1MB (which is expected as it is all 3 websites), I am guessing that this is because it is happening on the main thread. Which I wouldnā€™t expect to have such a huge performance hit to do.

I think it could be having something to do with how large the output strings are in the returned array as I get errors when trying to view the results in the console. These are the following errors:

count <= std::numeric_limits::max()
maxVertices < 65536 && maxIndices < 65536*3

I guess there is no way to task a background thread to output to the console is there?

It is Debug.Logā€™s problem and performance.
If you write

Debug.Log(new String(ā€˜aā€™, 20000));

then you can see same error.

here is clean sample for UniRx and Observable.WhenAll.

// check no freeze(picture moves left,right)
public override void Start()
{
    UpdateAsObservable()
        .Select(_ => this.transform.position)
        .Scan(true, (isMoveLeft, p) =>
                (isMoveLeft && p.x <= -2) ? false
            : (!isMoveLeft && p.x >= 6) ? true
            : isMoveLeft)
        .Subscribe(isMoveLeft =>
        {
            var p = this.transform.position;
            this.transform.position = new Vector2(p.x + (0.1f) * (isMoveLeft ? -1 : 1), p.y);
        });
}
public override void OnMouseDown()
{
    var parallel = Observable.WhenAll(
            ObservableWWW.Get("http://google.com/"),
            ObservableWWW.Get("http://www.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
    });
    Debug.Log("start!"); // no freeze, always runnning picture
}

Thanks, I didnā€™t know Debug.Log and long string cause problem.
Iā€™ll fix sample and ReadMe.

I didnā€™t know this either till nowā€¦ and thank you!

Hi, Today ā€˜UniRx - Reactive Extensions for Unity - 4.4ā€™ has been accepted!
This release contains big performance improvements.

changed log:

Add : Observable.FromEvent
Add : Observable.Merge Overload(params IObservable[TSource][ ] / IEnumerable[IObserable[TSource]])
Add : Observable.Buffer Overload(timeSpan, timeShift)
Add : IDisposable.AddTo
Add : ObservableLogger(UniRx.Diagnostics)
Add : Observable.StartAsCoroutine
Add : MainThreadDispatcher.RegisterUnhandledExceptionCallback
Add : Examples/Sample08, Sample09, Sample10, Sample11
Performance Improvment : Subject[T], OnNext avoids copy and lock
Performance Improvment : MainThreadDispatcher, avoids copy on every update
Change : Observable.ToCoroutine ā†’ ToAwaitableEnumerator
Fix : ObservableMonoBehaviourā€™s OnTriggerStay2D doesnā€™t pass Collider2D

1 Like

Hey!

Did you see this smooth LINQ replacement in the comment section of your feedback Post? Does this help you in any way?
http://feedback.unity3d.com/suggestions/upgrade-enumerable-dot-cs-for-avoid-aot-problem-of-linq-average-etc-dot-dot-dot

Iā€™m using copy monoā€™s Enumerable.cs(and rename namespace to System.LinqEx) for LINQ to Objects.

Clever!

Hi, Today ā€˜UniRx - Reactive Extensions for Unity - 4.5ā€™ has been accepted!
Big Change is defult timebased scheduler to MainThreadScheduler.
I think this to be good choice for Unity.

And new feature, UnityEvent.AsObservable for uGUI(Unity 4.6 Beta)
for example, button.onClick.AsObservable().Subscribe();


Add : ObservableWWW Overload(byte[ ] postData)
Add : Observable.Buffer Overload(windowBoundaries)
Add : LazyTask - yieldable value container like Task
Add : Observable.StartWith
Add : Observable.Distinct
Add : Observable.DelaySubscription
Add : UnityEvent.AsObservable - only for Unity 4.6 uGUI
Add : UniRx.UI.ObserveEveryValueChanged(Extension Method)
Add : RefCountDisposable
Add : Scheduler.MainThreadIgnoreTimeScale - difference with MainThreadScheduler, not follow Unity Timescale
Add : Scheduler.DefaultSchedulers - can configure default scheduler for any operation
Fix : DistinctUntilChanged iOS AOT issue.
Fix : Remove IObservable/IObserver/ISubjectā€™s covariance/contravariance(Unity is not support)
Fix : UnityDebugSink throws exception when called from other thread
Fix : Remove compiler error for Windows Phone 8/Windows Store App
Breaking Change : MainThreadSchduler follow Unity Timescale
Breaking Change : All Timebased operatorā€™s default scheduler changed to MainThreadScheduler
Breaking Change : Remove TypedMonoBehaviour.OnGUI for performance improvment
Performance Improvment : AsyncSubject[T]
Performance Improvment : CurrentThreadScheduler
Performance Improvment : MainThreadScheduler

Hi, i have a problem when i try to create a Singleton class to handle a double click, and subscribe the observable from other class. While clicking or double clicking i got the same result.

public class TapHandler: ObservableMonoBehaviour
{
    private IObservable<Unit> mouseDown;
 
    private static TapHandler instance;
    public static TapHandler Instance
    {
      get {return instance ?? (instance = new GameObject("Manager").AddComponent<TapHandler>());}
    }

    public override void Awake()
    {
      this.mouseDown = this.UpdateAsObservable().Where(_ => Input.GetMouseButton(0));
      base.Awake();
    }
 
   public IObservable<IList<Unit>> DoubleClick()
   {
      return this.mouseDown
      .Buffer(this.mouseDown.Throttle(TimeSpan.FromMilliseconds(200)))
      .Where(clx => clx.Count >= 2);
    }
}

Subscribe from other class.

public class TrackerSenseBehaviour : ObservableMonoBehaviour {
    public override void Awake(){
        TapHandler.Instance.DoubleClick().Subscribe(o => Debug.Log("Double click"));

        base.Awake();
    }
}

Did i missed something, Rx pattern is relatively new to me. Iā€™m still on the blues of itā€¦