MRTK v2.4.0 ボタンクリックイベントの送信元を取得する (Update)
しばらくアウトプットする暇も取れない感じでしたが久しぶりの投稿(小ネタ)です!
MRTKのUIコンポーネントを利用し、複数ボタンのOnClickイベントを同一のイベントハンドラにリンクさせました。
この時OnClickイベントはイベント送信元の情報をパラメータで渡すようにはなっていないため、どのボタンからイベントが送信されたか判断できず困ってしまいました。
ボタンごとにイベントハンドラを書くなんて嫌ですよね笑!
解決した方法をサンプルプロジェクトとしてシェアします。
(もっとスマートな方法が用意されているんじゃないのかなぁ。。)
2020/06/07 Update
すぐにホロラボの @deco_c_ 君がヒントをくれました! 確かにこの手があったか~。シンプルで素敵。
これ、ButtonCofigついているオブジェクトにButtonConfigのOnClickをリッスンするコンポーネントつけて、UnityEvent<GameObject>にラップしてイベント飛ばすとかだと微妙です?
— Decoc (@deco_c_) 2020年6月6日
こちらのケースもサンプルプロジェクトに追加しました。
問題
OnClick()イベントはパラメータを持たないので送信元の情報などは一切通知されません。
(senderが欲しいだけだったのですがかなり調べることにww)
case01: Post detect
サンプルプロジェクトの構成
- MRTK v2.4.0 を利用
https://github.com/microsoft/MixedRealityToolkit-Unity/releases/tag/v2.4.0 - ProfileはHoloLens2用
- Menu上に3つのボタンが配置されている
- それぞれのボタンのButtonConfigHelper.OnClick() はすべて同一のイベントハンドラにリンクされている
- イベントハンドラではクリックされたボタンの名称をツールチップに表示する
プロジェクト一式
github.com
解決策
イベントハンドラ内でFocusProviderよりフォーカスが当たっているInteractableを取得する。
そこにたどり着くまでに色々とステップがある(下記 GetEventSender() メソッド参照)
using Microsoft.MixedReality.Toolkit; using Microsoft.MixedReality.Toolkit.UI; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public ToolTip toolTip; public void ButtonClicked() { this.toolTip.ToolTipText = this.GetEventSender()?.name; } private GameObject GetEventSender() { foreach (var inputSource in CoreServices.InputSystem.DetectedInputSources) { foreach (var pointer in inputSource.Pointers) { if (pointer.IsInteractionEnabled) { var sender = CoreServices.InputSystem.FocusProvider.GetFocusedObject(pointer); if (sender?.GetComponent<Interactable>() != null) { return sender; } } } } return null; } }
こんな感じに動きます。
case02: Event wrap
サンプルプロジェクトの構成
- MRTK v2.4.0 を利用
https://github.com/microsoft/MixedRealityToolkit-Unity/releases/tag/v2.4.0 - ProfileはHoloLens2用
- Menu上に3つのボタンが配置されている
- それぞれのボタンにButtonConfigHelper.OnClick() を拡張する自作コンポーネントをアタッチし、アプリケーションはそのOnClickedにハンドラをリンクしている
- イベントハンドラではクリックされたボタンの名称をツールチップに表示する
解決策
各ボタンにアタッチした ClickEventRouter コンポーネントが、ButtonConfigHelper.OnClickイベントをフックして自オブジェクトをパラメータに追加したかたちでInvoke()する。
using Microsoft.MixedReality.Toolkit.UI; using System; using UnityEngine; using UnityEngine.Events; public class ClickEventRouter : MonoBehaviour { [Serializable] public class InternalButtonClickedEvent : UnityEvent<GameObject> { } public InternalButtonClickedEvent OnClick; void Start() { this.GetComponent<ButtonConfigHelper>()?.OnClick.AddListener(() => { this.OnClick?.Invoke(this.gameObject); }); } }
こんな感じにcase01と同様に動きます。
MRTK Button Sender Sample take2
まとめ
複数UIコンポーネントでイベントハンドラを共有するという構成はコードをシンプルにするためによく使われるものだと思うのでSDKにきっと機能が用意されてているに違いないと探しまくったのですがどうやら無さそうなのでひと手間必要そうです(笑)