feat: 优化autopath MoveTo逻辑

This commit is contained in:
gqzcl 2024-10-05 13:26:15 +08:00
parent b1f6c062fb
commit 504f853893
11 changed files with 207 additions and 63 deletions

View File

@ -5,6 +5,7 @@
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWPF>true</UseWPF>
</PropertyGroup>

View File

@ -4,6 +4,7 @@
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<Platforms>x64</Platforms>
</PropertyGroup>

View File

@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationIcon>Assets\Images\logo.ico</ApplicationIcon>

View File

@ -27,6 +27,8 @@ public class PathExecutor(CancellationTokenSource cts)
private CameraRotateTask _rotateTask = new(cts);
private bool SkipWaypoint = false;
public async Task Pathing(PathingTask task)
{
_dpi = TaskContext.Instance().DpiScale;
@ -55,6 +57,12 @@ public class PathExecutor(CancellationTokenSource cts)
{
// Path不用走得很近Target需要接近但都需要先移动到对应位置
await MoveTo(waypoint);
// 跳过路径点后,当前路径点不处理
if (SkipWaypoint)
{
SkipWaypoint = false;
continue;
}
if (waypoint.Type == WaypointType.Target.Code || !string.IsNullOrEmpty(waypoint.Action))
{
@ -69,6 +77,8 @@ public class PathExecutor(CancellationTokenSource cts)
{
// 不管咋样,松开所有按键
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_SHIFT);
Simulation.SendInput.Mouse.RightButtonUp();
}
}
@ -112,107 +122,116 @@ public class PathExecutor(CancellationTokenSource cts)
private async Task MoveTo(Waypoint waypoint)
{
Logger.LogInformation("粗略接近路径点,位置({x2},{y2})", $"{waypoint.X:F1}", $"{waypoint.Y:F1}");
var screen = CaptureToRectArea();
var position = Navigation.GetPosition(screen);
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
Logger.LogInformation("粗略接近途经点,位置({x2},{y2})", $"{waypoint.X:F1}", $"{waypoint.Y:F1}");
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
var hasCharacter = false;
// TODO 增加识别角色并切换的逻辑
// 可以考虑放到游泳,攀爬,等移动逻辑中
var startTime = DateTime.UtcNow;
var lastPositionRecord = DateTime.UtcNow;
var fastMode = false;
var prevPositions = new List<Point2f>();
// 按下w一直走
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
// 新增逻辑:普通向前移动,疾跑向前移动,飞行向前移动,游泳向前移动,攀爬向前移动,角色技能向前移动,脱离卡死
// NormalForward
// SprintForward
// FlightForward
// SwimmingForward
// ClimbForward
// CharacterSkillForward
// GetOutOfTheJam
while (!cts.IsCancellationRequested)
{
var now = DateTime.UtcNow;
if ((now - startTime).TotalSeconds > 60)
{
Logger.LogWarning("执行超时,跳过路径点");
break;
}
screen = CaptureToRectArea();
position = Navigation.GetPosition(screen);
var distance = Navigation.GetDistance(waypoint, position);
Debug.WriteLine($"接近目标点中,距离为{distance}");
if (distance < 4)
if (distance < 5)
{
Logger.LogInformation("到达路径点附近");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
Logger.LogInformation("已到达路径点附近");
break;
}
// TODO 异常情况直接放到一个函数中处理,然后退出
// 超时
if (IsTimedOut(startTime))
{
Logger.LogWarning("执行超时,跳过路径点");
SkipWaypoint = true;
break;
}
// 距离终止判断
if (distance > 500)
{
Logger.LogWarning("距离过远,跳过路径点");
SkipWaypoint = true;
break;
}
if ((now - lastPositionRecord).TotalMilliseconds > 1000)
// 卡死
// TODO 攀爬时应该跳过但是如何处理看似是walk实际是攀爬的
if (IsStuck(prevPositions, position, lastPositionRecord))
{
lastPositionRecord = now;
prevPositions.Add(position);
if (prevPositions.Count > 8)
{
var delta = prevPositions[^1] - prevPositions[^8];
if (Math.Abs(delta.X) + Math.Abs(delta.Y) < 3)
{
Logger.LogWarning("疑似卡死,尝试脱离并跳过路径点");
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
await Delay(1500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
await Delay(500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_S);
await Delay(1500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_A);
await Delay(1500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_D);
await Delay(500, cts);
return;
}
}
lastPositionRecord = DateTime.UtcNow;
// 脱离卡死
await GetOutOfTheJam();
SkipWaypoint = true;
break;
}
Logger.LogInformation($"接近目标点中3距离为{distance}");
// 旋转视角
targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
_rotateTask.RotateToApproach(targetOrientation, screen);
// 根据指定方式进行移动
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
// 根据移动模式选择相应的行为
if (waypoint.MoveMode == MoveModeEnum.Fly.Code)
{
var isFlying = Bv.GetMotionStatus(screen) == MotionStatus.Fly;
if (!isFlying)
{
Debug.WriteLine("未进入飞行状态,按下空格");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
await Delay(200, cts);
}
continue;
await FlightForward();
await Delay(1000, cts);
}
// if (isFlying)
// {
// Simulation.SendInput.Mouse.LeftButtonClick();
// await Delay(1000, cts);
// continue;
// }
if (waypoint.MoveMode == MoveModeEnum.Jump.Code)
{
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
await Delay(200, cts);
continue;
ClimbForward();
await Delay(1000, cts);
}
// 跑步或者游泳
if (distance > 20 != fastMode)// 距离大于20时可以使用疾跑/自由泳
if (waypoint.MoveMode == MoveModeEnum.Swim.Code)
{
if (fastMode)
SwimmingForward();
await Delay(1000, cts);
}
if (waypoint.MoveMode == MoveModeEnum.Walk.Code)
{
if (distance >= 20)
{
Simulation.SendInput.Mouse.RightButtonUp();
if (hasCharacter)
{
CharacterSkillForward();
await Delay(200, cts);
}
else
{
SprintForward();
await Delay(500, cts);
}
}
else
{
Simulation.SendInput.Mouse.RightButtonDown();
// 结束e技能
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_E);
NormalForward();
await Delay(600, cts);
}
fastMode = !fastMode;
}
await Delay(100, cts);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_SHIFT);
}
// 抬起w键
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
}
private async Task MoveCloseTo(Waypoint waypoint)
@ -276,4 +295,119 @@ public class PathExecutor(CancellationTokenSource cts)
await handler.RunAsync(cts);
}
}
// 普通向前移动
private void NormalForward()
{
Logger.LogInformation("正常向前移动");
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
}
// 攀爬向前移动
private void ClimbForward()
{
Logger.LogInformation("进行攀爬");
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
// TODO 角色处理逻辑:卡其娜,西诺宁
}
// 疾跑向前移动
private void SprintForward()
{
Logger.LogInformation("疾跑向前移动");
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_SHIFT);
}
// 飞行向前移动
private async Task FlightForward()
{
Logger.LogInformation("进入飞行模式");
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
var screen = CaptureToRectArea();
var isFlying = Bv.GetMotionStatus(screen) == MotionStatus.Fly;
if (!isFlying)
{
Logger.LogInformation("未进入飞行状态,按下空格展开翅膀");
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
await Delay(200, cts); // 延迟,确保飞行动作完成
}
}
// 游泳向前移动
private void SwimmingForward()
{
Logger.LogInformation("进入游泳模式");
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
// TODO 添加芙宁娜处理
// 有芙宁娜时定时释放e技能
}
private void CharacterSkillForward()
{
Logger.LogInformation("使用角色技能向前移动");
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_E).Sleep(500).KeyUp(User32.VK.VK_E);
// TODO 根据不同角色进行处理:夜兰,闲云,散兵,早柚,玛拉妮,基尼奇
// 玛拉妮,散兵正常移动即可,但是夜兰,早柚是持续向前移动的,需要特殊处理
// 闲云,基尼奇容易超出距离,但是问题不大
}
// 脱离卡死
private async Task GetOutOfTheJam()
{
Logger.LogWarning("脱离卡死状态");
// 脱离攀爬状态
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
await Delay(1500, cts);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
await Delay(500, cts);
// 向后移动
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_S);
await Task.Delay(1500);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_S);
// 向左移动
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_A);
await Task.Delay(1000);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_A);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
await Delay(500, cts);
// 向右移动
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_D);
await Task.Delay(1000);
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_D);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
await Delay(500, cts);
// 跳跃
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
await Task.Delay(200); // 等待跳跃动作
}
public bool IsTimedOut(DateTime startTime)
{
var now = DateTime.UtcNow;
return (now - startTime).TotalSeconds > 60;
}
public bool IsStuck(List<Point2f> prevPositions, Point2f position, DateTime lastPositionRecord)
{
if ((DateTime.UtcNow - lastPositionRecord).TotalMilliseconds > 1000)
{
prevPositions.Add(position);
if (prevPositions.Count > 8)
{
var delta = prevPositions[^1] - prevPositions[^8];
if (Math.Abs(delta.X) + Math.Abs(delta.Y) < 3)
{
Logger.LogWarning("疑似卡死,尝试脱离并跳过路径点");
return true;
}
}
}
return false;
}
}

View File

@ -7,6 +7,7 @@
<LangVersion>12.0</LangVersion>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWinUI>false</UseWinUI>
<Platforms>AnyCPU;x64</Platforms>
<ApplicationIcon>Resources\Images\FaviconUninst.ico</ApplicationIcon>

View File

@ -7,6 +7,7 @@
<LangVersion>12.0</LangVersion>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWinUI>false</UseWinUI>
<Platforms>AnyCPU;x64</Platforms>
<ApplicationIcon>Resources\Images\FaviconSetup.ico</ApplicationIcon>

View File

@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<UseWindowsForms>True</UseWindowsForms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWPF>True</UseWPF>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

View File

@ -7,6 +7,7 @@
<Platforms>x64</Platforms>
<LangVersion>12.0</LangVersion>
<UseWindowsForms>True</UseWindowsForms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>

View File

@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<LangVersion>12.0</LangVersion>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>

View File

@ -7,6 +7,7 @@
<UseWPF>true</UseWPF>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<Platforms>x64</Platforms>
</PropertyGroup>

View File

@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>12.0</LangVersion>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
</PropertyGroup>