DoozyUIを使ってボタンの長押し中に定期的にイベントを発行する

# はじめに

この記事では次のAssetを利用しています。

* [UniRx](https://github.com/neuecc/UniRx)
* [DoozyUI](https://assetstore.unity.com/packages/tools/gui/doozyui-complete-ui-management-system-138361)

# DoozyUIで長押しを検知する

DoozyUIを使うと、かなり簡単にボタンの長押しを検知することができます。
これを応用することで、複雑そうな挙動もかなり簡単に実現することができます。

![UpDown.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/f4aeb0df-316f-5442-2a1f-a65cb62b56ea.gif)
（長押し中は定期的にイベント発行を行う例）


## 長押しイベントをまず取得する

DoozyUIで「長押し」が検知できるようにしてみましょう。

やり方としては次の2ステップ。

#### 1. InspectorViewからOnLongClickを有効にする

`UIButton`コンポーネントの`OnLongClick`の左側のトグルを押して有効化します。
これを有効にしないとスクリプトからイベントが取得できません。

![OnLongClick.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/6aeab86e-894c-eeab-8296-3bf9e484a958.png)


なお、`Register Interval`の部分が長押し判定されるまでの秒数です。

#### 2. スクリプトからイベントを取得する

こんな感じでOK。

```cs
using Doozy.Engine.UI;
using UniRx;
using UnityEngine;

public class LongClickSample : MonoBehaviour
{
    /// <summary>
    /// DoozyUIのUIButtonコンポーネント
    /// </summary>
    [SerializeField] private UIButton _uiButton;

    void Start()
    {
        // 長押し
        _uiButton
            .OnLongClick
            .OnTrigger
            .Event
            .AsObservable()
            .Subscribe(_ =>
            {
                Debug.Log("OnLongClick!");
            }).AddTo(this);
        
        // uGUIのButtonはプロパティからアクセス可能
        _uiButton.Button.OnClickAsObservable().Subscribe(_ => { }).AddTo(this);
    }
}
```

## 拡張メソッド経由にする

`Observable`化するのがちょっと冗長なので、次のような拡張メソッドを用意する。

```cs
using System;
using Doozy.Engine.UI;
using UniRx;

namespace Samples.Utils
{
    public static class UIButtonBehaviorExtension
    {
        public static IObservable<Unit> AsObservable(this UIButtonBehavior b)
        {
            return b.OnTrigger.Event.AsObservable();
        }
    }
}
```

そうすると拡張メソッド経由で呼び出せるのでちょっと記述が減る。

```cs
using Doozy.Engine.UI;
using Samples.Utils;
using UniRx;
using UnityEngine;

public class LongClickSample : MonoBehaviour
{
    /// <summary>
    /// DoozyUIのUIButtonコンポーネント
    /// </summary>
    [SerializeField] private UIButton _uiButton;

    void Start()
    {
        // 長押し
        _uiButton
            .OnLongClick
            .AsObservable() //拡張メソッド
            .Subscribe(_ => { Debug.Log("OnLongClick!"); }).AddTo(this);
    }
}
```

# 長押しをしている間、一定間隔でイベントを発行させる

ボタンを押しっぱなしにしている間は一定間隔でイベントを発行できると便利です。

![UpDown.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/f4aeb0df-316f-5442-2a1f-a65cb62b56ea.gif)

この機能があれば、たとえば「長押しをしている間は連続で数字が変化する」といった機能が簡単に実装できるようになります。


## 作り方

#### 1. InspectorViewからOnPointerUpを有効にする

ボタンを離したときにイベントを止める必要があるので、OnPointerUpイベントが発行されるようにしておきます。

![OnPointerUp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/b37caba2-1910-394d-7780-d61defa11dac.png)



#### 2. 拡張メソッド用意する

次のような拡張メソッドを用意します。

```cs
using System;
using Doozy.Engine.UI;
using UniRx;

namespace Samples.Utils
{
    public static class UIButtonExtension
    {
        public static IObservable<Unit> OnLongPressAsObservable(this UIButton b, TimeSpan timeSpan)
        {
            var longPress = b.OnLongClick.AsObservable();
            var up = b.OnPointerUp.AsObservable();

            return longPress.Take(1)
                .ContinueWith(_ => Observable.Interval(timeSpan).TakeUntil(up))
                .AsUnitObservable()
                .RepeatSafe();
        }
    }
}
```

#### 3. OnLongPressAsObservableを使う

あとは拡張メソッド経由で`Observable`を取得すればOKです。

```cs
using System;
using Doozy.Engine.UI;
using Samples.Utils;
using TMPro;
using UniRx;
using UnityEngine;

public class UpDownSample : MonoBehaviour
{
    [SerializeField] private UIButton _upButton;
    [SerializeField] private UIButton _downButton;
    [SerializeField] private TextMeshProUGUI _text;

    private IntReactiveProperty Count = new IntReactiveProperty(0);

    private void Start()
    {
        // 変化した数値をTextに反映
        Count.Subscribe(x => _text.text = x.ToString()).AddTo(this);
        
        //----------------------------
        
        // 長押しのUp！！！
        _upButton
            // 拡張メソッド, 引数で長押し中に発行されるイベントの間隔を指定できる
            .OnLongPressAsObservable(TimeSpan.FromMilliseconds(100))
            .Subscribe(_ => Count.Value++).AddTo(this);
        
        // 長押しのDown！！！
        _downButton
            // 拡張メソッド, 引数で長押し中に発行されるイベントの間隔を指定できる
            .OnLongPressAsObservable(TimeSpan.FromMilliseconds(100))
            .Subscribe(_ => Count.Value--).AddTo(this);

        // ----------------------------
        
        // 単発 Up
        _upButton.Button.OnClickAsObservable()
            .Subscribe(_ => Count.Value++).AddTo(this);

        // 単発 Down
        _downButton.Button.OnClickAsObservable()
            .Subscribe(_ => Count.Value--).AddTo(this);
    }
}
```

# まとめ

Doozy UIを使うとかなり簡単に各種イベントが取得できるようになります。
そこにUniRxを組み合わせると、だいたいのことは少ないコード量で実現できるのでいいですね。