auto pathing: update

- 移除了不必要的 `using` 语句,添加了新的 `using` 语句。
- 将 `Waypoints` 属性重命名为 `Positions`。
- 更新了 `BuildFromFilePath` 方法中的 `JsonOptions` 引用。
- 将 `Waypoint` 类移到单独的文件中,并添加了相关属性和注释。
- 添加了 `SaveToFile` 方法,用于将 `PathingTask` 对象保存到文件。
- 更新了 `PathExecutor` 中的 `TaskControl` 引用,简化了代码。
- 更新了 `PathRecorder` 的属性和方法,使用私有字段 `_pathingTask`。
- 更新了 `MapPathingPage.xaml` 的命令绑定。
- 更新了 `HotKeyPageViewModel` 和 `MapPathingViewModel` 的逻辑。
- 添加了新的 `PathingTaskConfig` 类,用于配置路径任务。
This commit is contained in:
辉鸭蛋
2024-09-08 02:23:45 +08:00
parent 5818bcd7ed
commit 1fd5320df2
9 changed files with 146 additions and 111 deletions

View File

@@ -1,12 +1,8 @@
using BetterGenshinImpact.Core.Recorder;
using BetterGenshinImpact.Core.Script.Group;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
namespace BetterGenshinImpact.GameTask.AutoPathing.Model;
@@ -14,33 +10,17 @@ namespace BetterGenshinImpact.GameTask.AutoPathing.Model;
public class PathingTask
{
public PathingTaskInfo Info { get; set; } = new();
public List<Waypoint> Waypoints { get; set; } = [];
public List<Waypoint> Positions { get; set; } = [];
public static PathingTask BuildFromFilePath(string filePath)
{
var json = File.ReadAllText(filePath);
return JsonSerializer.Deserialize<PathingTask>(json, KeyMouseRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize PathingTask");
return JsonSerializer.Deserialize<PathingTask>(json, PathRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize PathingTask");
}
public void SaveToFile(string filePath)
{
var json = JsonSerializer.Serialize(this, PathRecorder.JsonOptions);
File.WriteAllText(filePath, json, Encoding.UTF8);
}
}
[Serializable]
public class Waypoint
{
public double X { get; set; }
public double Y { get; set; }
/// <summary>
/// <see cref="WaypointType"/>
/// </summary>
public string Type { get; set; } = WaypointType.Path.Code;
/// <summary>
/// <see cref="MoveModeEnum"/>
/// </summary>
public string MoveMode { get; set; } = MoveModeEnum.Walk.Code;
/// <summary>
/// <see cref="ActionEnum"/>
/// </summary>
public string? Action { get; set; }
}

View File

@@ -0,0 +1,14 @@
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace BetterGenshinImpact.GameTask.AutoPathing.Model;
[Serializable]
public class PathingTaskConfig
{
// 持续操作 切换某个角色 长E or 短E
// 持续疾跑
// 边跳边走
}

View File

@@ -1,6 +1,5 @@
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace BetterGenshinImpact.GameTask.AutoPathing.Model;

View File

@@ -0,0 +1,26 @@
using System;
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
namespace BetterGenshinImpact.GameTask.AutoPathing.Model;
[Serializable]
public class Waypoint
{
public double X { get; set; }
public double Y { get; set; }
/// <summary>
/// <see cref="WaypointType"/>
/// </summary>
public string Type { get; set; } = WaypointType.Path.Code;
/// <summary>
/// <see cref="MoveModeEnum"/>
/// </summary>
public string MoveMode { get; set; } = MoveModeEnum.Walk.Code;
/// <summary>
/// <see cref="ActionEnum"/>
/// </summary>
public string? Action { get; set; }
}

View File

@@ -15,6 +15,7 @@ using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
using Vanara.PInvoke;
using Wpf.Ui.Violeta.Controls;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.GameTask.AutoPathing;
@@ -30,22 +31,19 @@ public class PathExecutor(CancellationTokenSource cts)
SystemControl.ActivateWindow();
if (task.Waypoints.Count == 0)
if (task.Positions.Count == 0)
{
TaskControl.Logger.LogWarning("没有路径点,寻路结束");
Logger.LogWarning("没有路径点,寻路结束");
return;
}
if (task.Waypoints.First().Type != WaypointType.Teleport.Code)
{
TaskControl.Logger.LogWarning("第一个路径点不是传送点,将不会进行传送");
}
task.Positions.First().Type = WaypointType.Teleport.Code;
// 这里应该判断一下自动拾取是否处于工作状态,但好像没有什么方便的读取办法
// TODO:大地图传送的时候使用游戏坐标追踪的时候应该使用2048地图图像坐标这里临时做转换后续改进
var waypoints = new List<Waypoint>();
foreach (var waypoint in task.Waypoints)
foreach (var waypoint in task.Positions)
{
if (waypoint.Type == WaypointType.Teleport.Code)
{
@@ -66,7 +64,7 @@ public class PathExecutor(CancellationTokenSource cts)
{
if (waypoint.Type == WaypointType.Teleport.Code)
{
TaskControl.Logger.LogInformation("正在传送到{x},{y}", waypoint.X, waypoint.Y);
Logger.LogInformation("正在传送到{x},{y}", waypoint.X, waypoint.Y);
await new TpTask(cts).Tp(waypoint.X, waypoint.Y);
continue;
}
@@ -85,11 +83,11 @@ public class PathExecutor(CancellationTokenSource cts)
private async Task MoveTo(Waypoint waypoint)
{
var screen = TaskControl.CaptureToRectArea();
var screen = CaptureToRectArea();
var position = Navigation.GetPosition(screen);
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
TaskControl.Logger.LogInformation("粗略接近路径点,当前位置({x1},{y1}),目标位置({x2},{y2})", position.X, position.Y, waypoint.X, waypoint.Y);
await WaitUntilRotatedTo(targetOrientation, 10);
Logger.LogInformation("粗略接近路径点,当前位置({x1},{y1}),目标位置({x2},{y2})", position.X, position.Y, waypoint.X, waypoint.Y);
await WaitUntilRotatedTo(targetOrientation, 5);
var startTime = DateTime.UtcNow;
var lastPositionRecord = DateTime.UtcNow;
var fastMode = false;
@@ -101,21 +99,21 @@ public class PathExecutor(CancellationTokenSource cts)
var now = DateTime.UtcNow;
if ((now - startTime).TotalSeconds > 30)
{
TaskControl.Logger.LogWarning("执行超时,跳过路径点");
Logger.LogWarning("执行超时,跳过路径点");
break;
}
screen = TaskControl.CaptureToRectArea();
screen = CaptureToRectArea();
position = Navigation.GetPosition(screen);
var distance = Navigation.GetDistance(waypoint, position);
TaskControl.Logger.LogInformation("接近目标点中,距离为{distance}", distance);
Logger.LogInformation("接近目标点中,距离为{distance}", distance);
if (distance < 4)
{
TaskControl.Logger.LogInformation("到达路径点附近");
Logger.LogInformation("到达路径点附近");
break;
}
if (distance > 500)
{
TaskControl.Logger.LogWarning("距离过远,跳过路径点");
Logger.LogWarning("距离过远,跳过路径点");
break;
}
if ((now - lastPositionRecord).TotalMilliseconds > 1000)
@@ -129,15 +127,15 @@ public class PathExecutor(CancellationTokenSource cts)
{
TaskControl.Logger.LogWarning("疑似卡死,尝试脱离并跳过路径点");
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
await Task.Delay(500);
await Delay(500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
await Task.Delay(500);
await Delay(500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_S);
await Task.Delay(500);
await Delay(500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_A);
await Task.Delay(500);
await Delay(500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_D);
await Task.Delay(500);
await Delay(500, cts);
return;
}
}
@@ -145,7 +143,7 @@ public class PathExecutor(CancellationTokenSource cts)
// 旋转视角
var isFlying = Bv.GetMotionStatus(screen) == MotionStatus.Fly;
targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
RotateTo(targetOrientation, screen, isFlying ? 2 : 6);
RotateTo(targetOrientation, screen);
// 根据指定方式进行移动
if (waypoint.MoveMode == MoveModeEnum.Fly.Code)
{
@@ -160,13 +158,13 @@ public class PathExecutor(CancellationTokenSource cts)
if (isFlying)
{
Simulation.SendInput.Mouse.LeftButtonClick();
await Task.Delay(1000);
await Delay(1000, cts);
continue;
}
if (waypoint.MoveMode == MoveModeEnum.Jump.Code)
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
await Task.Delay(1000);
await Delay(1000, cts);
continue;
}
// 跑步或者游泳
@@ -182,7 +180,7 @@ public class PathExecutor(CancellationTokenSource cts)
}
fastMode = !fastMode;
}
await Task.Delay(100);
await Delay(100, cts);
}
// 抬起w键
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
@@ -190,10 +188,10 @@ public class PathExecutor(CancellationTokenSource cts)
private async Task MoveCloseTo(Waypoint waypoint)
{
var screen = TaskControl.CaptureToRectArea();
var screen = CaptureToRectArea();
var position = Navigation.GetPosition(screen);
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
TaskControl.Logger.LogInformation("精确接近路径点,当前位置({x1},{y1}),目标位置({x2},{y2})", position.X, position.Y, waypoint.X, waypoint.Y);
Logger.LogInformation("精确接近路径点,当前位置({x1},{y1}),目标位置({x2},{y2})", position.X, position.Y, waypoint.X, waypoint.Y);
var isFlying = Bv.GetMotionStatus(screen) == MotionStatus.Fly;
if (waypoint.MoveMode == MoveModeEnum.Fly.Code && waypoint.Action == ActionEnum.StopFlying.Code && isFlying)
{
@@ -209,14 +207,14 @@ public class PathExecutor(CancellationTokenSource cts)
stepsTaken++;
if (stepsTaken > 8)
{
TaskControl.Logger.LogWarning("精确接近超时");
Logger.LogWarning("精确接近超时");
break;
}
screen = TaskControl.CaptureToRectArea();
screen = CaptureToRectArea();
position = Navigation.GetPosition(screen);
if (Navigation.GetDistance(waypoint, position) < 2)
{
TaskControl.Logger.LogInformation("已到达路径点");
Logger.LogInformation("已到达路径点");
break;
}
RotateTo(targetOrientation, screen); //不再改变视角
@@ -231,7 +229,7 @@ public class PathExecutor(CancellationTokenSource cts)
{
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
}
await Task.Delay(500);
await Delay(100, cts);
}
if (wPressed)
{
@@ -239,7 +237,7 @@ public class PathExecutor(CancellationTokenSource cts)
}
}
private int RotateTo(int targetOrientation, ImageRegion imageRegion, int controlRatio = 6)
private int RotateTo(int targetOrientation, ImageRegion imageRegion, int controlRatio = 1)
{
var cao = CameraOrientation.Compute(imageRegion.SrcGreyMat);
var diff = (cao - targetOrientation + 180) % 360 - 180;
@@ -248,7 +246,19 @@ public class PathExecutor(CancellationTokenSource cts)
{
return diff;
}
Simulation.SendInput.Mouse.MoveMouseBy(-controlRatio * (diff + (diff > 0 ? 1 : -1)), 0);
if (Math.Abs(diff) > 90)
{
controlRatio = 5;
}
else if (Math.Abs(diff) > 30)
{
controlRatio = 3;
}
else if (Math.Abs(diff) > 5)
{
controlRatio = 2;
}
Simulation.SendInput.Mouse.MoveMouseBy(-controlRatio * diff, 0);
return diff;
}
@@ -257,7 +267,7 @@ public class PathExecutor(CancellationTokenSource cts)
int count = 0;
while (true)
{
var screen = TaskControl.CaptureToRectArea();
var screen = CaptureToRectArea();
if (Math.Abs(RotateTo(targetOrientation, screen)) < maxDiff)
{
break;
@@ -266,7 +276,7 @@ public class PathExecutor(CancellationTokenSource cts)
{
break;
}
await Task.Delay(50);
await Delay(50, cts);
count++;
}
}

View File

@@ -1,24 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.Map;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using BetterGenshinImpact.Core.Config;
using System.IO;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask.AutoPathing.Model;
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.Map;
using BetterGenshinImpact.ViewModel.Pages;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace BetterGenshinImpact.GameTask.AutoPathing;
public class PathRecorder
{
public PathingTask PathingTask { get; set; } = new();
public static readonly JsonSerializerOptions JsonOptions = new()
{
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, // 下划线
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
private PathingTask _pathingTask = new();
public void Start()
{
_pathingTask = new PathingTask();
TaskControl.Logger.LogInformation("开始路径点记录");
var waypoint = new Waypoint();
var screen = TaskControl.CaptureToRectArea();
@@ -28,7 +38,7 @@ public class PathRecorder
waypoint.Y = position.Y;
waypoint.Type = WaypointType.Teleport.Code;
waypoint.MoveMode = MoveModeEnum.Walk.Code;
PathingTask.Waypoints.Add(waypoint);
_pathingTask.Positions.Add(waypoint);
TaskControl.Logger.LogInformation("已创建初始路径点({x},{y})", waypoint.X, waypoint.Y);
}
@@ -56,18 +66,12 @@ public class PathRecorder
// waypoint.MoveStatus = MoveStatusType.Walk.Code;
// break;
// }
PathingTask.Waypoints.Add(waypoint);
_pathingTask.Positions.Add(waypoint);
TaskControl.Logger.LogInformation("已添加途径点({x},{y})", waypoint.X, waypoint.Y);
}
public void Save()
{
var json = JsonSerializer.Serialize(PathingTask);
File.WriteAllText(Global.Absolute($@"log\way\{DateTime.Now:yyyy-MM-dd HHmmssffff}.json"), json);
}
public void Clear()
{
PathingTask = new PathingTask();
_pathingTask.SaveToFile(Path.Combine(MapPathingViewModel.PathJsonPath, $@"{DateTime.Now:yyyy-MM-dd HHmmssffff}.json"));
}
}

View File

@@ -94,10 +94,7 @@
</ListView.View>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenScriptProjectFolderCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
Header="打开目录" />
<MenuItem Command="{Binding StartRunCommand}"
<MenuItem Command="{Binding StartCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
Header="执行脚本" />
</ContextMenu>

View File

@@ -453,7 +453,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
var pathRecording = false;
HotKeySettingModels.Add(new HotKeySettingModel(
"(测试)路径记录器",
"启动/停止路径记录器",
nameof(Config.HotKeyConfig.PathRecorderHotkey),
Config.HotKeyConfig.PathRecorderHotkey,
Config.HotKeyConfig.PathRecorderHotkeyType,
@@ -465,7 +465,6 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
}
else
{
pathRecorder.Clear();
pathRecorder.Start();
}
pathRecording = !pathRecording;
@@ -473,7 +472,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
));
HotKeySettingModels.Add(new HotKeySettingModel(
"(测试)添加记录点",
"添加记录点",
nameof(Config.HotKeyConfig.AddWaypointHotkey),
Config.HotKeyConfig.AddWaypointHotkey,
Config.HotKeyConfig.AddWaypointHotkeyType,
@@ -493,11 +492,11 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
Config.HotKeyConfig.ExecutePathHotkeyType,
(_, _) =>
{
if (pathRecording)
{
new TaskRunner(DispatcherTimerOperationEnum.UseCacheImageWithTrigger)
.FireAndForget(async () => await new PathExecutor(CancellationContext.Instance.Cts).Pathing(pathRecorder.PathingTask));
}
// if (pathRecording)
// {
// new TaskRunner(DispatcherTimerOperationEnum.UseCacheImageWithTrigger)
// .FireAndForget(async () => await new PathExecutor(CancellationContext.Instance.Cts).Pathing(pathRecorder._pathingTask));
// }
}
));
}

View File

@@ -10,6 +10,11 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.AutoPathing;
using BetterGenshinImpact.GameTask.Model.Enum;
using Wpf.Ui.Controls;
using Wpf.Ui.Violeta.Controls;
@@ -18,7 +23,7 @@ namespace BetterGenshinImpact.ViewModel.Pages;
public partial class MapPathingViewModel : ObservableObject, INavigationAware, IViewModel
{
private readonly ILogger<MapPathingViewModel> _logger = App.GetLogger<MapPathingViewModel>();
private readonly string jsonPath = Global.Absolute(@"User\AutoPathing");
public static readonly string PathJsonPath = Global.Absolute(@"User\AutoPathing");
[ObservableProperty]
private ObservableCollection<PathingTask> _pathItems = [];
@@ -26,7 +31,7 @@ public partial class MapPathingViewModel : ObservableObject, INavigationAware, I
private void InitScriptListViewData()
{
_pathItems.Clear();
var fileInfos = LoadScriptFolder(jsonPath);
var fileInfos = LoadScriptFolder(PathJsonPath);
foreach (var f in fileInfos)
{
try
@@ -65,11 +70,11 @@ public partial class MapPathingViewModel : ObservableObject, INavigationAware, I
[RelayCommand]
public void OnOpenScriptsFolder()
{
if (!Directory.Exists(jsonPath))
if (!Directory.Exists(PathJsonPath))
{
Directory.CreateDirectory(jsonPath);
Directory.CreateDirectory(PathJsonPath);
}
Process.Start("explorer.exe", jsonPath);
Process.Start("explorer.exe", PathJsonPath);
}
[RelayCommand]
@@ -82,13 +87,14 @@ public partial class MapPathingViewModel : ObservableObject, INavigationAware, I
Process.Start("explorer.exe", item.ProjectPath);
}
// [RelayCommand]
// public async Task OnStartRun(ScriptProject? item)
// {
// if (item == null)
// {
// return;
// }
// await _scriptService.RunMulti([item.FolderName]);
// }
[RelayCommand]
public async Task OnStart(PathingTask? item)
{
if (item == null)
{
return;
}
await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
.RunAsync(async () => await new PathExecutor(CancellationContext.Instance.Cts).Pathing(item));
}
}