【Unity】【エディタ拡張】Sceneビューでオブジェクトを特定の軸上でしか動かせないようにする

f:id:halya_11:20180213221545p:plain:w500

Sceneビューでオブジェクトを指定した軸上でのみ移動可能にするエディタ拡張を作ってみます。
Sceneビュー操作に慣れていない人にレベルの編集を任せるときなどの誤操作防止などに使えるのではないかと思います。

ソースコード

クラスはMonoBehaviourとEditorの2つで構成されます。

using UnityEngine;

public class RestrictedPositionHandler : MonoBehaviour
{
    [SerializeField, Tooltip("X軸を操作可能にするか")]
    private bool _isXAxisControllable = false;
    [SerializeField, Tooltip("Y軸を操作可能にするか")]
    private bool _isYAxisControllable = false;
    [SerializeField, Tooltip("Z軸を操作可能にするか")]
    private bool _isZAxisControllable = false;
}

MonoBehaviourはシリアライズされた値を持っているだけです。

using UnityEngine;
using UnityEditor;

[CustomEditor (typeof(RestrictedPositionHandler))]
public class RestrictedPositionHandlerEditor : Editor
{
    private RestrictedPositionHandler _target;
    private Transform _transform;
    private Tool _previousTool;
    private Vector3 _snap;

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

        var isXAxisControllable = serializedObject.FindProperty("_isXAxisControllable").boolValue;
        var isYAxisControllable = serializedObject.FindProperty("_isYAxisControllable").boolValue;
        var isZAxisControllable = serializedObject.FindProperty("_isZAxisControllable").boolValue;
        
        // 3軸ともコントロール可能な場合はデフォルトのMoveツールを使う
        if (isXAxisControllable && isYAxisControllable && isZAxisControllable) {
            Tools.current = Tool.Move;
            return;
        }
        
        Tools.current = Tool.None;

        // Slider
        var position = _transform.position;
        if (isXAxisControllable) {
            Handles.color = Handles.xAxisColor;
            position = Handles.Slider(position, _transform.right);
        }
        if (isYAxisControllable) {
            Handles.color = Handles.yAxisColor;
            position = Handles.Slider(position, _transform.up);
        }
        if (isZAxisControllable) {
            Handles.color = Handles.zAxisColor;
            position = Handles.Slider(position, _transform.forward);
        }

        // 2軸コントロール可能な場合はFreeMoveも
        Vector3? handleCapEuler = null;
        if (isXAxisControllable && isYAxisControllable) {
            handleCapEuler = Vector3.zero;
        }
        if (isXAxisControllable && isZAxisControllable) {
            handleCapEuler = new Vector3(90.0f, 0.0f, 0.0f);
        }
        if (isYAxisControllable && isZAxisControllable) {
            handleCapEuler = new Vector3(0.0f, 90.0f, 0.0f);
        }
        if (handleCapEuler.HasValue) {
            Handles.CapFunction RectangleHandleCap2D = (id, pos, rot, size, eventType) => {
                Handles.RectangleHandleCap(id, pos, Quaternion.Euler(handleCapEuler.Value), size, eventType);
            };
            var freeMoved = Handles.FreeMoveHandle(position, _transform.rotation, 1.0f, _snap, RectangleHandleCap2D);
            if (isXAxisControllable) {
                position.x = freeMoved.x;
            }
            if (isYAxisControllable) {
                position.y = freeMoved.y;
            }
            if (isZAxisControllable) {
                position.z = freeMoved.z;
            }
        }

        // 位置を更新
        _transform.position = position;
    }

    private void OnEnable()
    {
        _target = target as RestrictedPositionHandler;
        _transform = _target.transform;
        _previousTool = Tools.current;
        _snap = Vector3.zero;
        _snap.x = EditorPrefs.GetFloat("MoveSnapX", 1.0f);
        _snap.y = EditorPrefs.GetFloat("MoveSnapY", 1.0f);
        _snap.z = EditorPrefs.GetFloat("MoveSnapZ", 1.0f);
    }

    private void OnDisable()
    {
        Tools.current = _previousTool;
    }
}

EditorではHandlesを使ってオブジェクトの位置を制御しています。
Handlesについては下記のサイトを見れば大体わかります。

anchan828.github.io

使い方

移動を制限したいオブジェクトにアタッチして、移動可能な軸を選択するだけです。

f:id:halya_11:20180213222746p:plain:w500

軸を2つにすれば2次元用のHandleになります。 ドラッグで移動しても2軸上でしかうごきません。

f:id:halya_11:20180213222813p:plain:w500

軸が1つの場合はこんなかんじ。

f:id:halya_11:20180213222836p:plain:w500

ちなみに、インスペクタのTransformからの移動は軸を制限しないように作っています。

参考

anchan828.github.io

関連

改善版をつくりました。 light11.hatenadiary.com