Compare commits

...

4 Commits

Author SHA1 Message Date
MistEO
eb6eb55161 兼容MuMu模拟器 2021-07-25 03:02:14 +08:00
MistEO
a195f559b8 优化分辨率自适应,现在不强制调整窗口大小了 2021-07-25 00:17:53 +08:00
MistEO
b5776d33b0 添加注释 2021-07-24 19:37:14 +08:00
MistEO
63aa3575c6 update readme 2021-07-24 18:05:23 +08:00
13 changed files with 237 additions and 102 deletions

View File

@@ -10,6 +10,10 @@
#include "AsstDef.h"
#include "Configer.h"
namespace cv {
class Mat;
}
namespace asst {
class WinMacro;
class Identify;
@@ -20,9 +24,9 @@ namespace asst {
Assistance();
~Assistance();
std::optional<std::string> set_emulator(const std::string & emulator_name = std::string());
void start(const std::string & task);
std::optional<std::string> set_emulator(const std::string& emulator_name = std::string());
void start(const std::string& task);
void stop(bool block = true);
bool set_param(const std::string& type, const std::string& param, const std::string& value);
@@ -32,6 +36,9 @@ namespace asst {
private:
static void working_proc(Assistance* pThis);
// pair<scale, image>
std::pair<double, cv::Mat> get_format_image();
std::shared_ptr<WinMacro> m_pWindow = nullptr;
std::shared_ptr<WinMacro> m_pView = nullptr;
std::shared_ptr<WinMacro> m_pCtrl = nullptr;

View File

@@ -108,6 +108,10 @@ namespace asst {
std::string path;
std::string connect;
std::string click;
std::string display;
std::string display_regex;
int display_width = 0;
int display_height = 0;
};
struct EmulatorInfo {
@@ -121,29 +125,31 @@ namespace asst {
int height = 0;
int x_offset = 0;
int y_offset = 0;
int right_offset = 0;
int bottom_offset = 0;
};
struct TaskInfo {
std::string filename;
double threshold = 0;
double cache_threshold = 0;
TaskType type = TaskType::Invalid;
std::vector<std::string> next;
int exec_times = 0;
int max_times = INT_MAX;
std::vector<std::string> exceeded_next;
std::vector<std::string> reduce_other_times;
asst::Rect specific_area;
int pre_delay = 0;
int rear_delay = 0;
std::string filename; // 图片文件名
double threshold = 0; // 模板匹配阈值
double cache_threshold = 0; // 直方图比较阈值
TaskType type = TaskType::Invalid; // 任务类型
std::vector<std::string> next; // 下一个可能的任务(列表)
int exec_times = 0; // 任务已执行了多少次
int max_times = INT_MAX; // 任务最多执行多少次
std::vector<std::string> exceeded_next; // 达到最多次数了之后,下一个可能的任务(列表)
std::vector<std::string> reduce_other_times; // 执行了该任务后,需要减少别的任务的执行次数。例如执行了吃理智药,则说明上一次点击蓝色开始行动按钮没生效,所以蓝色开始行动要-1
asst::Rect specific_area; // 指定区域目前仅针对ClickRect任务有用会点这个区域
int pre_delay = 0; // 执行该任务前的延时
int rear_delay = 0; // 执行该任务后的延时
};
struct Options {
int identify_delay = 0;
bool identify_cache = false;
int control_delay_lower = 0;
int control_delay_upper = 0;
bool print_window = false;
int print_window_delay = 0;
bool identify_cache = false; // 图像识别缓存功能开启后可以大幅降低CPU消耗但需要保证要识别的按钮每次的位置不会改变
int identify_delay = 0; // 图像识别延时越快操作越快但会增加CPU消耗
int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时
int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时
bool print_window = false; // 截图功能开启后每次结算界面会截图到screenshot目录下
int print_window_delay = 0; // 截图延时:每次到结算界面,掉落物品不是一次性出来的,有个动画,所以需要等一会再截图
};
}

View File

@@ -27,8 +27,8 @@ namespace asst {
Configer& operator=(const Configer& rhs);
Configer& operator=(Configer&& rhs) noexcept;
constexpr static int DefaultWindowWidth = 1280;
constexpr static int DefaultWindowHeight = 720;
constexpr static int DefaultWindowWidth = 1271;
constexpr static int DefaultWindowHeight = 716;
constexpr static double DefaultThreshold = 0.9;
constexpr static double DefaultCacheThreshold = 0.9;

View File

@@ -26,9 +26,9 @@ namespace asst {
void clear_cache();
private:
cv::Mat image2hist(const cv::Mat& src);
cv::Mat image_2_hist(const cv::Mat& src);
double image_hist_comp(const cv::Mat& src, const cv::MatND& hist);
static asst::Rect cvrect2rect(const cv::Rect& cvRect) {
static asst::Rect cvrect_2_rect(const cv::Rect& cvRect) {
return asst::Rect(cvRect.x, cvRect.y, cvRect.width, cvRect.height);
}
@@ -37,7 +37,7 @@ namespace asst {
std::unordered_map<std::string, cv::Mat> m_mat_map;
bool m_use_cache = true;
std::unordered_map<std::string, std::pair<cv::Rect, cv::Mat>> m_cache_map;
std::unordered_map<std::string, std::pair<cv::Rect, cv::Mat>> m_cache_map; // λÖá¢Ö±·½Í¼»º´æ
};
}

View File

@@ -2,7 +2,6 @@
#include <fstream>
#include <mutex>
#include <chrono>
#include <iostream>
#include "AsstAux.h"

View File

@@ -2,11 +2,15 @@
#include <string>
#include <random>
#include <optional>
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include "AsstDef.h"
namespace cv {
class Mat;
}
namespace asst {
class WinMacro
{
@@ -21,6 +25,7 @@ namespace asst {
bool hideWindow();
bool click(const Point & p);
bool click(const Rect & rect);
void setControlScale(double scale);
cv::Mat getImage(const Rect& rect);
Rect getWindowRect();
const EmulatorInfo& getEmulatorInfo() const noexcept { return m_emulator_info; }
@@ -29,17 +34,18 @@ namespace asst {
static double getScreenScale();
private:
bool findHandle();
DWORD callCmd(const std::string& cmd, int wait_time = 1000);
std::optional<std::string> callCmd(const std::string& cmd, bool use_pipe = true);
const EmulatorInfo m_emulator_info;
const HandleType m_handle_type;
HWND m_handle = NULL;
bool m_is_adb = false;
std::string m_click_cmd;
std::string m_click_cmd; // adb点击命令不是adb的句柄用不到这个
std::minstd_rand m_rand_engine;
int m_width = 0;
int m_height = 0;
int m_x_offset = 0;
int m_y_offset = 0;
//int m_x_offset = 0;
//int m_y_offset = 0;
double m_control_scale = 1;
};
}

View File

@@ -6,6 +6,8 @@
#include "Logger.hpp"
#include "AsstAux.h"
#include <opencv2/opencv.hpp>
#include <time.h>
#include <filesystem>
@@ -62,6 +64,7 @@ std::optional<std::string> Assistance::set_emulator(const std::string& emulator_
std::unique_lock<std::mutex> lock(m_mutex);
// 自动匹配模拟器,逐个找
if (emulator_name.empty()) {
for (auto&& [name, info] : m_configer.m_handles)
{
@@ -147,19 +150,8 @@ bool asst::Assistance::print_window(const std::string& filename, bool block)
lock = std::unique_lock<std::mutex>(m_mutex);
}
auto cur_image = m_pView->getImage(m_pView->getWindowRect());
if (cur_image.empty() || cur_image.cols < m_configer.DefaultWindowWidth || cur_image.rows < m_configer.DefaultWindowHeight) {
DebugTraceError("Window image error");
return false;
}
// 把模拟器边框的一圈裁剪掉,不然企鹅物流识别不出来
auto&& window_info = m_pView->getEmulatorInfo();
int x_offset = -window_info.x_offset + 5;
int y_offset = -window_info.y_offset;// + 5;
int width = m_configer.DefaultWindowWidth - 5;
int height = m_configer.DefaultWindowHeight - 5;
cv::Mat resize_mat( cur_image, cv::Rect(x_offset, y_offset, width, height));
bool ret = cv::imwrite(filename.c_str(), resize_mat);
auto&& [scale, image] = get_format_image();
bool ret = cv::imwrite(filename.c_str(), image);
if (ret) {
DebugTraceInfo("PrintWindow to", filename);
@@ -177,14 +169,15 @@ void Assistance::working_proc(Assistance* pThis)
while (!pThis->m_thread_exit) {
std::unique_lock<std::mutex> lock(pThis->m_mutex);
if (pThis->m_thread_running) {
auto cur_image = pThis->m_pView->getImage(pThis->m_pView->getWindowRect());
auto && [scale, cur_image] = pThis->get_format_image();
pThis->m_pCtrl->setControlScale(scale);
if (cur_image.empty()) {
DebugTraceError("Unable to capture window image!!!");
pThis->stop(false);
continue;
}
if (cur_image.cols < pThis->m_configer.DefaultWindowWidth || cur_image.rows < pThis->m_configer.DefaultWindowHeight) {
if (cur_image.rows < 100) {
DebugTraceInfo("Window Could not be minimized!!!");
pThis->m_pWindow->showWindow();
pThis->m_condvar.wait_for(lock,
@@ -195,7 +188,7 @@ void Assistance::working_proc(Assistance* pThis)
std::string matched_task;
Rect matched_rect;
// 逐个匹配当前可能的图像
for (auto&& task_name : pThis->m_next_tasks) {
double threshold = pThis->m_configer.m_tasks[task_name].threshold;
@@ -212,9 +205,11 @@ void Assistance::working_proc(Assistance* pThis)
}
}
// 执行任务
if (!matched_task.empty()) {
auto&& task = pThis->m_configer.m_tasks[matched_task];
DebugTraceInfo("***Matched***", matched_task, "Type:", task.type);
// 前置固定延时
if (task.pre_delay > 0) {
DebugTrace("PreDelay", task.pre_delay);
// std::this_thread::sleep_for(std::chrono::milliseconds(task.pre_delay));
@@ -227,6 +222,7 @@ void Assistance::working_proc(Assistance* pThis)
DebugTrace("CurTimes:", task.exec_times, "MaxTimes:", task.max_times);
}
if (task.exec_times < task.max_times) {
// 随机延时功能
if ((task.type & TaskType::BasicClick)
&& pThis->m_configer.m_options.control_delay_upper != 0) {
static std::default_random_engine rand_engine(std::chrono::system_clock::now().time_since_epoch().count());
@@ -237,6 +233,7 @@ void Assistance::working_proc(Assistance* pThis)
[&]() -> bool { return !pThis->m_thread_running; });
if (cv_ret) { continue; }
}
switch (task.type) {
case TaskType::ClickRect:
matched_rect = task.specific_area;
@@ -256,6 +253,7 @@ void Assistance::working_proc(Assistance* pThis)
break;
case TaskType::PrintWindow:
if (pThis->m_configer.m_options.print_window) {
// 每次到结算界面,掉落物品不是一次性出来的,有个动画,所以需要等一会再截图
int print_delay = pThis->m_configer.m_options.print_window_delay;
DebugTraceInfo("Ready to print window, delay", print_delay);
pThis->m_condvar.wait_for(lock,
@@ -274,10 +272,15 @@ void Assistance::working_proc(Assistance* pThis)
break;
}
++task.exec_times;
// 减少其他任务的执行次数
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效
// 所以要给蓝色开始行动的次数减一
for (auto&& reduce : task.reduce_other_times) {
--pThis->m_configer.m_tasks[reduce].exec_times;
DebugTrace("Reduce exec times", reduce, pThis->m_configer.m_tasks[reduce].exec_times);
}
// 后置固定延时
if (task.rear_delay > 0) {
DebugTrace("RearDelay", task.rear_delay);
// std::this_thread::sleep_for(std::chrono::milliseconds(task.rear_delay));
@@ -292,6 +295,7 @@ void Assistance::working_proc(Assistance* pThis)
pThis->m_next_tasks = pThis->m_configer.m_tasks[matched_task].exceeded_next;
}
// 单纯为了打印日志。。感觉可以优化下
std::string nexts_str;
for (auto&& name : pThis->m_next_tasks) {
nexts_str += name + ",";
@@ -301,6 +305,7 @@ void Assistance::working_proc(Assistance* pThis)
}
DebugTrace("Next:", nexts_str);
}
pThis->m_condvar.wait_for(lock,
std::chrono::milliseconds(pThis->m_configer.m_options.identify_delay),
[&]() -> bool { return !pThis->m_thread_running; });
@@ -310,3 +315,28 @@ void Assistance::working_proc(Assistance* pThis)
}
}
}
std::pair<double, cv::Mat> asst::Assistance::get_format_image()
{
auto && cur_image = m_pView->getImage(m_pView->getWindowRect());
if (cur_image.empty() || cur_image.rows < 100) {
DebugTraceError("Window image error");
return { 0, cur_image };
}
// 把模拟器边框的一圈裁剪掉
auto&& window_info = m_pView->getEmulatorInfo();
int x_offset = window_info.x_offset;
int y_offset = window_info.y_offset;
int width = cur_image.cols - x_offset - window_info.right_offset;
int height = cur_image.rows - y_offset - window_info.bottom_offset;
cv::Mat cropped(cur_image, cv::Rect(x_offset, y_offset, width, height));
// 调整尺寸,与资源中截图的标准尺寸一致
cv::Mat dst;
cv::resize(cropped, dst, cv::Size(m_configer.DefaultWindowWidth, m_configer.DefaultWindowHeight));
double scale = static_cast<double>(width) / m_configer.DefaultWindowWidth;
return { scale, dst };
}

View File

@@ -162,17 +162,25 @@ bool Configer::reload(const std::string& filename)
handle_info.window_name = info["window"].as_string();
emulator_info.control.emplace_back(handle_info);
}
if (emulator_json.exist("adbControl")) {
if (emulator_json.exist("adb")) {
emulator_info.is_adb = true;
// meojson的bug暂时没空修先转个字符串
emulator_info.adb.path = StringReplaceAll(emulator_json["adbControl"]["path"].as_string(), "\\\\", "\\");
emulator_info.adb.connect = emulator_json["adbControl"]["connect"].as_string();
emulator_info.adb.click = emulator_json["adbControl"]["click"].as_string();
emulator_info.adb.path = StringReplaceAll(emulator_json["adb"]["path"].as_string(), "\\\\", "\\");
emulator_info.adb.connect = emulator_json["adb"]["connect"].as_string();
emulator_info.adb.click = emulator_json["adb"]["click"].as_string();
emulator_info.adb.display = emulator_json["adb"]["display"].as_string();
emulator_info.adb.display_regex = emulator_json["adb"]["displayRegex"].as_string();
}
emulator_info.width = emulator_json["width"].as_integer();
emulator_info.height = emulator_json["height"].as_integer();
emulator_info.x_offset = emulator_json["xOffset"].as_integer();
emulator_info.y_offset = emulator_json["yOffset"].as_integer();
if (emulator_json.exist("rightOffset")) {
emulator_info.right_offset = emulator_json["rightOffset"].as_integer();
}
if (emulator_json.exist("bottomOffset")) {
emulator_info.bottom_offset = emulator_json["bottomOffset"].as_integer();
}
temp.m_handles.emplace(name, emulator_info);
}
@@ -189,6 +197,7 @@ bool Configer::reload(const std::string& filename)
bool Configer::set_param(const std::string& type, const std::string& param, const std::string& value)
{
// 暂时只用到了这些,总的参数太多了,后面要用啥再加上
if (type == "task.type") {
if (m_tasks.find(param) == m_tasks.cend()) {
return false;
@@ -227,6 +236,7 @@ bool Configer::set_param(const std::string& type, const std::string& param, cons
std::optional<std::string> Configer::get_param(const std::string& type, const std::string& param)
{
// 暂时只用到了这些,总的参数太多了,后面要用啥再加上
if (type == "task.execTimes" && m_tasks.find(param) != m_tasks.cend()) {
return std::to_string(m_tasks.at(param).exec_times);
}

View File

@@ -27,7 +27,7 @@ void Identify::set_use_cache(bool b) noexcept
}
}
Mat Identify::image2hist(const cv::Mat& src)
Mat Identify::image_2_hist(const cv::Mat& src)
{
Mat src_hsv;
cvtColor(src, src_hsv, COLOR_BGR2HSV);
@@ -49,7 +49,7 @@ Mat Identify::image2hist(const cv::Mat& src)
double Identify::image_hist_comp(const cv::Mat& src, const cv::MatND& hist)
{
// keep the interface return value unchanged
return 1 - compareHist(image2hist(src), hist, CV_COMP_BHATTACHARYYA);
return 1 - compareHist(image_2_hist(src), hist, CV_COMP_BHATTACHARYYA);
}
std::pair<double, cv::Point> Identify::find_image(const cv::Mat& image, const cv::Mat& templ)
@@ -74,21 +74,22 @@ std::tuple<AlgorithmType, double, asst::Rect> Identify::find_image(const Mat& cu
return { AlgorithmType::JustReturn, 0, asst::Rect() };
}
// 有缓存用直方图比较CPU占用会低很多但要保证每次按钮图片的位置不变
if (m_use_cache && m_cache_map.find(templ) != m_cache_map.cend()) {
auto&& [rect, hist] = m_cache_map.at(templ);
double value = image_hist_comp(cur(rect), hist);
return { AlgorithmType::CompareHist, value, cvrect2rect(rect).center_zoom(0.8) };
return { AlgorithmType::CompareHist, value, cvrect_2_rect(rect).center_zoom(0.8) };
}
else {
else { // 没缓存就模板匹配
auto&& templ_mat = m_mat_map.at(templ);
auto&& [value, point] = find_image(cur, templ_mat);
cv::Rect raw_rect(point.x, point.y, templ_mat.cols, templ_mat.rows);
if (m_use_cache && value >= threshold) {
m_cache_map.emplace(templ, std::make_pair(raw_rect, image2hist(cur(raw_rect))));
m_cache_map.emplace(templ, std::make_pair(raw_rect, image_2_hist(cur(raw_rect))));
}
return { AlgorithmType::MatchTemplate, value, cvrect2rect(raw_rect).center_zoom(0.8) };
return { AlgorithmType::MatchTemplate, value, cvrect_2_rect(raw_rect).center_zoom(0.8) };
}
}

View File

@@ -8,8 +8,11 @@
#include <stdint.h>
#include <WinUser.h>
#include <opencv2/opencv.hpp>
#include "AsstDef.h"
#include "Logger.hpp"
#include "Configer.h"
using namespace asst;
@@ -40,8 +43,8 @@ bool WinMacro::findHandle()
break;
case HandleType::Control:
m_is_adb = m_emulator_info.is_adb;
m_x_offset = m_emulator_info.x_offset;
m_y_offset = m_emulator_info.y_offset;
//m_x_offset = m_emulator_info.x_offset;
//m_y_offset = m_emulator_info.y_offset;
handle_vec = m_emulator_info.control;
break;
default:
@@ -96,11 +99,21 @@ bool WinMacro::findHandle()
adb_dir = '"' + StringReplaceAll(m_emulator_info.adb.path, "[EmulatorPath]", adb_dir) + '"';
std::string connect_cmd = adb_dir + m_emulator_info.adb.connect;
auto ret = callCmd(connect_cmd, 10000);
if (ret != 0) {
DebugTraceError("Connect ADB Error", ret);
if (!callCmd(connect_cmd)) {
DebugTraceError("Connect Adb Error");
return false;
}
auto display_ret = callCmd(adb_dir + m_emulator_info.adb.display);
if (display_ret) {
std::string pipe_str = std::move(display_ret).value();
sscanf_s(pipe_str.c_str(), m_emulator_info.adb.display_regex.c_str(),
&m_emulator_info.adb.display_width, &m_emulator_info.adb.display_height);
}
else {
DebugTraceError("Get Display Error");
return false;
}
m_click_cmd = adb_dir + m_emulator_info.adb.click;
}
DebugTrace("Handle:", m_handle, "Name:", m_emulator_info.name, "Type:", m_handle_type);
@@ -113,32 +126,69 @@ bool WinMacro::findHandle()
}
}
DWORD asst::WinMacro::callCmd(const std::string& cmd, int wait_time)
std::optional<std::string> asst::WinMacro::callCmd(const std::string& cmd, bool use_pipe)
{
// int ret = system(cmd.c_str());
// 初始化管道
constexpr int PipeBuffSize = 1024;
HANDLE pipe_read = NULL;
HANDLE pipe_write = NULL;
SECURITY_ATTRIBUTES sa_out_pipe;
::ZeroMemory(&sa_out_pipe, sizeof(sa_out_pipe));
sa_out_pipe.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_out_pipe.lpSecurityDescriptor = NULL;
sa_out_pipe.bInheritHandle = TRUE;
BOOL pipe_ret = FALSE;
if (use_pipe) {
pipe_ret = ::CreatePipe(&pipe_read, &pipe_write, &sa_out_pipe, PipeBuffSize);
DebugTrace("Create Pipe ret", pipe_ret);
}
// 准备启动ADB进程
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&process_info, sizeof(process_info));
startup_info.cb = sizeof(STARTUPINFO);
startup_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup_info.hStdOutput = pipe_write;
startup_info.hStdError = pipe_write;
startup_info.wShowWindow = SW_HIDE;
DWORD ret = -1;
if (::CreateProcessA(NULL, const_cast<LPSTR>(cmd.c_str()), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startup_info, &process_info)) {
::WaitForSingleObject(process_info.hProcess, wait_time);
::GetExitCodeProcess(process_info.hProcess, &ret);
::CloseHandle(process_info.hProcess);
::CloseHandle(process_info.hThread);
DebugTrace("Call", cmd, "—— ret", ret);
}
else {
if (!::CreateProcessA(NULL, const_cast<LPSTR>(cmd.c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &startup_info, &process_info)) {
DebugTraceError("Create process error");
return std::nullopt;
}
::WaitForSingleObject(process_info.hProcess, 30000);
std::string pipe_str;
if (use_pipe && pipe_ret) {
DWORD read_num = 0;
DWORD std_num = 0;
if (::PeekNamedPipe(pipe_read, NULL, 0, NULL, &read_num, NULL) && read_num > 0) {
char pipe_buffer[PipeBuffSize] = { 0 };
::ReadFile(pipe_read, pipe_buffer, read_num, &std_num, NULL);
pipe_str = std::string(pipe_buffer, std_num);
}
}
return ret;
::GetExitCodeProcess(process_info.hProcess, &ret);
::CloseHandle(process_info.hProcess);
::CloseHandle(process_info.hThread);
DebugTrace("Call", cmd, "ret", ret);
if (use_pipe) {
DebugTrace("Pipe:", pipe_str);
}
if (pipe_read != NULL) {
::CloseHandle(pipe_read);
}
if (pipe_write != NULL) {
::CloseHandle(pipe_write);
}
return pipe_str;
}
bool WinMacro::resizeWindow(int width, int height)
@@ -216,25 +266,19 @@ bool WinMacro::click(const Point& p)
return false;
}
int x = p.x * m_control_scale;
int y = p.y * m_control_scale;
DebugTrace("Click, raw:", p.x, p.y, "corr:", x, y);
if (m_is_adb) {
int x = (p.x + m_x_offset);
int y = (p.y + m_y_offset);
std::string cur_cmd = StringReplaceAll(m_click_cmd, "[x]", std::to_string(x));
cur_cmd = StringReplaceAll(cur_cmd, "[y]", std::to_string(y));
auto ret = callCmd(cur_cmd);
DebugTrace("Call", cur_cmd, "—— ret", ret);
return !ret;
return callCmd(cur_cmd, false).has_value();
}
else {
int x = (p.x + m_x_offset) / getScreenScale();
int y = (p.y + m_y_offset) / getScreenScale();
DebugTrace("Click, raw:", p.x, p.y, "corr:", x, y);
LPARAM lparam = MAKELPARAM(x, y);
::SendMessage(m_handle, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
::SendMessage(m_handle, WM_LBUTTONUP, 0, lparam);
return true;
}
}
@@ -266,6 +310,16 @@ bool WinMacro::click(const Rect& rect)
return click({ x, y });
}
void asst::WinMacro::setControlScale(double scale)
{
if (m_is_adb) {
m_control_scale = scale * scale * Configer::DefaultWindowWidth / m_emulator_info.adb.display_width;
}
else {
m_control_scale = scale;
}
}
Rect WinMacro::getWindowRect()
{
if (!::IsWindow(m_handle)) {

View File

@@ -45,14 +45,14 @@ A game assistance for Arknights
#### 腾讯手游助手
兼容但需要手动设置分辨率设置中心——引擎设置——分辨率设置——1280x720——保存后重启模拟器
理论上完美兼容,未多做测试
#### MuMu模拟器
MuMu是个奇葩它的所有的窗口句柄均不响应SendMessage鼠标消息
- MuMu模拟器
兼容使用了adb控制的方式。但需要手动设置分辨率设置中心——界面——分辨率设置——1280x720——保存后重启模拟器
完美兼容专门单独做了一套基于Adb控制逻辑
- MuMu手游助手星云引擎
不兼容,正在想办法……
@@ -76,6 +76,7 @@ MuMu是个奇葩它的所有的窗口句柄均不响应SendMessage鼠标消
1. 明日方舟处于任意界面均可,会自动帮你点过去
2. 点击"访问基建"
3. 达到10次上限或者所有可访问的好友都访问完了就会自动停的
4. 然后会贴心的帮你跳转到信用商店,顺便收了当天信用~
### 设置操作延时
@@ -83,6 +84,12 @@ MuMu是个奇葩它的所有的窗口句柄均不响应SendMessage鼠标消
![图例](readme/controlDelayRange.png)
### 自动截图功能
每次刷完结算界面,会自动截一张图,保存在`screenshot`文件夹中
这个功能默认是打开的,不需要的话可以手动关掉:请手动修改`resource\config.json`文件中,`options`.`printWindow`字段的值,`true`是打开,`false`是关闭。文件保存后请重新打开程序。
![图例](readme/printWindow.png)
## Todo
~~在做了在做了.jpg~~

BIN
readme/printWindow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -2,14 +2,20 @@
"version": "0.4",
"options": {
"identifyCache": true,
"identifyCache_Doc": "图像识别缓存功能开启后可以大幅降低CPU消耗但需要保证要识别的按钮每次的位置不会改变。true-开启false-关闭默认true",
"identifyDelay": 1000,
"identifyDelay_Doc": "图像识别延时越快操作越快但会增加CPU消耗。单位毫秒默认1000",
"printWindow": true,
"printWindow_Doc": "截图功能开启后每次结算界面会截图到screenshot目录下。true-开启false-关闭默认true",
"printWindowDelay": 3000,
"printWindowDelay_Doc": "截图延时每次到结算界面掉落物品不是一次性出来的有个动画所以需要等一会再截图。单位毫秒默认3000",
"controlDelayRange": [
0,
0
]
],
"controlDelayRange_Doc": "点击随机延时:每次点击操作会进行随机延时,降低封号风险(好像也没听说过谁被封号的)。格式为 [ 最小延时, 最大延时 ]单位为毫秒。例如想设置3~5秒延时即修改为[ 3000, 5000 ]默认0~0"
},
"handle_Doc": "下面的和模拟器窗口捕获逻辑有关,不需要修改",
"handle": {
"BlueStacks": {
"window": [
@@ -40,8 +46,10 @@
],
"width": 1294,
"height": 773,
"xOffset": -7,
"yOffset": -47
"xOffset": 11,
"yOffset": 46,
"rightOffset": 11,
"bottomOffset": 10
},
"Nox": {
"window": [
@@ -65,7 +73,7 @@
"width": 1298,
"height": 754,
"xOffset": 0,
"yOffset": -32
"yOffset": 32
},
"LDPlayer": {
"window": [
@@ -93,7 +101,7 @@
"width": 1304,
"height": 756,
"xOffset": 0,
"yOffset": -35
"yOffset": 35
},
"XYAZ": {
"window": [
@@ -121,7 +129,7 @@
"width": 1282,
"height": 769,
"xOffset": 0,
"yOffset": -47
"yOffset": 47
},
"Tencent": {
"window": [
@@ -149,7 +157,7 @@
"width": 1282,
"height": 769,
"xOffset": 0,
"yOffset": -42
"yOffset": 42
},
"MuMuAsst": {
"window": [
@@ -178,7 +186,7 @@
"width": 1280,
"height": 809,
"xOffset": 0,
"yOffset": -36
"yOffset": 36
},
"MuMuEmulator": {
"window": [
@@ -199,17 +207,22 @@
"window": "明日方舟 - MuMu模拟器"
}
],
"adbControl": {
"adb": {
"path": "[EmulatorPath]..\\vmonitor\\bin\\adb_server.exe",
"connect": " connect 127.0.0.1:7555",
"click": " shell input tap [x] [y]"
"click": " shell input tap [x] [y]",
"display": " shell dumpsys window displays | grep init= | awk ' { print $3 } '",
"displayRegex": "cur=%dx%d"
},
"width": 1280,
"height": 809,
"xOffset": 0,
"yOffset": -36
"yOffset": 36,
"rightBottom": 0,
"bottomOffset": 53
}
},
"tasks_Doc": "下面的和任务队列执行逻辑有关,不需要修改",
"tasks": {
"SanityBegin": {
"filename": "",
@@ -363,6 +376,8 @@
},
"ReturnToFriends": {
"filename": "Return.png",
"threshold": 0.85,
"cacheThreshold": 0.85,
"type": "clickSelf",
"next": [
"Friends",