解きたい問題
- ボタンが 3 つあるとします(それぞれ 1, 2, 3 番とする)
- ボタンが特定の順番で押されたときに限り、指定の処理を行いたい(よくあるパズル)
UniRx を使うと、外部に変数を定義することなく、オペレータの組み合わせのみで実現することができます。
プログラム
以下は、ボタンが 1, 3, 2 の順番で押されたかどうかを判定するプログラムです。
var o = Observable.Merge(
_button1.OnClickAsObservable().Select(_ => _button1),
_button2.OnClickAsObservable().Select(_ => _button2),
_button3.OnClickAsObservable().Select(_ => _button3));
o.Buffer(3, 1)
.Where(e => e[0] == _button1 && e[1] == _button3 && e[2] == _button2)
.First()
.Subscribe(e => Debug.Log("1, 3, 2 の順にボタンが押されました"));
-
Merge
で 3 つのボタンクリックイベントをマージ -
Buffer
でボタンのクリックイベントを 3 つずつにまとめる -
Where
で押された順番が 1, 3, 2 かどうかフィルタリング -
First
で条件を満たした先頭要素のみ取り出す
正しいボタンが押されたときに何らかの処理を行いたい
- 押されたボタンが正しい手順と一致する場合は、そのボタンの色を変える
- 誤ったボタンが押された場合は、全てのボタンの色をリセットする(最初からやり直し)
プログラムは以下になります。
var o = Observable.Merge(
_button1.OnClickAsObservable().Select(_ => _button1),
_button2.OnClickAsObservable().Select(_ => _button2),
_button3.OnClickAsObservable().Select(_ => _button3));
o.Scan(0, (step, button) =>
{
switch (step)
{
case 0: // はじめに _button1 を押したか
if (button == _button1)
{
_button1.GetComponent<Image>().color = Color.gray;
return 1; // 次のステップへ
}
break;
case 1: // つぎに _button3 を押したか
if (button == _button3)
{
_button3.GetComponent<Image>().color = Color.gray;
return 2; // 次のステップへ
}
break;
case 2: // さいごに _button2 を押したか
if (button == _button2)
{
_button2.GetComponent<Image>().color = Color.gray;
return 3; // Where でフィルタリングする値
}
break;
}
// 誤ったボタンが押されたときは、ボタンの色を初期値に戻す
foreach (var b in new[] { _button1, _button2, _button3 })
b.GetComponent<Image>().color = Color.white;
return 0; // 最初のステップにもどる
}).Where(e => e == 3).First().Subscribe(e => Debug.Log("1, 3, 2 の順番にボタンが押されました"));
-
Merge
で 3 つのボタンのクリックイベントをマージ -
Scan
でボタンの押された順番をチェック。進行状況を step で引き回す -
Where
で条件を満たすものだけフィルタリング -
First
で先頭要素のみ取り出す
Scan
を使えば、今回の問題に限らず、イベントが特定の順番で発生したかを、内部処理だけで完結するように書けますね。