Files
better-genshin-impact/BetterGenshinImpact/GameTask/Common/Job/ScanPickTask.cs
FishmanTheMurloc 0c02808626 使用TorchSharp重写RodNet,以利后续优化 (#1613)
* 使用TorchSharp重写RodNet,以利后续优化

* 增加一个外部torch加载配置来代替直接的依赖,如配置不生效则使用原先手搓的算法

* BgiOnnxFactory取消单例,改为在App服务类中注册为单例,由此修复了一堆单元测试

* BgiOnnxFactory中几个静态方法改为成员方法以和App解耦;因不再有多个mat源供消耗,FishBite中文字块算法不再改动传入的mat,使得后续串联的算法不受其影响

* 将BehavioursTests中临时的配置读取方式改为读取主项目编译环境中的json文件;新建单元测试的README

* 将RodNet算法更新到 010006a44c 的版本;RodNet中关于torch库推理和直接数学计算的校验移至单元测试

* 更新RodNet算法至最新:add5672731

* 注释调试用的代码
2025-06-01 15:16:54 +08:00

170 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.GameTask.Common.Job;
/// <summary>
/// 扫描拾取任务
/// 请在安全地区使用
/// </summary>
public class ScanPickTask
{
private readonly BgiYoloPredictor _predictor = App.ServiceProvider.GetRequiredService<BgiOnnxFactory>().CreateYoloPredictor(BgiOnnxModel.BgiWorld);
private readonly double _dpi = TaskContext.Instance().DpiScale;
private readonly RECT _realCaptureRect = TaskContext.Instance().SystemInfo.CaptureAreaRect;
public async Task Start(CancellationToken ct)
{
try
{
await DoOnce(ct);
}
catch (Exception e)
{
Logger.LogDebug(e, "拾取周边物品异常");
Logger.LogError("拾取周边物品异常: {Msg}", e.Message);
}
finally
{
VisionContext.Instance().DrawContent.ClearAll();
}
}
public async Task DoOnce(CancellationToken ct)
{
var sec = TaskContext.Instance().Config.AutoFightConfig.PickDropsAfterFightSeconds;
Stopwatch timeoutStopwatch = Stopwatch.StartNew();
TimeSpan finishTime = TimeSpan.FromSeconds(sec);
Simulation.SendInput.SimulateAction(GIActions.Drop);
await ResetCamera(ct);
while (!ct.IsCancellationRequested && timeoutStopwatch.Elapsed < finishTime)
{
var (hasItems, pickItems) = DetectPickableItems();
// Logger.LogInformation("存在可拾取物品: {0}", hasItems);
if (!hasItems)
{
Simulation.ReleaseAllKey();
await ResetCamera(ct);
for (var i = 0; i < 10; i++)
{
Simulation.SendInput.Mouse.MoveMouseBy(400, 0);
if (i > 5) //前期不考虑移动扫描
await WalkByDirection(ct, GIActions.MoveForward, 100);
Simulation.SendInput.SimulateAction(GIActions.Drop);
await Delay(300, ct);
(hasItems, pickItems) = DetectPickableItems();
if (hasItems) break;
}
}
if (!hasItems) break;
// Assume 1080p resolution
// approximate dist=(x-960)**2+14*(y-888.88)**2
pickItems = pickItems.OrderBy(rect => Math.Pow(rect.X - 960, 2) +
14 * Math.Pow(rect.Bottom - 888.88, 2)).ToList();
var toPickItem = pickItems[0];
Logger.LogDebug("Fetching: {0}", toPickItem);
Logger.LogDebug("Using coord: {0} {1}", toPickItem.X, toPickItem.Bottom);
MoveTowardsItem(toPickItem);
await Delay(200, ct);
Simulation.SendInput.SimulateAction(GIActions.Drop);
}
Logger.LogInformation("超时或视野内没有可拾取物品,结束扫描");
Simulation.ReleaseAllKey();
Simulation.SendInput.SimulateAction(GIActions.Drop);
}
/// <summary>
/// Moves the character towards the specified item by controlling movement keys
/// </summary>
/// <param name="toPickItem">The item to move towards</param>
private static void MoveTowardsItem(Rect toPickItem)
{
// 对于比较远的物品Y坐标靠上先用前进靠近
// 需要避免两个对向的键同时按下
if (toPickItem.Bottom > 560)
{
if (toPickItem.X < 760)
{
Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown);
}
else if (toPickItem.X > 1040)
{
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyDown);
}
else
{
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp);
}
}
if (toPickItem.Bottom < 770)
{
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
}
else if (toPickItem.Bottom > 900)
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown);
}
else
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp);
}
}
/// <summary>
/// Detects pickable items in the current view
/// </summary>
/// <returns>A tuple containing whether items were found and the list of pickable items</returns>
private (bool hasItems, List<Rect> pickItems) DetectPickableItems()
{
var ra = CaptureToRectArea();
var resultDic = _predictor.Detect(ra);
// 过滤出可拾取物品
var pickItems = resultDic.Where(x => x.Key is "drops" or "ore")
.SelectMany(x => x.Value).ToList();
return (pickItems.Count > 0, pickItems);
}
private static async Task WalkByDirection(CancellationToken ct, GIActions act, int ms = 1000)
{
Simulation.SendInput.SimulateAction(act, KeyType.KeyDown);
await Delay(ms, ct);
Simulation.SendInput.SimulateAction(act, KeyType.KeyUp);
}
// 回正 并下移视角
private async Task ResetCamera(CancellationToken ct)
{
Simulation.SendInput.Keyboard.Mouse.MiddleButtonClick();
await Delay(500, ct);
Simulation.SendInput.Keyboard.Mouse.MoveMouseBy(0, (int)(500 * _dpi));
await Delay(100, ct);
}
}