動機
例えばInputSystemをGenerate C# Classで利用する場合、基本的には各Actionのイベントハンドラとしてメソッドを登録することになると思います。しかしC# eventは登録したハンドラを自分で解除する必要があり面倒です。
using System;
using UnityEngine.InputSystem;
public class PlayerInputHandler : IDisposable
{
readonly PlayerInputActions inputActions;
public PlayerInputHandler(PlayerInputActions inputActions)
{
this.inputActions = inputActions;
// ハンドラを登録
inputActions.Player.Jump.performed += OnJump;
inputActions.Player.Move.performed += OnMove;
inputActions.Player.Enable();
}
void OnJump(InputAction.CallbackContext context)
{
// Jump の入力処理
}
void OnMove(InputAction.CallbackContext context)
{
// Move の入力処理
}
public void Dispose()
{
// ハンドラを解除
// これを忘れるとバグの原因になる
inputActions.Player.Jump.performed -= OnJump;
inputActions.Player.Move.performed -= OnMove;
inputActions.Player.Disable();
}
}
方法
そこで、R3を用いてInputSystemのeventをObservableに変換することで一気に扱いやすくなります。具体的には、R3.Observable.FromEvent<T> を用いてC# eventをObservableに変換します。
例えば、InputAction.performed を変換するこのようなメソッドを用意します。
static Observable<InputAction.CallbackContext> ObservePerformed(InputAction action)
{
return Observable.FromEvent<InputAction.CallbackContext>(
h => action.performed += h,
h => action.performed -= h);
}
これを使うことで購読を CompositeDisposable にまとめることができ、解除忘れのリスクを大幅に減らすことができます。また、元々R3を使っていた場合は書き方をR3に統一できて綺麗です。R3のオペレータを使用することができるというメリットもあるので、特に理由がない限りこの方法を使うのがおすすめです。
using System;
using R3;
using UnityEngine.InputSystem;
public class PlayerInputHandler : IDisposable
{
readonly PlayerInputActions inputActions;
readonly CompositeDisposable disposables = new();
public PlayerInputHandler(PlayerInputActions inputActions)
{
this.inputActions = inputActions;
// Observableとして購読できる
ObservePerformed(inputActions.Player.Jump)
.Subscribe(OnJump)
.AddTo(disposables);
ObservePerformed(inputActions.Player.Move)
.Subscribe(OnMove)
.AddTo(disposables);
inputActions.Player.Enable();
}
void OnJump(InputAction.CallbackContext context)
{
// Jump の入力処理
}
void OnMove(InputAction.CallbackContext context)
{
// Move の入力処理
}
public void Dispose()
{
// まとめて購読解除できる
disposables.Dispose();
inputActions.Player.Disable();
}
static Observable<InputAction.CallbackContext> ObservePerformed(InputAction action)
{
return Observable.FromEvent<InputAction.CallbackContext>(
h => action.performed += h,
h => action.performed -= h);
}
}