neuecc / UniRx

Reactive Extensions for Unity
MIT License
7.01k stars 895 forks source link

Using the new Input System with UniRx? #481

Open taylorstine opened 3 years ago

taylorstine commented 3 years ago

I would like to use UniRx with Unity's new Input System. Maybe the API would look something like this:

Observable.FromInput(actions.Player.Fire.performed).Subscribe(context=>{});

Is there a suggested way to do this?

elhimp commented 3 years ago

I'm binding my action with InputActionType.PassThrough param and then just have this little piece:

public static partial class Extensions {
    public static IObservable<InputAction.CallbackContext> ToObservable(this InputAction action) =>
        Observable.FromEvent<InputAction.CallbackContext>(
            h => action.performed += h,
            h => action.performed -= h
        );
}

You can add type param there (make it generic) and read value accordingly, or just use it separately, because single responsibility and you know. So, let's say you need bool out of some 1D axis:

myAction.ToObservalbe()
.Select(context => context.ReadValue<float>() != 0)
achimmihca commented 3 years ago

I had the same idea (although I would call it PerformedAsObservable). However, my problem is that the removeHandler is never called: inputAction.performed -= h. I guess this makes sense because it is not bound to any IDisposable.

Can I tell UniRx somehow to call the removeHandler on scene change?

achimmihca commented 3 years ago

Well, I guess one solution is to call the removeHandler in OnDestroy of some GameObject. I already have an InputManager in every scene. Thus, my solution is

public static class InputActionExtensions
{
    public static IObservable<InputAction.CallbackContext> PerformedAsObservable(this InputAction inputAction)
    {
        Action<Action<InputAction.CallbackContext>> removeHandler = h => inputAction.performed -= h;
        Action<Action<InputAction.CallbackContext>> addHandler = h =>
        {
            inputAction.performed += h;
            InputManager.Instance.OnDestroyAsObservable().Subscribe(_ => removeHandler(h));
        };
        return Observable.FromEvent<InputAction.CallbackContext>(addHandler, removeHandler);
    }
}
scho commented 3 years ago

There is a helper method for removing the handler: UniRx.DisposableExtensions.AddTo. You use it like this:

protected void Start()
{
    SomeModel.OnEventObservable().Subscribe(value => ...).AddTo(this);
}

So when the Monobehaviour is destroyed, the handler will be removed.

aballano commented 2 years ago

Thank you folks for this post, it helped me achieve the same thing but slightly modified, in case someone wants to read it I just wrote an answer to a post in Reddit https://www.reddit.com/r/Unity3D/comments/qeugyw/somebody_tried_use_unirx_with_input_system/

anavalaka commented 1 year ago

would love to have an out-of-the-box way to do this via uniRX tho

achimmihca commented 1 year ago

I created a lib with my solution: https://github.com/achimmihca/PrimeInputActions

It is Unity's InputActions with UniRx and a priority queue.