以前同じようなものを作りましたが、オブジェクトを回転したときの移動に対応していなかったり操作感がいけてなかったので改修。
ソースコード
まずはSliderとFreeMoveHandleで作った独自Handleを生成するUtilityを作ります。
#if UNITY_EDITOR using UnityEngine; using UnityEditor; public class AdvancedHandles { /// <summary> /// 指定した軸のFreeMoveハンドルを生成する /// </summary> public static Vector3 AxisMove (Vector3 position, Quaternion rotation, bool enableXAxis = true, bool enableYAxis = true, bool enableZAxis = true) { var rotationMatrix = Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one); var dirX = rotationMatrix.MultiplyPoint(Vector3.right); var dirY = rotationMatrix.MultiplyPoint(Vector3.up); var dirZ = rotationMatrix.MultiplyPoint(Vector3.forward); var snap = Vector3.one; snap.x = EditorPrefs.GetFloat("MoveSnapX", 1.0f); snap.y = EditorPrefs.GetFloat("MoveSnapY", 1.0f); snap.z = EditorPrefs.GetFloat("MoveSnapZ", 1.0f); // FreeMove var handleCapPosOffset = Vector3.zero; var handleCapEuler = rotation.eulerAngles; var handleSize = HandleUtility.GetHandleSize(position) * 0.2f; Handles.CapFunction RectangleHandleCap2D = (id, pos, rot, size, eventType) => { Handles.RectangleHandleCap(id, pos + rotationMatrix.MultiplyPoint(handleCapPosOffset), rotation * Quaternion.Euler(handleCapEuler), size, eventType); }; if (enableXAxis && enableYAxis) { Handles.color = Handles.zAxisColor; handleCapPosOffset = new Vector3(1.0f, 1.0f, 0.0f) * handleSize; handleCapEuler = Vector3.zero; var movePoint = Handles.FreeMoveHandle(position, rotation, handleSize, snap, RectangleHandleCap2D); // XY平面上の近傍点を新しい位置とする position = movePoint - dirZ * Vector3.Dot(movePoint - position, dirZ); } if (enableXAxis && enableZAxis) { Handles.color = Handles.yAxisColor; handleCapPosOffset = new Vector3(1.0f, 0.0f, 1.0f) * handleSize; handleCapEuler = new Vector3(90.0f, 0.0f, 0.0f); var movePoint = Handles.FreeMoveHandle(position, rotation, handleSize, snap, RectangleHandleCap2D); // XZ平面上の近傍点を新しい位置とする position = movePoint - dirY * Vector3.Dot(movePoint - position, dirY); } if (enableYAxis && enableZAxis) { Handles.color = Handles.xAxisColor; handleCapPosOffset = new Vector3(0.0f, 1.0f, 1.0f) * handleSize; handleCapEuler = new Vector3(0.0f, 90.0f, 0.0f); var movePoint = Handles.FreeMoveHandle(position, rotation, handleSize, snap, RectangleHandleCap2D); // YZ平面上の近傍点を新しい位置とする position = movePoint - dirX * Vector3.Dot(movePoint - position, dirX); } // Slider return AxisSlider(position, rotation, enableXAxis, enableYAxis, enableZAxis); } /// <summary> /// 指定した軸のSliderハンドルを生成する /// </summary> public static Vector3 AxisSlider (Vector3 position, Quaternion rotation, bool enableXAxis = true, bool enableYAxis = true, bool enableZAxis = true) { var rotationMatrix = Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one); if (enableXAxis) { Handles.color = Handles.xAxisColor; position = Handles.Slider(position, rotationMatrix.MultiplyPoint(Vector3.right)); } if (enableYAxis) { Handles.color = Handles.yAxisColor; position = Handles.Slider(position, rotationMatrix.MultiplyPoint(Vector3.up)); } if (enableZAxis) { Handles.color = Handles.zAxisColor; position = Handles.Slider(position, rotationMatrix.MultiplyPoint(Vector3.forward)); } return position; } } #endif
これをエディタスクリプトで制御します。
using UnityEngine; using UnityEditor; [CustomEditor (typeof(RestrictedPositionHandler))] public class RestrictedPositionHandlerEditor : Editor { private RestrictedPositionHandler _target; private Transform _transform; private Tool _previousTool; private void OnSceneGUI() { serializedObject.Update(); var isXAxisControllable = serializedObject.FindProperty("_isXAxisControllable").boolValue; var isYAxisControllable = serializedObject.FindProperty("_isYAxisControllable").boolValue; var isZAxisControllable = serializedObject.FindProperty("_isZAxisControllable").boolValue; Tools.current = Tool.None; // 位置を更新 _transform.position = AdvancedHandles.AxisMove(_transform.position, _transform.rotation, isXAxisControllable, isYAxisControllable, isZAxisControllable); } private void OnEnable() { _target = target as RestrictedPositionHandler; _transform = _target.transform; _previousTool = Tools.current; } private void OnDisable() { Tools.current = _previousTool; } }
MonoBehaviourはフィールドを持つだけです。
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; }
使い方
使い方は前回の記事と同様、RestrictedPositionHandlerをアタッチして軸を選択するだけです。
Undoを追加したいなー