【Unity Editor拡張】Illustratorのスナップのようにオブジェクトをグリッドにあわせて配置させる


Adobe IllustratorやInkscapeにあるスナップ機能 これをUnityでも使いたいと思ったので作りました。

Boxをスナップ配置させる

以下のスクリプトを作成し、Unityプロジェクト内の適当な場所に配置すればBox.csがアタッチされたオブジェクトをスナップ配置できるようになります。

Box.cs
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Box))]
public class BoxEditor : Editor
{
    //グリッドの幅
    public const float GRID = 0.32f;

    void OnSceneGUI()
    {
        Box box = target as Box;

        //グリッドの色
        Color color = Color.cyan * 0.7f;

        //グリッドの中心座標
        Vector3 orig = Vector3.zero;

        const int num = 10;
        const float size = GRID * num;

        //グリッド描画
        for (int x = - num; x <= num; x++)
        {
            Vector3 pos = orig  + Vector3.right * x * GRID;
            Debug.DrawLine(pos + Vector3.up * size , pos + Vector3.down * size, color);
        }
        for (int y = -num; y <= num; y++)
        {
            Vector3 pos = orig + Vector3.up * y * GRID;
            Debug.DrawLine(pos + Vector3.left * size, pos + Vector3.right * size, color);
        }

        //グリッドの位置にそろえる
        Vector3 position = box.transform.position;
        position.x = Mathf.Floor(position.x / GRID) * GRID;
        position.y = Mathf.Floor(position.y / GRID) * GRID;
        box.transform.position = position;

        //Sceneビュー更新
        EditorUtility.SetDirty(target);
    }

    //フォーカスが外れたときに実行
    void OnDisable()
    {
        //Sceneビュー更新
        EditorUtility.SetDirty(target);
    }
}
#endif //UNITY_EDITOR

public class Box : MonoBehaviour
{
}

結果

Box.csがアタッチされたオブジェクトを選択するとグリッドが表示され、移動させると勝手にグリッドにくっついてくれるようになります。マップを作るときとかに便利そうです

あらゆるオブジェクトをスナップ配置させる

以下のスクリプトを作成し、Unityプロジェクト内の適当な場所に配置すればすべてのオブジェクトをスナップ配置できるようになります。こちらのほうが便利かもしれません

TransformEditor.cs
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Transform))]
public class TransformEditor : Editor
{
    //グリッドの幅
    public const float GRID = 0.32f;

    void OnSceneGUI()
    {
        Transform tr = target as Transform;

        //グリッドの色
        Color color = Color.cyan * 0.7f;

        //グリッドの中心座標
        Vector3 orig = Vector3.zero;

        const int num = 10;
        const float size = GRID * num;

        //グリッド描画
        for (int x = - num; x <= num; x++)
        {
            Vector3 pos = orig  + Vector3.right * x * GRID;
            Debug.DrawLine(pos + Vector3.up * size , pos + Vector3.down * size, color);
        }
        for (int y = -num; y <= num; y++)
        {
            Vector3 pos = orig + Vector3.up * y * GRID;
            Debug.DrawLine(pos + Vector3.left * size, pos + Vector3.right * size, color);
        }

        //グリッドの位置にそろえる
        Vector3 position = tr.transform.position;
        position.x = Mathf.Floor(position.x / GRID) * GRID;
        position.y = Mathf.Floor(position.y / GRID) * GRID;
        tr.transform.position = position;

        //Sceneビュー更新
        EditorUtility.SetDirty(target);
    }

    //フォーカスが外れたときに実行
    void OnDisable()
    {
        //Sceneビュー更新
        EditorUtility.SetDirty(target);
    }
}
#endif //UNITY_EDITOR

複数オブジェクトをスナップ配置させる

Selection.transformsを使うことで選択中の全てのオブジェクトのtransformにアクセスできるみたいです。

using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
[CanEditMultipleObjects]
[CustomEditor(typeof(Transform))]
public class TransformEditor : Editor
{
    //グリッドの幅
    public const float GRID = 1.0f;

    void OnSceneGUI()
    {
        //グリッドの色
        Color color = Color.cyan * 0.7f;

        //グリッドの中心座標
        Vector3 orig = Vector3.zero;

        const int num = 10;
        const float size = GRID * num;

        Vector3 right = new Vector3(1f, 0f, 0f);
        Vector3 up    = new Vector3(0f, 0f, 1f);
        Vector3 left = -right;
        Vector3 down = -up;
        //グリッド描画
        for (int x = -num; x <= num; x++)
        {
            Vector3 pos = orig + right * x * GRID;
            Debug.DrawLine(pos + up * size, pos + down * size, color);
        }
        for (int y = -num; y <= num; y++)
        {
            Vector3 pos = orig + up * y * GRID;
            Debug.DrawLine(pos + left * size, pos + right * size, color);
        }

        foreach (Transform transform in Selection.transforms)
        {
            //グリッドの位置にそろえる
            Vector3 position = transform.transform.position;
            position.x = Mathf.Floor(position.x / GRID) * GRID;
            position.y = Mathf.Floor(position.y / GRID) * GRID;
            position.z = Mathf.Floor(position.z / GRID) * GRID;
            transform.transform.position = position;
        }

        //Sceneビュー更新
        EditorUtility.SetDirty(target);
    }

    //フォーカスが外れたときに実行
    void OnDisable()
    {
        //Sceneビュー更新
        EditorUtility.SetDirty(target);
    }
}
#endif //UNITY_EDITOR

追記

2015-10-16 コード一部修正
2015-10-23 コード一部修正