diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index 6a180a35..2d6a15d8 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -34,6 +34,9 @@ + + Always + Always diff --git a/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/menu.png b/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/menu.png new file mode 100644 index 00000000..c424325b Binary files /dev/null and b/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/menu.png differ diff --git a/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/option.png b/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/option.png index ce6c2a66..ab1400fc 100644 Binary files a/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/option.png and b/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/option.png differ diff --git a/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/stop_auto.png b/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/stop_auto.png index e933a9ba..09838c52 100644 Binary files a/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/stop_auto.png and b/BetterGenshinImpact/GameTask/AutoSkip/Assets/1920x1080/stop_auto.png differ diff --git a/BetterGenshinImpact/GameTask/AutoSkip/Assets/AutoSkipAssets.cs b/BetterGenshinImpact/GameTask/AutoSkip/Assets/AutoSkipAssets.cs index 1f326594..5370213a 100644 --- a/BetterGenshinImpact/GameTask/AutoSkip/Assets/AutoSkipAssets.cs +++ b/BetterGenshinImpact/GameTask/AutoSkip/Assets/AutoSkipAssets.cs @@ -10,7 +10,8 @@ namespace BetterGenshinImpact.GameTask.AutoSkip.Assets { public class AutoSkipAssets { - public static Mat StopAutoButtonMat = new(Global.Absolute("GameTask/AutoSkip/Assets/stop_auto.png"), ImreadModes.Grayscale); - public static Mat OptionMat = new(Global.Absolute("GameTask/AutoSkip/Assets/option.png"), ImreadModes.Grayscale); + public static Mat StopAutoButtonMat = new(Global.Absolute(@"GameTask\AutoSkip\Assets\1920x1080\stop_auto.png"), ImreadModes.Grayscale); + public static Mat OptionMat = new(Global.Absolute(@"GameTask\AutoSkip\Assets\1920x1080\option.png"), ImreadModes.Grayscale); + public static Mat MenuMat = new(Global.Absolute(@"GameTask\AutoSkip\Assets\1920x1080\menu.png"), ImreadModes.Grayscale); } } diff --git a/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs b/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs index b7b3591a..4e1947c0 100644 --- a/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs +++ b/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs @@ -1,5 +1,8 @@ using System; +using System.Diagnostics; using BetterGenshinImpact.GameTask.AutoSkip.Assets; +using BetterGenshinImpact.Utils.Extensions; +using Microsoft.Extensions.Logging; using OpenCvSharp; using Vision.Recognition.Helper.OpenCv; using Vision.Recognition.Task; @@ -9,44 +12,68 @@ namespace BetterGenshinImpact.GameTask.AutoSkip { public class AutoSkipTrigger : ITaskTrigger { + private ILogger _logger = App.GetLogger(); + public string Name => "自动剧情"; public bool IsEnabled { get; set; } public int Priority => 20; public bool IsExclusive => false; - public void Init(ITaskContext context) + public void Init() { - + IsEnabled = true; } public void OnCapture(Mat matSrc, int frameIndex) { - //TODO 切割图片加快效率 + if (frameIndex % 2 == 0) + { + return; + } + var grayMat = new Mat(); Cv2.CvtColor(matSrc, grayMat, ColorConversionCodes.BGR2GRAY); // 找左上角剧情自动的按钮 - var p1 = MatchTemplateHelper.FindSingleTarget(grayMat, AutoSkipAssets.StopAutoButtonMat); + var grayLeftTopMat = CutHelper.CutLeftTop(grayMat, grayMat.Width / 5, grayMat.Height / 5); + var p1 = MatchTemplateHelper.FindSingleTarget(grayLeftTopMat, AutoSkipAssets.StopAutoButtonMat, 0.9); if (p1 is { X: > 0, Y: > 0 }) { - //TODO 无效操作代码 需要替换 new InputSimulator().Keyboard.KeyPress(VirtualKeyCode.SPACE); - return; + Debug.WriteLine($"按下空格"); } + // 不存在则找右下的选项按钮 - var p2 = MatchTemplateHelper.FindSingleTarget(grayMat, AutoSkipAssets.OptionMat); + var grayRightBottomMat = CutHelper.CutRightBottom(grayMat, grayMat.Width / 2, grayMat.Height / 3 * 2); + var p2 = MatchTemplateHelper.FindSingleTarget(grayRightBottomMat, AutoSkipAssets.OptionMat); if (p2 is { X: > 0, Y: > 0 }) { - new InputSimulator().Mouse.MoveMouseTo(p2.X, p2.Y).LeftButtonClick(); - return; + // 不存在菜单的情况下 剧情在播放中 + var grayLeftTopMat2 = CutHelper.CutLeftTop(grayMat, grayMat.Width / 4, grayMat.Height / 4); + var pMenu = MatchTemplateHelper.FindSingleTarget(grayLeftTopMat2, AutoSkipAssets.MenuMat); + if (pMenu is { X: 0, Y: 0 }) + { + + p2 = p2.ToDesktopPositionOffset65535(grayMat.Width - grayMat.Width / 2, + grayMat.Height - grayMat.Height / 3 * 2); + new InputSimulator().Mouse.MoveMouseTo(p2.X, p2.Y).LeftButtonClick(); + _logger.LogInformation($"点击选项按钮:{p2}"); + Debug.WriteLine($"点击选项按钮:{p2}"); + return; + } + } - // 判断左上角的黑色像素个数 + + // 黑屏剧情要点击鼠标(多次) 几乎全黑的时候不用点击 var blackCount = OpenCvCommonHelper.CountGrayMatColor(grayMat, 0); var rate = blackCount * 1.0 / (grayMat.Width * grayMat.Height); - if (rate > 0.9) + if (rate > 0.7 && rate < 0.99) { - //TODO click center + var p3 = new Point(grayMat.Width / 2, grayMat.Height / 2).ToDesktopPosition65535(); + new InputSimulator().Mouse.MoveMouseTo(p3.X, p3.Y).LeftButtonClick(); + Debug.WriteLine($"点击黑屏剧情:{rate}"); return; } + // TODO 自动交付材料 } } } \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/GameTaskManager.cs b/BetterGenshinImpact/GameTask/GameTaskManager.cs index e55d9097..cb1cb906 100644 --- a/BetterGenshinImpact/GameTask/GameTaskManager.cs +++ b/BetterGenshinImpact/GameTask/GameTaskManager.cs @@ -33,6 +33,8 @@ namespace BetterGenshinImpact.GameTask List loadedTriggers = new(); loadedTriggers.Add(new AutoSkip.AutoSkipTrigger()); + loadedTriggers.ForEach(i => i.Init()); + return loadedTriggers.OrderByDescending(i => i.Priority).ToList(); } diff --git a/BetterGenshinImpact/GameTask/SystemControl.cs b/BetterGenshinImpact/GameTask/SystemControl.cs index 9a50e333..96f77f64 100644 --- a/BetterGenshinImpact/GameTask/SystemControl.cs +++ b/BetterGenshinImpact/GameTask/SystemControl.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml.Linq; using Vanara.PInvoke; @@ -50,5 +51,32 @@ namespace BetterGenshinImpact.GameTask return null; } } + + /// + /// 获取窗口位置 + /// + /// + /// + public static RECT GetWindowRect(IntPtr hWnd) + { + User32.GetWindowRect(hWnd, out var windowRect); + return windowRect; + } + + /// + /// 游戏本身分辨率获取 + /// + /// + /// + public static RECT GetGameScreenRect(IntPtr hWnd) + { + User32.GetClientRect(hWnd, out var clientRect); + return clientRect; + } + + //public static int GetCaptionHeight() + //{ + // return User32.GetSystemMetrics(User32.SystemMetric.SM_CYFRAME) + User32.GetSystemMetrics(User32.SystemMetric.SM_CYCAPTION); + //} } } \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/TaskContext.cs b/BetterGenshinImpact/GameTask/TaskContext.cs new file mode 100644 index 00000000..2247bcb0 --- /dev/null +++ b/BetterGenshinImpact/GameTask/TaskContext.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Vision.Recognition; + +namespace BetterGenshinImpact.GameTask +{ + /// + /// 任务上下文 + /// + public class TaskContext + { + + private static TaskContext? _uniqueInstance; + private static readonly object Locker = new(); + + private TaskContext() + { + } + + public static TaskContext Instance() + { + if (_uniqueInstance == null) + { + lock (Locker) + { + _uniqueInstance ??= new TaskContext(); + } + } + return _uniqueInstance; + } + + public IntPtr GameHandle { get; set; } + } +} diff --git a/BetterGenshinImpact/GameTask/TaskDispatcher.cs b/BetterGenshinImpact/GameTask/TaskDispatcher.cs index 5576979f..0af898a4 100644 --- a/BetterGenshinImpact/GameTask/TaskDispatcher.cs +++ b/BetterGenshinImpact/GameTask/TaskDispatcher.cs @@ -20,13 +20,13 @@ namespace BetterGenshinImpact.GameTask private readonly ILogger _logger = App.GetLogger(); private readonly Timer _timer = new(); - private List _triggers = new(); + private readonly List _triggers; private IWindowCapture? _capture; private int _frameIndex = 0; - private int _frameRate = 60; + private int _frameRate = 30; public TaskDispatcher() @@ -36,7 +36,7 @@ namespace BetterGenshinImpact.GameTask _timer.Elapsed += Tick; } - public void Start(CaptureMode mode, int frameRate) + public void Start(CaptureMode mode, int frameRate = 30) { IntPtr hWnd = SystemControl.FindGenshinImpactHandle(); if (hWnd == IntPtr.Zero) @@ -44,6 +44,7 @@ namespace BetterGenshinImpact.GameTask MessageBox.Show("未找到原神窗口"); return; } + TaskContext.Instance().GameHandle = hWnd; _frameRate = frameRate; @@ -63,22 +64,29 @@ namespace BetterGenshinImpact.GameTask public void Tick(object? sender, EventArgs e) { - if (_capture == null) + // 检查截图器是否初始化 + if (_capture == null || !_capture.IsCapturing) { _logger.LogError("截图器未初始化!"); Stop(); return; } + + // 检查游戏是否在前台 + if (!SystemControl.IsGenshinImpactActive()) + { + return; + } // 帧序号自增 1分钟后归零 _frameIndex = (_frameIndex + 1) % (_frameRate * 60); // 捕获游戏画面 - var sw = new Stopwatch(); - sw.Start(); + //var sw = new Stopwatch(); + //sw.Start(); var bitmap = _capture.Capture(); - sw.Stop(); - Debug.WriteLine("截图耗时:" + sw.ElapsedMilliseconds); + //sw.Stop(); + //Debug.WriteLine("截图耗时:" + sw.ElapsedMilliseconds); if (bitmap == null) { diff --git a/BetterGenshinImpact/Utils/Extensions/PointExtension.cs b/BetterGenshinImpact/Utils/Extensions/PointExtension.cs new file mode 100644 index 00000000..dc175843 --- /dev/null +++ b/BetterGenshinImpact/Utils/Extensions/PointExtension.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BetterGenshinImpact.GameTask; +using OpenCvSharp; +using Vision.Recognition.Helper.Simulator; + +namespace BetterGenshinImpact.Utils.Extensions +{ + public static class PointExtension + { + public static Point ToDesktopPosition(this Point point) + { + if (TaskContext.Instance().GameHandle == IntPtr.Zero) + { + return point; + } + + var rc = SystemControl.GetWindowRect(TaskContext.Instance().GameHandle); + return new Point(rc.X + point.X, rc.Y + point.Y); + } + + public static Point ToDesktopPosition65535(this Point point) + { + var p = point.ToDesktopPosition(); + return new Point(p.X * 65535 / PrimaryScreen.WorkingArea.Width, p.Y * 65535 / PrimaryScreen.WorkingArea.Height); + } + + public static Point ToDesktopPositionOffset(this Point point, int offsetX, int offsetY) + { + if (TaskContext.Instance().GameHandle == IntPtr.Zero) + { + return point; + } + + var rc = SystemControl.GetWindowRect(TaskContext.Instance().GameHandle); + return new Point(rc.X + point.X + offsetX, rc.Y + point.Y + offsetY); + } + + public static Point ToDesktopPositionOffset65535(this Point point, int offsetX, int offsetY) + { + var p = point.ToDesktopPositionOffset(offsetX, offsetY); + return new Point(p.X * 65535 / PrimaryScreen.WorkingArea.Width, p.Y * 65535 / PrimaryScreen.WorkingArea.Height); + } + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/Utils/PrimaryScreen.cs b/BetterGenshinImpact/Utils/PrimaryScreen.cs deleted file mode 100644 index d40c7c64..00000000 --- a/BetterGenshinImpact/Utils/PrimaryScreen.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Drawing; -using System.Runtime.InteropServices; -using Vanara.PInvoke; -using static Vanara.PInvoke.Gdi32; - -namespace BetterGenshinImpact.Utils -{ - public class PrimaryScreen - { - /// - /// 获取屏幕分辨率当前物理大小 - /// - public static Size WorkingArea - { - get - { - var hdc = User32.GetDC(IntPtr.Zero); - var size = new Size - { - Width = Gdi32.GetDeviceCaps(hdc, DeviceCap.HORZRES), - Height = Gdi32.GetDeviceCaps(hdc, DeviceCap.VERTRES) - }; - User32.ReleaseDC(IntPtr.Zero, hdc); - return size; - } - } - /// - /// 当前系统DPI_X 大小 一般为96 - /// - public static int DpiX - { - get - { - var hdc = User32.GetDC(IntPtr.Zero); - var dpiX = Gdi32.GetDeviceCaps(hdc, DeviceCap.LOGPIXELSX); - User32.ReleaseDC(IntPtr.Zero, hdc); - return dpiX; - } - } - /// - /// 当前系统DPI_Y 大小 一般为96 - /// - public static int DpiY - { - get - { - var hdc = User32.GetDC(IntPtr.Zero); - var dpiX = Gdi32.GetDeviceCaps(hdc, DeviceCap.LOGPIXELSY); - User32.ReleaseDC(IntPtr.Zero, hdc); - return dpiX; - } - } - /// - /// 获取真实设置的桌面分辨率大小 - /// - public static Size DESKTOP - { - get - { - var hdc = User32.GetDC(IntPtr.Zero); - var size = new Size - { - Width = Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPHORZRES), - Height = Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPVERTRES) - }; - User32.ReleaseDC(IntPtr.Zero, hdc); - return size; - } - } - - /// - /// 获取宽度缩放百分比 - /// - public static float ScaleX - { - get - { - var hdc = User32.GetDC(IntPtr.Zero); - var scaleX = (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPHORZRES) / (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.HORZRES); - User32.ReleaseDC(IntPtr.Zero, hdc); - return scaleX; - } - } - /// - /// 获取高度缩放百分比 - /// - public static float ScaleY - { - get - { - var hdc = User32.GetDC(IntPtr.Zero); - var scaleY = (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.DESKTOPVERTRES) / (float)Gdi32.GetDeviceCaps(hdc, DeviceCap.VERTRES); - User32.ReleaseDC(IntPtr.Zero, hdc); - return scaleY; - } - } - } -} diff --git a/BetterGenshinImpact/View/MainWindow.xaml b/BetterGenshinImpact/View/MainWindow.xaml index 4e3492cc..86d233fe 100644 --- a/BetterGenshinImpact/View/MainWindow.xaml +++ b/BetterGenshinImpact/View/MainWindow.xaml @@ -26,5 +26,6 @@ +