#if UNITY_EDITOR && UNITY_2021_2_OR_NEWER using UnityEditor; using UnityEditor.Overlays; using UnityEngine; using UnityEngine.UIElements; [Overlay(typeof(SceneView), id: "mouse-world-position", displayName: "Mouse World Position")] [Icon("d_Grid.BoxTool")] public class MouseWorldPositionOverlay : Overlay { private enum PlaneMode { XY, XZ, YZ, CameraFacing } private static readonly float[] Multipliers = { 1f, 10f, 100f, 1000f, 10000f, 100000f, 1000000f }; private PlaneMode planeMode = PlaneMode.XY; private float planeOffset; private int decimals = 2; private Vector3 displayWorld; private bool hasPoint; private Label positionLabel; public override void OnCreated() { SceneView.duringSceneGui += OnSceneGUI; } public override void OnWillBeDestroyed() { SceneView.duringSceneGui -= OnSceneGUI; } public override VisualElement CreatePanelContent() { VisualElement root = new() { style = { minWidth = 220 } }; // Плоскость EnumField planeModeField = new("Плоскость", planeMode); planeModeField.RegisterValueChangedCallback(evt => planeMode = (PlaneMode)evt.newValue); root.Add(planeModeField); // Смещение FloatField offsetField = new("Смещение") { value = planeOffset }; offsetField.RegisterValueChangedCallback(evt => planeOffset = evt.newValue); root.Add(offsetField); // Знаки после запятой SliderInt decimalsSlider = new("Знаки", 0, 6) { value = decimals, showInputField = true }; decimalsSlider.RegisterValueChangedCallback(evt => decimals = evt.newValue); root.Add(decimalsSlider); // Позиция (только для чтения) positionLabel = new Label("—") { style = { marginTop = 8, unityFontStyleAndWeight = FontStyle.Bold } }; root.Add(positionLabel); // Кнопка копирования Button copyButton = new(() => { if (!hasPoint) return; string f = "F" + decimals; EditorGUIUtility.systemCopyBuffer = $"{displayWorld.x.ToString(f)}, {displayWorld.y.ToString(f)}, {displayWorld.z.ToString(f)}"; }) { text = "Копировать (x,y,z)" }; root.Add(copyButton); return root; } private void OnSceneGUI(SceneView sv) { if (containerWindow != sv) return; Event e = Event.current; if (e == null) return; 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 Vector3 world); if (hasPoint) { float mult = Multipliers[decimals]; displayWorld = new Vector3( Mathf.Ceil(world.x * mult) / mult, Mathf.Ceil(world.y * mult) / mult, Mathf.Ceil(world.z * mult) / mult ); string f = "F" + decimals; positionLabel?.schedule.Execute(() => positionLabel.text = $"X:{displayWorld.x.ToString(f)} Y:{displayWorld.y.ToString(f)} Z:{displayWorld.z.ToString(f)}" ); } else { positionLabel?.schedule.Execute(() => positionLabel.text = "—"); } } private bool TryGetPoint(Ray ray, Camera cam, out Vector3 world) { Plane plane; switch (planeMode) { case PlaneMode.XY: plane = new Plane(Vector3.forward, new Vector3(0f, 0f, planeOffset)); break; case PlaneMode.XZ: plane = new Plane(Vector3.up, new Vector3(0f, planeOffset, 0f)); break; case PlaneMode.YZ: plane = new Plane(Vector3.right, new Vector3(planeOffset, 0f, 0f)); break; case PlaneMode.CameraFacing: default: plane = new Plane(cam.transform.forward, cam.transform.position); break; } if (plane.Raycast(ray, out float enter)) { world = ray.GetPoint(enter); return true; } world = default; return false; } } #endif