Quick Rx question

Hi, I have a two drag handle with which you can control camera rotation and height respectively. And I wrote following code to handle drag events using Rx, but I’m wondering if there could be a way to eliminate that ugly hack with ‘_dragFinished’ flag:

Action<IPointerDragAware, Vector3> register = (button, direction) =>
{
    var events = button.OnDrag.Select(v => v.position);

    Observable.Zip(events, events.Skip(1))
        .Select(v => v[1] - v[0])
        .Select(v => Normalize(v, direction))
        .Subscribe(OnDrag)
        .AddTo(this);

    // I want to remove this hack.
    button.OnDragEnd
        .Subscribe(_ => _dragFinished = true)
        .AddTo(this);
};

register(RotateButton, Vector3.right);
register(MoveButton, Vector3.up);

private void OnDrag(Vector2 delta)
{
    // And this too...
    if (_dragFinished)
    {
        _dragFinished = false;
        return;
    }

    // Code to move camera around follows...
}

The reason why I needed the '_dragFinished` hack was to prevent a spike in camera movement when I drag one button then drag the other, because the current code just calculates pointer deltas without considering if they are from the same continuous drag action or not.

I tried comparing timestamps, but put ‘.Where’ filter to the zipped event stream made it hang. Probably I’m doing something wrong, but I’m still new to Rx API so I’m pretty much stuck right now.

Thanks in advance!

Anyone, please?

I’m totally unfamiliar with this API and its implicit calling sequences and assumptions, so the only thing I would do is replace some of your callbacks with compound statements (i.e., do the thing you were going to do but also spit out a Debug.Log() statement) in order to give you some more insight in to when and where they are happening, what the state of various things are at each of those points, etc.

You may discover something about the API that reveals a better way to use it.

I’m not familiar with Rx…could you not have used Unity’s drag interfaces?

@Kurt-Dekker It’s Unity version of Rx.NET library called UniRx which I found to be quite useful in handling events and other async operations. I tried to put some logging statements as you suggested, but all I found was I shouldn’t put ‘.Where’ filter to a zipped stream, as it resulted in a hang.

@Brathnann The OnDrag part is from my own UI library and I think it works fine in itself and so does UniRx.

It’s just that I want to find some clever way to do away with the member flag which I suspect to be possible if I was more experienced with Rx. Unity’s drag interface is fine, but I don’t think it to be possible to write such an event handler without adding couple of member properties.

Anyway, thanks for the advice, both of you! :slight_smile:

Any decent framework should have an OnDragBegin, OnDrag and OnDragEnd. If your framework doesn’t have a specific OnDragEnd, I suggest you create one or use a different framework.

Sorry if I haven’t been clear with my question. It already has OnDragBegin, and OnDragEnd, but what I really want to do is to use them in a ‘Rx way’ - that is, without using any global state variables.

Finally, I’ve made it work somehow. According to an answer from StackOverflow, it can be done by using GroupByUntil method which UniRx doesn’t support at the moment.

So I ended up with this alternative, which I post here for others who might be interested:

var events = button.OnDrag.Select(v => v.position);

Observable
    .Zip(events, events.Skip(1))
    .TakeUntil(button.OnDragEnd)
    .RepeatSafe()
    .Select(i => Normalize(i[1] - i[0], direction))
    .Subscribe(MoveCamera)
    .AddTo(this);