mirror of
https://github.com/babalae/better-genshin-impact
synced 2025-01-09 04:19:47 +08:00
feat: auto lifting rod
This commit is contained in:
parent
c0704c58e2
commit
6fd51f1635
@ -32,6 +32,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="GameTask\AutoFishing\Assets\1920x1080\Space.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="GameTask\AutoSkip\Assets\1920x1080\menu.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,15 @@
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFishing.Assets
|
||||
{
|
||||
public class AutoFishingAssets
|
||||
{
|
||||
public static Mat SpaceButtonMat = new(Global.Absolute(@"GameTask\AutoFishing\Assets\1920x1080\Space.png"), ImreadModes.Grayscale);
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Vision.Recognition;
|
||||
using Vision.Recognition.Helper.OpenCv;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
@ -18,22 +20,83 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
/// <returns></returns>
|
||||
public static List<Rect>? GetFishBarRect(Mat src)
|
||||
{
|
||||
using var mask = new Mat();
|
||||
using var rgbMat = new Mat();
|
||||
|
||||
Cv2.CvtColor(src, rgbMat, ColorConversionCodes.BGR2RGB);
|
||||
var lowPurple = new Scalar(255, 255, 192);
|
||||
var highPurple = new Scalar(255, 255, 192);
|
||||
Cv2.InRange(rgbMat, lowPurple, highPurple, mask);
|
||||
Cv2.Threshold(mask, mask, 0, 255, ThresholdTypes.Binary); //二值化
|
||||
|
||||
Cv2.FindContours(mask, out var contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple, null);
|
||||
if (contours.Length > 0)
|
||||
try
|
||||
{
|
||||
var boxes = contours.Select(Cv2.BoundingRect).Where(w => w.Height >= 10);
|
||||
return boxes.ToList();
|
||||
using var mask = new Mat();
|
||||
using var rgbMat = new Mat();
|
||||
|
||||
Cv2.CvtColor(src, rgbMat, ColorConversionCodes.BGR2RGB);
|
||||
var lowPurple = new Scalar(255, 255, 192);
|
||||
var highPurple = new Scalar(255, 255, 192);
|
||||
Cv2.InRange(rgbMat, lowPurple, highPurple, mask);
|
||||
Cv2.Threshold(mask, mask, 0, 255, ThresholdTypes.Binary); //二值化
|
||||
|
||||
Cv2.FindContours(mask, out var contours, out _, RetrievalModes.External,
|
||||
ContourApproximationModes.ApproxSimple, null);
|
||||
if (contours.Length > 0)
|
||||
{
|
||||
var boxes = contours.Select(Cv2.BoundingRect).Where(w => w.Height >= 10);
|
||||
return boxes.ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 匹配 “鱼儿上钩拉!”文字区域
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="liftingWordsAreaRect"></param>
|
||||
/// <returns></returns>
|
||||
public static Rect MatchFishBiteWords(Mat src, Rect liftingWordsAreaRect)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cv2.CvtColor(src, src, ColorConversionCodes.BGR2RGB);
|
||||
var lowPurple = new Scalar(253, 253, 253);
|
||||
var highPurple = new Scalar(255, 255, 255);
|
||||
Cv2.InRange(src, lowPurple, highPurple, src);
|
||||
Cv2.Threshold(src, src, 0, 255, ThresholdTypes.Binary);
|
||||
var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(20, 20),
|
||||
new OpenCvSharp.Point(-1, -1));
|
||||
Cv2.Dilate(src, src, kernel); //膨胀
|
||||
|
||||
var color = new Scalar(0, 0, 255);
|
||||
Cv2.FindContours(src, out var contours, out _, RetrievalModes.External,
|
||||
ContourApproximationModes.ApproxSimple, null);
|
||||
if (contours.Length > 0)
|
||||
{
|
||||
var boxes = contours.Select(Cv2.BoundingRect);
|
||||
var rects = boxes.ToList();
|
||||
if (rects.Count > 1)
|
||||
{
|
||||
rects.Sort((a, b) => b.Height.CompareTo(a.Height));
|
||||
}
|
||||
|
||||
//VisionContext.Instance().DrawContent.PutRect("FishBiteTipsDebug",
|
||||
// rects[0].ToWindowsRectangleOffset(liftingWordsAreaRect.X, liftingWordsAreaRect.Y)
|
||||
// .ToRectDrawable());
|
||||
if (rects[0].Height < src.Height
|
||||
&& rects[0].Width * 1.0 / rects[0].Height >= 3 // 长宽比判断
|
||||
&& liftingWordsAreaRect.Width > rects[0].Width * 3 // 文字范围3倍小于钓鱼条范围的
|
||||
&& liftingWordsAreaRect.Width * 1.0 / 2 > rects[0].X // 中轴线判断左
|
||||
&& liftingWordsAreaRect.Width * 1.0 / 2 < rects[0].X + rects[0].Width) // 中轴线判断右
|
||||
{
|
||||
return rects[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
|
||||
return Rect.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,19 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
|
||||
using Vision.Recognition;
|
||||
using Vision.Recognition.Helper.OpenCv;
|
||||
using Vision.Recognition.Helper.Simulator;
|
||||
using Vision.Recognition.Task;
|
||||
using WindowsInput;
|
||||
using static Vanara.PInvoke.User32;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Serilog.Core;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
{
|
||||
@ -23,7 +28,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
/// <summary>
|
||||
/// 钓鱼是要独占模式的
|
||||
/// 在钓鱼的时候,不应该有其他任务在执行
|
||||
/// 在触发器发现正在钓鱼的时候,启用独占模式
|
||||
/// 在触发器发现正在钓鱼的时候,启用独占模式(通过右下角的 Space 判断)
|
||||
/// </summary>
|
||||
public bool IsExclusive { get; set; }
|
||||
|
||||
@ -40,31 +45,73 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
|
||||
public void OnCapture(CaptureContent content)
|
||||
{
|
||||
// TODO 进入独占的判定
|
||||
|
||||
if (_fishBoxRect.Width == 0)
|
||||
// 进入独占的判定 通过右下角的 Space 判断
|
||||
if (!IsExclusive)
|
||||
{
|
||||
GetFishBoxArea(content.SrcMat);
|
||||
if (!content.IsReachInterval(TimeSpan.FromSeconds(1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 找右下角的 Space 按钮
|
||||
IsExclusive = FindSpaceButtonForExclusive(content);
|
||||
if (IsExclusive)
|
||||
{
|
||||
_logger.LogInformation("进入钓鱼界面");
|
||||
_fishBoxRect = new(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Fishing(content, new Mat(content.SrcMat, _fishBoxRect));
|
||||
// 进入钓鱼界面先尝试获取钓鱼框的位置
|
||||
if (_fishBoxRect.Width == 0)
|
||||
{
|
||||
if (!content.IsReachInterval(TimeSpan.FromMilliseconds(200)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_fishBoxRect = GetFishBoxArea(content.SrcMat);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 上钩判断
|
||||
FishBite(content, _fishBoxRect);
|
||||
// 钓鱼拉条
|
||||
Fishing(content, new Mat(content.SrcMat, _fishBoxRect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 找右下角的 Space 按钮
|
||||
/// 用于判断是否进入钓鱼界面
|
||||
/// 进入钓鱼界面时该触发器进入独占模式
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
private bool FindSpaceButtonForExclusive(CaptureContent content)
|
||||
{
|
||||
var grayMat = content.SrcGreyMat;
|
||||
var grayRightBottomMat = CutHelper.CutRightBottom(grayMat, grayMat.Width / 3, grayMat.Height / 5);
|
||||
var p = MatchTemplateHelper.FindSingleTarget(grayRightBottomMat, AutoFishingAssets.SpaceButtonMat);
|
||||
return p is { X: > 0, Y: > 0 };
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取钓鱼框的位置
|
||||
/// </summary>
|
||||
private void GetFishBoxArea(Mat srcMat)
|
||||
private Rect GetFishBoxArea(Mat srcMat)
|
||||
{
|
||||
srcMat = CutHelper.CutTop(srcMat, srcMat.Height / 2);
|
||||
var rects = AutoFishingImageRecognition.GetFishBarRect(srcMat);
|
||||
if (rects != null && rects.Count == 2)
|
||||
{
|
||||
if (Math.Abs(rects[0].Height - rects[1].Height) > 10)
|
||||
{
|
||||
Debug.WriteLine("两个矩形高度差距过大,未识别到钓鱼框");
|
||||
return;
|
||||
return Rect.Empty;
|
||||
}
|
||||
|
||||
if (rects[0].Width < rects[1].Width)
|
||||
@ -78,10 +125,14 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
_left = rects[0];
|
||||
}
|
||||
|
||||
// cur 是游标位置, 在初始状态下,cur 一定在left左边
|
||||
if (_left.X < _cur.X)
|
||||
|
||||
if (_left.X < _cur.X // cur 是游标位置, 在初始状态下,cur 一定在left左边
|
||||
|| _cur.Width > _left.Width // left一定比cur宽
|
||||
|| _cur.X + _cur.Width > srcMat.Width / 2 // cur 一定在屏幕左侧
|
||||
|| !(_left.X < srcMat.Width / 2 && _left.X + _left.Width > srcMat.Width / 2) // left肯定穿过游戏中轴线
|
||||
)
|
||||
{
|
||||
return;
|
||||
return Rect.Empty;
|
||||
}
|
||||
|
||||
int hExtra = _cur.Height, vExtra = _cur.Height / 4;
|
||||
@ -89,12 +140,77 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
(_left.X + _left.Width / 2 - _cur.X) * 2 + hExtra * 2, _cur.Height + vExtra * 2);
|
||||
VisionContext.Instance().DrawContent
|
||||
.PutRect("FishBox", _fishBoxRect.ToRectDrawable(new Pen(Color.LightPink, 2)));
|
||||
return _fishBoxRect;
|
||||
}
|
||||
VisionContext.Instance().DrawContent.RemoveRect("FishBox");
|
||||
return Rect.Empty;
|
||||
}
|
||||
|
||||
|
||||
private bool _isFishingProcess = false; // 提杆后会设置为true
|
||||
int _biteTipsExitCount = 0; // 钓鱼提示持续时间
|
||||
int _notFishingAfterBiteCount = 0; // 提竿后没有钓鱼的时间
|
||||
Rect _baseBiteTips = Rect.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 自动提竿
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="fishBoxRect"></param>
|
||||
private void FishBite(CaptureContent content, Rect fishBoxRect)
|
||||
{
|
||||
if (_isFishingProcess || fishBoxRect == Rect.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 自动识别的钓鱼框向下延伸到屏幕中间
|
||||
var liftingWordsAreaRect = new Rect(fishBoxRect.X, fishBoxRect.Y + fishBoxRect.Height * 2,
|
||||
fishBoxRect.Width, content.SrcMat.Height / 2 - fishBoxRect.Y - fishBoxRect.Height * 5);
|
||||
//VisionContext.Instance().DrawContent.PutRect("liftingWordsAreaRect", liftingWordsAreaRect.ToRectDrawable(new Pen(Color.Cyan, 2)));
|
||||
var currentBiteWordsTips =
|
||||
AutoFishingImageRecognition.MatchFishBiteWords(new Mat(content.SrcMat, liftingWordsAreaRect),
|
||||
liftingWordsAreaRect);
|
||||
if (currentBiteWordsTips != Rect.Empty)
|
||||
{
|
||||
if (_baseBiteTips == Rect.Empty)
|
||||
{
|
||||
_baseBiteTips = currentBiteWordsTips;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Abs(_baseBiteTips.X - currentBiteWordsTips.X) < 10
|
||||
&& Math.Abs(_baseBiteTips.Y - currentBiteWordsTips.Y) < 10
|
||||
&& Math.Abs(_baseBiteTips.Width - currentBiteWordsTips.Width) < 10
|
||||
&& Math.Abs(_baseBiteTips.Height - currentBiteWordsTips.Height) < 10)
|
||||
{
|
||||
_biteTipsExitCount++;
|
||||
VisionContext.Instance().DrawContent.PutRect("FishBiteTips",
|
||||
currentBiteWordsTips
|
||||
.ToWindowsRectangleOffset(liftingWordsAreaRect.X, liftingWordsAreaRect.Y)
|
||||
.ToRectDrawable());
|
||||
}
|
||||
else
|
||||
{
|
||||
_biteTipsExitCount = 0;
|
||||
_baseBiteTips = currentBiteWordsTips;
|
||||
}
|
||||
|
||||
if (_biteTipsExitCount >= content.FrameRate / 2d)
|
||||
{
|
||||
new InputSimulator().Mouse.LeftButtonClick();
|
||||
_logger.LogInformation(@"┌------------------------┐");
|
||||
_logger.LogInformation(" 自动提竿");
|
||||
_isFishingProcess = true;
|
||||
_biteTipsExitCount = 0;
|
||||
_baseBiteTips = Rect.Empty;
|
||||
VisionContext.Instance().DrawContent.RemoveRect("FishBiteTips");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int _noRectsCount = 0;
|
||||
private bool _isFishingProcess = false; // 提杆后会设置为true
|
||||
private Rect _cur, _left, _right;
|
||||
private MOUSEEVENTF _prevMouseEvent = 0x0;
|
||||
private bool _findFishBoxTips;
|
||||
@ -145,6 +261,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonDown();
|
||||
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonDown();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
|
||||
//Debug.WriteLine("进度不到 左键按下");
|
||||
}
|
||||
@ -154,6 +271,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonUp();
|
||||
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonUp();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
|
||||
//Debug.WriteLine("进度超出 左键松开");
|
||||
}
|
||||
@ -172,6 +290,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonUp();
|
||||
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonUp();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
|
||||
//Debug.WriteLine("进入框内中间 左键松开");
|
||||
}
|
||||
@ -181,6 +300,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonDown();
|
||||
//Simulator.PostMessage(TaskContext.Instance().GameHandle).LeftButtonDown();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
|
||||
//Debug.WriteLine("未到框内中间 左键按下");
|
||||
}
|
||||
@ -202,8 +322,32 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
_isFishingProcess = false;
|
||||
_prevMouseEvent = 0x0;
|
||||
_logger.LogInformation(" 钓鱼结束");
|
||||
//_logger.LogInformation(@"└------------------------┘");
|
||||
_logger.LogInformation(@"└------------------------┘");
|
||||
}
|
||||
|
||||
IsExclusive = FindSpaceButtonForExclusive(content);
|
||||
if (!IsExclusive)
|
||||
{
|
||||
_logger.LogInformation("退出钓鱼界面");
|
||||
//_fishBoxRect = new(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 提竿后没有钓鱼的情况
|
||||
if (_isFishingProcess && !_findFishBoxTips)
|
||||
{
|
||||
_notFishingAfterBiteCount++;
|
||||
if (_notFishingAfterBiteCount >= decimal.ToDouble(content.FrameRate) * 2)
|
||||
{
|
||||
_isFishingProcess = false;
|
||||
_notFishingAfterBiteCount = 0;
|
||||
_logger.LogInformation(" X 提竿后没有钓鱼,重置!");
|
||||
_logger.LogInformation(@"└------------------------┘");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_notFishingAfterBiteCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace BetterGenshinImpact.GameTask.AutoSkip
|
||||
|
||||
public void OnCapture(CaptureContent content)
|
||||
{
|
||||
if (content.FrameIndex % 2 == 0)
|
||||
if (content.IsReachInterval(TimeSpan.FromMilliseconds(200)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -32,14 +32,16 @@ namespace BetterGenshinImpact.GameTask
|
||||
private int _frameRate = 30;
|
||||
|
||||
|
||||
|
||||
public TaskDispatcher()
|
||||
{
|
||||
_triggers = GameTaskManager.LoadTriggers();
|
||||
|
||||
_timer.Elapsed += Tick;
|
||||
//_timer.Tick += Tick;
|
||||
}
|
||||
|
||||
public void Start(CaptureMode mode, int frameRate = 60)
|
||||
public void Start(CaptureMode mode, int frameRate = 30)
|
||||
{
|
||||
IntPtr hWnd = SystemControl.FindGenshinImpactHandle();
|
||||
if (hWnd == IntPtr.Zero)
|
||||
@ -81,8 +83,8 @@ namespace BetterGenshinImpact.GameTask
|
||||
return;
|
||||
}
|
||||
|
||||
// 帧序号自增 1分钟后归零
|
||||
_frameIndex = (_frameIndex + 1) % (_frameRate * 60);
|
||||
// 帧序号自增 1分钟后归零(MaxFrameIndexSecond)
|
||||
_frameIndex = (_frameIndex + 1) % (_frameRate * CaptureContent.MaxFrameIndexSecond);
|
||||
|
||||
// 捕获游戏画面
|
||||
//var sw = new Stopwatch();
|
||||
|
@ -68,7 +68,7 @@ namespace Vision.Recognition
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
Logger?.LogInformation("绘制识别结果");
|
||||
//Logger?.LogInformation("绘制识别结果");
|
||||
try
|
||||
{
|
||||
foreach (var kv in VisionContext.Instance().DrawContent.RectList)
|
||||
|
@ -15,6 +15,8 @@ namespace Vision.Recognition.Task
|
||||
/// </summary>
|
||||
public class CaptureContent
|
||||
{
|
||||
public static readonly int MaxFrameIndexSecond = 60;
|
||||
|
||||
public Bitmap SrcBitmap { get; }
|
||||
public int FrameIndex { get; private set; }
|
||||
public int FrameRate { get; private set; }
|
||||
@ -27,6 +29,7 @@ namespace Vision.Recognition.Task
|
||||
}
|
||||
|
||||
private Mat? _srcMat;
|
||||
|
||||
public Mat SrcMat
|
||||
{
|
||||
get
|
||||
@ -37,6 +40,7 @@ namespace Vision.Recognition.Task
|
||||
}
|
||||
|
||||
private Mat? _srcGreyMat;
|
||||
|
||||
public Mat SrcGreyMat
|
||||
{
|
||||
get
|
||||
@ -46,5 +50,21 @@ namespace Vision.Recognition.Task
|
||||
return _srcGreyMat;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 达到了什么时间间隔
|
||||
/// 最大MaxFrameIndexSecond秒
|
||||
/// </summary>
|
||||
/// <param name="interval"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsReachInterval(TimeSpan interval)
|
||||
{
|
||||
if (interval.TotalSeconds > MaxFrameIndexSecond)
|
||||
{
|
||||
throw new ArgumentException($"时间间隔不能超过{MaxFrameIndexSecond}s");
|
||||
}
|
||||
|
||||
return FrameIndex % (FrameRate * interval.TotalSeconds) == 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user