mirror of
https://github.com/babalae/better-genshin-impact
synced 2025-01-09 04:19:47 +08:00
team identification support online
修复错误信息,改进联机状态处理 更新了 `BetterGenshinImpact.csproj` 文件中的程序集版本号,从 `0.35.2` 更新为 `0.35.4`。 修正了 `ScriptProject.cs` 文件中抛出 `FileNotFoundException` 异常时的错误信息,将 "manifest.json文件存在" 改为 "manifest.json文件不存在"。 在 `AutoFightAssets.cs` 文件中: - 为 `AvatarSideIconRectList` 和 `AvatarIndexRectList` 添加了注释,解释其在非联机状态下的用途。 - 添加了多个新的属性和注释,用于处理联机状态下的角色头像和对应的白色块位置。 - 初始化了 `OnePRa` 和 `PRa` 两个识别对象,用于识别联机状态下的1P和P图标。 在 `Avatar.cs` 文件中: - 修改了角色切换逻辑,使用 `CombatScenes.ExpectedTeamAvatarNum` 替代硬编码的数字。 - 在 `TrySwitch` 方法中添加了 `needLog` 参数,并在切换成功时记录日志。 - 移除了部分注释代码,并在日志中保存了角色切换和索引区域的截图。 - 添加了 `System.Diagnostics` 的引用。 在 `CombatScenes.cs` 文件中: - 将 `Avatars` 初始化为空数组。 - 添加了 `ExpectedTeamAvatarNum` 属性,默认值为4。 - 在 `InitializeTeam` 方法中添加了联机状态的判断和处理逻辑。 - 修改了队伍识别逻辑,使用动态数组替代固定长度的数组。 - 修改了 `CheckTeamInitialized` 方法,使用 `ExpectedTeamAvatarNum` 替代硬编码的数字。 - 修改了 `BuildAvatars` 方法,添加了对联机状态下角色编号位置信息的处理。 - 修改了 `SelectAvatar` 方法,使用 `GetValueOrDefault` 替代 `TryGetValue`。 在 `ScriptControlViewModel.cs` 文件中,设置 `WindowStartupLocation` 为 `WindowStartupLocation.CenterOwner`。 添加了 `1p.png` 和 `p.png` 两个新图像文件,用于识别联机状态下的1P和P图标。
This commit is contained in:
parent
98d2664c28
commit
44190a522b
@ -10,7 +10,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationIcon>Assets\Images\logo.ico</ApplicationIcon>
|
||||
<AssemblyName>BetterGI</AssemblyName>
|
||||
<AssemblyVersion>0.35.2</AssemblyVersion>
|
||||
<AssemblyVersion>0.35.4</AssemblyVersion>
|
||||
<Platforms>x64</Platforms>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
@ -29,7 +29,7 @@ public class ScriptProject
|
||||
ManifestFile = Path.GetFullPath(Path.Combine(ProjectPath, "manifest.json"));
|
||||
if (!File.Exists(ManifestFile))
|
||||
{
|
||||
throw new FileNotFoundException("manifest.json文件存在,请确认此脚本是JS脚本类型。" + ManifestFile);
|
||||
throw new FileNotFoundException("manifest.json文件不存在,请确认此脚本是JS脚本类型。" + ManifestFile);
|
||||
}
|
||||
|
||||
Manifest = Manifest.FromJson(File.ReadAllText(ManifestFile));
|
||||
|
BIN
BetterGenshinImpact/GameTask/AutoFight/Assets/1920x1080/1p.png
Normal file
BIN
BetterGenshinImpact/GameTask/AutoFight/Assets/1920x1080/1p.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
BetterGenshinImpact/GameTask/AutoFight/Assets/1920x1080/p.png
Normal file
BIN
BetterGenshinImpact/GameTask/AutoFight/Assets/1920x1080/p.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 607 B |
@ -9,8 +9,8 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
|
||||
{
|
||||
public Rect TeamRectNoIndex;
|
||||
public Rect TeamRect;
|
||||
public List<Rect> AvatarSideIconRectList;
|
||||
public List<Rect> AvatarIndexRectList;
|
||||
public List<Rect> AvatarSideIconRectList; // 侧边栏角色头像 非联机状态下
|
||||
public List<Rect> AvatarIndexRectList; // 侧边栏角色头像对应的白色块 非联机状态下
|
||||
public Rect ERect;
|
||||
public Rect QRect;
|
||||
public Rect EndTipsUpperRect; // 挑战达成提示
|
||||
@ -29,6 +29,13 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
|
||||
|
||||
public Dictionary<string, string> AvatarCostumeMap;
|
||||
|
||||
// 联机
|
||||
public RecognitionObject OnePRa;
|
||||
|
||||
public RecognitionObject PRa;
|
||||
public Dictionary<string, List<Rect>> AvatarSideIconRectListMap; // 侧边栏角色头像 联机状态下
|
||||
public Dictionary<string, List<Rect>> AvatarIndexRectListMap; // 侧边栏角色头像对应的白色块 联机状态下
|
||||
|
||||
private AutoFightAssets()
|
||||
{
|
||||
TeamRectNoIndex = new Rect(CaptureRect.Width - (int)(355 * AssetScale), (int)(220 * AssetScale),
|
||||
@ -79,6 +86,78 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
|
||||
{ "Sea", "海风之梦" },
|
||||
};
|
||||
|
||||
// 联机
|
||||
// 1p_2 与 p_2 为同一位置
|
||||
// 1p_4 与 p_4 为同一位置
|
||||
AvatarSideIconRectListMap = new Dictionary<string, List<Rect>>
|
||||
{
|
||||
{
|
||||
"1p_2", [
|
||||
new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(375 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale)),
|
||||
new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(470 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale)),
|
||||
]
|
||||
},
|
||||
{
|
||||
"1p_3", [
|
||||
new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(375 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale)),
|
||||
new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(470 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale)),
|
||||
]
|
||||
},
|
||||
{ "1p_4", [new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(515 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale))] },
|
||||
{
|
||||
"p_2", [
|
||||
new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(375 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale)),
|
||||
new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(470 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale)),
|
||||
]
|
||||
},
|
||||
{ "p_3", [new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(475 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale))] },
|
||||
{ "p_4", [new Rect(CaptureRect.Width - (int)(155 * AssetScale), (int)(515 * AssetScale), (int)(76 * AssetScale), (int)(76 * AssetScale))] },
|
||||
};
|
||||
|
||||
AvatarIndexRectListMap = new Dictionary<string, List<Rect>>
|
||||
{
|
||||
{
|
||||
"1p_2", [
|
||||
new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(412 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale)),
|
||||
new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(508 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale)),
|
||||
]
|
||||
},
|
||||
{
|
||||
"1p_3", [
|
||||
new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(459 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale)),
|
||||
new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(555 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale)),
|
||||
]
|
||||
},
|
||||
{ "1p_4", [new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(552 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale))] },
|
||||
{
|
||||
"p_2", [
|
||||
new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(412 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale)),
|
||||
new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(508 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale)),
|
||||
]
|
||||
},
|
||||
{ "p_3", [new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(412 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale))] },
|
||||
{ "p_4", [new Rect(CaptureRect.Width - (int)(61 * AssetScale), (int)(507 * AssetScale), (int)(28 * AssetScale), (int)(24 * AssetScale))] },
|
||||
};
|
||||
|
||||
// 左上角的 1P 图标
|
||||
OnePRa = new RecognitionObject
|
||||
{
|
||||
Name = "1P",
|
||||
RecognitionType = RecognitionTypes.TemplateMatch,
|
||||
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "1p.png"),
|
||||
RegionOfInterest = new Rect(0, 0, CaptureRect.Width / 4, CaptureRect.Height / 7),
|
||||
DrawOnWindow = false
|
||||
}.InitTemplate();
|
||||
// 右侧联机的 P 图标
|
||||
PRa = new RecognitionObject
|
||||
{
|
||||
Name = "P",
|
||||
RecognitionType = RecognitionTypes.TemplateMatch,
|
||||
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "p.png"),
|
||||
RegionOfInterest = new Rect(CaptureRect.Width - CaptureRect.Width / 7, CaptureRect.Height / 5, CaptureRect.Width / 7, CaptureRect.Height / 2 - CaptureRect.Width / 7),
|
||||
DrawOnWindow = false
|
||||
}.InitTemplate();
|
||||
|
||||
WandererIconRa = new RecognitionObject
|
||||
{
|
||||
Name = "WandererIcon",
|
||||
|
@ -8,6 +8,7 @@ using BetterGenshinImpact.Helpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using BetterGenshinImpact.GameTask.AutoTrackPath;
|
||||
@ -137,14 +138,14 @@ public class Avatar
|
||||
ThrowWhenDefeated(region);
|
||||
|
||||
var notActiveCount = CombatScenes.Avatars.Count(avatar => !avatar.IsActive(region));
|
||||
if (IsActive(region) && notActiveCount == 3)
|
||||
if (IsActive(region) && notActiveCount == CombatScenes.ExpectedTeamAvatarNum - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
|
||||
// Debug.WriteLine($"切换到{Index}号位");
|
||||
// Cv2.ImWrite($"log/切换.png", content.CaptureRectArea.SrcMat);
|
||||
Cv2.ImWrite($"log/切换.png", region.SrcMat);
|
||||
Sleep(250, Ct);
|
||||
}
|
||||
}
|
||||
@ -153,6 +154,7 @@ public class Avatar
|
||||
/// 尝试切换到本角色
|
||||
/// </summary>
|
||||
/// <param name="tryTimes"></param>
|
||||
/// <param name="needLog"></param>
|
||||
/// <returns></returns>
|
||||
public bool TrySwitch(int tryTimes = 4, bool needLog = true)
|
||||
{
|
||||
@ -167,7 +169,7 @@ public class Avatar
|
||||
ThrowWhenDefeated(region);
|
||||
|
||||
var notActiveCount = CombatScenes.Avatars.Count(avatar => !avatar.IsActive(region));
|
||||
if (IsActive(region) && notActiveCount == 3)
|
||||
if (IsActive(region) && notActiveCount == CombatScenes.ExpectedTeamAvatarNum - 1)
|
||||
{
|
||||
if (needLog && i > 0)
|
||||
{
|
||||
@ -221,7 +223,7 @@ public class Avatar
|
||||
{
|
||||
// 剪裁出IndexRect区域
|
||||
var indexRa = region.DeriveCrop(IndexRect);
|
||||
// Cv2.ImWrite($"log/indexRa_{Name}.png", indexRa.SrcMat);
|
||||
Cv2.ImWrite($"log/indexRa_{Name}.png", indexRa.SrcMat);
|
||||
var count = OpenCvCommonHelper.CountGrayMatColor(indexRa.SrcGreyMat, 251, 255);
|
||||
if (count * 1.0 / (IndexRect.Width * IndexRect.Height) > 0.5)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ public class CombatScenes : IDisposable
|
||||
/// <summary>
|
||||
/// 当前配队
|
||||
/// </summary>
|
||||
public Avatar[] Avatars { get; set; } = new Avatar[1];
|
||||
public Avatar[] Avatars { get; set; } = Array.Empty<Avatar>();
|
||||
|
||||
public Dictionary<string, Avatar> AvatarMap { get; set; } = [];
|
||||
|
||||
@ -43,6 +43,8 @@ public class CombatScenes : IDisposable
|
||||
.WithSessionOptions(BgiSessionOption.Instance.Options)
|
||||
.Build();
|
||||
|
||||
public int ExpectedTeamAvatarNum { get; private set; } = 4;
|
||||
|
||||
/// <summary>
|
||||
/// 通过YOLO分类器识别队伍内角色
|
||||
/// </summary>
|
||||
@ -56,14 +58,49 @@ public class CombatScenes : IDisposable
|
||||
return this;
|
||||
}
|
||||
|
||||
// 判断当前是否处于联机状态
|
||||
List<Rect> avatarSideIconRectList;
|
||||
List<Rect> avatarIndexRectList;
|
||||
var pRaList = imageRegion.FindMulti(AutoFightAssets.Instance.PRa);
|
||||
if (pRaList.Count > 0)
|
||||
{
|
||||
var num = pRaList.Count + 1;
|
||||
if (num > 4)
|
||||
{
|
||||
throw new Exception("当前处于联机状态,但是队伍人数超过4人,无法识别");
|
||||
}
|
||||
// 联机状态下判断
|
||||
var onePRa = imageRegion.Find(AutoFightAssets.Instance.OnePRa);
|
||||
var p = "p";
|
||||
if (onePRa.IsEmpty())
|
||||
{
|
||||
Logger.LogInformation("当前处于联机状态,且当前账号是房主,联机人数{Num}人", num);
|
||||
p = "1p";
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation("当前处于联机状态,且在别人世界中,联机人数{Num}人", num);
|
||||
}
|
||||
|
||||
avatarSideIconRectList = AutoFightAssets.Instance.AvatarSideIconRectListMap[$"{p}_{num}"];
|
||||
avatarIndexRectList = AutoFightAssets.Instance.AvatarIndexRectListMap[$"{p}_{num}"];
|
||||
|
||||
ExpectedTeamAvatarNum = avatarSideIconRectList.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
avatarSideIconRectList = AutoFightAssets.Instance.AvatarSideIconRectList;
|
||||
avatarIndexRectList = AutoFightAssets.Instance.AvatarIndexRectList;
|
||||
}
|
||||
|
||||
// 识别队伍
|
||||
var names = new string[4];
|
||||
var displayNames = new string[4];
|
||||
var names = new string[avatarSideIconRectList.Count];
|
||||
var displayNames = new string[avatarSideIconRectList.Count];
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < AutoFightAssets.Instance.AvatarSideIconRectList.Count; i++)
|
||||
for (var i = 0; i < avatarSideIconRectList.Count; i++)
|
||||
{
|
||||
var ra = imageRegion.DeriveCrop(AutoFightAssets.Instance.AvatarSideIconRectList[i]);
|
||||
var ra = imageRegion.DeriveCrop(avatarSideIconRectList[i]);
|
||||
var pair = ClassifyAvatarCnName(ra.SrcBitmap, i + 1);
|
||||
names[i] = pair.Item1;
|
||||
if (!string.IsNullOrEmpty(pair.Item2))
|
||||
@ -81,14 +118,16 @@ public class CombatScenes : IDisposable
|
||||
displayNames[i] = pair.Item1;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInformation("识别到的队伍角色:{Text}", string.Join(",", displayNames));
|
||||
Avatars = BuildAvatars([.. names]);
|
||||
Avatars = BuildAvatars([.. names], null, avatarIndexRectList);
|
||||
AvatarMap = Avatars.ToDictionary(x => x.Name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogWarning(e.Message);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -164,7 +203,7 @@ public class CombatScenes : IDisposable
|
||||
|
||||
public bool CheckTeamInitialized()
|
||||
{
|
||||
if (Avatars.Length < 4)
|
||||
if (Avatars.Length != ExpectedTeamAvatarNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -172,8 +211,18 @@ public class CombatScenes : IDisposable
|
||||
return true;
|
||||
}
|
||||
|
||||
private Avatar[] BuildAvatars(List<string> names, List<Rect>? nameRects = null)
|
||||
private Avatar[] BuildAvatars(List<string> names, List<Rect>? nameRects = null, List<Rect>? avatarIndexRectList = null)
|
||||
{
|
||||
if (avatarIndexRectList == null && ExpectedTeamAvatarNum == 4)
|
||||
{
|
||||
avatarIndexRectList = AutoFightContext.Instance.FightAssets.AvatarIndexRectList;
|
||||
}
|
||||
|
||||
if (avatarIndexRectList == null)
|
||||
{
|
||||
throw new Exception("联机状态下,此方法必须传入队伍角色编号位置信息");
|
||||
}
|
||||
|
||||
AvatarCount = names.Count;
|
||||
var avatars = new Avatar[AvatarCount];
|
||||
for (var i = 0; i < AvatarCount; i++)
|
||||
@ -181,7 +230,7 @@ public class CombatScenes : IDisposable
|
||||
var nameRect = nameRects?[i] ?? Rect.Empty;
|
||||
avatars[i] = new Avatar(this, names[i], i + 1, nameRect)
|
||||
{
|
||||
IndexRect = AutoFightContext.Instance.FightAssets.AvatarIndexRectList[i]
|
||||
IndexRect = avatarIndexRectList[i]
|
||||
};
|
||||
}
|
||||
|
||||
@ -198,7 +247,7 @@ public class CombatScenes : IDisposable
|
||||
|
||||
public Avatar? SelectAvatar(string name)
|
||||
{
|
||||
return AvatarMap.TryGetValue(name, out var avatar) ? avatar : null;
|
||||
return AvatarMap.GetValueOrDefault(name);
|
||||
}
|
||||
|
||||
#region OCR识别队伍(已弃用)
|
||||
|
@ -603,6 +603,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
Title = "配置组设置",
|
||||
Content = new ScriptGroupConfigView(SelectedScriptGroup.Config),
|
||||
SizeToContent = SizeToContent.WidthAndHeight,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
// var dialogWindow = new WpfUiWindow(new ScriptGroupConfigView(SelectedScriptGroup.Config))
|
||||
|
Loading…
Reference in New Issue
Block a user