UnityのuGUIでスクロール中にボタンの長押しをするスクリプト
あまり需要はなさそうなスクリプトですが、使い方や実装等をまとめておきます。
こちらが今回のスクリプトを使ったサンプルです。 github.com
UnityのuGUIで、ボタンの押下判定時に通常使われるOnClick()は、ボタンを離したタイミングでのみイベントが呼ばれます。デフォルトのButtonではOnClick()以外のメソッドが用意されていないため、長押しの判定をするためにはUnityEngine.EventSystems.EventTrigger内のインターフェースを用いて実装をする必要があります。
こちらの方が、既に長押しの機能を持つスクリプトを公開していました。
ただし、今回はスクロールビュー中のボタンの長押しを実現したかったため、上記スクリプトでは少し問題がありました。長押し開始の座標から少しでも位置がずれるとドラッグイベントが始まり、押下中の判定が終了してしまうことが判明しました。
今回作成したスクリプトを用いると、スクロールビュー中であってもボタンの長押しを判定出来るようになりました。
使い方
スクロールビューの作成は、こちらの記事を参考にしました。
1.
ScrollRectの代わりにLongPressScrollをアタッチ。
ScrollRectの時と同様に、ContentオブジェクトをContentにドラッグしておきます。
2.
Buttonの代わりにLongPressButtonをアタッチ。
OnClick()と同様に、オブジェクトをドラッグし、呼び出したいメソッドを選択します。
今回は同じオブジェクトにアタッチしてあるButtonClassのChangeRed()メソッドを設定します。
インスペクタ上での操作を例にしていますが、スクリプトからAddListenerを使って設定することも可能です。
以上の手順でスクロール中の長押しが実現出来たはずです。
実装
想定する動作は以下の二つ。
ボタン押下:OnPointerDown() → ボタンを離す:OnPointerUp()
ボタン押下:OnPointerDown() → ドラッグ開始:OnBeginDrag(),OnPointerUp() → ドラッグ終了:OnEndDrag()
上記でも説明しましたが、ドラッグ開始と同じタイミングでボタンを離す判定OnPointerUp()が呼ばれてしまいます。
そのため、ボタンを離す判定OnPointerUp()が呼ばれた時点でドラッグ中であれば、長押し判定は継続させる必要があります。その場合はドラッグ終了判定OnEndDrag()を使い、長押し判定を終了させます。
ちなみにドラッグ開始判定は押下状態で座標を動かしたタイミングに呼ばれ、ドラッグ終了判定は押下状態を解放したタイミングで呼ばれます(座標の移動が止まったタイミングではありません)。
初めに子ノードをLongPressButtonを全て取得し、自身の参照を渡しています。ノードをスクリプトで生成している場合は、処理順によっては参照が渡されませんので、ご注意ください。具体的には、ノードの生成処理をAwake()に書く、もしくはEditor / Project Setting / Script Execution Orderより、スクリプトの処理順を設定する等で対応可能です。
ボタンを離す判定OnPointerUp()でCheckPressedStill()メソッドを呼び、呼び出し元の参照を保存します。その後、ドラッグ終了判定時に保存した参照を使って長押し判定を終了させることが出来ます。
こちらに関しては、はじめに紹介した記事のスクリプトを元にしています。長押し判定の細かい調整等が出来るようになっています。scrollの参照が渡されていない場合、Null結合演算子を用いてFindObjectOfType()の結果を使用しています。必要になったら取得し、2回目以降は保存した参照を利用出来るため、非常に効率的なロジックになっています。LongPressScroll.Start()の処理はしなくても問題ないですが、状況によって使い分けてください。
余談
OnEndDrag()やOnBeginDrag()をオーバーライドした際、スクロールビューの挙動が変わり困りました。そこで知ったのがbaseキーワードで、派生先クラスから基本クラスのメソッドにアクセス出来るみたいです。便利ですね。
※ 5月11日追記
通常のスクロール時に長押しの判定が取られてしまうという指摘を受けました。 そこで、開始までの間隔を設定出来るよう仕様を変更しました。
これによって、判定開始まで同じ座標を押下し続けた場合のみ長押し判定となり、通常のスクロールや単一の押下等で誤認識される可能性が軽減しました。
アプローチとしては、押下時に開始を遅らせるコルーチンを呼び、判定開始前にボタンが離れた場合はコルーチンを停止するというやり方をとっています。
判定開始までの時間はインスペクターから変更可能になっているので、状況に応じて調整してください。