UniRx + MessagePipe で、マウス操作をメッセージ化する


解説

  1. マウス操作をメッセージ化
    IPublisher <- マウスクリック時にMouseEventを通知 ※マウスクリックについては省略しています
    IPublisher <- マウスドラッグ中にDragEventを通知

  2. ドラッグ中の間、UpdateAsObservable から Drag 先の座標をIPublisher へ流す

        // ドラッグ中マウス座標をストリームし続ける
        mouseDrag = this.UpdateAsObservable ()
            .SkipUntil (mouseDown)
            .TakeUntil (mouseUp)
            .Repeat ()
            .Select (_ => { return GetMouseEvent (); });
    
        // 購読して通知させる
        // .AddTo(this) をしない理由は後述
        dragDisposable = mouseDrag.Subscribe (d => {
            drag.Publish (new DragEvent {
                position = d.position, time = d.time
            });
        });
    
  3. AddTo() をしない理由について

Repeatをしているため、依存しているObservableが存在していることが影響しています。

AddTo(this) してもいいんですが、破棄される際に関係するObservable を確認できず 必ずエラーを起こします。
そのため明示的に OnDestroy() で事前に破棄させる手順を踏んでいます。

今回は同一のオブジェクト内で完結しているのでわかりやすいのですが、Repeatを持ったObservableを公開する場合注意が必要です。

ソース

public class MouseEventManager : MonoBehaviour {

    [Inject] private IPublisher<MouseEvent> mouse { get; set; }

    [Inject] private IPublisher<DragEvent> drag { get; set; }

    private IObservable<MouseEvent> mouseDown { get; set; }
    private IObservable<MouseEvent> mouseUp { get; set; }
    private IObservable<MouseEvent> mouseDrag { get; set; }

    private Camera mainCamera;
    private IDisposable dragDisposable;

    private void Start () {

        mainCamera = Camera.main;

        // Stream の作成
        mouseDown = this.UpdateAsObservable ()
            .Where (_ => Input.GetMouseButtonDown (0))
            .Select (_ => { return GetMouseEvent (); });

        mouseUp = this.UpdateAsObservable ()
            .Where (_ => Input.GetMouseButtonUp (0))
            .Select (_ => { return GetMouseEvent (); });

        // ドラッグ中マウス座標をストリームし続ける
        mouseDrag = this.UpdateAsObservable ()
            .SkipUntil (mouseDown)
            .TakeUntil (mouseUp)
            .Repeat ()
            .Select (_ => { return GetMouseEvent (); });

        // SubScribe
        mouseDown.Subscribe (_ => {
            mouse.Publish (GetMouseEvent ());
        }).AddTo (this);

        dragDisposable = mouseDrag.Subscribe (d => {
            drag.Publish (new DragEvent {
                position = d.position, time = d.time
            });
        });

    }
    private MouseEvent GetMouseEvent () {
        var p = new Vector3 (
            Input.mousePosition.x,
            Input.mousePosition.y,
            12
        );

        return new MouseEvent {
            position = mainCamera.ScreenToWorldPoint (p), time = Time.time
        };
    }

    private void OnDestroy () {
        dragDisposable?.Dispose ();
    }
}

public class MouseEvent : IMouseEvent {
    public Vector3 position { get; set; }
    public float time { get; set; }
}

// MonoBehavior に取り付けて LifetimeScope へ登録するだけでマウスドラッグ先にくっつく
public class Player : MonoBehaviour {

    [Inject] private ISubscriber<DragEvent> drag { get; set; }

    void Start () {
        drag.Subscribe (d => {
            this.transform.position = d.position;
        });
    }

}

記事について

ソースに関しては、抜粋のため動作しない可能性があります。
コメント、補足大歓迎です。

次回、フリック入力をメッセージ化するに続きます。
少し予告と違いますが、更新しました

参考にさせていただいた記事