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