UnityのUI Toolkitにおけるマウスのキャプチャの仕組みについてまとめます。
Unity 2021.3.16f1
マウスのキャプチャとは
いま、UI要素の上でマウスをクリックし、ドラッグして離すことを考えます。
このようなマウスイベントは以下のイベントのコールバックを登録することで受け取ることができます。
- マウスの押下:
MouseDownEvent
- マウス移動:
MouseMoveEvent
- マウスを押下を解除:
MouseUpEvent
これらの挙動を試すために以下のようにUIを配置したEditorWindowを作成します。
using UnityEditor; using UnityEngine; using UnityEngine.UIElements; public sealed class CaptureExample : EditorWindow { private bool _isCapturing; public void CreateGUI() { // レイアウト // rootの中央にchildを配置 var root = rootVisualElement; root.style.paddingTop = 100; root.style.paddingBottom = 100; root.style.paddingLeft = 100; root.style.paddingRight = 100; root.style.backgroundColor = new StyleColor(new Color(0.8f, 0.8f, 0.8f)); var child = new VisualElement { style = { height = new StyleLength(Length.Percent(100)), borderBottomLeftRadius = 12, borderBottomRightRadius = 12, borderTopLeftRadius = 12, borderTopRightRadius = 12, backgroundColor = new StyleColor(new Color(0.3f, 0.3f, 0.3f)) } }; root.Add(child); // childに対してイベントを登録 child.RegisterCallback<MouseUpEvent>(_ => Debug.Log("MouseUpEvent")); child.RegisterCallback<MouseDownEvent>(_ => Debug.Log("MouseDownEvent")); child.RegisterCallback<MouseMoveEvent>(_ => Debug.Log("MouseMoveEvent")); } [MenuItem("Window/CaptureExample")] public static void ShowWindow() { GetWindow<CaptureExample>("CaptureExample"); } }
これを実行すると以下のようなウィンドウが表示されます。
中央の角丸四角形(child)の中でマウスをドラッグすると、上記で登録したそれぞれのイベントが発行されることを確認できます。
次にこのchildの中でマウスを押下、押下したままchildの外側までマウスを持っていきます。
すると、角丸四角形の外側ではMouseMoveEvent
やMouseUpEvent
が呼ばれないことがわかります。
このようにマウスイベントは、コールバックを登録したUI要素の上にマウスが乗っている時にだけ呼ばれる仕様になっています。
ここでchildのUI要素にマウスをキャプチャさせると、キャプチャが解除されるまではマウスイベントがこのUI要素に対して発行されるようになります。
つまり、マウスをキャプチャしている間はchildの外側でもMouseMoveEvent
やMouseUpEvent
が呼ばれます。
マウスキャプチャは明示的にリリースするか、他の要素をクリックするとキャプチャが解放されます。
マウスキャプチャの挙動を確認する
さてそれでは実際にマウスキャプチャの挙動を確認してみます。
上記のEditorWindowにキャプチャの処理を追記したものが以下のスクリプトです。
using UnityEditor; using UnityEngine; using UnityEngine.UIElements; public sealed class CaptureExample : EditorWindow { private bool _isCapturing; public void CreateGUI() { // レイアウト // rootの中央にchildを配置 var root = rootVisualElement; root.style.paddingTop = 100; root.style.paddingBottom = 100; root.style.paddingLeft = 100; root.style.paddingRight = 100; root.style.backgroundColor = new StyleColor(new Color(0.8f, 0.8f, 0.8f)); var child = new VisualElement { style = { height = new StyleLength(Length.Percent(100)), borderBottomLeftRadius = 12, borderBottomRightRadius = 12, borderTopLeftRadius = 12, borderTopRightRadius = 12 } }; root.Add(child); // childに対してイベントを登録 // childがマウスをキャプチャしていると、これらのイベントがChild以外の場所でも反応する child.RegisterCallback<MouseUpEvent>(_ => Debug.Log("MouseUpEvent")); child.RegisterCallback<MouseDownEvent>(_ => Debug.Log("MouseDownEvent")); child.RegisterCallback<MouseMoveEvent>(_ => Debug.Log("MouseMoveEvent")); // マウスキャプチャ状態に応じて色を変える void SetChildColor() { child.style.backgroundColor = _isCapturing ? new StyleColor(new Color(0.3f, 0.8f, 0.3f)) : new StyleColor(new Color(0.3f, 0.3f, 0.3f)); } child.RegisterCallback<MouseDownEvent>(evt => { // クリックされたらマウスをキャプチャ/リリースする if (_isCapturing) { child.ReleaseMouse(); _isCapturing = false; } else { child.CaptureMouse(); // これでも可能 //MouseCaptureController.ReleaseMouse(); _isCapturing = true; } SetChildColor(); }); SetChildColor(); } [MenuItem("Window/CaptureExample")] public static void ShowWindow() { GetWindow<CaptureExample>("CaptureExample"); } }
このウィンドウを表示し、childをクリックするとマウスがキャプチャされ、childが緑色に変わります。
マウスがキャプチャされた状態でchildの外側にマウスを動かすと、マウスイベントが呼ばれることを確認できます。
マウスのキャプチャはもう一度childをクリックするか、あるいは他のUI要素をクリックすると解除されます。
キャプチャイベント
当該要素がマウスをキャプチャ・キャプチャ解除した時のイベントをコールバックとして登録することもできます。
これを行うには、以下のようにMouseCaptureEvent
やMouseCaptureOutEvent
を使用します。
child.RegisterCallback<MouseCaptureEvent>(_ => Debug.Log("MouseCaptureEvent")); child.RegisterCallback<MouseCaptureOutEvent>(_ => Debug.Log("MouseCaptureOutEvent"));