using System; using System.Runtime.InteropServices; using UnityEngine; namespace WulaFallenEmpire.EventSystem.AI.Agent { /// /// 模拟鼠标输入工具 - 用于 VLM 视觉操作模式 /// 支持移动鼠标、点击、拖拽等操作 /// public static class MouseSimulator { // Windows API 导入 [DllImport("user32.dll")] private static extern bool SetCursorPos(int X, int Y); [DllImport("user32.dll")] private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, int dwExtraInfo); [DllImport("user32.dll")] private static extern bool GetCursorPos(out POINT lpPoint); [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; } // 鼠标事件标志 private const uint MOUSEEVENTF_LEFTDOWN = 0x0002; private const uint MOUSEEVENTF_LEFTUP = 0x0004; private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; private const uint MOUSEEVENTF_RIGHTUP = 0x0010; private const uint MOUSEEVENTF_WHEEL = 0x0800; /// /// 将比例坐标 (0-1) 转换为屏幕坐标 /// public static (int x, int y) ProportionalToScreen(float propX, float propY) { int screenWidth = Screen.width; int screenHeight = Screen.height; int x = Mathf.Clamp(Mathf.RoundToInt(propX * screenWidth), 0, screenWidth - 1); int y = Mathf.Clamp(Mathf.RoundToInt(propY * screenHeight), 0, screenHeight - 1); return (x, y); } /// /// 移动鼠标到指定屏幕坐标 /// public static bool MoveTo(int screenX, int screenY) { try { // Unity 坐标是左下角为原点,Windows 是左上角 // 需要翻转 Y 坐标 int windowsY = Screen.height - screenY; // 获取游戏窗口位置并加上偏移 // 注意:这在全屏模式下可能需要调整 return SetCursorPos(screenX, windowsY); } catch (Exception ex) { WulaLog.Debug($"[MouseSimulator] MoveTo failed: {ex.Message}"); return false; } } /// /// 移动鼠标到比例坐标 (0-1) /// public static bool MoveToProportional(float propX, float propY) { var (x, y) = ProportionalToScreen(propX, propY); return MoveTo(x, y); } /// /// 在当前位置执行左键点击 /// public static void LeftClick() { try { mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); } catch (Exception ex) { WulaLog.Debug($"[MouseSimulator] LeftClick failed: {ex.Message}"); } } /// /// 在当前位置执行右键点击 /// public static void RightClick() { try { mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); } catch (Exception ex) { WulaLog.Debug($"[MouseSimulator] RightClick failed: {ex.Message}"); } } /// /// 在指定屏幕坐标执行左键点击 /// public static bool ClickAt(int screenX, int screenY, bool rightClick = false) { try { if (!MoveTo(screenX, screenY)) { return false; } // 短暂延迟确保鼠标位置更新 System.Threading.Thread.Sleep(10); if (rightClick) { RightClick(); } else { LeftClick(); } return true; } catch (Exception ex) { WulaLog.Debug($"[MouseSimulator] ClickAt failed: {ex.Message}"); return false; } } /// /// 在比例坐标 (0-1) 执行点击 /// public static bool ClickAtProportional(float propX, float propY, bool rightClick = false) { var (x, y) = ProportionalToScreen(propX, propY); return ClickAt(x, y, rightClick); } /// /// 滚动鼠标滚轮 /// public static void Scroll(int delta) { try { // delta 为正向上滚动,为负向下滚动 // Windows 使用 WHEEL_DELTA = 120 作为一个单位 uint wheelDelta = (uint)(delta * 120); mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelDelta, 0); } catch (Exception ex) { WulaLog.Debug($"[MouseSimulator] Scroll failed: {ex.Message}"); } } /// /// 执行拖拽操作 /// public static bool Drag(int startX, int startY, int endX, int endY, int durationMs = 200) { try { // 移动到起始位置 MoveTo(startX, startY); System.Threading.Thread.Sleep(20); // 按下左键 mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); // 平滑移动到目标位置 int steps = Math.Max(5, durationMs / 20); for (int i = 1; i <= steps; i++) { float t = (float)i / steps; int x = Mathf.RoundToInt(Mathf.Lerp(startX, endX, t)); int y = Mathf.RoundToInt(Mathf.Lerp(startY, endY, t)); MoveTo(x, y); System.Threading.Thread.Sleep(durationMs / steps); } // 释放左键 mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); return true; } catch (Exception ex) { WulaLog.Debug($"[MouseSimulator] Drag failed: {ex.Message}"); return false; } } /// /// 获取当前鼠标位置 /// public static (int x, int y) GetCurrentPosition() { try { if (GetCursorPos(out POINT point)) { return (point.X, point.Y); } } catch { } return (0, 0); } } }