7080 + 1

ゲームプログラミングの記事を書いてます。

【Unity】矩形の空間を定義、シーン上でいじれる処理を書いてみる【BoxBoundsHandle】

ゲームを作っていると、ゲーム空間の中にエリアを定義したくなることがあると思います。
今回はそれをシーン上で調整できるようにする BoxBoundsHandle を紹介します。

とりあえずシーンに矩形を描画してみる

単純にシーン上で範囲を表すために図形を描画したい場合、GizmosクラスのDraw〇〇というメソッドを使います。今回は矩形を描画したいので、DrawWireCube()を使いました。

public class BoxArea : MonoBehaviour
{
    [SerializeField] 
    private Bounds bounds;

    private void OnDrawGizmosSelected()
    {
        Gizmos.DrawWireCube(bounds.center, bounds.size);
    }
}

これで範囲をシーン上でグラフィカルにみることができますが、矩形の調整が数値でしかできないです。
ピッタリ数値を合わすときなどは数値でできたほうが便利なことがありますが、シーン上で矩形の大きさを見ながら直接調節できたほうがやりやすい場合もあるかと思います。
そこで、シーン上から直接矩形の範囲を調節することができる方法を紹介します。

BoxBoundsHandle を使う

シーン上で矩形の大きさを調整できるBoxBoundsHandleというものがあります。
OnDrawGizmos() 上では使えないので、インスペクタをエディタ拡張する必要があります。

[CustomEditor(typeof(BoxArea))]
public class BoxAreaInspector : Editor
{
    BoxBoundsHandle handle;

    Bounds cacheBounds;

    private void OnEnable()
    {
        handle = new BoxBoundsHandle();
    }

    private void OnSceneGUI()
    {
        serializedObject.Update();

        var bounds = serializedObject.FindProperty("bounds");
        handle.center = bounds.boundsValue.center;
        handle.size = bounds.boundsValue.size;

        using (var check = new EditorGUI.ChangeCheckScope()) {
            // 描画
            handle.DrawHandle();

            // 変更を適用する
            if (check.changed) {
                cacheBounds.center = handle.center;
                cacheBounds.size = handle.size;
                bounds.boundsValue = cacheBounds;
            }
        }

        serializedObject.ApplyModifiedProperties();
    }
}

これでシーン上からグラフィカルに矩形を調整できるようになりました。
が、実はまだ問題があります。

回転ができない

BoxBoundsHandleには回転の値がありません。
Handles.DrawingScope()を使う必要があります。
ここに行列を渡すことで、回転させることができます。

[CustomEditor(typeof(BoxArea))]
public class BoxAreaInspector : Editor
{
    BoxBoundsHandle handle;

    Bounds cacheBounds;

    private void OnEnable()
    {
        handle = new BoxBoundsHandle();
    }

    private void OnSceneGUI()
    {
        var self = target as BoxArea;
        serializedObject.Update();
        var bounds = serializedObject.FindProperty("bounds");

        // GameObjectの空間で回転、拡縮、移動させる
        var matrix = Handles.matrix * self.transform.localToWorldMatrix; 

        handle.center = bounds.boundsValue.center;
        handle.size = bounds.boundsValue.size;

        using (var check = new EditorGUI.ChangeCheckScope()) {
            // 描画する
            using (new Handles.DrawingScope(matrix)) {
                handle.DrawHandle();
            }

             // 変更を適用する
            if (check.changed) {
                cacheBounds.center = handle.center;
                cacheBounds.size = handle.size;
                bounds.boundsValue = cacheBounds;
            }
        }

        serializedObject.ApplyModifiedProperties();
    }
}

最後に

BoxBoundsHandle以外にも、球を同じように制御できるSphereBoundsHandleなどもあるので、いろいろ探してみてください。
docs.unity3d.com

また、PrimitiveBoundsHandleを継承することで、自分だけの Handle を作ることもできそうです。
docs.unity3d.com