From fc5d37670c14d0497de02b0344e99bc1cc51d7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E9=B8=AD=E8=9B=8B?= Date: Thu, 6 Jun 2024 00:44:52 +0800 Subject: [PATCH] auto map test --- .../OpenCv/FeatureMatch/FeatureMatcher.cs | 16 +-- .../AutoTrackPath/AutoTrackPathTask.cs | 101 ++++++++++++++---- .../AutoTrackPath/Model/GiPathPoint.cs | 4 +- .../GameTask/AutoTrackPath/MovementControl.cs | 33 ++++++ .../GameTask/Common/Map/CameraOrientation.cs | 7 +- .../Common/Map/CharacterOrientation.cs | 16 ++- .../GameTask/Common/Map/EntireMap.cs | 7 +- BetterGenshinImpact/Helpers/MathHelper.cs | 29 +++++ 8 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 BetterGenshinImpact/GameTask/AutoTrackPath/MovementControl.cs create mode 100644 BetterGenshinImpact/Helpers/MathHelper.cs diff --git a/BetterGenshinImpact/Core/Recognition/OpenCv/FeatureMatch/FeatureMatcher.cs b/BetterGenshinImpact/Core/Recognition/OpenCv/FeatureMatch/FeatureMatcher.cs index 6dae152e..1b074a1d 100644 --- a/BetterGenshinImpact/Core/Recognition/OpenCv/FeatureMatch/FeatureMatcher.cs +++ b/BetterGenshinImpact/Core/Recognition/OpenCv/FeatureMatch/FeatureMatcher.cs @@ -68,19 +68,20 @@ public class FeatureMatcher /// /// /// - public Point2f[]? Match(Mat queryMat) + public Point2f[]? Match(Mat queryMat, Mat? queryMatMask = null) { - return Match(_trainKeyPoints, _trainRet, queryMat); + return Match(_trainKeyPoints, _trainRet, queryMat, queryMatMask); } /// /// 合并邻近的特征点后匹配(临近特征) /// - /// + /// 查询的图 /// 上次匹配到的坐标x /// 上次匹配到的坐标y + /// 查询Mask /// - public Point2f[]? Match(Mat queryMat, int prevX, int prevY) + public Point2f[]? Match(Mat queryMat, int prevX, int prevY, Mat? queryMatMask = null) { var (cellRow, cellCol) = KeyPointFeatureBlockHelper.GetCellIndex(_trainMat, _splitRow, _splitCol, prevX, prevY); Debug.WriteLine($"当前坐标({prevX},{prevY})在特征块({cellRow},{cellCol})中"); @@ -90,7 +91,7 @@ public class FeatureMatcher _lastMergedBlock = KeyPointFeatureBlockHelper.MergeNeighboringFeatures(_blocks, _trainRet, cellRow, cellCol); } - return Match(_lastMergedBlock.KeyPointArray, _lastMergedBlock.Descriptor!, queryMat); + return Match(_lastMergedBlock.KeyPointArray, _lastMergedBlock.Descriptor!, queryMat, queryMatMask); } /// @@ -99,14 +100,15 @@ public class FeatureMatcher /// /// /// + /// /// - public Point2f[]? Match(KeyPoint[] trainKeyPoints, Mat trainRet, Mat queryMat) + public Point2f[]? Match(KeyPoint[] trainKeyPoints, Mat trainRet, Mat queryMat, Mat? queryMatMask = null) { SpeedTimer speedTimer = new(); using var queryRet = new Mat(); - _feature2D.DetectAndCompute(queryMat, null, out var queryKeyPoints, queryRet); + _feature2D.DetectAndCompute(queryMat, queryMatMask, out var queryKeyPoints, queryRet); speedTimer.Record("模板生成KeyPoint"); using var flnMatcher = new FlannBasedMatcher(); diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs index bbf89b44..d7d0ffca 100644 --- a/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs +++ b/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs @@ -12,6 +12,8 @@ using BetterGenshinImpact.GameTask.AutoFight.Assets; using BetterGenshinImpact.GameTask.AutoFight.Config; using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception; using BetterGenshinImpact.GameTask.AutoTrackPath.Model; +using BetterGenshinImpact.GameTask.Common; +using BetterGenshinImpact.GameTask.Common.Element.Assets; using BetterGenshinImpact.GameTask.Common.Map; using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.GameTask.Model.Enum; @@ -42,11 +44,19 @@ public class AutoTrackPathTask { "枫丹", [4515, 3631] }, }; + private GiPath _way; + + // 视角偏移移动单位 + private const int CharMovingUnit = 100; + public AutoTrackPathTask(AutoTrackPathParam taskParam) { _taskParam = taskParam; var json = File.ReadAllText(Global.Absolute(@"GameTask\AutoTrackPath\Assets\tp.json")); - _tpPositions = JsonSerializer.Deserialize>(json, ConfigService.JsonOptions) ?? throw new Exception("tp.json deserialize failed"); ; + _tpPositions = JsonSerializer.Deserialize>(json, ConfigService.JsonOptions) ?? throw new Exception("tp.json deserialize failed"); + + var wayJson = File.ReadAllText(Global.Absolute(@"log\way\way1.json")); + _way = JsonSerializer.Deserialize(wayJson, ConfigService.JsonOptions) ?? throw new Exception("way json deserialize failed"); } public async void Start() @@ -103,40 +113,88 @@ public class AutoTrackPathTask public void DoTask() { - // 解析路线,第一个点为起点 + // 1. 传送到最近的传送点 + var first = _way.WayPointList[0]; // 解析路线,第一个点为起点 + Tp(first.Pt.X, first.Pt.Y); - // 找到起点最近的传送点位置 + // 2. 等待传送完成 + Sleep(1000); + NewRetry.Do(() => + { + var ra = GetRectAreaFromDispatcher(); + var miniMapMat = GetMiniMapMat(ra); + if (miniMapMat == null) + { + throw new RetryException("等待传送完成"); + } + }, TimeSpan.FromSeconds(1), 100); + Logger.LogInformation("传送完成"); - // 初始化全地图特征 + // 3. 横向移动偏移量校准,移动指定偏移、按下W后识别朝向 + var angleOffset = GetOffsetAngle(); - // --- 地图传送模块 --- + // 4. 针对点位进行直线追踪 - // M 打开地图识别当前位置,中心点为当前位置 + // 循环每个点位 - // 计算传送点位置离哪个地图切换后的中心点最近,切换到该地图 + // 识别当前位置、人物朝向任务 偏差太大要修正 - // 快速移动到目标传送点所在的区域 + // 计算当前位置到目标点的角度 - // 计算坐标后点击 + // 旋转到目标角度 - // 触发一次快速传送功能 + // 移动到目标点 不用太精准,只要在一定范围内就行 - // --- 地图传送模块 --- - - // 横向移动偏移量校准,移动指定偏移、按下W后识别朝向 - - // 针对点位进行直线追踪 + // 移动时循环识别当前位置、人物朝向任务 } - public void Tp(string name) + public void Track(List pList) { - // 通过大地图传送到指定传送点 } + public int GetOffsetAngle() + { + var angle1 = GetCharacterOrientationAngle(); + Simulation.SendInput.Mouse.MoveMouseBy(CharMovingUnit, 0).Sleep(200) + .Keyboard.KeyPress(User32.VK.VK_W).Sleep(500); + var angle2 = GetCharacterOrientationAngle(); + var angleOffset = angle2 - angle1; + Logger.LogInformation("横向移动偏移量校准:鼠标平移100单位,角度转动{AngleOffset}", angleOffset); + return angleOffset; + } + + public Mat? GetMiniMapMat(ImageRegion ra) + { + var paimon = ra.Find(ElementAssets.Instance.PaimonMenuRo); + if (paimon.IsExist()) + { + return new Mat(ra.SrcMat, new Rect(paimon.X + 24, paimon.Y - 15, 210, 210)); + } + return null; + } + + public int GetCharacterOrientationAngle() + { + var ra = GetRectAreaFromDispatcher(); + var miniMapMat = GetMiniMapMat(ra); + if (miniMapMat == null) + { + throw new InvalidOperationException("当前不在主界面"); + } + + var angle = CharacterOrientation.Compute(miniMapMat); + Logger.LogInformation("当前角度:{Angle}", angle); + CameraOrientation.DrawDirection(ra, angle); + return angle; + } + + /// + /// 通过大地图传送到指定坐标最近的传送点,然后移动到指定坐标 + /// + /// + /// public void Tp(double tpX, double tpY) { - // 通过大地图传送到指定坐标最近的传送点,然后移动到指定坐标 - // 获取最近的传送点位置 var (x, y) = GetRecentlyTpPoint(tpX, tpY); Logger.LogInformation("({TpX},{TpY}) 最近的传送点位置 ({X},{Y})", tpX, tpY, x, y); @@ -337,6 +395,11 @@ public class AutoTrackPathTask } } + public void Tp(string name) + { + // 通过大地图传送到指定传送点 + } + public void TpByF1(string name) { // 传送到指定传送点 diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/Model/GiPathPoint.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/Model/GiPathPoint.cs index 0719bf63..0c6978e2 100644 --- a/BetterGenshinImpact/GameTask/AutoTrackPath/Model/GiPathPoint.cs +++ b/BetterGenshinImpact/GameTask/AutoTrackPath/Model/GiPathPoint.cs @@ -22,11 +22,11 @@ public class GiPathPoint public static GiPathPoint BuildFrom(Rect matchRect, int index) { - var pt = MapCoordinate.Main1024ToGame(matchRect.GetCenterPoint()); + var pt = MapCoordinate.Main2048ToGame(matchRect.GetCenterPoint()); return new GiPathPoint { Pt = pt, - MatchRect = new Rect(pt, matchRect.Size), + MatchRect = matchRect, Index = index, Time = DateTime.Now }; diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/MovementControl.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/MovementControl.cs new file mode 100644 index 00000000..dc90c748 --- /dev/null +++ b/BetterGenshinImpact/GameTask/AutoTrackPath/MovementControl.cs @@ -0,0 +1,33 @@ +using BetterGenshinImpact.Core.Simulator; +using BetterGenshinImpact.Model; +using Vanara.PInvoke; + +namespace BetterGenshinImpact.GameTask.AutoTrackPath; + +public class MovementControl : Singleton +{ + private bool _wDown = false; + + public void WDown() + { + if (!_wDown) + { + _wDown = true; + Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W); + } + } + + public void WUp() + { + if (_wDown) + { + _wDown = false; + Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W); + } + } + + public void SpacePress() + { + Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE); + } +} diff --git a/BetterGenshinImpact/GameTask/Common/Map/CameraOrientation.cs b/BetterGenshinImpact/GameTask/Common/Map/CameraOrientation.cs index 979c4677..c7e52031 100644 --- a/BetterGenshinImpact/GameTask/Common/Map/CameraOrientation.cs +++ b/BetterGenshinImpact/GameTask/Common/Map/CameraOrientation.cs @@ -1,10 +1,9 @@ -using System; +using BetterGenshinImpact.GameTask.Model.Area; +using OpenCvSharp; +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; -using BetterGenshinImpact.GameTask.Model.Area; -using BetterGenshinImpact.View.Drawable; -using OpenCvSharp; using Point = OpenCvSharp.Point; using Size = OpenCvSharp.Size; diff --git a/BetterGenshinImpact/GameTask/Common/Map/CharacterOrientation.cs b/BetterGenshinImpact/GameTask/Common/Map/CharacterOrientation.cs index d5a487f2..391dade3 100644 --- a/BetterGenshinImpact/GameTask/Common/Map/CharacterOrientation.cs +++ b/BetterGenshinImpact/GameTask/Common/Map/CharacterOrientation.cs @@ -1,14 +1,12 @@ -using BetterGenshinImpact.View.Drawable; -using OpenCvSharp; +using OpenCvSharp; +using System; namespace BetterGenshinImpact.GameTask.Common.Map; public class CharacterOrientation { - public static void Compute(CaptureContent content) + public static int Compute(Mat mat) { - var mat = new Mat(content.CaptureRectArea.SrcMat, new Rect(62, 19, 212, 212)); - var splitMat = mat.Split(); // 1. 红蓝通道按位与 @@ -93,12 +91,20 @@ public class CharacterOrientation maxBlackCount = blackCount; correctP1 = midPoint; // 底边中点 correctP2 = targetPoint; // 顶点 + + // 计算弧度 + double radians = Math.Atan2(correctP2.Y - correctP1.Y, correctP2.X - correctP1.X); + + // 将弧度转换为度数 + double angle = radians * (180.0 / Math.PI); + return (int)angle; } } // VisionContext.Instance().DrawContent.PutLine("co", new LineDrawable(correctP1, correctP2 + (correctP2 - correctP1) * 3)); } } + return -1; } static Point Midpoint(Point p1, Point p2) diff --git a/BetterGenshinImpact/GameTask/Common/Map/EntireMap.cs b/BetterGenshinImpact/GameTask/Common/Map/EntireMap.cs index 826313b7..daeade1d 100644 --- a/BetterGenshinImpact/GameTask/Common/Map/EntireMap.cs +++ b/BetterGenshinImpact/GameTask/Common/Map/EntireMap.cs @@ -81,19 +81,20 @@ public class EntireMap : Singleton /// 移动匹配 /// /// 灰度图 + /// 遮罩 /// - public Rect GetMiniMapPositionByFeatureMatch(Mat greyMat) + public Rect GetMiniMapPositionByFeatureMatch(Mat greyMat, Mat? mask = null) { try { Point2f[]? pArray; if (_prevX != -1 && _prevY != -1) { - pArray = _featureMatcher.Match(greyMat, _prevX, _prevY); + pArray = _featureMatcher.Match(greyMat, _prevX, _prevY, mask); } else { - pArray = _featureMatcher.Match(greyMat); + pArray = _featureMatcher.Match(greyMat, mask); } if (pArray == null || pArray.Length < 4) diff --git a/BetterGenshinImpact/Helpers/MathHelper.cs b/BetterGenshinImpact/Helpers/MathHelper.cs new file mode 100644 index 00000000..07900863 --- /dev/null +++ b/BetterGenshinImpact/Helpers/MathHelper.cs @@ -0,0 +1,29 @@ +using System; +using OpenCvSharp; + +namespace BetterGenshinImpact.Helpers; + +public class MathHelper +{ + /// + /// 点到直线的最短距离 + /// + /// + /// + /// + /// + public static double CalculateDistance(Point point, Point point1, Point point2) + { + // 直线的方向向量 + double a = point2.Y - point1.Y; + double b = point1.X - point2.X; + double c = point2.X * point1.Y - point1.X * point2.Y; + + // 使用距离公式计算点到直线的最短距离 + double numerator = Math.Abs(a * point.X + b * point.Y + c); + double denominator = Math.Sqrt(a * a + b * b); + double distance = numerator / denominator; + + return distance; + } +}