Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
136 lines
4.3 KiB
C#
136 lines
4.3 KiB
C#
#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
|