見出し画像

【Unity】GUIの範囲外でもドラッグの効果を継続する

*uGUIじゃなくてIMGUI(OnGUI()で使うやつ)のほうです

ドラッグできるGUI

画像1

こんな感じものを作る方法の紹介です。ポイントは2点で

1.矩形の範囲外からドラッグを始めても反応しない
2.矩形の範囲内でドラッグを始めたら範囲外に出ても反応する

普通に作るとどちらかが欠けがちで、1.がないと誤操作しやすく、2.がないと端っこあたりを狙うときにちょっとはみ出ちゃってギリギリのところでポイントが止まったりしちゃいます。UnityのColorPickerなんかはこの辺お作法をちゃんとしているようでした。

コントロールID

このような挙動を実装する際はコントロールIDを使います。コントロールIDとは「いまこのIDのGUIを処理してまっせ」という数値でこれを登録、監視することでマウスの位置などほかの情報に依存することなくGUI処理を排他的に行えます。

コントールIDの取得関数

var controlID = GUIUtility.GetControlID(FocusType.Passive);

引数のFocusTypeはキーボードの入力を考慮するときに必要なものです。今回は関係ないのでFocusType.Passiveでオッケーです。これは呼ばれた順にIDを発行するのでイベントタイプによって呼んだり呼ばなかったりしてはいけません。常に呼ぶか常に呼ばないかになります。

コントロールIDの登録、監視

// 登録
GUIUtility.hotControl = controlID;
// 監視
if ( GUIUtility.hotControl == controlID )
{
 〜自身のGUIの処理〜
}

GUIUtility.hotControlが現在処理中のコントロールIDですので、これに自身のコントロールIDを代入したり比較することでコントロール権の操作が行えます。自身のコントール権を手放すときは0を代入します。

コード

というわけで上記gifのコードはこんな感じになりました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GUIOutSideDraggableSample : MonoBehaviour {

    public Vector2 mousePos;
    Vector2 boxSize = new Vector2(100f, 100f);

    GUIStyle whiteBoxStyle;

    private void Awake()
    {
        whiteBoxStyle = new GUIStyle("box");
        whiteBoxStyle.normal.background = Texture2D.whiteTexture;
    }

    void OnGUI()
    {
        using(new GUILayout.HorizontalScope())
        {
            GUILayout.Label("x");
            GUILayout.TextField(mousePos.x.ToString());
        }

        using (new GUILayout.HorizontalScope())
        {
            GUILayout.Label("y");
            GUILayout.TextField(mousePos.y.ToString());
        }

        // 可動範囲のBox
        GUILayout.Box("", GUILayout.Width(boxSize.x), GUILayout.Height(boxSize.y));

        // BoxのRect取得
        var rect = GUILayoutUtility.GetLastRect();

        // コントロールID取得
        var controlID = GUIUtility.GetControlID(FocusType.Passive);

        var ev = Event.current;
        var eventType = ev.GetTypeForControl(controlID);

        // イベントタイプ別処理
        switch(eventType)
        {
            case EventType.MouseDown:
                {
                    if ( rect.Contains(ev.mousePosition))
                    {
                        GUIUtility.hotControl = controlID;
                    }
                }
                break;

            case EventType.MouseUp:
                {
                    if ( GUIUtility.hotControl == controlID)
                    {
                        GUIUtility.hotControl = 0;
                    }
                }
                break;
        }

        // このGUIがコントロール中の処理
        if ( ev.isMouse && (GUIUtility.hotControl == controlID))
        {
            mousePos = Rect.PointToNormalized(rect, ev.mousePosition);

            // 他のGUI処理を無効化
            ev.Use();
        }

        GUI.Box(new Rect(Rect.NormalizedToPoint(rect, mousePos), Vector2.one), "", whiteBoxStyle);
    }
}

gistにも上げてみました。
https://gist.github.com/fuqunaga/036b8397f67217b04b693e5a7ba373c2

参考

https://blogs.unity3d.com/jp/2015/12/22/going-deep-with-imgui-and-editor-customization/

もしお役に立ちましたらスキ(♡マーク)をお願いします!!! noteアカウントがなくても大丈夫です。サポートもお待ちしています!