#if UNITY_EDITOR using UnityEditor; using UnityEngine; public class MouseWorldPositionWindow : EditorWindow { private enum PlaneMode { XY, XZ, YZ, CameraFacing } private PlaneMode planeMode = PlaneMode.XZ; private float planeOffset = 0f; // смещение плоскости по нормали (Z для XY, Y для XZ, X для YZ) private bool lockToSceneView = true; private int decimals = 3; private Vector3 lastWorld; private bool hasPoint; [MenuItem("Tools/Mouse World Position")] public static void Open() { GetWindow("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("Plane", planeMode); if (planeMode != PlaneMode.CameraFacing) planeOffset = EditorGUILayout.FloatField("Plane Offset", 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", lastWorld); } using (new EditorGUI.DisabledScope(!hasPoint)) { if (GUILayout.Button("Copy (x,y,z)")) { EditorGUIUtility.systemCopyBuffer = FormatVec(lastWorld, decimals); } } EditorGUILayout.HelpBox( "Move mouse in Scene View. The window shows world position under cursor.\n" + "3D uses ray-plane intersection (choose plane). 2D обычно: XY @ Z=0.", MessageType.Info); } private void OnSceneGUI(SceneView sv) { if (!lockToSceneView && sv != SceneView.lastActiveSceneView) return; var 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); // чтобы окно обновлялось живо 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: var normal = cam.transform.forward; var 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; } private static string FormatVec(Vector3 v, int d) { string f = "F" + Mathf.Clamp(d, 0, 6); return $"{v.x.ToString(f)}, {v.y.ToString(f)}, {v.z.ToString(f)}"; } } #endif