mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-21 09:45:48 +08:00
map match test & add map viewer
This commit is contained in:
@@ -12,8 +12,10 @@ namespace BetterGenshinImpact.Test
|
||||
InitializeComponent();
|
||||
|
||||
// new HsvTestWindow().Run();
|
||||
MapPuzzle.Put();
|
||||
// MapPuzzle.Put();
|
||||
// OcrTest.TestYap();
|
||||
// MatchTemplateTest.Test();
|
||||
MatchTest.Test();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class MapPuzzle
|
||||
int totalHeight = (lenRow + 1) * block;
|
||||
|
||||
// 创建空白大图
|
||||
Mat largeImage = new Mat(totalHeight, totalWidth, MatType.CV_8UC3, new Scalar(255, 255, 255));
|
||||
Mat largeImage = new Mat(totalHeight, totalWidth, MatType.CV_8UC3, new Scalar(0, 0, 0));
|
||||
|
||||
// 拼接图片
|
||||
int[,] arr = new int[lenRow + 1, lenCol + 1];
|
||||
@@ -145,6 +145,8 @@ public class MapPuzzle
|
||||
|
||||
// 保存大图
|
||||
Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\combined_image.png", largeImage);
|
||||
Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\combined_image_sd4x.png", largeImage.Resize(new Size(largeImage.Width / 4, largeImage.Height / 4), 0, 0, InterpolationFlags.Cubic));
|
||||
Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\combined_image_small.png", largeImage.Resize(new Size(1400, 1300), 0, 0, InterpolationFlags.Cubic));
|
||||
|
||||
// 释放资源
|
||||
largeImage.Dispose();
|
||||
|
||||
27
BetterGenshinImpact.Test/MatchTemplateTest.cs
Normal file
27
BetterGenshinImpact.Test/MatchTemplateTest.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace BetterGenshinImpact.Test;
|
||||
|
||||
public class MatchTemplateTest
|
||||
{
|
||||
public static readonly Size TemplateSize = new(240, 135);
|
||||
|
||||
// 对无用部分进行裁剪(左160,上80,下96)
|
||||
public static readonly Rect TemplateSizeRoi = new Rect(20, 10, TemplateSize.Width - 20, TemplateSize.Height - 22);
|
||||
|
||||
public static void Test()
|
||||
{
|
||||
var tar = new Mat(@"E:\HuiTask\更好的原神\地图匹配\比较\叠图\涂黑匹配2.png", ImreadModes.Color);
|
||||
var sTar = tar.Resize(new Size(240, 135), 0, 0, InterpolationFlags.Cubic);
|
||||
// sTar = new Mat(sTar, TemplateSizeRoi);
|
||||
Cv2.ImShow("sTar", sTar);
|
||||
var src = new Mat(@"E:\HuiTask\更好的原神\地图匹配\combined_image_small.png", ImreadModes.Color);
|
||||
var src2 = src.Clone();
|
||||
var p = MatchTemplateHelper.MatchTemplate(src, sTar, TemplateMatchModes.CCoeffNormed, null, 0.1);
|
||||
|
||||
Cv2.Rectangle(src2, new Rect(p.X, p.Y, sTar.Width, sTar.Height), new Scalar(0, 0, 255));
|
||||
|
||||
Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\x1.png", src2);
|
||||
}
|
||||
}
|
||||
343
BetterGenshinImpact.Test/MatchTest.cs
Normal file
343
BetterGenshinImpact.Test/MatchTest.cs
Normal file
@@ -0,0 +1,343 @@
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Features2D;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Xml;
|
||||
|
||||
namespace BetterGenshinImpact.Test;
|
||||
|
||||
public class MatchTest
|
||||
{
|
||||
public static void Test()
|
||||
{
|
||||
var tar = new Mat(@"E:\HuiTask\更好的原神\地图匹配\比较\小地图\Clip_20240323_185641.png", ImreadModes.Color);
|
||||
tar = tar.Resize(new Size(tar.Width * 2, tar.Height * 2), 0, 0, InterpolationFlags.Nearest);
|
||||
var src = new Mat(@"E:\HuiTask\更好的原神\地图匹配\combined_image.png", ImreadModes.Color);
|
||||
var res = MatchPicBySurf(src, tar);
|
||||
|
||||
Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\s1.png", res);
|
||||
}
|
||||
|
||||
public static Mat MatchPicBySift(Mat matSrc, Mat matTo)
|
||||
{
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Mat matSrcRet = new Mat();
|
||||
using Mat matToRet = new Mat();
|
||||
KeyPoint[] keyPointsSrc, keyPointsTo;
|
||||
using (var sift = SIFT.Create())
|
||||
{
|
||||
var kpPath = @"E:\HuiTask\更好的原神\地图匹配\sift.kp";
|
||||
var kpMatPath = @"E:\HuiTask\更好的原神\地图匹配\sift.mat";
|
||||
if (File.Exists(kpPath) && File.Exists(kpMatPath))
|
||||
{
|
||||
keyPointsSrc = (KeyPoint[])DeserializeObject(File.ReadAllBytes(kpPath));
|
||||
GCHandle pinnedArray = GCHandle.Alloc(DeserializeObject(File.ReadAllBytes(kpMatPath)), GCHandleType.Pinned);
|
||||
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
|
||||
matSrcRet = new Mat(166767, 128, MatType.CV_32FC1, pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
sift.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
|
||||
byte[] arr = new byte[matSrcRet.Step(0) * matSrcRet.Rows]; // matSrcRet.Total() * matSrcRet.ElemSize()
|
||||
Marshal.Copy(matSrcRet.Data, arr, 0, arr.Length);
|
||||
File.WriteAllBytes(kpMatPath, SerializeObject(arr));
|
||||
File.WriteAllBytes(kpPath, SerializeObject(keyPointsSrc));
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"大地图kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
sift.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"模板kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
}
|
||||
|
||||
using (var bfMatcher = new OpenCvSharp.BFMatcher())
|
||||
{
|
||||
var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 2);
|
||||
|
||||
var pointsSrc = new List<Point2f>();
|
||||
var pointsDst = new List<Point2f>();
|
||||
var goodMatches = new List<DMatch>();
|
||||
foreach (DMatch[] items in matches.Where(x => x.Length > 1))
|
||||
{
|
||||
if (items[0].Distance < 0.5 * items[1].Distance)
|
||||
{
|
||||
pointsSrc.Add(keyPointsSrc[items[0].QueryIdx].Pt);
|
||||
pointsDst.Add(keyPointsTo[items[0].TrainIdx].Pt);
|
||||
goodMatches.Add(items[0]);
|
||||
Debug.WriteLine($"{keyPointsSrc[items[0].QueryIdx].Pt.X}, {keyPointsSrc[items[0].QueryIdx].Pt.Y}");
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"bfMatcher耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
|
||||
var outMat = new Mat();
|
||||
|
||||
// algorithm RANSAC Filter the matched results
|
||||
var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d);
|
||||
var pDst = pointsDst.ConvertAll(Point2fToPoint2d);
|
||||
var outMask = new Mat();
|
||||
// If the original matching result is null, Skip the filtering step
|
||||
if (pSrc.Count > 0 && pDst.Count > 0)
|
||||
Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
|
||||
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"FindHomography耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
// If passed RANSAC After processing, the matching points are more than 10.,Only filters are used. Otherwise, use the original matching point result(When the matching point is too small, it passes through RANSAC After treatment,It is possible to get the result of 0 matching points.).
|
||||
if (outMask.Rows > 10)
|
||||
{
|
||||
byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
|
||||
outMask.GetArray(out maskBytes);
|
||||
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
|
||||
}
|
||||
else
|
||||
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);
|
||||
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"绘图耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
return outMat;
|
||||
}
|
||||
}
|
||||
|
||||
//This method may be missed, you may read a lot of blogs, but none of them wrote
|
||||
private static Point2d Point2fToPoint2d(Point2f input)
|
||||
{
|
||||
Point2d p2 = new Point2d(input.X, input.Y);
|
||||
return p2;
|
||||
}
|
||||
|
||||
public static Mat MatchPicBySurf(Mat matSrc, Mat matTo, double threshold = 400)
|
||||
{
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Mat matSrcRet = new Mat();
|
||||
using Mat matToRet = new Mat();
|
||||
KeyPoint[] keyPointsSrc, keyPointsTo;
|
||||
using (var surf = OpenCvSharp.XFeatures2D.SURF.Create(threshold, 4, 3, true, true))
|
||||
{
|
||||
var kpPath = @"E:\HuiTask\更好的原神\地图匹配\surf.kp";
|
||||
var kpMatPath = @"E:\HuiTask\更好的原神\地图匹配\surf.mat";
|
||||
if (File.Exists(kpPath) && File.Exists(kpMatPath))
|
||||
{
|
||||
keyPointsSrc = (KeyPoint[])DeserializeObject(File.ReadAllBytes(kpPath));
|
||||
GCHandle pinnedArray = GCHandle.Alloc(DeserializeObject(File.ReadAllBytes(kpMatPath)), GCHandleType.Pinned);
|
||||
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
|
||||
matSrcRet = new Mat(166767, 128, MatType.CV_32FC1, pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
|
||||
byte[] arr = new byte[matSrcRet.Step(0) * matSrcRet.Rows]; // matSrcRet.Total() * matSrcRet.ElemSize()
|
||||
Marshal.Copy(matSrcRet.Data, arr, 0, arr.Length);
|
||||
File.WriteAllBytes(kpMatPath, SerializeObject(arr));
|
||||
File.WriteAllBytes(kpPath, SerializeObject(keyPointsSrc));
|
||||
}
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"大地图kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
|
||||
surf.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"模板kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
}
|
||||
|
||||
using (var flnMatcher = new OpenCvSharp.FlannBasedMatcher())
|
||||
{
|
||||
var matches = flnMatcher.Match(matSrcRet, matToRet);
|
||||
//Finding the Minimum and Maximum Distance
|
||||
double minDistance = 1000; //Backward approximation
|
||||
double maxDistance = 0;
|
||||
for (int i = 0; i < matSrcRet.Rows; i++)
|
||||
{
|
||||
double distance = matches[i].Distance;
|
||||
if (distance > maxDistance)
|
||||
{
|
||||
maxDistance = distance;
|
||||
}
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"max distance : {maxDistance}");
|
||||
Debug.WriteLine($"min distance : {minDistance}");
|
||||
|
||||
var pointsSrc = new List<Point2f>();
|
||||
var pointsDst = new List<Point2f>();
|
||||
//Screening better matching points
|
||||
var goodMatches = new List<DMatch>();
|
||||
for (int i = 0; i < matSrcRet.Rows; i++)
|
||||
{
|
||||
double distance = matches[i].Distance;
|
||||
if (distance < Math.Max(minDistance * 2, 0.02))
|
||||
{
|
||||
pointsSrc.Add(keyPointsSrc[matches[i].QueryIdx].Pt);
|
||||
pointsDst.Add(keyPointsTo[matches[i].TrainIdx].Pt);
|
||||
//Compression of new ones with distances less than ranges DMatch
|
||||
goodMatches.Add(matches[i]);
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"flnMatcher耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
|
||||
var outMat = new Mat();
|
||||
|
||||
// algorithm RANSAC Filter the matched results
|
||||
var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d);
|
||||
var pDst = pointsDst.ConvertAll(Point2fToPoint2d);
|
||||
var outMask = new Mat();
|
||||
// If the original matching result is null, Skip the filtering step
|
||||
if (pSrc.Count > 0 && pDst.Count > 0)
|
||||
Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"FindHomography耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
|
||||
// If passed RANSAC After processing, the matching points are more than 10.,Only filters are used. Otherwise, use the original matching point result(When the matching point is too small, it passes through RANSAC After treatment,It's possible to get the result of 0 matching points.).
|
||||
if (outMask.Rows > 10)
|
||||
{
|
||||
Debug.WriteLine($"使用了Ransac的结果");
|
||||
byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
|
||||
outMask.GetArray(out maskBytes);
|
||||
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
|
||||
}
|
||||
else
|
||||
Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);
|
||||
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"绘图耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
return outMat;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
private static byte[] Serialize(object obj)
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
DataContractSerializer ser = new DataContractSerializer(typeof(object));
|
||||
ser.WriteObject(memoryStream, obj);
|
||||
var data = memoryStream.ToArray();
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
private static T Deserialize<T>(byte[] data)
|
||||
{
|
||||
using var memoryStream = new MemoryStream(data);
|
||||
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(memoryStream, new XmlDictionaryReaderQuotas());
|
||||
DataContractSerializer ser = new DataContractSerializer(typeof(T));
|
||||
var result = (T)ser.ReadObject(reader, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
[Obsolete("Obsolete")]
|
||||
public static byte[] SerializeObject(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
//内存实例
|
||||
MemoryStream ms = new MemoryStream();
|
||||
//创建序列化的实例
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
formatter.Serialize(ms, obj); //序列化对象,写入ms流中
|
||||
byte[] bytes = ms.GetBuffer();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
[Obsolete("Obsolete")]
|
||||
public static object DeserializeObject(byte[] bytes)
|
||||
{
|
||||
object obj = null;
|
||||
if (bytes == null)
|
||||
return obj;
|
||||
//利用传来的byte[]创建一个内存流
|
||||
MemoryStream ms = new MemoryStream(bytes);
|
||||
ms.Position = 0;
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
obj = formatter.Deserialize(ms); //把内存流反序列成对象
|
||||
ms.Close();
|
||||
return obj;
|
||||
}
|
||||
|
||||
static bool WriteRawImage(Mat image, string filename)
|
||||
{
|
||||
using (FileStream file = new FileStream(filename, FileMode.Create))
|
||||
{
|
||||
if (file == null || !file.CanWrite)
|
||||
return false;
|
||||
|
||||
BinaryWriter writer = new BinaryWriter(file);
|
||||
writer.Write(image.Rows);
|
||||
writer.Write(image.Cols);
|
||||
int depth = image.Depth();
|
||||
int type = image.Type();
|
||||
int channels = image.Channels();
|
||||
writer.Write(depth);
|
||||
writer.Write(type);
|
||||
writer.Write(channels);
|
||||
int sizeInBytes = (int)image.Step() * image.Rows;
|
||||
writer.Write(sizeInBytes);
|
||||
byte[] arr = new byte[sizeInBytes];
|
||||
Marshal.Copy(image.Data, arr, 0, arr.Length);
|
||||
writer.Write(arr, 0, sizeInBytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ReadRawImage(out Mat image, string filename)
|
||||
{
|
||||
int rows, cols, data, depth, type, channels;
|
||||
image = null;
|
||||
|
||||
using (FileStream file = new FileStream(filename, FileMode.Open))
|
||||
{
|
||||
if (file == null || !file.CanRead)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
BinaryReader reader = new BinaryReader(file);
|
||||
rows = reader.ReadInt32();
|
||||
cols = reader.ReadInt32();
|
||||
depth = reader.ReadInt32();
|
||||
type = reader.ReadInt32();
|
||||
channels = reader.ReadInt32();
|
||||
data = reader.ReadInt32();
|
||||
image = new OpenCvSharp.Mat(rows, cols, (OpenCvSharp.MatType)type);
|
||||
// reader.Read(image.Data, 0, data);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
BIN
BetterGenshinImpact/Assets/Map/map_sd1024.png
Normal file
BIN
BetterGenshinImpact/Assets/Map/map_sd1024.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -27,6 +27,10 @@
|
||||
<Resource Include="Assets\Highlighting\*.xshd" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Map\map_sd1024.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
@@ -82,6 +86,9 @@
|
||||
<Compile Update="View\PickerWindow.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="View\Windows\MapViewer.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -528,4 +535,8 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Assets\Map\map_sd1024.png" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -7,7 +7,6 @@ using BetterGenshinImpact.GameTask.AutoFight.Script;
|
||||
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
|
||||
using BetterGenshinImpact.GameTask.AutoPick.Assets;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.GameTask.Common.MiniMap;
|
||||
using BetterGenshinImpact.GameTask.Model.Enum;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
@@ -22,6 +21,7 @@ using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.GameTask.Common.Map;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
using static Vanara.PInvoke.User32;
|
||||
|
||||
|
||||
42
BetterGenshinImpact/GameTask/Common/Map/BigMap.cs
Normal file
42
BetterGenshinImpact/GameTask/Common/Map/BigMap.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Diagnostics;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using OpenCvSharp;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class BigMap
|
||||
{
|
||||
public static readonly Size TemplateSize = new(240, 135);
|
||||
|
||||
// 对无用部分进行裁剪(左160,上80,下96)
|
||||
public static readonly Rect TemplateSizeRoi = new Rect(20, 10, TemplateSize.Width - 20, TemplateSize.Height - 22);
|
||||
|
||||
private readonly Mat _mapSrcMat;
|
||||
|
||||
public BigMap()
|
||||
{
|
||||
var stream = ResourceHelper.GetStream(@"pack://application:,,,/Assets/Map/map_sd1024.png");
|
||||
_mapSrcMat = Mat.FromStream(stream, ImreadModes.Color);
|
||||
}
|
||||
|
||||
public Point GetMapPosition(Mat captureMat)
|
||||
{
|
||||
Cv2.CvtColor(captureMat, captureMat, ColorConversionCodes.BGRA2BGR);
|
||||
using var tar = new Mat(captureMat.Resize(TemplateSize, 0, 0, InterpolationFlags.Cubic), TemplateSizeRoi);
|
||||
var p = MatchTemplateHelper.MatchTemplate(_mapSrcMat, tar, TemplateMatchModes.CCoeffNormed, null, 0.2);
|
||||
Debug.WriteLine($"BigMap Match Template: {p}");
|
||||
return p;
|
||||
}
|
||||
|
||||
public void GetMapPositionAndDraw(Mat captureMat)
|
||||
{
|
||||
var p = GetMapPosition(captureMat);
|
||||
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "UpdateBigMapRect", new object(),
|
||||
new System.Windows.Rect(p.X, p.Y, TemplateSizeRoi.Width, TemplateSizeRoi.Height)));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using OpenCvSharp;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.MiniMap;
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class CameraOrientation
|
||||
{
|
||||
@@ -47,7 +47,6 @@ public class CameraOrientation
|
||||
var left2 = left.Zip(right, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
var right2 = right.Zip(left, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
|
||||
|
||||
// 左移后相乘 在附近2°内寻找最大值
|
||||
var sum = new int[360];
|
||||
for (var i = -2; i <= 2; i++)
|
||||
@@ -132,4 +131,4 @@ public class CameraOrientation
|
||||
return LeftShift(array, -k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.MiniMap;
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class CharacterOrientation
|
||||
{
|
||||
@@ -112,5 +107,4 @@ public class CharacterOrientation
|
||||
var midY = (p1.Y + p2.Y) / 2;
|
||||
return new Point(midX, midY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
5
BetterGenshinImpact/GameTask/Common/Map/MiniMap.cs
Normal file
5
BetterGenshinImpact/GameTask/Common/Map/MiniMap.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class MiniMap
|
||||
{
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.MiniMap;
|
||||
|
||||
public class MiniMap
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.MiniMap.Model;
|
||||
|
||||
public class MiniMap
|
||||
{
|
||||
}
|
||||
@@ -1,18 +1,12 @@
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.GameTask.Common.Map;
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using Compunet.YoloV8;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BetterGenshinImpact.GameTask.Common.MiniMap;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
using BetterGenshinImpact.Core.Simulator;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Placeholder;
|
||||
|
||||
@@ -31,7 +25,9 @@ public class TestTrigger : ITaskTrigger
|
||||
|
||||
//private readonly AutoGeniusInvokationAssets _autoGeniusInvokationAssets;
|
||||
|
||||
private readonly YoloV8 _predictor = new(Global.Absolute("Assets\\Model\\Domain\\bgi_tree.onnx"));
|
||||
// private readonly YoloV8 _predictor = new(Global.Absolute("Assets\\Model\\Domain\\bgi_tree.onnx"));
|
||||
|
||||
private readonly BigMap _bigMap = new();
|
||||
|
||||
public TestTrigger()
|
||||
{
|
||||
@@ -49,24 +45,23 @@ public class TestTrigger : ITaskTrigger
|
||||
{
|
||||
// TestArrow(content);
|
||||
// TestCamera(content);
|
||||
Detect(content);
|
||||
var angle = CameraOrientation.Compute(content);
|
||||
Debug.WriteLine(angle);
|
||||
if (angle < 180)
|
||||
{
|
||||
// 左移视角
|
||||
Simulation.SendInputEx.Mouse.MoveMouseBy(-angle, 0);
|
||||
}
|
||||
else if (angle is > 180 and < 360)
|
||||
{
|
||||
// 右移视角
|
||||
Simulation.SendInputEx.Mouse.MoveMouseBy(360 - angle, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 360 度 东方向视角
|
||||
}
|
||||
|
||||
// Detect(content);
|
||||
// var angle = CameraOrientation.Compute(content);
|
||||
// Debug.WriteLine(angle);
|
||||
// if (angle < 180)
|
||||
// {
|
||||
// // 左移视角
|
||||
// Simulation.SendInputEx.Mouse.MoveMouseBy(-angle, 0);
|
||||
// }
|
||||
// else if (angle is > 180 and < 360)
|
||||
// {
|
||||
// // 右移视角
|
||||
// Simulation.SendInputEx.Mouse.MoveMouseBy(360 - angle, 0);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 360 度 东方向视角
|
||||
// }
|
||||
|
||||
//var dictionary = GeniusInvokationControl.FindMultiPicFromOneImage2OneByOne(content.CaptureRectArea.SrcGreyMat, _autoGeniusInvokationAssets.RollPhaseDiceMats, 0.7);
|
||||
//if (dictionary.Count > 0)
|
||||
@@ -88,7 +83,6 @@ public class TestTrigger : ITaskTrigger
|
||||
// Debug.WriteLine("找到了" + i + "个");
|
||||
//}
|
||||
|
||||
|
||||
//var foundRectArea = content.CaptureRectArea.Find(_autoGeniusInvokationAssets.ElementalTuningConfirmButtonRo);
|
||||
//if (!foundRectArea.IsEmpty())
|
||||
//{
|
||||
@@ -98,24 +92,26 @@ public class TestTrigger : ITaskTrigger
|
||||
//{
|
||||
// Debug.WriteLine("没找到");
|
||||
//}
|
||||
|
||||
_bigMap.GetMapPositionAndDraw(content.CaptureRectArea.SrcMat);
|
||||
}
|
||||
|
||||
private void Detect(CaptureContent content)
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
content.CaptureRectArea.SrcBitmap.Save(memoryStream, ImageFormat.Bmp);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
var result = _predictor.Detect(memoryStream);
|
||||
Debug.WriteLine(result);
|
||||
var list = new List<RectDrawable>();
|
||||
foreach (var box in result.Boxes)
|
||||
{
|
||||
var rect = new System.Windows.Rect(box.Bounds.X, box.Bounds.Y, box.Bounds.Width, box.Bounds.Height);
|
||||
list.Add(new RectDrawable(rect, _pen));
|
||||
}
|
||||
|
||||
VisionContext.Instance().DrawContent.PutOrRemoveRectList("TreeBox", list);
|
||||
}
|
||||
// private void Detect(CaptureContent content)
|
||||
// {
|
||||
// using var memoryStream = new MemoryStream();
|
||||
// content.CaptureRectArea.SrcBitmap.Save(memoryStream, ImageFormat.Bmp);
|
||||
// memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
// var result = _predictor.Detect(memoryStream);
|
||||
// Debug.WriteLine(result);
|
||||
// var list = new List<RectDrawable>();
|
||||
// foreach (var box in result.Boxes)
|
||||
// {
|
||||
// var rect = new System.Windows.Rect(box.Bounds.X, box.Bounds.Y, box.Bounds.Width, box.Bounds.Height);
|
||||
// list.Add(new RectDrawable(rect, _pen));
|
||||
// }
|
||||
//
|
||||
// VisionContext.Instance().DrawContent.PutOrRemoveRectList("TreeBox", list);
|
||||
// }
|
||||
|
||||
public static void TestArrow(CaptureContent content)
|
||||
{
|
||||
@@ -220,7 +216,6 @@ public class TestTrigger : ITaskTrigger
|
||||
return new Point(midX, midY);
|
||||
}
|
||||
|
||||
|
||||
public void TestCamera(CaptureContent content)
|
||||
{
|
||||
var mat = new Mat(content.CaptureRectArea.SrcGreyMat, new Rect(62, 19, 212, 212));
|
||||
@@ -252,7 +247,6 @@ public class TestTrigger : ITaskTrigger
|
||||
var left2 = left.Zip(right, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
var right2 = right.Zip(left, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
|
||||
|
||||
// 左移后相乘 在附近2°内寻找最大值
|
||||
var sum = new int[360];
|
||||
for (var i = -2; i <= 2; i++)
|
||||
@@ -328,4 +322,4 @@ public class TestTrigger : ITaskTrigger
|
||||
return LeftShift(array, -k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ internal static class ResourceHelper
|
||||
public static byte[] GetBytes(string uriString)
|
||||
{
|
||||
Uri uri = new(uriString);
|
||||
StreamResourceInfo info = Application.GetResourceStream(uri);
|
||||
StreamResourceInfo? info = Application.GetResourceStream(uri);
|
||||
using BinaryReader stream = new(info.Stream);
|
||||
return stream.ReadBytes((int)info.Stream.Length);
|
||||
}
|
||||
@@ -26,14 +26,18 @@ internal static class ResourceHelper
|
||||
public static Stream GetStream(string uriString)
|
||||
{
|
||||
Uri uri = new(uriString);
|
||||
StreamResourceInfo info = Application.GetResourceStream(uri);
|
||||
StreamResourceInfo? info = Application.GetResourceStream(uri);
|
||||
return info?.Stream!;
|
||||
}
|
||||
|
||||
public static string GetString(string uriString, Encoding encoding = null!)
|
||||
{
|
||||
Uri uri = new(uriString);
|
||||
StreamResourceInfo info = Application.GetResourceStream(uri);
|
||||
StreamResourceInfo? info = Application.GetResourceStream(uri);
|
||||
if (info == null)
|
||||
{
|
||||
throw new FileNotFoundException($"Resource not found: {uriString}");
|
||||
}
|
||||
using StreamReader stream = new(info.Stream, encoding ?? Encoding.UTF8);
|
||||
return stream.ReadToEnd();
|
||||
}
|
||||
|
||||
@@ -309,6 +309,31 @@
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<!-- 地图 -->
|
||||
<ui:CardControl Margin="0,0,0,12" Icon="{ui:SymbolIcon Cursor24}">
|
||||
<ui:CardControl.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="查看地图(开发者)"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="查看当前识别到的位置"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</ui:CardControl.Header>
|
||||
<ui:Button Margin="0,0,36,0"
|
||||
Command="{Binding OpenMapViewerCommand}"
|
||||
Content="查看地图" />
|
||||
</ui:CardControl>
|
||||
|
||||
<!--<ui:CardControl Margin="0,0,0,12" Icon="{ui:SymbolIcon Keyboard24}">
|
||||
<ui:CardControl.Header>
|
||||
<Grid>
|
||||
|
||||
44
BetterGenshinImpact/View/Windows/MapViewer.xaml
Normal file
44
BetterGenshinImpact/View/Windows/MapViewer.xaml
Normal file
@@ -0,0 +1,44 @@
|
||||
<ui:FluentWindow x:Class="BetterGenshinImpact.View.Windows.MapViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:windows="clr-namespace:BetterGenshinImpact.ViewModel.Windows"
|
||||
Title="地图"
|
||||
Width="1400"
|
||||
Height="1330"
|
||||
MinWidth="700"
|
||||
MinHeight="650"
|
||||
d:DataContext="{d:DesignInstance Type=windows:MapViewerViewModel}"
|
||||
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
ResizeMode="CanMinimize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="SingleBorderWindow"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Canvas Grid.Row="1">
|
||||
<ui:Image Source="pack://application:,,,/Assets/Map/map_sd1024.png" Stretch="Uniform" />
|
||||
<Rectangle Canvas.Left="{Binding BigMapRect.Left}"
|
||||
Canvas.Top="{Binding BigMapRect.Top}"
|
||||
Width="{Binding BigMapRect.Width}"
|
||||
Height="{Binding BigMapRect.Height}"
|
||||
Stroke="Red" />
|
||||
</Canvas>
|
||||
|
||||
|
||||
<ui:TitleBar Title="地图" Grid.Row="0">
|
||||
<ui:TitleBar.Icon>
|
||||
<ui:ImageIcon Source="pack://application:,,,/Assets/Kirara.png" />
|
||||
</ui:TitleBar.Icon>
|
||||
</ui:TitleBar>
|
||||
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
15
BetterGenshinImpact/View/Windows/MapViewer.xaml.cs
Normal file
15
BetterGenshinImpact/View/Windows/MapViewer.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using BetterGenshinImpact.ViewModel.Windows;
|
||||
using System.Windows;
|
||||
|
||||
namespace BetterGenshinImpact.View.Windows;
|
||||
|
||||
public partial class MapViewer
|
||||
{
|
||||
public MapViewerViewModel ViewModel { get; }
|
||||
|
||||
public MapViewer()
|
||||
{
|
||||
DataContext = ViewModel = new();
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -4,39 +4,47 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d"
|
||||
MinWidth="400"
|
||||
MinHeight="200"
|
||||
Title="配置"
|
||||
Width="500"
|
||||
Height="210"
|
||||
WindowStyle="SingleBorderWindow"
|
||||
ResizeMode="CanMinimize"
|
||||
MinWidth="400"
|
||||
MinHeight="200"
|
||||
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
Title="配置"
|
||||
>
|
||||
ResizeMode="CanMinimize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="SingleBorderWindow"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="12">
|
||||
<ui:TextBlock Name="TxtQuestion" Margin="5"/>
|
||||
<ui:TextBox Name="TxtResponse" Margin="5"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
|
||||
<ui:Button Content="确定" IsDefault="True" Margin="5" Name="BtnOk" Click="BtnOkClick" Appearance="Primary" />
|
||||
<ui:Button Content="取消" IsCancel="True" Margin="5" Name="BtnCancel" Click="BtnCancelClick" />
|
||||
<StackPanel Grid.Row="1" Margin="12">
|
||||
<ui:TextBlock Name="TxtQuestion" Margin="5" />
|
||||
<ui:TextBox Name="TxtResponse" Margin="5" />
|
||||
<StackPanel Margin="5"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<ui:Button Name="BtnOk"
|
||||
Margin="5"
|
||||
Appearance="Primary"
|
||||
Click="BtnOkClick"
|
||||
Content="确定"
|
||||
IsDefault="True" />
|
||||
<ui:Button Name="BtnCancel"
|
||||
Margin="5"
|
||||
Click="BtnCancelClick"
|
||||
Content="取消"
|
||||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<ui:TitleBar Title="配置" Grid.Row="0">
|
||||
<ui:TitleBar.Icon>
|
||||
<ui:ImageIcon Source="pack://application:,,,/Assets/logo.png" />
|
||||
<ui:ImageIcon Source="pack://application:,,,/Assets/Kirara.png" />
|
||||
</ui:TitleBar.Icon>
|
||||
</ui:TitleBar>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using System;
|
||||
using System.IO;
|
||||
using BetterGenshinImpact.GameTask;
|
||||
using BetterGenshinImpact.GameTask.Model.Enum;
|
||||
using BetterGenshinImpact.View.Windows;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel.Pages;
|
||||
|
||||
@@ -70,4 +71,10 @@ public partial class CommonSettingsPageViewModel : ObservableObject, INavigation
|
||||
|
||||
Process.Start("explorer.exe", path);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void OnOpenMapViewer()
|
||||
{
|
||||
new MapViewer().Show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using BetterGenshinImpact.Core;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using BetterGenshinImpact.GameTask;
|
||||
using BetterGenshinImpact.Genshin.Paths;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
@@ -15,12 +14,9 @@ using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using Windows.System;
|
||||
using OpenCvSharp;
|
||||
using Wpf.Ui.Controls;
|
||||
using Rect = OpenCvSharp.Rect;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel.Pages;
|
||||
|
||||
@@ -32,10 +28,12 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware
|
||||
|
||||
[ObservableProperty] private bool _taskDispatcherEnabled = false;
|
||||
|
||||
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(StartTriggerCommand))]
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(StartTriggerCommand))]
|
||||
private bool _startButtonEnabled = true;
|
||||
|
||||
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(StopTriggerCommand))]
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(StopTriggerCommand))]
|
||||
private bool _stopButtonEnabled = true;
|
||||
|
||||
public AllConfig Config { get; set; }
|
||||
@@ -281,4 +279,4 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
BetterGenshinImpact/ViewModel/Windows/MapViewerViewModel.cs
Normal file
23
BetterGenshinImpact/ViewModel/Windows/MapViewerViewModel.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel.Windows;
|
||||
|
||||
public partial class MapViewerViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private Rect _bigMapRect = new(0, 0, 0, 0);
|
||||
|
||||
public MapViewerViewModel()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<object>>(this, (sender, msg) =>
|
||||
{
|
||||
if (msg.PropertyName == "UpdateBigMapRect")
|
||||
{
|
||||
BigMapRect = (Rect)msg.NewValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user