コルーチンの中でボタンのクリックを待つ


## ボタンがひとつの場合

```csharp:
    [SerializeField]
    private Button _button1;

    void Start()
    {
        StartCoroutine(WaitButtonClick(_button1));
    }

    IEnumerator WaitButtonClick(Button button)
    {
        yield return button.OnClickAsObservable().First().ToYieldInstruction();
        Debug.LogFormat("{0} がクリックされました", button.name);
    }
```


## ボタンがふたつの場合

どちらのボタンが押されたのかも取得できるようにします。

```csharp:
    [SerializeField]
    private Button _button1;

    [SerializeField]
    private Button _button2;

    void Start()
    {
        StartCoroutine(WaitButtonClick(_button1, _button2));
    }

    IEnumerator WaitButtonClick(Button button1, Button button2)
    {
        var s = Observable.Merge(button1.OnClickAsObservable().Select(_ => button1),
                                 button2.OnClickAsObservable().Select(_ => button2));
        var y = s.First().ToYieldInstruction();
        yield return y;
        Debug.LogFormat("{0} がクリックされました", y.Result.name);
    }
```

## ボタンが 3 つ以上の場合

```csharp:
    [SerializeField]
    private Button _button1;

    [SerializeField]
    private Button _button2;

    [SerializeField]
    private Button _button3;

    void Start()
    {    
        StartCoroutine(WaitButtonClick(_button1, _button2, _button3));
    }

    IEnumerator WaitButtonClick(params Button[] buttons)
    {
        var y = buttons.Select(b => b.OnClickAsObservable().Select(_ => b))
            .Aggregate((a, b) => a.Merge(b))
            .First()
            .ToYieldInstruction();
        yield return y;                                                        
        Debug.LogFormat("{0} がクリックされました", y.Result.name);
    }
```

## タイムアウトを設けてキャンセルできるようにする

`Timeout` メソッドを使うと、タイムアウトするまでの時間が設定できます。

```csharp
IEnumerator WaitButtonClick(int timeoutSeconds, params Button[] buttons)
{
    var y = buttons.Select(b => b.OnClickAsObservable().Select(_ => b))
        .Aggregate((a, b) => a.Merge(b))
        .Timeout(TimeSpan.FromSeconds(timeoutSeconds)) // timeoutSeconds 秒でタイムアウト
        .First()
        .ToYieldInstruction(throwOnError: false);
    yield return y;

    if (y.HasError)
    {
        if (y.Error is TimeoutException)
        {
            Debug.LogFormat("タイムアウトしました");
        }
    }
    else
    {
        Debug.LogFormat("{0} がクリックされました", y.Result.name);
    }
}
```

## `ToYieldInstruction` の引数に `CancellationToken` を渡して、外部からキャンセルできるようにする

```csharp
class Canceller : ICancelable
{
    private bool _isDisposed;

    public bool IsDisposed { get { return _isDisposed; } }

    public void Dispose()
    {
        _isDisposed = true;
    }
}

private void Start()
{
    var canceller = new Canceller();
    // _button0 をクリックしたらキャンセル
    _button0.onClick.AddListener(() => canceller.Dispose());

    StartCoroutine(WaitButtonClick(new CancellationToken(canceller), _button1, _button2, _button3));
}

IEnumerator WaitButtonClick(CancellationToken cancel, params Button[] buttons)
{
    var y = buttons.Select(b => b.OnClickAsObservable().Select(_ => b))
        .Aggregate((a, b) => a.Merge(b))
        .First()
        .ToYieldInstruction(cancel);
    yield return y;

    if (y.IsCanceled)
    {
        Debug.LogFormat("キャンセルされました");
    }
    else
    {
        Debug.LogFormat("{0} がクリックされました", y.Result.name);
    }
}
```