Danro/Assets/Scripts/Editor/MouseWorldPositionWindow.cs
2025-12-24 10:58:23 +03:00

142 lines
4.5 KiB
C#

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
public class MouseWorldPositionWindow : EditorWindow
{
private enum PlaneMode { XY, XZ, YZ, CameraFacing }
// Множители для быстрого доступа: multipliers[d] = 10^d
private static readonly float[] Multipliers = { 1f, 10f, 100f, 1000f, 10000f, 100000f, 1000000f };
private PlaneMode planeMode = PlaneMode.XZ;
private float planeOffset;
private bool lockToSceneView = true;
private int decimals = 3;
private Vector3 lastWorld;
private Vector3 displayWorld;
private bool hasPoint;
[MenuItem("Tools/Mouse World Position")]
public static void Open()
{
GetWindow<MouseWorldPositionWindow>("Mouse World Pos");
}
private void OnEnable()
{
SceneView.duringSceneGui += OnSceneGUI;
}
private void OnDisable()
{
SceneView.duringSceneGui -= OnSceneGUI;
}
private void OnGUI()
{
EditorGUILayout.LabelField("Source", EditorStyles.boldLabel);
lockToSceneView = EditorGUILayout.ToggleLeft("Use last active Scene View", lockToSceneView);
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Projection", EditorStyles.boldLabel);
planeMode = (PlaneMode)EditorGUILayout.EnumPopup("Плоскость", planeMode);
if (planeMode != PlaneMode.CameraFacing)
planeOffset = EditorGUILayout.FloatField("Смещение плоскости", planeOffset);
decimals = EditorGUILayout.IntSlider("Decimals", decimals, 0, 6);
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Result", EditorStyles.boldLabel);
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.Toggle("Has Point", hasPoint);
EditorGUILayout.Vector3Field("World", displayWorld);
}
using (new EditorGUI.DisabledScope(!hasPoint))
{
if (GUILayout.Button("Copy (x,y,z)"))
{
string f = "F" + decimals;
EditorGUIUtility.systemCopyBuffer = $"{displayWorld.x.ToString(f)}, {displayWorld.y.ToString(f)}, {displayWorld.z.ToString(f)}";
}
}
}
private void OnSceneGUI(SceneView sv)
{
if (!lockToSceneView && sv != SceneView.lastActiveSceneView) return;
Event e = Event.current;
if (e == null) return;
// Берём позицию мыши только когда курсор над SceneView
if (e.type != EventType.MouseMove && e.type != EventType.MouseDrag && e.type != EventType.Repaint)
return;
Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
hasPoint = TryGetPoint(ray, sv.camera, out lastWorld);
// Вычисляем displayWorld один раз при изменении позиции
if (hasPoint)
{
float mult = Multipliers[decimals];
displayWorld = new Vector3(
Mathf.Ceil(lastWorld.x * mult) / mult,
Mathf.Ceil(lastWorld.y * mult) / mult,
Mathf.Ceil(lastWorld.z * mult) / mult
);
}
else
{
displayWorld = default;
}
Repaint();
}
private bool TryGetPoint(Ray ray, Camera cam, out Vector3 world)
{
// Для CameraFacing: плоскость, перпендикулярная камере, проходящая через origin камеры + (forward * planeOffset)
Plane plane;
switch (planeMode)
{
case PlaneMode.XY:
plane = new Plane(Vector3.forward, new Vector3(0f, 0f, planeOffset)); // Z = offset
break;
case PlaneMode.XZ:
plane = new Plane(Vector3.up, new Vector3(0f, planeOffset, 0f)); // Y = offset
break;
case PlaneMode.YZ:
plane = new Plane(Vector3.right, new Vector3(planeOffset, 0f, 0f)); // X = offset
break;
case PlaneMode.CameraFacing:
default:
Vector3 normal = cam.transform.forward;
Vector3 point = cam.transform.position; // можно поменять на position + forward*dist если надо
plane = new Plane(normal, point);
break;
}
if (plane.Raycast(ray, out float enter))
{
world = ray.GetPoint(enter);
return true;
}
world = default;
return false;
}
}
#endif