using UnityEngine; using UnityEngine.InputSystem; /// /// OS 커서 위치를 Unity 화면 좌표로 변환한다. /// /// 우리 창은 WS_EX_NOACTIVATE 라 포커스를 받지 않으므로 Unity 의 Mouse.position 을 /// 신뢰할 수 없다. 또 창 밖 좌표는 Unity 가 아예 모른다. 그래서 GetCursorPos 로 /// OS 커서를 직접 읽고 창 사각형 기준으로 환산한다. /// /// 히트테스트와 시선 추적이 같은 변환을 쓰므로 여기 한 곳에만 둔다. /// public static class DesktopCursor { /// /// 데스크톱 물리 좌표(Y 아래로) <-> Unity 화면 좌표(Y 위로) 변환에 필요한 정보. /// 창 사각형을 매번 다시 묻지 않도록 한 번 만들어 재사용한다. /// public struct Mapping { public int Left, Top, Width, Height; public float ScaleX, ScaleY; public float ToScreenX(float desktopX) => (desktopX - Left) * ScaleX; public float ToScreenY(float desktopY) => (Height - (desktopY - Top)) * ScaleY; public Vector2 ToScreen(float desktopX, float desktopY) => new Vector2(ToScreenX(desktopX), ToScreenY(desktopY)); } /// 우리 창을 기준으로 한 좌표 변환 정보를 구한다. public static bool TryGetMapping(System.IntPtr hwnd, out Mapping mapping) { mapping = default; #if !UNITY_EDITOR && UNITY_STANDALONE_WIN if (hwnd == System.IntPtr.Zero) return false; if (!Win32.GetWindowRect(hwnd, out var rc)) return false; if (rc.Width <= 0 || rc.Height <= 0) return false; mapping = new Mapping { Left = rc.left, Top = rc.top, Width = rc.Width, Height = rc.Height, // 창 크기와 백버퍼 크기가 어긋날 경우를 대비해 비율로 환산한다. ScaleX = Screen.width / (float)rc.Width, ScaleY = Screen.height / (float)rc.Height, }; return true; #else mapping = new Mapping { Left = 0, Top = 0, Width = Screen.width, Height = Screen.height, ScaleX = 1f, ScaleY = 1f, }; return true; #endif } /// /// 커서의 Unity 화면 좌표(좌하단 원점)를 구한다. 커서가 창 밖이면 false. /// public static bool TryGetScreenPosition(System.IntPtr hwnd, out Vector2 screenPos) { screenPos = default; #if !UNITY_EDITOR && UNITY_STANDALONE_WIN if (!Win32.GetCursorPos(out var pt)) return false; if (!TryGetMapping(hwnd, out Mapping map)) return false; // 창 밖이면 무효로 본다. if (pt.x < map.Left || pt.y < map.Top || pt.x >= map.Left + map.Width || pt.y >= map.Top + map.Height) return false; screenPos = map.ToScreen(pt.x, pt.y); return true; #else // 에디터에서는 일반 마우스 입력으로 대체해 미리보기가 가능하게 한다. var mouse = Mouse.current; if (mouse == null) return false; Vector2 p = mouse.position.ReadValue(); if (p.x < 0f || p.y < 0f || p.x >= Screen.width || p.y >= Screen.height) return false; screenPos = p; return true; #endif } }