明日の自分へ

主にUnityのゲーム開発に関することを呟いていく予定

UniRxでマウス操作を実装してみる

UniRxの勉強のためにマウス操作を実装してみた。
スワイプとか結構面倒なイメージがあったけど、意外と簡単に書けた気がする。

やったこと

・左右のシングルクリックでオブジェクトの色を変える
・左右の同時クリックでオブジェクトの色を元に戻す
・左右のマウスボタンホールドで、収縮を繰り返す
・左クリックのスワイプでオブジェクトを移動させる

動作イメージ

f:id:e684-creaim:20210219020607g:plain

左クリックでオブジェクトを黄色くします。
左クリックのホールドで、縮小⇔復元を繰り返します。

右クリックでオブジェクトを水色っぽくします。
右クリックのホールドで、拡大⇔復元を繰り返します。

左クリックでスワイプすると、距離に応じてオブジェクトが移動します。

左右の同時クリックでオブジェクトを元の色に戻します。 

ソースコード

On***はオブザーバパターンのObservableで、メッセージを発行する側を想定した実装です。
Do***はオブザーバパターンのObserverで、メッセージを購読して処理をする側を想定した実装です。

左右同時クリックと、シングルクリックの排他は今回実装していません。

using System;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
using DG.Tweening;

public class PlayerController : MonoBehaviour
{
    private float bounceScale = 1.0f; // 収縮するときの大きさ
    private int sign = 1; // 拡大⇔縮小を反転させる符号
    private float cameraScale = 5f / 540f; // カメラ領域とUnityの単位とを合わせるScale
    private Color leftClickColor = Color.yellow; // 左クリックしたときの色
    private Color rightClickColor = Color.cyan; // 右クリックしたときの色
    private Color originalColor = Color.white; // 同時クリックしたときの色(元の色)


    // Start is called before the first frame update
    void Start()
    {
        // 左クリック処理の登録
        OnLeftMouseDownObservable().Subscribe(_ => DoLeftClick()).AddTo(this);

        // 右クリック処理の登録
        OnRightMouseDownObservable().Subscribe(_ => DoRightClick()).AddTo(this);

        // 左右同時クリック処理の登録
        Observable
            .Merge(OnLeftMouseDownObservable().Select(_ => KeyCode.Mouse0),
                   OnRightMouseDownObservable().Select(_ => KeyCode.Mouse1))
            .Buffer(timeSpan: TimeSpan.FromSeconds(0.1f)) // 100msの間クリックを受け付け
            .Where(x => x.Count == 2 && x[0] != x[1]) // 二回クリック、かつ左右クリック
            .Subscribe(_ => DoSimultaneousClick())
            .AddTo(this);

        // 左マウスホールド処理の登録
        OnLeftMouseHoldObservable().Subscribe(_ => DoLeftMouseHold()).AddTo(this);

        // 右マウスホールド処理の登録
        OnRightMouseHoldObservable().Subscribe(_ => DoRightMouseHold()).AddTo(this);

        // スワイプ処理の登録
        OnLeftMouseDownObservable().Select(_ => Input.mousePosition).Take(1)
            .Concat(OnLeftMouseUpObservable().Select(_ => Input.mousePosition).Take(1))
            .Aggregate((pre, cur) => cur - pre)
            .RepeatUntilDestroy(this)
            .Subscribe(x => DoSwipe(x))
            .AddTo(this);

    }

    /// <summary>
    /// 左クリック(押す)をイベント発行するObservable
    /// </summary>
    private IObservable<Unit> OnLeftMouseDownObservable()
    {
        return this.UpdateAsObservable().Where(_ => Input.GetMouseButtonDown(0));
    }

    /// <summary>
    /// 左クリック(離す)をイベント発行するObservable
    /// </summary>
    private IObservable<Unit> OnLeftMouseUpObservable()
    {
        return this.UpdateAsObservable().Where(_ => Input.GetMouseButtonUp(0));
    }

    /// <summary>
    /// 右クリックをイベント発行するObservable
    /// </summary>
    private IObservable<Unit> OnRightMouseDownObservable()
    {
        return this.UpdateAsObservable().Where(_ => Input.GetMouseButtonDown(1));
    }

    /// <summary>
    /// マウス左ホールドイベントを発行するObservable
    /// </summary>
    private IObservable<Unit> OnLeftMouseHoldObservable()
    {
        return this.UpdateAsObservable().Where(_ => Input.GetMouseButton(0));
    }

    /// <summary>
    /// マウス右ホールドイベントを発行するObservable
    /// </summary>
    private IObservable<Unit> OnRightMouseHoldObservable()
    {
        return this.UpdateAsObservable().Where(_ => Input.GetMouseButton(1));
    }


    /// <summary>
    /// 左クリック処理
    /// </summary>
    private void DoLeftClick()
    {
        GetComponent<SpriteRenderer>().color = leftClickColor;
    }

    /// <summary>
    /// 右クリック処理
    /// </summary>
    private void DoRightClick()
    {
        GetComponent<SpriteRenderer>().color = rightClickColor;
    }

    /// <summary>
    /// 同時クリック
    /// </summary>
    private void DoSimultaneousClick()
    {
        GetComponent<SpriteRenderer>().color = originalColor;
    }

    private void DoLeftMouseHold()
    {
        if (bounceScale < 0.8f) sign = 1;
        else if (bounceScale >= 1.0f) sign = -1;

        bounceScale += Time.deltaTime * sign;
        bounceScale = bounceScale > 1.0f ? 1.0f : bounceScale;

        transform.localScale = Vector3.one * bounceScale;
    }

    private void DoRightMouseHold()
    {
        if (bounceScale <= 1.0f) sign = 1;
        else if (bounceScale > 1.2f) sign = -1;

        bounceScale += Time.deltaTime * sign;
        bounceScale = bounceScale < 1.0f ? 1.0f : bounceScale;

        transform.localScale = Vector3.one * bounceScale;
    }

    private void DoSwipe(Vector3 move)
    {
        transform.DOMove(move * cameraScale, 0.5f)
            .SetRelative(true)
            .SetEase(Ease.OutQuint);
    }
}