原作者在1.85版本的时候将代码重构了,有很多步骤发生了变更,重新瞅瞅
素材包下载
https://download.csdn.net/download/qq_42883074/89787126 这个是3-5章所需的所有相关素材包,1.85版本的,解压到项目目录下即可按下面的步骤走
一、main入口函数 (不带事件循环及ui)
和第一章一样,先取main,然后去找对应的函数在哪里,是做什么的
import ctypes
import os
import subprocess
import sys
import time
import traceback
import webbrowserimport state
from lanrenauto.jianchaupdate import get_var_data, get_updatetxt
from lanrenauto.logger_module import logger
from lanrenauto.moni.moni import find_child_window_handle, get_windows_screen_scale, set_console_positionif __name__ == '__main__':try:#忽略qt颜色警告os.environ['QT_LOGGING_RULES'] = 'qt.qpa.*=false'if ctypes.windll.shell32.IsUserAnAdmin():hwnd_cmd = find_child_window_handle(0, "ConsoleWindowClass", "YuanShenAi")if hwnd_cmd == 0:hwnd_cmd = find_child_window_handle(0, "ConsoleWindowClass", "懒人原神")vardata = get_var_data()if vardata != False:updatetxt = get_updatetxt()print(updatetxt)print("当前版本号:" + state.版本号)if float(vardata[1]) > float(state.版本号):print("如果更新失败手动下更新包手动覆盖: https://gitee.com/LanRenZhiNeng/lanren-genshin-impact-ai/releases")var = input(f"发现更新的版本 v{vardata[1]} 是否需要更新? (输入yes or no 默认更新)\n")if var == "":var = "yes"if var.lower() != "no":if float(state.版本号)>=float(vardata[3]):update_process = subprocess.Popen(f'update.exe "{vardata[0]}.exe" "{vardata[2]}"',creationflags=subprocess.CREATE_NEW_CONSOLE)time.sleep(1)sys.exit()else:print(f"更新失败!因为你当前版本低于{vardata[3]}最低可自动更新的版本,需要去下载最新的一键安装包")# 检测当前桌面的缩放比例if not get_windows_screen_scale() == 1:input(f"桌面缩放比例不是100% 禁止使用!请在桌面 右键 显示设置 缩放与布局改成100%\n设置好后如果还不行请检查是否有多个屏幕 可以拔掉其它屏幕 单独设置缩放100% 然后重启电脑\n如果非要用按回车继续")# 检测 连招服务是否存在if not os.path.exists(state.LIANZHAOFUWU):webbrowser.open("https://www.runker.net/windows-defender.html")input(f"文件 '{state.LIANZHAOFUWU}' 不存在!极有可能被杀毒软件干掉了!请关闭杀毒软件后重新安装!")exit()# 运行需要管理员权限的代码kernel32 = ctypes.windll.kernel32kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), 128)os.environ['PNG_IGNORE_WARNINGS'] = '1'#设置cmd窗口位置set_console_position(state.CMD_LEFT,state.CMD_TOP,state.CMD_WIDTH,state.CMD_HEIGHT)# # 在事件循环中创建一个任务对象并加入事件循环# ysyl = YolovYuanShen(weights=state.WEIGHTS, providers=state.PROVIDERS, dic_labels=state.dic_labels,# model_h=state.IMGSIZE_WIDTH, model_w=state.IMGSIZE_HEIGHT) # 启动yolov检测服务# ysam = YuanShenAutoMap() # 启动寻路类# app = QApplication(sys.argv) # 初始化Qt应用# ratio = screen_width / 2560 # 分辨率比例# # 设置全局字体大小# # 计算字体大小# base_font_size = 13# # 基准字体大小,适合1920*1080分辨率# new_font_size = int(base_font_size * ratio)# font = QFont("Arial", new_font_size)## # 子控件的宽度# item_width=320# item_height=240# item_height_min = 60# window_main = MainWindow()# window_main.show()# sys.exit(app.exec_()) # 监听消息不关闭else:# 如果不是管理员,则请求以管理员权限重新运行程序ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)except Exception as err:if str(err).find("sys.exit")==-1:logger.error(traceback.format_exc())
1、版本新增: 桌面缩放检测、检测录制服务是否存在
经过第一章的整理这里我们也能大致看到上面是什么作用,和之前的变化在于
# 检测当前桌面的缩放比例if not get_windows_screen_scale() == 1:input(f"桌面缩放比例不是100% 禁止使用!请在桌面 右键 显示设置 缩放与布局改成100%\n设置好后如果还不行请检查是否有多个屏幕 可以拔掉其它屏幕 单独设置缩放100% 然后重启电脑\n如果非要用按回车继续")# 检测 连招服务是否存在if not os.path.exists(state.LIANZHAOFUWU):webbrowser.open("https://www.runker.net/windows-defender.html")input(f"文件 '{state.LIANZHAOFUWU}' 不存在!极有可能被杀毒软件干掉了!请关闭杀毒软件后重新安装!")exit()
可以看到新增了一个坐标缩放检测,以及连招(录制操作及回放键鼠操作)
2、查找子窗口句柄及鼠标方法
lanrenauto/moni/moni.py
# -*- coding: utf-8 -*-
import ctypes
import math
import time
from ctypes import wintypes
import pyautogui
import win32api
import win32con
import win32gui
# 获取当前屏幕宽度和高度
screen_width = ctypes.windll.user32.GetSystemMetrics(0)
screen_height = ctypes.windll.user32.GetSystemMetrics(1)
# 定义MOUSEINPUT结构体
class MOUSEINPUT(ctypes.Structure):_fields_ = [("dx", ctypes.c_long),("dy", ctypes.c_long),("mouseData", ctypes.c_ulong),("dwFlags", ctypes.c_ulong),("time", ctypes.c_ulong),("dwExtraInfo", ctypes.POINTER(ctypes.c_ulong))]
# 定义KEYBDINPUT结构体
class KEYBDINPUT(ctypes.Structure):_fields_ = [("wVk", ctypes.wintypes.WORD),("wScan", ctypes.wintypes.WORD),("dwFlags", ctypes.c_ulong),("time", ctypes.c_ulong),("dwExtraInfo", ctypes.POINTER(ctypes.c_ulong))]
# 定义INPUT结构体
class INPUT(ctypes.Structure):class _INPUT(ctypes.Union):_fields_ =[("mi", MOUSEINPUT),("ki", KEYBDINPUT)]_fields_ = [("type", ctypes.c_ulong),("input", _INPUT)]
# 鼠标相对移动函数
def mouse_moveR(dx, dy):# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=dx, dy=dy, mouseData=0, dwFlags=win32con.MOUSEEVENTF_MOVE, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
def mouse_move(x, y):# 计算目标位置的绝对坐标值absolute_x = int(x / screen_width * 65535)absolute_y = int(y / screen_height * 65535)# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=absolute_x, dy=absolute_y, mouseData=0, dwFlags=win32con.MOUSEEVENTF_MOVE | win32con.MOUSEEVENTF_ABSOLUTE,time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 鼠标左键按下
def mouse_left_down():# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=win32con.MOUSEEVENTF_LEFTDOWN, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 鼠标左键弹起
def mouse_left_up():# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=win32con.MOUSEEVENTF_LEFTUP, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 鼠标右键按下
def mouse_right_down():# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=win32con.MOUSEEVENTF_RIGHTDOWN, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))# 鼠标右键弹起
def mouse_right_up():# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=win32con.MOUSEEVENTF_RIGHTUP, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 鼠标中键按下
def mouse_middle_down():# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=win32con.MOUSEEVENTF_MIDDLEDOWN, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 鼠标中键弹起
def mouse_middle_up():# 构造输入事件列表inputs = []inputs.append(INPUT(type=0, input=INPUT._INPUT(mi=MOUSEINPUT(dx=0, dy=0, mouseData=0, dwFlags=win32con.MOUSEEVENTF_MIDDLEUP, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
original_style=0
def set_mouse_through(hwnd,setPenetrate=True):global original_style"""设置窗口鼠标穿透"""if setPenetrate:style = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)original_style = style # 保存原始样式,以便恢复new_style = style | win32con.WS_EX_TRANSPARENT | win32con.WS_EX_LAYEREDwin32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, new_style)else:if original_style==0:win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, win32con.WS_EX_LAYERED)else:win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, original_style)
def set_window_activate(hwnd):"""强制激活窗口"""try:#判断窗口是否为最小化if win32gui.IsIconic(hwnd):# 还原窗口win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)# 将窗口置顶win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)# 取消置顶win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)win32gui.SetForegroundWindow(hwnd)except:pass
def set_window_display_affinity(hwnd):# WDA_NONE# 0x00000000# 对窗口的显示位置没有限制。# WDA_MONITOR# 0x00000001# 窗口内容仅显示在监视器上。 在其他任何位置,窗口都会显示,其中没有任何内容。# WDA_EXCLUDEFROMCAPTURE# 0x00000011# 窗口仅显示在监视器上。 在其他位置,窗口根本不显示。# 此相关性的一个用途是用于显示视频录制控件的窗口,以便控件不包含在捕获中。dwAffinity = wintypes.DWORD(0x00000011) # 使用wintypes模块,“0x00000001”可换成其他值dll = ctypes.cdll.LoadLibrary("C:\\WINDOWS\\system32\\user32.dll") # 接着导入user32.dll 模块dll.SetWindowDisplayAffinity(hwnd, dwAffinity)
# 键盘按下函数
def key_down(vk_code):inputs = []inputs.append(INPUT(type=1, input=INPUT._INPUT(ki=KEYBDINPUT(wVk=vk_code, wScan=0, dwFlags=0, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 键盘弹起函数
def key_up(vk_code):inputs = []inputs.append(INPUT(type=1, input=INPUT._INPUT(ki=KEYBDINPUT(wVk=vk_code, wScan=0, dwFlags=win32con.KEYEVENTF_KEYUP, time=0, dwExtraInfo=None))))# 发送输入事件ctypes.windll.user32.SendInput(len(inputs), ctypes.byref(inputs[0]), ctypes.sizeof(INPUT))
# 键盘按下函数
def key_down2(vk_code):win32api.keybd_event(vk_code, 0, 0, 0)
# 键盘弹起函数
def key_up2(vk_code):win32api.keybd_event(vk_code, 0, win32con.KEYEVENTF_KEYUP, 0)
def get_windows_screen_scale():""" 调用 Windows API 函数获取缩放比例 """try:user32 = ctypes.windll.user32user32.SetProcessDPIAware()dpi = user32.GetDpiForSystem()# 计算缩放比例scale = round(dpi / 96.0, 2)return scaleexcept Exception as e:print("获取缩放比例时出错:", e)return 1
def set_console_position(x, y, w,h):hwnd = ctypes.windll.kernel32.GetConsoleWindow()if hwnd:ctypes.windll.user32.MoveWindow(hwnd, x, y, w, h, True)
def get_window_handle_at_mouse_position():active_hwnd = ctypes.windll.user32.GetForegroundWindow()return active_hwnd
def find_child_window_handle(parent, class_name, window_title):hwnd_child = 0while True:hwnd_child = win32gui.FindWindowEx(parent, hwnd_child, class_name, None)if hwnd_child:buffer=win32gui.GetWindowText(hwnd_child)if window_title in buffer:return hwnd_childelse:return 0
def get_windows_screen_scale():""" 调用 Windows API 函数获取缩放比例 """try:user32 = ctypes.windll.user32user32.SetProcessDPIAware()dpi = user32.GetDpiForSystem()# 计算缩放比例scale = round(dpi / 96.0, 2)return scaleexcept Exception as e:print("获取缩放比例时出错:", e)return 1
def set_console_position(x, y, w,h):hwnd = ctypes.windll.kernel32.GetConsoleWindow()if hwnd:ctypes.windll.user32.MoveWindow(hwnd, x, y, w, h, True)
def set_angle(angle_now, angle_target, fault=6):'''模拟鼠标 控制镜头到指定方向角度Args:angle_now:angle_target:fault: 最少容差Returns:是否需要调整'''ret = Trueangle_diff = abs(angle_now - angle_target)if angle_diff <= fault:ret = Falseif ret == False:return Falseangle_diff = int((angle_target - angle_now) % 360)if angle_diff > 180:fangxiang = -1angle_diff = 360 - angle_diffelse:fangxiang = 1if abs(angle_diff < fault):return True# print(f"调整视角 相差:{fangxiang * angle_diff}° 当前:{angle_now}° 目标:{angle_target}° ")if angle_diff >= 100:mouse_moveR(int(fangxiang * 6 * angle_diff), 0)else:mouse_moveR(int(fangxiang * 5 * angle_diff), 0)return True
def get_angle(start_pos, next_pos):'''计算两个坐标之间的角度 相对以北方向为0度的角度Args:start_pos:next_pos:Returns:'''# 计算方向向量direction_x = next_pos[0] - start_pos[0]direction_y = next_pos[1] - start_pos[1]# 使用反正切函数计算弧度角度值angle_rad = math.atan2(direction_y, direction_x)# 将弧度角度值转换为以北方向为0度的角度angle_deg = math.degrees(angle_rad)# 将角度值转换为顺时针方向angle_deg = int((angle_deg + 360 + 90) % 360)return angle_deg
def running(bengpao=True):pyautogui.keyDown('w')if bengpao:mouse_right_down()time.sleep(1)mouse_right_up()
def speed_up():pyautogui.keyDown('w')mouse_right_down()time.sleep(0.2)mouse_right_up()def show_hide_window(hwnd, show):if show:win32gui.ShowWindow(hwnd, 5) # 5对应SW_SHOWelse:win32gui.ShowWindow(hwnd, 0) # 0对应SW_HIDE
3、检查版本更新
lanrenauto/jianchaupdate.py
import htmlimport requestsdef get_var_data():try:headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.47"}res=requests.get("https://note.youdao.com/yws/api/personal/preview/WEB96c82d6b37c9fd60f8b1d146731080d6?method=convert&shareToken=f3fb8b827c2fbcd651073cf7b55a6b3e&engine=nyozo",headers=headers)if res.status_code==200:return html.unescape(res.text).split("<pre>")[-1].split("</pre>")[0].split("\n")[:-1]else:return Falseexcept:print("请不要开梯子")return False
def get_updatetxt():try:headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.47"}res = requests.get("https://note.youdao.com/yws/api/personal/preview/WEB9b9f3f25e3921ae407edf8ba9537b847?method=convert&shareToken=8b937504aee5dcb1f8c62678d7fffdad&engine=nyozo",headers=headers)if res.status_code == 200:return html.unescape(res.text).split("<pre>")[-1].split("</pre>")[0][:-1]else:return ""except:print("无法获取更新日志,请不要开梯子!")return ""
if __name__ == '__main__':print(get_var_data())print(get_updatetxt())
4、全局变量
state.py
# -*- coding: utf-8 -*-
import configparser
import json
import os
import sys# 创建 ConfigParser 对象
config = configparser.ConfigParser()
# 加载 INI 文件
config.read("./datas/setting.ini")
GAME_TITLE=config.get( 'seting','GAME_TITLE', fallback='原神')
LIANZHAO=config.get( 'seting', 'LIANZHAO',fallback='阵容1草神2久岐忍3钟离4雷神.txt')
PATH_TASK=config.get( 'seting','PATH_TASK', fallback='./datas/Task/')
PATH_JIAOBEN=config.get( 'seting', 'PATH_JIAOBEN',fallback='./datas/JiaoBen/')
PACKS_TASK=config.get( 'seting', 'PACKS_TASK',fallback='./datas/Task/')
PATH_JUESE=config.get( 'seting', 'PATH_JUESE',fallback='./datas/img/juese/')
PATH_ADDRESS=config.get( 'seting', 'PATH_ADDRESS',fallback='./datas/img/address/')
WEIGHTS=config.get( 'seting','WEIGHTS', fallback='./datas/yolov5s_320.onnx')
IMGSIZE_WIDTH=int(config.get( 'seting','IMGSIZE_WIDTH', fallback='320'))
IMGSIZE_HEIGHT=int(config.get( 'seting','IMGSIZE_HEIGHT', fallback='320'))
PROVIDERS=config.get( 'seting','PROVIDERS', fallback="""["CUDAExecutionProvider", "CPUExecutionProvider"]""")
PROVIDERS=json.loads(PROVIDERS.replace("'",'"'))
LIANZHAOFUWU=config.get( 'seting','LIANZHAOFUWU', fallback='./datas/jiaoben/躺宝连招插件.exe')
DUANGKOUHAO=config.get( 'seting','DUANGKOUHAO', fallback='29943')
WINDOW_WIDTH=int(config.get( 'seting','WINDOW_WIDTH', fallback="640"))
WINDOW_HEIGHT=int(config.get('seting','WINDOW_HEIGHT', fallback="900"))
WINDOW_LEFT=int(config.get( 'seting', 'WINDOW_LEFT',fallback="0"))
WINDOW_TOP=int(config.get( 'seting','WINDOW_TOP', fallback="300"))CMD_WIDTH=int(config.get( 'seting','CMD_WIDTH', fallback="800"))
CMD_HEIGHT=int(config.get('seting','CMD_HEIGHT', fallback="400"))
CMD_LEFT=int(config.get( 'seting', 'CMD_LEFT',fallback="500"))
CMD_TOP=int(config.get( 'seting','CMD_TOP', fallback="300"))if CMD_TOP<0:CMD_TOP=300
if CMD_LEFT<0:CMD_LEFT=500
if CMD_WIDTH<100:CMD_WIDTH=800CMD_HEIGHT = 400
if CMD_HEIGHT<100:CMD_WIDTH = 800CMD_HEIGHT = 400ON_SHUTDOWN=int(config.get( 'seting','ON_SHUTDOWN', fallback="0"))
ON_JIXING=int(config.get( 'seting','ON_JIXING', fallback="0"))
ON_NEXTPACK=int(config.get( 'seting','ON_NEXTPACK', fallback="0"))
ON_LIANZHAOBUJIANCE=int(config.get( 'seting','ON_LIANZHAOBUJIANCE', fallback="0"))
ON_STARTWITH=int(config.get('seting', 'ON_STARTWITH', fallback="0"))
TIMEOUT_DAGUAI=int(config.get( 'seting', 'TIMEOUT_DAGUAI',fallback='120'))
dic_labels={0:"玩家",1:"采集物",2:"生命树",3:"开关",4:"怪物",5:"提示关闭",6:"使用原粹树脂",7:"使用浓缩树脂",8:"关闭",9:"继续挑战",10:"退出秘境",11:"副本门框",12:"副本打完了",13:"副本楼梯",14:"往下落",15:"矿石",16:"往上跳",17:"F交互",18:"采集物-风",19:"采集物-冰",20:"确定",21:"取消",22:"返回",23:"被控了",24:"在水面",25:"宠物",26:"掉落物"}
状态_寻路中=True
状态_YOLOV=False
状态_检测中 = False
状态_循环开关 = False
状态_在爬墙=False
状态_需重新传送=False
状态_全局暂停=False
状态_已经有寻路了=False
状态_传送中=False
状态_是否回放中=False
状态_是否暂停=False
状态_是否开始录制=False
状态_是否禁止录制=True
状态_开伞时间=-1
录制_当前脚本名=""
录制_脚本文本=""
录制_path=""
录制_当前任务索引=-1
计时_未寻路=0
计次_误差过大=0
计次_移动停滞=0
计次_定位失败=0
计次_识别次数=0
计数_没找到怪物=0
计数_卡主次数=0
计数_没找到任何目标=0
计次_传送重试次数=0
开关_是否展预测结果=True
图片_YOLOV=False
图片_找图=False
游戏_当前视野角度=0
游戏_当前导航角度=0
游戏_当前坐标=[0,0]
游戏_当前目标坐标=[0,0]
游戏_打怪前坐标=[0,0]
QT_信号=None
版本号="1.85"python_var=sys.version_info[1]
# 添加临时路径
temp_path = os.path.dirname(__file__)+"\\"
sys.path.append(temp_path)
# 获取当前的PATH环境变量
original_path = os.environ['PATH']
os.environ['PATH'] = temp_path + os.pathsep + original_path
5、日志格式化
lanrenauto/logger_module.py
import logging
import os
from logging.handlers import TimedRotatingFileHandler# 创建全局的 logger
logger = logging.getLogger("LenRenAI")
logger.setLevel(logging.DEBUG)
# 创建一个handler,用于将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)log_dir = './log'
if not os.path.exists(log_dir):os.makedirs(log_dir)
# 创建一个handler,用于将日志输出到文件
file_handler = TimedRotatingFileHandler('./log/lanrenai.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.DEBUG)# 定义日志消息格式
class CustomFormatter(logging.Formatter):FORMATS = {logging.DEBUG: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',logging.INFO: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ,logging.WARNING: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ,logging.ERROR: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',logging.CRITICAL: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ,}def format(self, record):log_fmt = self.FORMATS.get(record.levelno)formatter = logging.Formatter(log_fmt)return formatter.format(record)#创建一个formatter格式类
formatter = CustomFormatter()#设置消息格式
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)# 将handler添加到logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
二、获取自动化游戏操作类(AutoYuanShen)
vi main.py
if __name__ == '__main__':....# 在事件循环中创建一个任务对象并加入事件循环from auto import YolovYuanShenysyl = YolovYuanShen(weights=state.WEIGHTS, providers=state.PROVIDERS, dic_labels=state.dic_labels,model_h=state.IMGSIZE_WIDTH, model_w=state.IMGSIZE_HEIGHT) # 启动yolov检测服务
1、添加操作类
操作太多了,我们用哪里看哪里
vi auto.py
# -*- coding: utf-8 -*-import random
import threading
import traceback
import tangbaowss
from lanrenauto.findpic.findimg import *
from lanrenauto.tools.screenshot import screenshot
import state
from lanrenauto.yolov.lanrenonnx import LanRenOnnxYolov
from lanrenauto.moni.moni import *
import pyautogui
imgs_duihuahuopengren = [cv2.imdecode(np.fromfile(file="./datas/对话.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED),cv2.imdecode(np.fromfile(file="./datas/烹饪.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED),cv2.imdecode(np.fromfile(file="./datas/钓鱼.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED),cv2.imdecode(np.fromfile(file="./datas/齿轮.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)]
imgs_pianpianhua = [cv2.imdecode(np.fromfile(file="./datas/薄荷.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED),cv2.imdecode(np.fromfile(file="./datas/甜甜花.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)]
def 开伞():__t=time.time()if -state.状态_开伞时间 > 2 or state.状态_开伞时间==-1:state.状态_开伞时间=__tlogger.info("检测到可能在下落 打风之翼")pyautogui.keyDown('space')time.sleep(0.2)pyautogui.keyUp('space')time.sleep(0.4)pyautogui.keyDown('space')time.sleep(0.2)pyautogui.keyUp('space')state.计次_误差过大 += 1state.状态_开伞时间=-1class YolovYuanShen():def __init__(self, weights, providers=["CUDAExecutionProvider",'CPUExecutionProvider'],dic_labels={},model_h=320, model_w=320):pyautogui.PAUSE = 0.01pyautogui.FAILSAFE = Falseself.yolov = LanRenOnnxYolov(weights=weights, providers=providers,dic_labels=dic_labels,model_h=model_h, model_w=model_w)self.interaction_label = ["怪物", "采集物", "矿石","掉落物"]self.centre_point = [(1920 // 2), (1080 // 2) + 100]self.key_w_down = Trueself.小地图区域 = (87 - 10, 45 - 10, 246 + 10, 204 + 10)state.状态_YOLOV = Truedef run_fight(self,wakuang=False,caiji=False,daguai=False):global loopstate.计时_未寻路 = 0# 获取窗口句柄hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄while True:if state.状态_循环开关 == False:logger.info("强制退出!")pyautogui.keyUp('w')state.状态_是否回放中 = Falsestate.计数_没找到任何目标 = 0tangbaowss.send_msg( "是否回放#@@#假")state.状态_检测中=Falsereturnif get_window_handle_at_mouse_position() == hwnd:breakstate.计数_没找到任何目标 = 0state.计数_没找到怪物 = 0state.状态_在爬墙 = Falsestate.状态_检测中 = Truestate.状态_开伞时间 = -1已经_F了=False历史_上次玩家高度 = 0.1玩家高度 = 0.1计数_没有玩家 = 0isIn_wxl = Falseset_window_activate(hwnd)jiaoben = (os.path.abspath(os.path.join(state.PATH_JIAOBEN, state.LIANZHAO)))# 临时发送消息给指定客户端message = f"解析脚本#@@#{jiaoben}"tangbaowss.send_msg(message)time.sleep(2)while True:try:if state.状态_循环开关 == False:state.状态_是否回放中 = Falsestate.计数_没找到任何目标 = 0state.状态_检测中 = Falsetangbaowss.send_msg( "是否回放#@@#假")returntime.sleep(0.005)rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = 0, 0, 1920, 1080 # 替换成你实际的区域坐标# F交互框位置left_dh, top_dh, right_dh, bottom_dh = [1161, 508, 1319, 572] # 替换成你实际的区域坐标p_left = rect[0] + w_pp_top = rect[1] + h_p计数_没有玩家 += 1if state.状态_寻路中 == False:state.计数_没找到任何目标 += 1state.计数_没找到怪物 += 1state.状态_在爬墙 = Falseif state.状态_已经有寻路了 == False or state.状态_检测中==False:state.状态_是否回放中 = Falsestate.计数_没找到任何目标 = 0tangbaowss.send_msg( "是否回放#@@#假")state.状态_检测中 = Falsereturnif state.状态_全局暂停:tangbaowss.send_msg( "是否回放#@@#假")time.sleep(2)continueif get_window_handle_at_mouse_position() != hwnd:tangbaowss.send_msg( "是否回放#@@#假")time.sleep(0.1)# 激活hwndhwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)if state.状态_传送中:time.sleep(1)continue# 设定保存截图的文件夹路径和文件名前缀# filename = f"./原神截图/{int(time.time())}{random.randint(1000,9999)}.jpg"ret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)if type(ret_scr) == bool:hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄logger.info("截图失败了!可能是内存溢出了")time.sleep(2)continuedatas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)if state.开关_是否展预测结果:state.QT_信号.mysig_show_yolov.emit()if state.状态_全局暂停:pyautogui.keyUp('w')tangbaowss.send_msg( "是否回放#@@#假")time.sleep(2)continueif datas == []:if state.状态_是否回放中 == False:if state.状态_在爬墙 == False and state.状态_寻路中 == True:计数_没有玩家 = 0ttt = threading.Thread(target=开伞)if state.python_var > 9:ttt.daemon = Trueelse:ttt.setDaemon(True)ttt.start()else:# 将离屏幕中心最近的点排在前面datas = sorted(datas, key=self.distance_to_target)isIn, idex = find_dict_index(datas, 'label', '往上跳')if isIn:state.状态_在爬墙 = Trueelse:state.状态_在爬墙 = FalseisIn_wxl, _ = find_dict_index(datas, 'label', '往下落')isIn, idex = find_dict_index(datas, 'label', '被控了')if isIn:logger.info("被控了!")pyautogui.press('space')pyautogui.press('space')state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")continueisIn_jhdh, idex = find_dict_index(datas, 'label', 'F交互')if isIn_jhdh: # 路过的顺手捡了# 二次排除对话烹饪 并且识别骗骗花if random.randint(1, 2) == 1:pyautogui.scroll(300)else:pyautogui.scroll(-300)pyautogui.keyUp('w')if not self.find_duihuahuopengren(hwnd, left_dh, top_dh, right_dh, bottom_dh, p_left,p_top) or self.find_pianpianhua(hwnd, left_dh, top_dh,right_dh, bottom_dh, p_left,p_top):self.key_w_down = False已经_F了 =Truestate.计次_识别次数 = 0state.计数_卡主次数 = 0logger.info(f"顺手捡采集物!{datas[idex]}")state.状态_有目标 = Truepyautogui.keyDown('f')time.sleep(0.1)pyautogui.keyUp('f')time.sleep(0.1)pyautogui.keyDown('f')time.sleep(0.1)pyautogui.keyUp('f')time.sleep(0.5)state.计次_识别次数 = 0state.计数_卡主次数 = 0ret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)else:pyautogui.keyDown('w')logger.info(f"是对话或烹饪{datas[idex]}")isIn_jhdh = FalseisIn, idex = find_dict_index(datas, 'label', '在水面')if isIn:logger.info("在水面!")state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")state.状态_寻路中 = Trueself.key_w_down = Trueif random.randint(1, 4) == 2:pyautogui.keyDown('shiftleft')time.sleep(0.6)pyautogui.keyUp('shiftleft')isIn, idex = find_dict_index(datas, 'label', '确定')if isIn:if state.状态_寻路中:state.状态_寻路中 = Falsestate.状态_有目标 = Truestate.状态_是否回放中 = Falseif self.key_w_down:self.key_w_down = Falsepyautogui.keyUp('w')tangbaowss.send_msg( "是否暂停#@@#真")time.sleep(0.2)mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])logger.info(f"发现确定按钮,可能是吃复活药{datas[idex]}")time.sleep(0.1)mouse_left_down()time.sleep(0.1)mouse_left_up()time.sleep(0.5)state.计数_卡主次数 = 0state.计次_识别次数 = 0ret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)isIn, idex = find_dict_index(datas, 'label', '确定')tangbaowss.send_msg( "是否暂停#@@#假")if not isIn:logger.info("停止脚本")tangbaowss.send_msg( "是否回放#@@#假")isIn, idex = find_dict_index(datas, 'label', '取消')if isIn:if state.状态_寻路中:state.状态_寻路中 = Falsestate.状态_是否回放中 = Falseif self.key_w_down:self.key_w_down = Falsepyautogui.keyUp('w')# 判断是否死亡ret_sw = self.find_zhuyidiren(hwnd, left, top, right, bottom, p_left, p_top)state.状态_有目标 = Truetangbaowss.send_msg( "是否暂停#@@#真")time.sleep(0.2)mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])logger.info(f"发现取消按钮,可能是吃复活药{datas[idex]}")time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.3)pyautogui.keyDown('shift')time.sleep(0.2)pyautogui.keyUp('shift')state.计数_卡主次数 = 0state.计次_识别次数 = 0tangbaowss.send_msg( "是否暂停#@@#假")if ret_sw:state.计次_移动停滞 = 1200state.计次_定位失败 = 1200state.状态_是否回放中 = Falsestate.计数_没找到任何目标 = 0logger.info("全军覆没了!跳过该任务!")pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')state.状态_检测中 = Falsereturnret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)isIn, idex = find_dict_index(datas, 'label', '返回')if isIn:logger.info("在派蒙界面,点返回")pyautogui.keyUp('w')self.key_w_down = Falsepyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")time.sleep(2)pyautogui.keyDown('shift')time.sleep(0.2)pyautogui.keyUp('shift')continueisIn, idex = find_dict_index(datas, 'label', '玩家')if isIn:玩家高度 = datas[idex]["points"][1][1] - datas[idex]["points"][0][1]计数_没有玩家 = 0isIn, idex = find_dict_index(datas, 'label', '关闭')if isIn:if state.状态_寻路中:state.状态_寻路中 = Falsestate.状态_有目标 = Truelogger.info(f"发现关闭按钮 点错了界面{datas[idex]}")pyautogui.keyDown('esc')time.sleep(0.2)pyautogui.keyUp('esc')time.sleep(1)mouse_moveR(6 * 180, 0)time.sleep(2)pyautogui.keyDown("w")time.sleep(0.1)pyautogui.keyUp("w")self.key_w_down = Falseret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)for data in datas:if isIn_jhdh == True:state.计数_卡主次数 = 0state.计次_识别次数 = 0break# 发现怪物则进行视角移动 以及 接近目标if data["label"] in self.interaction_label:if state.状态_寻路中 == False:# 将它的中心点和游戏正中间进行靠近if data["label"] == "怪物" and data["points"][1][0] - \data["points"][0][0] >= 70:if not daguai:continuestate.计数_没找到任何目标 = 0state.计数_没找到怪物 = 0# 如果怪物就再旁边则直接连招if self.centre_point[1] - data["points"][1][1] <= 20:if self.key_w_down:self.key_w_down = Falsepyautogui.keyUp('w')state.计数_卡主次数 = 0state.计次_识别次数 = 0if not state.状态_是否回放中:mouse_moveR(0, 800)logger.info("在连招中....")pyautogui.keyDown('x')time.sleep(0.2)pyautogui.keyUp('x')state.状态_是否回放中 = Truetangbaowss.send_msg( "脚本执行#@@#2")else:angle=get_angle(self.centre_point, (data["x"], data["y"]))angle_diff = abs(0 - angle)if state.状态_在爬墙==True:#如果角度大于45度则建议按X绕过柱子if angle_diff>45:pyautogui.keyDown('x')time.sleep(0.2)pyautogui.keyUp('x')set_angle(0, angle)if random.randint(1,2)==2:pyautogui.keyDown('a')time.sleep(0.2)pyautogui.keyUp('a')else:pyautogui.keyDown('d')time.sleep(0.2)pyautogui.keyUp('d')else:pyautogui.keyDown('space')time.sleep(0.2)pyautogui.keyUp('space')else:set_angle(0, angle)pyautogui.keyDown('w')if random.randint(0, 50) == 5:pyautogui.keyDown('space')time.sleep(0.2)pyautogui.keyUp('space')self.key_w_down = Truebreak # 只判断第一只怪elif data["label"] == "矿石":if not wakuang:continuestate.计数_没找到任何目标 = 0state.计数_没找到怪物 = 0set_angle(0, get_angle(self.centre_point, (data["x"], data["y"])))# 如果矿石就再旁边则直接连招if self.centre_point[1] - data["points"][1][1] <= 40:if self.key_w_down:self.key_w_down = Falsetime.sleep(0.2)pyautogui.keyUp('w')logger.info("在挖矿中....")state.计数_卡主次数 = 0state.计次_识别次数 = 0if not state.状态_是否回放中:logger.info("在挖矿中....")pyautogui.press("x")state.状态_是否回放中 = Truetangbaowss.send_msg( "脚本执行#@@#2")else:pyautogui.keyDown('w')if random.randint(0, 50) == 5:pyautogui.keyDown('space')time.sleep(0.2)pyautogui.keyUp('space')self.key_w_down = Truebreak # 只判断第1个矿else: # '发现采集物'或 "掉落物"state.计数_没找到任何目标 = 0angle_now = 0angle_target = get_angle(self.centre_point, (data["x"], data["y"]))r_caiji=set_angle(angle_now, angle_target)pyautogui.keyDown('w')if random.randint(0, 30) == 5:pyautogui.keyDown('space')pyautogui.keyUp('space')self.key_w_down = Trueif r_caiji==False:pyautogui.keyDown('shift')time.sleep(0.2)pyautogui.keyUp('shift')break # 只判断第一个采集物else:if data["sim"]>=0.6:if state.状态_在爬墙==True:continueif data["label"] == "矿石":if not wakuang:continuelogger.info("发现有矿石.顺手挖一下吧!")已经_F了 = Falseelif data["label"] == "怪物" and data["points"][1][0] - data["points"][0][0] >= 90:if not daguai:continue已经_F了 = Falselogger.info("发现有怪拦路.顺手消灭吧!")elif data["label"] == "采集物":if not caiji:continue已经_F了 = Falselogger.info("发现有采集物.顺手捡一下吧!")elif data["label"] =="掉落物":if daguai or wakuang or caiji:已经_F了 = Falselogger.info("发现有掉落物.顺手捡一下吧!")else:continueelse:continueset_angle(0, get_angle(self.centre_point, (data["x"], data["y"])))state.计时_未寻路 = int(time.time())state.状态_寻路中 = Falsestate.游戏_打怪前坐标 = copy.copy(state.游戏_当前目标坐标)state.计数_没找到任何目标 = 0state.计数_没找到怪物 = 0pyautogui.keyUp('w')self.key_w_down = Falsestate.计数_卡主次数 = 0state.计次_识别次数 = 0mouse_left_down()time.sleep(0.1)mouse_left_up()break# print("state.计时_未寻路", state.计时_未寻路, "state.计数_没找到任何目标", state.计数_没找到任何目标,# "state.计数_没找到怪物", state.计数_没找到怪物)# 如果玩家突然不见了,说明正在快速下落的概率很大,则开伞if state.状态_寻路中 == True and state.状态_在爬墙 == False and isIn_wxl == False:if 计数_没有玩家 >= 3 or (历史_上次玩家高度 / 玩家高度 >= 1.5):计数_没有玩家 = 0ttt = threading.Thread(target=开伞)if state.python_var > 9:ttt.daemon = Trueelse:ttt.setDaemon(True)ttt.start()if 计数_没有玩家 == 0:历史_上次玩家高度 = copy.copy(玩家高度)if state.状态_寻路中 == False:# 如果按下了W键了,并且当前没有任何目标等于4次了,则松开w键if state.计数_没找到任何目标 == 6:self.key_w_down = Falsestate.计次_识别次数 = 0state.计数_卡主次数 = 0pyautogui.keyUp('w')if state.计次_定位失败 > 0:state.计数_没找到怪物 = 0if state.ON_LIANZHAOBUJIANCE==0 and wakuang==False and state.计数_没找到怪物 >= 25 and state.计次_定位失败 == 0 and state.状态_是否回放中 == True:state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")# 如果连续8次没找到目标,则进行旋转if 20 > state.计数_没找到任何目标 >= 15 or (state.计数_没找到任何目标 >= 1 and state.计数_没找到怪物 >= 10 and state.状态_是否回放中 == True):state.计数_卡主次数 = 0state.计次_识别次数 = 0历史_上次玩家高度 = 0if state.计数_没找到任何目标 == 15:if state.计次_定位失败 == 0:if state.状态_是否回放中 == False:logger.info("重置视角")pyautogui.keyUp('w')self.key_w_down = Falsepyautogui.keyDown('x')time.sleep(0.1)pyautogui.keyUp('x')# 重置镜头mouse_middle_down()time.sleep(0.2)mouse_middle_up()time.sleep(0.3)mouse_moveR(0, 600)state.计次_识别次数 = 0state.计数_卡主次数 = 0else:state.计数_没找到任何目标 = 0else:state.计数_没找到任何目标 = 0else:mouse_moveR(700, 0)time.sleep(0.1)elif state.计数_没找到任何目标 == 13 and state.状态_是否回放中 == False:mouse_moveR(0, 800)time.sleep(0.1)elif state.计数_没找到任何目标 == 10 and state.状态_是否回放中 == False:if not 已经_F了:pyautogui.keyDown('shift')time.sleep(0.2)pyautogui.keyUp('shift')# 如果连续18次没找到目标,则继续寻路Xelif state.计数_没找到任何目标 >= 20:if not state.状态_是否回放中:logger.info("附近没有怪和采集物了.继续寻路吧!")state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")state.状态_寻路中 = Truestate.计次_识别次数 = 0state.计数_卡主次数 = 0state.计数_没找到任何目标 = 0mouse_left_up()pyautogui.keyUp('w')continuetime_now=time.time()if time_now-state.计时_未寻路 >= state.TIMEOUT_DAGUAI:kkk = self.处理异常情况(hwnd, left, top, right, bottom, p_left, p_top)if kkk == 0:logger.info("在意外界面 超时!")time.sleep(3)state.状态_检测中 = Falsestate.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Falsestate.计次_移动停滞 = 1200state.计次_定位失败 = 1200state.状态_是否回放中 = Falsestate.计数_没找到任何目标 = 0return Falseelif kkk == -1:logger.info("全军覆没了!")time.sleep(3)state.计次_移动停滞 = 1200state.计次_定位失败 = 1200state.状态_是否回放中 = Falsestate.计数_没找到任何目标 = 0state.状态_检测中 = Falsestate.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Truereturn Falseif state.计时_未寻路 != 0:logger.info("附近可能还有怪,但是打不到.继续寻路吧!")state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")state.状态_寻路中 = Truestate.计数_卡主次数 = 0mouse_left_up()pyautogui.keyUp('w')state.计数_没找到任何目标 = 0state.游戏_打怪前坐标 = [0, 0]time.sleep(5)else:state.状态_是否回放中 = Falsetangbaowss.send_msg("是否回放#@@#假")state.状态_寻路中 = Truestate.计数_卡主次数 = 0mouse_left_up()pyautogui.keyUp('w')state.计数_没找到任何目标 = 0state.游戏_打怪前坐标 = [0, 0]continueelif time_now-state.计时_未寻路 == state.TIMEOUT_DAGUAI-15 and state.状态_是否回放中==False and state.计数_没找到怪物>=1:mouse_left_up()logger.info("可能是有矿石 连招中....")if random.randint(1,3)==2:pyautogui.press("x")state.状态_是否回放中 = Truetangbaowss.send_msg( "脚本执行#@@#2")elif state.TIMEOUT_DAGUAI//2<= time_now-state.计时_未寻路 >= state.TIMEOUT_DAGUAI//2-10 and state.状态_是否回放中==False and state.计数_没找到怪物==0:mouse_left_up()logger.info("可能是飞行物 连招中....")if random.randint(1,3)==2:pyautogui.press("x")state.状态_是否回放中 = Truetangbaowss.send_msg( "脚本执行#@@#2")except:time.sleep(0.5)logger.error(traceback.format_exc())def run_fuben(self, cishu=99, isFanxiang=False):global loopstate.状态_已经有寻路了 = True# 获取窗口句柄hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)while True:if state.状态_循环开关 == False:logger.info("强制退出!")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")state.状态_需重新传送 = Falsereturn Falseif get_window_handle_at_mouse_position() == hwnd:breakrect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = 0, 0, 1920, 1080 # 替换成你实际的区域坐标p_left = rect[0] + w_pp_top = rect[1] + h_ppaimeng_img = cv2.imdecode(np.fromfile(file="./datas/派蒙.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)kkk = self.处理异常情况(hwnd, left, top, right, bottom, p_left, p_top)if kkk == 0:logger.info("在意外界面 超时!")time.sleep(3)state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsereturn Falseelif kkk == -1:logger.info("全军覆没了!")time.sleep(3)state.状态_需重新传送 = Truestate.状态_已经有寻路了 = Falsereturn False计数_没有玩家 = 0jiaoben = (os.path.abspath(os.path.join(state.PATH_JIAOBEN, state.LIANZHAO)))# 临时发送消息给指定客户端message = f"解析脚本#@@#{jiaoben}"tangbaowss.send_msg(message)try:# 往前走或者往后走,直到找到 Fif not self.find_f_keep_going(hwnd, left, top, right, bottom, p_left, p_top,isFanxiang): state.状态_需重新传送 = True;return False# 点击 单人挑战if not self.click_danrentiaozhan(hwnd, left, top, right, bottom, p_left,p_top): state.状态_需重新传送 = True;return False# 判断弹窗是否有树脂不够提醒if self.find_shuzhibugou(hwnd, left, top, right, bottom, p_left, p_top):pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')time.sleep(2)pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')state.状态_已经有寻路了 = Falselogger.info("没体力了 成功退出副本!")time.sleep(10)state.状态_需重新传送 = Falsereturn True# 如果没提醒则选择阵容if not self.click_kaishitiaozhan(hwnd, left, top, right, bottom, p_left, p_top):pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')state.状态_需重新传送 = Truestate.状态_已经有寻路了 = Falsereturn False# 开始yolov自动进入副本except:time.sleep(0.5)logger.error(traceback.format_exc())state.状态_需重新传送 = Truestate.状态_已经有寻路了 = Falsereturn False# 0 镜头对准北方向 走过去开副本# 1 开始打怪阶段# 2 打完了 开始找生命树 并且把自己移动到0° 对准生命树的位置# 3 走路到 领取奖励 直到点击 继续挑战为止 完成一轮阶段 = 0计次_阶段0 = 0计时_阶段3_开始时间 = 0state.计数_没找到怪物=0isinF = Falsefor i in range(cishu):logger.info(f"刷副本中 {i + 1}/{cishu}")while True:try:time.sleep(0.01)if state.状态_循环开关 == False:state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsereturn Falserect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = 0, 0, 1920, 1080 # 替换成你实际的区域坐标p_left = rect[0] + w_pp_top = rect[1] + h_pif state.状态_全局暂停:tangbaowss.send_msg( "是否回放#@@#假")time.sleep(2)continueif get_window_handle_at_mouse_position() != hwnd:# 激活hwndhwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)# 设定保存截图的文件夹路径和文件名前缀# filename = f"./datas/lanrenyolov{random.randint(1, 20)}.jpg"# 游戏全图ret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)if type(ret_scr) == bool:hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄logger.info("截图失败了!可能是内存溢出了")time.sleep(2)continue# 这里是小地图的截图left_small, top_small, right_small, bottom_small = self.小地图区域 # 替换成你实际的区域坐标height_small = bottom_small - top_smallwidth_small = right_small - left_smallsmall_img = screenshot(hwnd, left_small, top_small, right_small, bottom_small,filename=None) # RGBAif type(small_img) == bool:hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄time.sleep(2)continuestate.游戏_当前视野角度 = get_view2(small_img[int(height_small / 2) - 70:int(height_small / 2) + 70,int(width_small / 2) - 70:int(width_small / 2) + 70], 70)# -----------------------------YOLOV检测--------------------------------------------------datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)if state.开关_是否展预测结果:state.QT_信号.mysig_show_yolov.emit()if state.状态_全局暂停:tangbaowss.send_msg( "是否回放#@@#假")time.sleep(2)continueif datas == []:passelse:# 将离屏幕中心最近的点排在前面datas = sorted(datas, key=self.distance_to_target)if 阶段 == 0: # 0 镜头对准北方向 走过去开副本计次_阶段0 += 1if 计次_阶段0 >= 2000:pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')time.sleep(1)state.状态_已经有寻路了 = Falselogger.info("没体力了 成功退出副本!")time.sleep(10)state.状态_需重新传送 = Falsereturn TrueisIn, idex = find_dict_index(datas, 'label', '提示关闭')if isIn: # 路过的顺手捡了logger.info("发现 提示关闭")mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(1)continueif self.click_dimaiyichang(hwnd, left, top, right, bottom, p_left, p_top):time.sleep(1)continueif not set_angle(state.游戏_当前视野角度, 90, fault=5):logger.info("调整整好")# 角度对了 开始一路往前走 直到找到F按钮pyautogui.keyDown("w")if not self.find_f_keep_going(hwnd, left, top, right, bottom, p_left, p_top,False): continuepyautogui.keyUp("w")阶段 = 1isIn, idex = find_dict_index(datas, 'label', '确定')isIn2, idex = find_dict_index(datas, 'label', '取消')# 判断弹窗是否有树脂不够提醒 如果发现 则点击退出返回 阶段4走起if isIn or isIn2:pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')time.sleep(1)state.状态_已经有寻路了 = Falselogger.info("没体力了 成功退出副本!")time.sleep(10)state.状态_需重新传送 = Truereturn Trueelif 阶段 == 1: # 1 开始打怪阶段isinF = False计次_阶段0 = 0# 判断是否打完了副本isIn, idex = find_dict_index(datas, 'label', '副本打完了')#or self.find_zidongtuichu(hwnd, 745,905,1206,1002, p_left, p_top)if isIn :if idex!=None:if datas[idex]["sim"]>0.75:self.key_w_down = Falsepyautogui.keyUp('w')mouse_left_up()state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")logger.info("打完了")time.sleep(2)# 重置镜头mouse_middle_down()time.sleep(0.1)mouse_middle_up()阶段 = 2continueelse:self.key_w_down = Falsepyautogui.keyUp('w')mouse_left_up()state.状态_是否回放中 = Falsetangbaowss.send_msg( "是否回放#@@#假")logger.info("打完了")time.sleep(2)# 重置镜头mouse_middle_down()time.sleep(0.1)mouse_middle_up()阶段 = 2continueisIn, idex = find_dict_index(datas, 'label', '往上跳')if isIn:logger.info("尝试跳起来")pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')isIn, idex = find_dict_index(datas, 'label', '被控了')if isIn:logger.info("被控了!")pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.1)pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.1)pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.1)pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')isIn, idex = find_dict_index(datas, 'label', '确定')if isIn:tangbaowss.send_msg( "是否暂停#@@#真")time.sleep(0.2)mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])logger.info(f"发现确定按钮,可能是吃复活药{datas[idex]}")time.sleep(0.1)mouse_left_down()time.sleep(0.1)mouse_left_up()time.sleep(0.5)ret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)isIn, idex = find_dict_index(datas, 'label', '确定')tangbaowss.send_msg( "是否暂停#@@#假")if not isIn:logger.info("停止脚本")tangbaowss.send_msg( "是否回放#@@#假")isIn, idex = find_dict_index(datas, 'label', '取消')if isIn:tangbaowss.send_msg( "是否暂停#@@#真")time.sleep(0.2)mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])logger.info(f"发现取消按钮,可能是吃复活药{datas[idex]}")time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.3)pyautogui.keyDown('shift')time.sleep(0.2)pyautogui.keyUp('shift')tangbaowss.send_msg( "是否暂停#@@#假")ret_scr = screenshot(hwnd, left, top, right, bottom, filename=None)datas, state.图片_YOLOV = self.yolov.detect(ret_scr, plot_box=state.开关_是否展预测结果)isIn, idex = find_dict_index(datas, 'label', '怪物')if not isIn:state.计数_没找到怪物+=1if random.randint(1, 50) == 2:mouse_middle_down()time.sleep(0.2)mouse_middle_up()if state.计数_没找到怪物>=20:tangbaowss.send_msg( "是否回放#@@#假")state.状态_是否回放中 = Falsepyautogui.keyUp('w')self.key_w_down=Falsemouse_moveR(int(6 * 120), 0)time.sleep(0.1)if template_matching(ret_scr, paimeng_img, mask=None) != []:logger.info("全军覆没了!跳重试该任务!")state.状态_需重新传送 = Truestate.状态_已经有寻路了 = Falsereturn Falseelse:state.计数_没找到怪物=0for data in datas:# 将它的中心点和游戏正中间进行靠近if data["label"] == "怪物":set_angle(0, get_angle(self.centre_point, (data["x"], data["y"])))# 如果怪物就再旁边则直接连招if self.centre_point[1] - data["points"][1][1] <= 80 and data["points"][1][0] - \data["points"][0][0] >= 90:if self.key_w_down:self.key_w_down = Falsepyautogui.keyUp('w')if not state.状态_是否回放中:logger.info("在连招中....")pyautogui.press("x")state.状态_是否回放中 = Truetangbaowss.send_msg( "脚本执行#@@#2")else:# 不在则继续跟着怪物方向走pyautogui.keyDown('w')self.key_w_down = Truebreakelif 阶段 == 2: # 2 打完了 开始找生命树 并且把自己移动到0° 对准生命树的位置调整视角 = set_angle(state.游戏_当前视野角度, 90, fault=6)发现了目标了 = FalseisIn, idex = find_dict_index(datas, 'label', '生命树')for data in datas:if data["label"] == "生命树":发现了目标了 = Truex_p = data["x"] - self.centre_point[0]if x_p >= 16:pyautogui.keyUp('a')logger.info("树偏右边 按下D键")pyautogui.keyDown('d')elif x_p <= -16:pyautogui.keyUp('d')logger.info("树偏左边 按下A键")pyautogui.keyDown('a')else:if 调整视角 == False:pyautogui.keyUp('d')pyautogui.keyUp('a')logger.info("树正对,走起!")pyautogui.keyDown('w')阶段 = 3计时_阶段3_开始时间 = time.time()breaklogger.info("树正对 但是角度还不对")elif data["label"] == "副本楼梯":发现了目标了 = Truex_p = data["x"] - self.centre_point[0]if x_p >= 16:pyautogui.keyUp('a')logger.info("副本楼梯偏右边 按下D键")pyautogui.keyDown('d')elif x_p <= -16:pyautogui.keyUp('d')logger.info("副本楼梯偏左边 按下A键")pyautogui.keyDown('a')else:if 调整视角 == False:pyautogui.keyUp('d')pyautogui.keyUp('a')logger.info("副本楼梯正对,走起!")pyautogui.keyDown('w')阶段 = 3计时_阶段3_开始时间=time.time()breaklogger.info("副本楼梯正对 但是角度还不对")elif data["label"] == "副本门框" and isIn == False:发现了目标了 = Truex_p = data["x"] - self.centre_point[0]if x_p >= 16:pyautogui.keyUp('a')logger.info("副本门框偏右边 按下D键")pyautogui.keyDown('d')elif x_p <= -16:pyautogui.keyUp('d')logger.info("副本门框偏左边 按下A键")pyautogui.keyDown('a')if 阶段 == 3:continueif 发现了目标了 == False:rrrr = random.randint(1, 20)if rrrr == 1:pyautogui.keyDown('s')pyautogui.keyDown('a')pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.2)pyautogui.keyUp('a')pyautogui.keyUp('s')elif rrrr == 2:pyautogui.keyDown('s')pyautogui.keyDown('a')pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.2)pyautogui.keyUp('a')pyautogui.keyUp('s')elif rrrr == 3:pyautogui.keyDown('w')pyautogui.keyDown('a')pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.2)pyautogui.keyUp('a')pyautogui.keyUp('w')elif rrrr == 4:pyautogui.keyDown('w')pyautogui.keyDown('d')pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')time.sleep(0.2)pyautogui.keyUp('d')pyautogui.keyUp('w')else:pyautogui.keyDown('s')time.sleep(0.1)pyautogui.keyUp('s')elif 阶段 == 3: # 3 走路到 领取奖励 直到点击 继续挑战为止 完成一轮pyautogui.keyDown('w')isIn, idex = find_dict_index(datas, 'label', 'F交互')if isIn: # 路过的顺手捡了logger.info("发现 F 交互")pyautogui.keyDown('f')time.sleep(0.1)pyautogui.keyUp('f')isinF = TruecontinueisIn, idex = find_dict_index(datas, 'label', '捡东西')if isIn: # 路过的顺手捡了logger.info("发现 F 交互")pyautogui.keyDown('f')time.sleep(0.1)pyautogui.keyUp('f')isinF = TruecontinueisIn, idex = find_dict_index(datas, 'label', '生命树')if isIn and isinF==False : # 路过的顺手捡了set_angle(0, get_angle(self.centre_point, (datas[idex]["x"], datas[idex]["y"])))isIn, idex = find_dict_index(datas, 'label', '使用浓缩树脂')if isIn: # 路过的顺手捡了if datas[idex]['sim'] > 0.8:logger.info("发现 使用浓缩树脂")logger.info(str(datas[idex]))mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.5)continueisIn, idex = find_dict_index(datas, 'label', '使用原粹树脂')if isIn: # 路过的顺手捡了if datas[idex]['sim'] > 0.8:logger.info("发现 使用原粹树脂")logger.info(str(datas[idex]))mouse_move(p_left + datas[idex]["x"], p_top + datas[idex]["y"])time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.5)continueisIn, idex = find_dict_index(datas, 'label', '继续挑战')if isIn or self.find_jixutiaozhan(hwnd, 953,923,1432,1065, p_left, p_top): # 路过的顺手捡了if idex!=None:if datas[idex]['sim'] > 0.75:logger.info("发现 继续挑战")mouse_move(p_left + 1178, p_top + 1006)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.5)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(2)# 判断弹窗是否有树脂不够提醒 如果发现 则点击退出返回 阶段4走起if self.find_shuzhibugou2(hwnd, left, top, right, bottom, p_left, p_top):pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')time.sleep(1)state.状态_已经有寻路了 = Falselogger.info("没体力了 成功退出副本!")time.sleep(10)state.状态_需重新传送 = Falsereturn Trueelse:isinF = False阶段 = 0breakelse:logger.info("发现 继续挑战")mouse_move(p_left + 1178, p_top + 1006)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.5)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(2)# 判断弹窗是否有树脂不够提醒 如果发现 则点击退出返回 阶段4走起if self.find_shuzhibugou2(hwnd, left, top, right, bottom, p_left, p_top):pyautogui.keyDown('esc')time.sleep(0.1)pyautogui.keyUp('esc')time.sleep(1)state.状态_已经有寻路了 = Falselogger.info("没体力了 成功退出副本!")time.sleep(10)state.状态_需重新传送 = Falsereturn Trueelse:isinF = False阶段 = 0breakif time.time() - 计时_阶段3_开始时间 > 30:rand_cz = random.randint(0, 4)if rand_cz == 0:pyautogui.keyDown('x')time.sleep(0.3)pyautogui.keyUp('x')pyautogui.keyUp('w')pyautogui.keyDown('d')time.sleep(0.5)pyautogui.keyUp('d')pyautogui.keyDown('w')elif rand_cz == 1:pyautogui.keyDown('x')time.sleep(0.3)pyautogui.keyUp('x')pyautogui.keyUp('w')pyautogui.keyDown('a')time.sleep(0.5)pyautogui.keyUp('a')pyautogui.keyDown('w')elif rand_cz == 2:pyautogui.keyDown('x')time.sleep(0.3)pyautogui.keyUp('x')elif rand_cz == 3:pyautogui.keyDown('x')time.sleep(0.3)pyautogui.keyUp('x')pyautogui.keyUp('w')pyautogui.keyDown('s')time.sleep(3)pyautogui.keyUp('s')pyautogui.keyDown('w')else:pyautogui.keyDown('space')time.sleep(0.3)pyautogui.keyDown('space')计时_阶段3_开始时间 = time.time()except:logger.error(traceback.format_exc())time.sleep(0.5)state.状态_已经有寻路了 = Falselogger.info("刷副本全部完成!")def run_chuansongjiaoben(self, jiaoben,cbb_address="手动录制"):global new_msgtry:hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄if state.状态_循环开关 == False:tangbaowss.send_msg( "是否回放#@@#假")logger.info("强制退出!")return Falseif get_window_handle_at_mouse_position() == hwnd:set_window_activate(hwnd)time.sleep(0.2)rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080p_left = rect[0] + w_pp_top = rect[1] + h_pset_window_activate(hwnd)if cbb_address=="手动录制":kkk = self.处理异常情况(hwnd, 0, 0, 1920, 1080, p_left, p_top)if kkk == 0:logger.info("在意外界面 超时!")time.sleep(3)state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsereturn Falseelif kkk == -1:logger.info("全军覆没了!")time.sleep(3)state.状态_需重新传送 = Truestate.状态_已经有寻路了 = Falsereturn False# 临时发送消息给指定客户端message = f"解析脚本#@@#{jiaoben}"tangbaowss.send_msg(message)time.sleep(2)state.状态_是否回放中 = Truetangbaowss.send_msg( "脚本执行#@@#1")while True:try:time.sleep(0.1)if state.状态_是否回放中 == False:state.状态_需重新传送 = Falsereturn Trueif state.状态_循环开关 == False:state.状态_需重新传送 = Falsetangbaowss.send_msg( "是否回放#@@#假")return Falseif get_window_handle_at_mouse_position() != hwnd:# 激活hwndhwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)except:time.sleep(0.5)except:logger.error(traceback.format_exc())state.状态_需重新传送 = Falsetangbaowss.send_msg( "是否回放#@@#假")return Falsedef run_jiaoben(self,jiaoben,is_auto_f):hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# F交互框位置left_dh, top_dh, right_dh, bottom_dh = [1161, 508, 1319, 572] # 替换成你实际的区域坐标p_left = rect[0] + w_pp_top = rect[1] + h_pset_window_activate(hwnd)if state.状态_循环开关 == False:logger.info("强制退出!")state.状态_已经有寻路了 = Falsereturn Falsemessage = f"解析脚本#@@#{jiaoben}"tangbaowss.send_msg(message)time.sleep(2)tangbaowss.send_msg( "脚本执行#@@#1")state.状态_是否回放中 = Truetry:while True:try:time.sleep(0.1)if state.状态_是否回放中 == False:state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falselogger.info("脚本运行完毕!")return Trueif state.状态_循环开关 == False:state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsetangbaowss.send_msg( "是否回放#@@#假")logger.info("强制退出!")return Falseif get_window_handle_at_mouse_position() != hwnd:# 激活hwndhwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)if is_auto_f:if self.find_f(hwnd, 1050, 424, 1187, 648, p_left, p_top):time.sleep(0.2)if not self.find_duihuahuopengren(hwnd, left_dh, top_dh, right_dh, bottom_dh, p_left,p_top) or self.find_pianpianhua(hwnd, left_dh, top_dh,right_dh, bottom_dh, p_left,p_top):logger.info(f"顺手捡东西!")state.状态_有目标 = Truepyautogui.keyDown('f')time.sleep(0.1)pyautogui.keyUp('f')time.sleep(0.1)pyautogui.keyDown('f')time.sleep(0.1)pyautogui.keyUp('f')except:time.sleep(0.5)except:logger.error(traceback.format_exc())state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsetangbaowss.send_msg( "是否回放#@@#假")return Falsedef run_jixing(self):'''领取纪行奖励:return:'''global new_msghwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = 0, 0, 1920, 1080 # 替换成你实际的区域坐标p_left = rect[0] + w_pp_top = rect[1] + h_phwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)paimeng_img = cv2.imdecode(np.fromfile(file="./datas/派蒙.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)big_img = screenshot(hwnd, left, top, right, bottom, None)if template_matching(big_img, paimeng_img, mask=None) == []:pyautogui.keyDown("esc")time.sleep(0.1)pyautogui.keyUp("esc")time.sleep(2)pyautogui.keyDown('alt')if not self.click_jixing(hwnd, left, top, right, bottom, p_left, p_top): logger.info("没有纪行要领取");pyautogui.keyUp('alt');returntime.sleep(1)pyautogui.keyUp('alt')time.sleep(2)mouse_move(p_left + 962, p_top + 52)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()if not self.click_jixing_yijianlingqu(hwnd, left, top, right, bottom, p_left, p_top): logger.info("领取纪行任务经验失败");returntime.sleep(1)mouse_move(p_left + 863, p_top + 55)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(1)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(1)if not self.click_jixing_yijianlingqu(hwnd, left, top, right, bottom, p_left, p_top): logger.info("没有奖励能领取");returntime.sleep(1)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(1)pyautogui.keyDown("esc")time.sleep(0.1)pyautogui.keyUp("esc")def run_huanjue(self,path_list=[],zhanchagn="1"):global new_msgstate.状态_检测中 = Falsehwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = 0, 0, 1920, 1080 # 替换成你实际的区域坐标p_left = rect[0] + w_pp_top = rect[1] + h_phwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)kkk = self.处理异常情况(hwnd, left, top, right, bottom, p_left, p_top)if kkk == 0:logger.info("在意外界面 超时!")time.sleep(3)state.状态_检测中 = Falsestate.状态_已经有寻路了=Falsestate.状态_需重新传送 = Falsereturn Falseelif kkk == -1:logger.info("全军覆没了!")time.sleep(3)state.状态_检测中 = Falsestate.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Truereturn False#按了后看有没有打开成功n=0while True:if n==0:n=0kkk = self.处理异常情况(hwnd, left, top, right, bottom, p_left, p_top)if kkk == 0:logger.info("在意外界面 超时!")time.sleep(3)state.状态_检测中 = Falsestate.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Falsereturn Falseelif kkk == -1:logger.info("全军覆没了!")time.sleep(3)state.状态_检测中 = Falsestate.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Truereturn Falsepyautogui.keyDown("x")time.sleep(0.2)pyautogui.keyUp("x")pyautogui.keyDown("l")time.sleep(0.2)pyautogui.keyUp("l")time.sleep(2)elif n>=20:n = 0time.sleep(1)if state.状态_循环开关 == False:state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsetangbaowss.send_msg( "是否回放#@@#假")logger.info("强制退出!")return Falseif get_window_handle_at_mouse_position() != hwnd:# 激活hwndhwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)if state.状态_全局暂停:tangbaowss.send_msg( "是否回放#@@#假")time.sleep(2)continueif self.click_kuaisubiandui(hwnd, left, top, right, bottom , p_left, p_top):time.sleep(2)breakn += 1#加载角色图片数据img_datas=[]point_list=[[96,189],[255,198],[404,200],[568,190],]for item in path_list:img_datas.append( cv2.imdecode(np.fromfile(file=item, dtype=np.uint8),cv2.IMREAD_UNCHANGED))#将历史角色清空掉for point in point_list:#点击对应坑位mouse_move(p_left +point[0],p_top +point[1])time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.2)if not self.find_baocunpeizhi(hwnd, left, top, right, bottom, p_left, p_top):breaklogger.info("清空角色成功!")# 循环选择角色a = 0for i,img in enumerate(img_datas):ret =Falsewhile not ret:logger.info(str(a))if state.状态_循环开关 == False:state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsetangbaowss.send_msg( "是否回放#@@#假")logger.info("强制退出!")return Falseif get_window_handle_at_mouse_position() != hwnd:# 激活hwndhwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)if state.状态_全局暂停:tangbaowss.send_msg( "是否回放#@@#假")time.sleep(2)continueret= find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, f"勾选 角色{i+1}", 1,1,threshold=0.76)if ret:time.sleep(1)breakif 5<a<11:pyautogui.moveTo(p_left +342,p_top +415)pyautogui.dragTo(p_left +346,p_top +833, duration=0.5, tween=pyautogui.easeOutSine)else:pyautogui.moveTo(p_left +346, p_top +833)pyautogui.dragTo(p_left +342, p_top +415, duration=0.5, tween=pyautogui.easeOutSine)a+=1if a==11:a=0time.sleep(1)#保存配置mouse_move(p_left +453,p_top +1016)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(5)if state.状态_循环开关 == False:state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsetangbaowss.send_msg( "是否回放#@@#假")logger.info("强制退出!")return Falsepyautogui.keyDown("esc")time.sleep(0.1)pyautogui.keyUp("esc")time.sleep(2)pyautogui.keyDown(zhanchagn)time.sleep(0.1)pyautogui.keyUp(zhanchagn)time.sleep(2)state.状态_需重新传送 = Falsestate.状态_已经有寻路了 = Falsereturn Truedef 处理异常情况(self,hwnd,left,top,right,bottom,p_left,p_top):''':param hwnd::param left::param top::param right::param bottom::param p_left::param p_top::return:'''paimeng_img = cv2.imdecode(np.fromfile(file="./datas/派蒙.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)# 判断是否卡在了对话界面for nnn in range(120):if state.状态_循环开关 == False:logger.info("强制退出!")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_是否回放中 = Falsestate.状态_检测中=Falsetangbaowss.send_msg( "是否回放#@@#假")state.状态_需重新传送 = Falsereturn 1big_img = screenshot(hwnd, left, top, right, bottom, None)rrrr = template_matching(big_img, paimeng_img, mask=None)if rrrr == []:rdd = random.randint(1, 4)if rdd == 1:pyautogui.keyDown("x")time.sleep(0.2)pyautogui.keyUp("x")mouse_move(p_left + 1397, p_top + 804)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.2)elif rdd == 2:pyautogui.keyDown("esc")time.sleep(0.1)pyautogui.keyUp("esc")time.sleep(1)else:pyautogui.keyDown('space')time.sleep(0.1)pyautogui.keyUp('space')logger.info("未知界面 按esc 和 点击对话! " + str(nnn))# 判断是否死亡ret_sw = self.find_zhuyidiren(hwnd, left, top, right, bottom, p_left, p_top)if ret_sw:state.状态_检测中 = Falsereturn -1else:if rrrr != []:return 1time.sleep(1)return 0def find_f_keep_going(self, hwnd, left, top, right, bottom, p_left, p_top, isFanxiang):img = cv2.imdecode(np.fromfile(file="./datas/F.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)if isFanxiang:keepKey = 's'else:keepKey = 'w'if find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 F", 1000, 0.05, False,keepKey=keepKey):if isFanxiang:pyautogui.keyUp('s')else:pyautogui.keyUp('w')pyautogui.keyDown('f')time.sleep(0.2)pyautogui.keyUp('f')time.sleep(0.3)pyautogui.keyDown('f')time.sleep(0.2)pyautogui.keyUp('f')return Trueelse:return Falsedef click_danrentiaozhan(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/单人挑战.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 单人挑战")def click_dimaiyichang(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/地脉异常.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 地脉异常", 1, 0.01,threshold=0.76)def click_jixing(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/纪行.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 纪行", 10, 1,threshold=0.76, mask=mask, double_click=True)def click_jixing_renwu(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/纪行_任务.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 纪行_任务", 10, 1,threshold=0.6, mask=mask)def click_jixing_yijianlingqu(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/纪行_一键领取.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 纪行_一键领取", 10, 1,threshold=0.76)def click_kuaisubiandui(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/快速编队.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 快速编队", 1, 1,threshold=0.76)def find_baocunpeizhi(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/保存配置.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "查找 保存配置", 1, 1,threshold=0.76,is_click=False)def click_jixing_jixing(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/纪行_纪行.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 纪行_纪行", 10, 1,threshold=0.76, mask=mask)def click_kaishitiaozhan(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/开始挑战.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 开始挑战")def find_shuzhibugou(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/树脂不够.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 树脂不够", 3,threshold=0.65, is_click=False, mask=mask)def find_shuzhibugou2(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/收取完成秘境.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "发现 树脂不够", 3,threshold=0.65, is_click=False, mask=mask)def find_zidongtuichu(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/自动退出.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "发现 自动退出", 1,threshold=0.65, is_click=False, mask=mask,step=0.001)def find_jixutiaozhan(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/继续挑战.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask = None # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "发现 继续挑战", 1,threshold=0.65, is_click=False, mask=mask,step=0.001)def find_f(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/f.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)mask =None# img[:, :, 3] # 提取透明度通道作为掩码return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "寻找 F", 1,step=0.001,threshold=0.75, is_click=False, mask=mask)def find_zhuyidiren(self, hwnd, left, top, right, bottom, p_left, p_top):img = cv2.imdecode(np.fromfile(file="./datas/注意敌人.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)return find_img_and_click(img, hwnd, left, top, right, bottom, p_left, p_top, "点击 注意敌人", 2,step=0.01,threshold=0.9, is_click=True)def find_duihuahuopengren(self, hwnd, left, top, right, bottom, p_left, p_top):global imgs_duihuahuopengrenreturn find_imgs_and_click(imgs_duihuahuopengren, hwnd, left, top, right, bottom, p_left, p_top, "发现 对话或烹饪", 1,step=0.001, is_click=False)def find_pianpianhua(self, hwnd, left, top, right, bottom, p_left, p_top):global imgs_pianpianhuareturn find_imgs_and_click(imgs_pianpianhua, hwnd, left, top, right, bottom, p_left, p_top, "发现 骗骗花", 1,step=0.001, is_click=False)def distance_to_target(self, d):h = float(d["points"][1][0] - d["points"][0][0])w = float(d["points"][1][1] - d["points"][0][1])# # # 计算距离的平方,这里使用欧几里得距离的平方作为衡量标准dist = (d["x"] - self.centre_point[0]) ** 2 + (d["y"] - self.centre_point[1]) ** 2angle = get_angle(self.centre_point, [d["x"], d["y"]])if angle >= 300 or angle <= 60:dist *= 999if w < 60:dist *= 0.1return dist + w * hif __name__ == '__main__':while True:time.sleep(0.0001)pass
2、添加通讯模块
vi tangbaowss.py
import asyncio
import configparser
import ctypes
import random
import subprocess
import sys
import threading
import time
import websockets
import state
from lanrenauto.logger_module import logger
#躺宝wss通讯模块
# 存储所有已连接的客户端
connected_clients = []
new_msg = ""
async def handle_client(websocket, path):global new_msgtry:# 发送连接成功消息await websocket.send("连接成功")# 添加新连接的客户端到集合中connected_clients.append(websocket)while True:# 接收客户端发送的消息message = await websocket.recv()new_msg = message# print(f"收到消息:{message}")# 处理接收到的消息if message == "是否回放中 真":state.状态_是否回放中 = Trueelif message == "是否回放中 假":state.状态_是否回放中 = Falseelif message == "是否暂停 真":state.状态_是否暂停 = Trueelif message == "是否暂停 假":state.状态_是否暂停 = Falseelif message == "是否开始录制 假":state.状态_是否开始录制 = Falseelif message == "是否开始录制 真":if state.状态_是否禁止录制 == False:logger.info("请按F8结束录制")state.QT_信号.mysig_tishi.emit(f"请按F8结束录制")state.状态_是否开始录制 = Trueelif message == "是否禁止录制 假":state.状态_是否禁止录制 = Falseelif message == "是否禁止录制 真":state.状态_是否禁止录制 = Trueelif message[:5] == "录制的脚本":state.录制_脚本文本 = message[6:]if state.录制_脚本文本 != "":logger.info("录制完毕!")# print(message)finally:# 客户端断开连接后,将其移出集合connected_clients.remove(websocket)
async def send_to_client(client_id, message):if len(connected_clients) > 0:# 查找指定的客户端await connected_clients[client_id].send(message)
async def server_main():try:if len(state.DUANGKOUHAO) < 3:state.DUANGKOUHAO = str(int(random.randint(1000, 19999)))server = await websockets.serve(handle_client, "localhost", int(state.DUANGKOUHAO), max_size=1024 * 1024 * 10)except:kernel32 = ctypes.windll.kernel32kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), 7)state.DUANGKOUHAO = input(f"端口 {state.DUANGKOUHAO} 被占用了! 请输入新的端口号(然后手动重开):")if len(state.DUANGKOUHAO) < 3:state.DUANGKOUHAO = str(int(random.randint(1000, 19999)))try:# 创建 ConfigParser 对象config = configparser.ConfigParser()# 添加节和键-值对config['seting'] = {'DUANGKOUHAO': state.DUANGKOUHAO}# 写入配置到 INI 文件with open("./datas/setting.ini", 'w') as configfile:config.write(configfile)except:passsys.exit()try:time.sleep(5)# 启动exe程序subprocess.Popen('"' + state.LIANZHAOFUWU + '" "' + state.DUANGKOUHAO + '"')logger.info("躺宝连招插件服务已经启动!")except:logger.error("文件没找到可以能被杀毒软件干掉了 " + state.LIANZHAOFUWU)input("回车结束!", state.LIANZHAOFUWU)sys.exit()# 启动事件循环,处理客户端连接await asyncio.gather(server.wait_closed())
def send_msg(msg="是否回放#@@#假"):'''给躺宝发送指令全部指令 :是否回放#@@#假 /真是否暂停#@@#假 /真是否开始录制#@@#假 /真是否禁止录制#@@#假 /真解析脚本#@@#jiaoben" jiaoben就是录制的脚本文本 不是文件 是直接文字全局hwnd#@@#12345 12345就是游戏的窗口句柄:param msg: 指令 用 #@@# 分割:return:'''asyncio.run_coroutine_threadsafe(send_to_client(-1, msg), loop)
#开线程启动监听
loop = asyncio.new_event_loop()
# 启动 WebSocket 服务器
t = threading.Thread(target=loop.run_until_complete, args=(server_main(),))
if state.python_var > 9:t.daemon = True
else:t.setDaemon(True)
t.start()
3、大图定位小图
lanrenauto/findpic/findimg.py
# -*- coding: utf-8 -*-
#opencv 必须 4.8.1.78import copy
import cv2
import os
import numpy as np
from ..moni.moni import *
from ..logger_module import loggerfrom demo.lanrenauto.tools.screenshot import screenshot #这里的demo是你的项目目录名
from demo import statedef get_view(small_img, radius=70):"""返回当前视野角度 感谢网友飞宇提供的识别方法Returns:以正北方向为0度计算"""channels = cv2.split(small_img)# 提取Alpha通道alpha3 = channels[3] # 索引为3,因为Alpha通道是第四个通道# cv2.imshow('Alpha Channel', alpha3)# cv2.waitKey(0)# cv2.destroyAllWindows()# _, alpha = cv2.threshold(small_img, 10, 255, cv2.THRESH_BINARY) #这个是提取蓝色箭头图像# 假设 gray_channel 是你的灰度图像# 创建一个掩膜,将150-200之间的值设为255,其他设为0mask = np.zeros_like(alpha3)mask[alpha3 >= 151] = 255mask[alpha3 < 151] = 0mask[alpha3 > 229] = 0img = np.zeros((radius * 2, radius * 2), dtype=np.uint8)center = (radius, radius)max_overlap = 0 # 最佳的重叠面积max_angle = -1 # 最佳的角度increment = 20 # 递增的角度,这里设置为10度for angle in range(0, 360, increment):img_copy = img.copy()# 画一个扇形cv2.ellipse(img_copy, center, (radius, radius), 225 + angle, 0, 90, 255, -1)overlap = np.logical_and(img_copy, mask).sum()if overlap > max_overlap:max_overlap = overlapmax_angle = anglestart_angle = max_angle - 10if start_angle < 0:start_angle = 0end_angle = start_angle + 10else:end_angle = start_angle + 20for angle in range(start_angle, end_angle, 1):img_copy = img.copy()# 画一个扇形cv2.ellipse(img_copy, center, (radius, radius), 225 + angle, 0, 90, 255, -1)overlap = np.logical_and(img_copy, mask).sum()if overlap > max_overlap:max_overlap = overlapmax_angle = angle# print(f'最匹配角度:{max_angle},重叠面积:{max_overlap}')# print(f'用时:{time.time() - old_time}')# cv2.destroyAllWindows()return max_angle
def get_view2(small_img, radius=70):"""返回当前视野角度 感谢网友飞宇提供的识别方法Returns:以正北方向为0度计算"""# radius = 70 # 定义截屏的宽度和长度# # 获取窗口句柄# hwnd = win32gui.FindWindow(state.GAME_CLASS, "原神") # 替换成你实际的窗口句柄### small_img = screenshot(hwnd,(98+radius,55+radius), radius=radius)##channels = cv2.split(small_img)# 提取Alpha通道alpha3 = channels[3] # 索引为3,因为Alpha通道是第四个通道# cv2.imshow('Alpha Channel', alpha3)# cv2.waitKey(0)# cv2.destroyAllWindows()# _, alpha = cv2.threshold(small_img, 10, 255, cv2.THRESH_BINARY) #这个是提取蓝色箭头图像# 假设 gray_channel 是你的灰度图像# 创建一个掩膜,将150-200之间的值设为255,其他设为0mask = np.zeros_like(alpha3)mask[alpha3 >= 9] = 255mask[alpha3 < 9] = 0mask[alpha3 > 190] = 0img = np.zeros((radius * 2, radius * 2), dtype=np.uint8)center = (radius, radius)max_overlap = 0 # 最佳的重叠面积max_angle = -1 # 最佳的角度increment = 20 # 递增的角度,这里设置为10度for angle in range(0, 360, increment):img_copy = img.copy()# 画一个扇形cv2.ellipse(img_copy, center, (radius, radius), 225 + angle, 0, 90, 255, -1)overlap = np.logical_and(img_copy, mask).sum()if overlap > max_overlap:max_overlap = overlapmax_angle = anglestart_angle = max_angle - 10if start_angle < 0:start_angle = 0end_angle = start_angle + 10else:end_angle = start_angle + 20for angle in range(start_angle, end_angle, 1):img_copy = img.copy()# 画一个扇形cv2.ellipse(img_copy, center, (radius, radius), 225 + angle, 0, 90, 255, -1)overlap = np.logical_and(img_copy, mask).sum()if overlap > max_overlap:max_overlap = overlapmax_angle = anglereturn max_angle
def pyramid_template_matching(image, template, mask=None):'''返回当前导航的角度:param image::param template::param mask::return:'''result = image.copy()# 创建掩码if mask is not None:mask = mask.astype(np.uint8)# 金字塔层级# pyramid_levels =1best_angle = -1 # 最佳匹配角度max_similarity = 0 # 最大相似度best_res = 0best_rotated_template = ""h, w = template.shape[:2]scaled_mask = mask.astype(np.uint8)scaled_image = imagescaled_template = template# for level in range(pyramid_levels):# # 缩放大图和小图# scaled_image = cv2.resize(image, (0, 0), fx=1 / (2 ** level), fy=1 / (2 ** level))# scaled_template = cv2.resize(template, (0, 0), fx=1 / (2 ** level), fy=1 / (2 ** level))# if mask is not None:# scaled_mask = cv2.resize(mask, (0, 0), fx=1 / (2 ** level), fy=1 / (2 ** level))# scaled_mask = scaled_mask.astype(np.uint8)# 旋转模板并进行匹配for angle in range(0, 360, 10):rotated_mask = rotate_image(scaled_mask, angle)rotated_mask[rotated_mask > 1] = 255 # 将灰色也变成白色rotated_template = rotate_image(scaled_template, angle)# save_rotated_image(rotated_template, "./datas/img", f"{angle}_template")# save_rotated_image(rotated_mask, "./datas/img",f"{angle}_mask")res = cv2.matchTemplate(scaled_image, rotated_template, cv2.TM_CCORR_NORMED, mask=rotated_mask)similarity = cv2.minMaxLoc(res)[1] # 获取匹配相似度if similarity > max_similarity:max_similarity = similaritybest_angle = anglebest_res = resbest_rotated_template = rotated_templatebest_angle_bk = copy.copy(best_angle)start_angle = best_angle_bk - 10if start_angle < 0:start_angle = 0end_angle = start_angle + 10else:end_angle = start_angle + 20for angle in range(start_angle, end_angle, 1):rotated_mask = rotate_image(scaled_mask, angle)rotated_mask[rotated_mask > 1] = 255 # 将灰色像素值改为黑色像素值rotated_template = rotate_image(scaled_template, angle)res = cv2.matchTemplate(scaled_image, rotated_template, cv2.TM_CCORR_NORMED, mask=rotated_mask)similarity = cv2.minMaxLoc(res)[1] # 获取匹配相似度if similarity > max_similarity:max_similarity = similaritybest_angle = anglebest_res = resbest_rotated_template = rotated_template# min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(best_res)# top_left = max_loc# bottom_right = (top_left[0] + w, top_left[1] + h)# 绘制角度# cv2.putText(result, f"{int(best_angle)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)# 在原始大图上绘制矩形框标记匹配区域# cv2.rectangle(result, top_left, bottom_right, (0, 255, 0), 2)return result, max_similarity, best_angle, best_rotated_template
def save_rotated_image(rotated_image, save_dir, index):save_path = os.path.join(save_dir, f"{index}.png")cv2.imwrite(save_path, rotated_image)
def rotate_image(img, angle):'''将图片旋转多少度:param img::param angle::return:'''h, w = img.shape[:2]center = (w // 2, h // 2)M = cv2.getRotationMatrix2D(center, -angle, 1.0)rotated_image = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_NEAREST, borderMode=cv2.BORDER_CONSTANT,borderValue=(0, 0, 0, 0))return rotated_image
def template_matching(img, template_img, threshold=0.8, maxcnt=0, mask=None):"""模板匹配 多个Args:img:template_img:threshold:maxcnt:mask:Returns:"""method = cv2.TM_CCOEFF_NORMED# 创建掩码if mask is not None:mask = mask.astype(np.uint8)scaled_mask = mask.astype(np.uint8)else:scaled_mask = maskscaled_image = imgscaled_template = template_imgif scaled_image is None:raise "大图不能为None"if scaled_template is None:raise "模板小图不能为None"h, w = scaled_template.shape[:2]res = cv2.matchTemplate(scaled_image, scaled_template, method, mask=scaled_mask)result = []while True:min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:top_left = min_locelse:top_left = max_locif max_val < threshold or max_val == np.inf:breakmiddle_point = [top_left[0] + w // 2, top_left[1] + h // 2]result.append(dict(result=middle_point,rectangle=(top_left, (top_left[0], top_left[1] + h), (top_left[0] + w, top_left[1]),(top_left[0] + w, top_left[1] + h)),confidence=round(max_val, 2)))if maxcnt and len(result) >= maxcnt:breakcv2.floodFill(res, None, max_loc, (-1000,), max_val - threshold + 0.1, 1, flags=cv2.FLOODFILL_FIXED_RANGE)return result
def find_img_and_click(img, hwnd=0, left=0, top=0, right=1920, bottom=1080, p_left=0, p_top=0, label="", times=10,step=1.0,is_click=True, threshold=0.8, keepKey=None, mask=None, double_click=False, big_img=None,is_show=False):'''Args:img: 小图 np图hwnd: 句柄left: 左边top: 顶边right: 右边bottom: 底边p_left: 窗口偏移left 有些窗口有黑边/透明边p_top: 窗口偏移toplabel: 输出的日志times: 找图次数step: 查找间隔is_click: 是否点击threshold: 最小相似度keepKey: 需要一直按住哪个键? 不填 就不按 Nonemask: 掩码double_click: 是否双击big_img: None 自动截图 非None 则提供大图np图 提供大图为了保证点击成功 left 和 top也要准确给is_show: False 是否展示Returns:'''for _ in range(times):if not state.状态_循环开关:return Falseif keepKey is not None:pyautogui.keyDown(keepKey)if big_img is None:big_img = screenshot(hwnd, left, top, right, bottom, None)res = template_matching(big_img, img, mask=mask, threshold=threshold)for r_item in res:result_post = [r_item["result"][0], r_item["result"][1]]logger.info(label)if is_show:# 转换为 NumPy 数组pts = np.array((r_item['rectangle'][0], r_item['rectangle'][1], r_item['rectangle'][3],r_item['rectangle'][2]),np.int32)cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, [pts], True, (0, 255, 0), 5)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()if is_click:mouse_move(p_left + result_post[0] + left, p_top + result_post[1] + top)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()#print(p_left + result_post[0] + left, p_top + result_post[1] + top)if double_click:mouse_left_down()time.sleep(0.2)mouse_left_up()return Trueif times > 1:time.sleep(step)big_img=Nonereturn False
def find_imgs_and_click(imgs, hwnd=0, left=0, top=0, right=1920, bottom=1080, p_left=0, p_top=0, label="", times=10,step=1.0,is_click=True, threshold=0.8, isMask=False,is_show=False):for _ in range(times):big_img = screenshot(hwnd, left, top, right, bottom, None)for img in imgs:if isMask:mask = img[:, :, 3] # 提取透明度通道作为掩码else:mask = Noneret = find_img_and_click(img, left, top, right, bottom, p_left, p_top, label=label, is_click=is_click,times=1, threshold=threshold, mask=mask, big_img=big_img,is_show=is_show)if ret:return Trueif times > 1:time.sleep(step)return Falsedef find_img_all_sift(big_img, small_img, roi=None, min_match_count=10, maxcnt=0):'''使用sift算法进行多个相同元素的查找Args:big_img(numpy.ndarray): 大图像small_img(numpy.ndarray): 需要查找的小图像roi(tuple): 感兴趣区域的坐标,格式为(x, y, width, height)min_match_count(int): 最小匹配数量maxcnt(int): 匹配的最大数量Returns:A list of found [{"result": point, "rectangle": rectangle, "confidence": 0.76}, ...]rectangle is a 4 points list'''try:sift = cv2.SIFT_create(sigma=1.3)flann = cv2.FlannBasedMatcher({'algorithm': 1, 'trees': 5}, dict(checks=50))g1 = cv2.cvtColor(small_img, cv2.COLOR_BGR2GRAY)if roi:x, y, width, height = roiroi_img = big_img[y:y + height, x:x + width]g2 = cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY)else:g2 = cv2.cvtColor(big_img, cv2.COLOR_BGR2GRAY)#增强对比度# g1 = cv2.equalizeHist(g1)# g2 = cv2.equalizeHist(g2)# #自适应阈值分割 减少透明背景影响g1 = cv2.adaptiveThreshold(g1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY, 21, 2)g2 = cv2.adaptiveThreshold(g2, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY, 21, 2)kp_sch, des_sch = sift.detectAndCompute(g1, None)if len(kp_sch) < min_match_count:return Nonekp_src, des_src = sift.detectAndCompute(g2, None)if len(kp_src) < min_match_count:return Noneresult = []while True:matches = flann.knnMatch(des_sch, des_src, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append(m)if len(good) < min_match_count:breaksch_pts = np.float32([kp_sch[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)img_pts = np.float32([kp_src[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(sch_pts, img_pts, cv2.RANSAC, 5.0)matches_mask = mask.ravel().tolist()h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)try:if roi:dst = cv2.perspectiveTransform(pts, M) + [x, y]else:dst = cv2.perspectiveTransform(pts, M)except:breakpypts = []for npt in dst.astype(int).tolist():pypts.append(tuple(npt[0]))#保留小数点后两位sim = round(min(1.0 * matches_mask.count(1) / len(matches_mask), 1.0),2)X = (pypts[0][0] + pypts[1][0] + pypts[2][0] + pypts[3][0]) // 4Y = (pypts[0][1] + pypts[1][1] + pypts[2][1] + pypts[3][1]) // 4middle_point = (X, Y)if 0 > middle_point[0] > w or 0 > middle_point[1] > h:continueresult.append(dict(result=middle_point,rectangle=pypts,confidence=sim,dst_pot=[np.int32(dst)]))if maxcnt and len(result) >= maxcnt:breakqindexes, tindexes = [], []for m in good:qindexes.append(m.queryIdx)tindexes.append(m.trainIdx)def filter_index(indexes, arr):r = []for i, item in enumerate(arr):if i not in indexes:r.append(item)return np.array(r)kp_src = filter_index(qindexes, kp_src)des_src = filter_index(tindexes, des_src)return resultexcept:return None
def find_img_sift_and_click(img, hwnd=0, left=0, top=0, right=1920, bottom=1080, p_left=0, p_top=0, label="", times=10,step=1.0,is_click=True, threshold=0.8, keepKey=None, roi=None, double_click=False, big_img=None,is_show=False):'''Args:img: 小图 np图hwnd: 句柄left: 左边top: 顶边right: 右边bottom: 底边p_left: 窗口偏移left 有些窗口有黑边/透明边p_top: 窗口偏移toplabel: 输出的日志times: 找图次数step: 查找间隔is_click: 是否点击threshold: 最小相似度keepKey: 需要一直按住哪个键? 不填 就不按 Noneroi: 感兴趣的区域 默认为none 全图搜索 (tuple): 感兴趣区域的坐标,格式为(x, y, width, height)double_click: 是否双击big_img: None 自动截图 非None 则提供大图np图 提供大图为了保证点击成功 left 和 top也要准确给is_show: False 是否展示Returns:'''for _ in range(times):if not state.状态_循环开关:return Falseif keepKey is not None:pyautogui.keyDown(keepKey)if big_img is None:big_img = screenshot(hwnd, left, top, right, bottom, None)res = find_img_all_sift(big_img, img,roi)for r_item in res:if r_item['confidence'] <threshold:continueresult_post = [ r_item["middle_point"][0], r_item["middle_point"][1]]logger.info(label)if is_show:cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, result['dst_pot'], True, (0, 255, 0), 5)# cv2.imshow('Genshin navigation' , big_img)# cv2.imwrite("./output.jpg", big_img)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()if is_click:mouse_move(p_left + result_post[0] + left, p_top + result_post[1] + top)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()#print(p_left + result_post[0] + left, p_top + result_post[1] + top)if double_click:mouse_left_down()time.sleep(0.2)mouse_left_up()return Trueif times > 1:time.sleep(step)big_img=Nonereturn False
def find_imgs_sift_and_click(imgs, hwnd=0, left=0, top=0, right=1920, bottom=1080, p_left=0, p_top=0, label="", times=10,step=1.0,is_click=True, threshold=0.8, roi=None,is_show=False):'''使用sift算法 进行搜图 支持形变 放大缩小 透明的干扰的图Args:img: 小图 np图 列表hwnd: 句柄left: 左边top: 顶边right: 右边bottom: 底边p_left: 窗口偏移left 有些窗口有黑边/透明边p_top: 窗口偏移toplabel: 输出的日志times: 找图次数step: 查找间隔is_click: 是否点击threshold: 最小相似度roi: 感兴趣的区域 默认为none 全图搜索 (tuple): 感兴趣区域的坐标,格式为(x, y, width, height)is_show: False 是否展示Returns:'''for _ in range(times):big_img = screenshot(hwnd, left, top, right, bottom, None)for img in imgs:ret = find_img_sift_and_click(img, left, top, right, bottom, p_left, p_top, label=label, is_click=is_click,times=1, threshold=threshold, roi=roi, big_img=big_img,is_show=is_show)if ret:return Trueif times > 1:time.sleep(step)return False
def find_dict_index(dict_list, key, value):'''查找列表成员中的 字典里指定key 中是否存在某个值 返回相似度最高的成员索引:param dict_list::param key::param value::return:'''max_sim = 0index = Nonefor i, dictionary in enumerate(dict_list):if dictionary.get(key) == value:if dictionary.get('sim') > max_sim:max_sim = dictionary.get('sim')index = ireturn index != None, index
def find_dict_num(dict_list, key, value):'''列表中 字典里指定key 存在某个值的次数:param dict_list::param key::param value::return:'''num_ = 0for i, dictionary in enumerate(dict_list):if dictionary.get(key) == value:num_ += 1return num_if __name__ == '__main__':# 读取图像和模板# old_time=time.time()# image = cv2.imdecode(np.fromfile(file=r"大图.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED) # 加载大图# template = cv2.imdecode(np.fromfile(file=r"小图.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED) # 加载透明图# mask = template[:, :, 3] # 提取透明度通道作为掩码# result,max_similarity, best_angle = pyramid_template_matching(image, template, mask=mask)# print(f"相似度:{max_similarity} 角度:{best_angle} 用时:{int((time.time()-old_time)*1000)}ms")## # 显示结果图像# cv2.imshow("Result", result)# cv2.waitKey(0)# cv2.destroyAllWindows()old_time = time.time()image = cv2.imdecode(np.fromfile(file=r"D:\QQ聊天记录\111.bmp", dtype=np.uint8), cv2.IMREAD_COLOR) # 加载大图template = cv2.imdecode(np.fromfile(file=r"D:\QQ聊天记录\p.png", dtype=np.uint8), cv2.IMREAD_COLOR) # 加载透明图RET = template_matching(image, template, mask=None)for result in RET:print(result)# 转换为 NumPy 数组pts = np.array((result['rectangle'][0], result['rectangle'][1], result['rectangle'][3],result['rectangle'][2]), np.int32)cv2.polylines(image, [pts], True, (0, 255, 0), 5)# 显示结果图像cv2.imshow("324234", result)cv2.waitKey(0)cv2.destroyAllWindows()
4、监听键盘鼠标事件
lanrenauto/moni/hookkeymose.py
# -*- coding: utf-8 -*-
from pynput import keyboard
from pynput import mouseclass HookKeyMose:def __init__(self,hookMouse=False,hookKey=True):'''可通过集成本类 然后重写on_函数来实现hook回返'''if hookMouse==True:self.listen_mouse_nblock()if hookKey==True:self.listen_key_nblock()def on_press(self,key):"""定义按下时候的响应,参数传入key"""passdef on_release(self,key):"""定义释放时候的响应"""pass#print(f'{key} up')def on_move(self,x, y):pass#print('move to', x, y)def on_click(self,x, y, button, pressed):pass#print('click at', x, y, button, pressed)def on_scroll(self,x, y, dx, dy):pass#print('scroll at', x, y, 'by', dx, dy)# 监听写法1def listen_key_block(self):with keyboard.Listener(on_press=self.on_press, on_release=self.on_release) as listener:listener.join() # 加入线程池,阻塞写法# 监听写法2def listen_key_nblock(self):self.listener_keyboard = keyboard.Listener(on_press=self.on_press, on_release=self.on_release)self.listener_keyboard .start() # 启动线程def listen_mouse_nblock(self):self.listener_mouse = mouse.Listener(on_move=None, # 因为on_move太多输出了,就不放进来了,有兴趣可以加入on_click=self.on_click,on_scroll=self.on_scroll)self.listener_mouse.start()if __name__ == '__main__':while True: # 这里应该用一个循环维持主线程,否则主线程结束了子线程就自动结束了pass
5、循环截图
lanrenauto/tools/screenshot.py
# -*- coding: utf-8 -*-
import os
import random
import time
import traceback
import numpy as np
import win32gui
import win32ui
import win32con
from PIL import Imagefrom ..logger_module import loggerdef screenshot(hwnd, left=0, top=0, right=0, bottom=0, filename=None):try:#old_time=time.time()width = right-leftheight = bottom - top# 判断窗口是否可见if not win32gui.IsWindowVisible(hwnd):return False# 创建设备描述表hwnd_dc = win32gui.GetDC(hwnd) #GetWindowDCmfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)save_dc = mfc_dc.CreateCompatibleDC()# 创建位图对象save_bitmap = win32ui.CreateBitmap()save_bitmap.CreateCompatibleBitmap(mfc_dc, width, height)# 将位图对象绑定到设备描述表save_dc.SelectObject(save_bitmap)# result = windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(),1) #0 1 或者3 3没有透明通道信息# if result == 0:# print("PrintWindow failed")# return False# # 将截图保存到位图对象中save_dc.BitBlt((0, 0), (width, height), mfc_dc, (left, top), win32con.SRCCOPY)#win32con.CAPTUREBLT win32con.SRCCOPY# 将位图对象转换为OpenCV图像bmp_info = save_bitmap.GetInfo()bmp_str = save_bitmap.GetBitmapBits(True)img = np.frombuffer(bmp_str, dtype='uint8').reshape((bmp_info['bmHeight'], bmp_info['bmWidth'], 4))#img = img[top:bottom, left:right]# 测试透明通道是否截到# img = cv2.split(img)# # 提取Alpha通道# img = img[3]# cv2.imshow('Alpha Channel', img)# cv2.waitKey(0)# cv2.destroyAllWindows()if filename is not None:# 保存位图对象到文件img_pil = Image.fromarray(img[..., [2, 1, 0]])img_pil.save(filename, format='JPEG', quality=90)# 删除对象,释放资源save_dc.DeleteDC()win32gui.ReleaseDC(hwnd, hwnd_dc)win32gui.DeleteObject(save_bitmap.GetHandle())#print(time.time()-old_time)return imgexcept :logger.error(traceback.format_exc())return Falseif __name__ == '__main__':try:step=int(input("多少秒截一张图?输入数字按回车确认:"))except:step=2input("请将游戏设置为1920*1080分辨率,然后按回车开始截图")# 获取窗口句柄hwnd = win32gui.FindWindow("UnityWndClass", "原神") # 替换成你实际的窗口句柄# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = 0, 0, 1920, 1080 # 替换成你实际的区域坐标#判断是否有这个目录 没有就创建if not os.path.exists("../../原神截图"):os.mkdir("../../原神截图")while True:time.sleep(step)filename = f"./原神截图/{int(time.time())}{random.randint(1000,9999)}.jpg"ret=screenshot(hwnd, left, top, right, bottom, filename=filename)print("截图成功保存在:",filename)
6、添加模型推理
lanrenauto/yolov/lanrenonnx.py
import os
import random
import time
import onnxruntime as ort
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFontdef get_font_path(font_path):# 获取 put.py 所在目录put_dir = os.path.dirname(__file__)put_path = os.path.join(put_dir, font_path)if os.path.exists(put_path):return put_pathelse:return ""
def put_chinese_text_on_image(img, label, position, color=[0,255,0],font_path= 'Arial.Unicode.ttf', font_size=15, font_thickness=5):"""在OpenCV图像上绘制中文字符串。参数:img -- OpenCV图像。label -- 要绘制的字符串。position -- 字符串的起始位置 (x, y) 元组。color -- 字体颜色,以BGR格式给出。font_path -- 支持中文的字体文件的路径。font_size -- 字体大小font_thickness -- 字体 加粗返回:带有中文字符串的图像。"""fp = get_font_path(font_path)if fp == "":return img# 转换图像以便使用PILpil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(pil_img)# 加载自定义字体pil_font = ImageFont.truetype(fp, font_size) # 字体大小可能需要调整以匹配OpenCV的font_scale# 获取文本的宽度和高度text_width, text_height = draw.textbbox((0, 0), label, font=pil_font)[2:]# 计算文本的起始位置,确保它不会超出图像边界x, y = positiony_adjusted = max(y - text_height, 0)position = (x, y_adjusted)# 使用PIL绘制文本draw.text(position, label, fill=tuple(color), font=pil_font,width=font_thickness)# 将PIL图像转换回OpenCV图像img_with_text = cv2.cvtColor(np.asarray(pil_img), cv2.COLOR_RGB2BGR)return img_with_text
class LanRenOnnxYolov():def __init__(self,weights='yolov5l_320.onnx',model_h=320, model_w=320, thred_nms=0.4, thred_cond=0.4,providers=["CUDAExecutionProvider",'CPUExecutionProvider'],dic_labels={}):"""yolov onnx推理providers: [] ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']dic_labels: {0: 'person', 1: 'bicycle'}"""self.weights=weightsself.model_h=model_hself.model_w=model_wself.thred_nms=thred_nmsself.thred_cond=thred_condself.colors=[[random.randint(0, 255) for _ in range(3)] for color in range(len(dic_labels))]#print(self.colors)self.openfile_name_model = weights # 模型名称# self.so = ort.SessionOptions() # 树莓派上保留以下两段代码,注释下面那行代码# self.net = ort.InferenceSession(self.openfile_name_model, self.so)# ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']try:self.net = ort.InferenceSession(self.openfile_name_model,providers=providers) # 在树莓派上这里不需指定推理设备p=self.net.get_providers()print("执行提供者:",p,)print("GPU加速:",str(p).find("CUDA")!=-1 or str(p).find("Tensorrt")!=-1 )# 标签字典self.dic_labels = dic_labelsbig_img = np.zeros((1080, 1920, 3), dtype=np.uint8)self.detect(big_img )old_time = time.time()self.detect(big_img )print("检测帧率:", 1000 // int((time.time() - old_time) * 1000))except:print("CUDA加速失败,使用CPU推理")providers=['CPUExecutionProvider']self.net = ort.InferenceSession(self.openfile_name_model,providers=providers) # 在树莓派上这里不需指定推理设备p = self.net.get_providers()print("执行提供者:", p, )print("GPU加速:", str(p).find("CUDA") != -1 or str(p).find("Tensorrt") != -1)# 标签字典self.dic_labels = dic_labelsbig_img = np.zeros((1080, 1920, 3), dtype=np.uint8)self.detect(big_img)old_time = time.time()self.detect(big_img)print("检测帧率:", 1000 // int((time.time() - old_time) * 1000))# 标注目标def plot_one_box(self,x, img, color=None, label=None, line_thickness=None):"""description: Plots one bounding box on image img,this function comes from YoLov5 project.param:x: a box likes [x1,y1,x2,y2]img: a opencv image objectcolor: color to draw rectangle, such as (0,255,0)label: strline_thickness: intreturn:img"""tl = (line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1) # line/font thicknesscolor = color or [random.randint(0, 255) for _ in range(3)]c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)if label:tf = max(tl - 1, 1) # font thicknesst_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]#支持中文的写法img = put_chinese_text_on_image(img, label, (c1[0], c1[1] - 2), [color[2],color[1],color[0]],font_size= t_size[1])#不支持中文# cv2.putText(# img,# label,# (c1[0], c1[1] - 2),# 0,# tl / 3,# [225, 255, 255],# thickness=tf,# lineType=cv2.LINE_AA,# )return img# 极大值抑制def post_process_opencv(self,outputs, img_h, img_w,):conf = outputs[:, 4].tolist()c_x = outputs[:, 0] / self.model_w * img_wc_y = outputs[:, 1] / self.model_h * img_hw = outputs[:, 2] / self.model_w * img_wh = outputs[:, 3] / self.model_h * img_hp_cls = outputs[:, 5:]if len(p_cls.shape) == 1:p_cls = np.expand_dims(p_cls, 1)cls_id = np.argmax(p_cls, axis=1)p_x1 = np.expand_dims(c_x - w / 2, -1)p_y1 = np.expand_dims(c_y - h / 2, -1)p_x2 = np.expand_dims(c_x + w / 2, -1)p_y2 = np.expand_dims(c_y + h / 2, -1)areas = np.concatenate((p_x1, p_y1, p_x2, p_y2), axis=-1)areas = areas.tolist()ids = cv2.dnn.NMSBoxes(areas, conf, self.thred_cond, self.thred_nms)if len(ids) > 0:return np.array(areas)[ids], np.array(conf)[ids], cls_id[ids]else:return [], [], []# 推理def detect(self,img_np,save_path=None,plot_box=True):'''预测'''img0 = img_np.copy()# 图像预处理img = cv2.resize(img0, [self.model_w, self.model_h], interpolation=cv2.INTER_AREA) # 缩放img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 格式转换img = img.astype(np.float32) / 255.0 # 归一化blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0) # 维度转换# 模型推理outs = self.net.run(None, {self.net.get_inputs()[0].name: blob})[0].squeeze(axis=0)# 输出坐标矫正# outs = cal_outputs(outs, nl, na, model_w, model_h, anchor_grid, stride)# 检测框计算img_h, img_w, _ = np.shape(img0)boxes, confs, ids = self.post_process_opencv(outs, img_h, img_w)res_loc=[]for box, score, id in zip(boxes, confs, ids):# 标签label = '%s:%.2f' % (self.dic_labels[id], score)# 坐标转换xyxy=box.astype(np.int16)#画框框if plot_box or save_path != None:img0=self.plot_one_box(xyxy, img0, label=label, line_thickness=None,color= self.colors[id])# 数据保存res_loc.append({"label": self.dic_labels[id], "x": int(xyxy[0]) + (int(xyxy[2]) - int(xyxy[0])) // 2,"y": int(xyxy[1]) + (int(xyxy[3]) - int(xyxy[1])) // 2, "sim": float(f"{score:.2f}"),"points": ((xyxy[0],xyxy[1]), (xyxy[2],xyxy[3]))})if save_path != None:cv2.imwrite(save_path, img0)if img0.shape[2]==4:return res_loc,cv2.cvtColor(img0, cv2.COLOR_BGRA2BGR) # 格式转换return res_loc,img0
if __name__ == '__main__':image_path="D:\\pythonProjects\\yuanshenAI\\datasets\\yuanshen\\images\\17141983915923.jpg"weights="./datas/yolov5l_320.onnx"model_h=320model_w=320dic_labels={0:"玩家",1:"采集物",2:"生命树",3:"开关",4:"怪物",5:"提示关闭",6:"使用原粹树脂",7:"使用浓缩树脂",8:"关闭",9:"继续挑战",10:"退出秘境",11:"副本门框",12:"副本打完了",13:"捡东西",14:"副本楼梯",15:"往下落",16:"矿石",17:"往上跳",18:"交互对话",19:"采集物-风",20:"采集物-冰",21:"确定",22:"取消",23:"返回",24:"被控了",25:"在水面",26:"宠物"}big_img = cv2.imdecode(np.fromfile(file=image_path, dtype=np.uint8), cv2.IMREAD_COLOR) # 加载大图yolov=LanRenOnnxYolov(weights=weights,model_w=model_w,model_h=model_h,dic_labels=dic_labels)old_time=time.time()res_loc,img=yolov.detect(big_img, save_path="../../test.jpg")print((time.time()-old_time)*1000,res_loc)old_time = time.time()res_loc, img = yolov.detect(big_img, save_path=None, plot_box=True)print((time.time() - old_time) * 1000, res_loc)old_time = time.time()res_loc, img = yolov.detect(big_img, save_path=None, plot_box=True)print((time.time() - old_time) * 1000, res_loc)old_time = time.time()res_loc, img = yolov.detect(big_img, save_path=None, plot_box=True)print((time.time() - old_time) * 1000, res_loc)old_time = time.time()res_loc, img = yolov.detect(big_img, save_path=None, plot_box=True)print((time.time() - old_time) * 1000, res_loc)
三、寻路
vi main.py
if __name__ == '__main__':....from wayfinding import *ysam = YuanShenAutoMap() # 启动寻路类
添加寻路类
wayfinding.py
# -*- coding: utf-8 -*-
#opencv 必须 4.8.1.78
from pynput import keyboard
from pynput.keyboard import Key
from lanrenauto.tools.screenshot import *
from lanrenauto.moni.hookkeymose import HookKeyMose
from auto import *
def setresize():pyautogui.keyUp('w')pyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")
class YuanShenAutoMap(HookKeyMose):def __init__(self):HookKeyMose.__init__(self, hookKey=True)pyautogui.PAUSE = 0.01pyautogui.FAILSAFE=Falseself.on_rec=Falseself.map_matrix = []self.now_point=(0,0)#当前的位置self.old_point=(0,0)#上一次的位置self.isRunRec=Falseself.小地图区域=(87-10,45-10,246+10,204+10 )# 让yolov接手的兴趣点用来存可能出现怪物,或者需要捡东西的地方self.interest_point = []def run_rec(self, image_path,image_path_lujin):'''录制路径:param image_path_map: 地图路径 有障碍物的:param image_path: 原图:return:'''self.on_rec = Trueself.isRunRec=Trueself.interest_point=[]# 获取窗口句柄hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom =self.小地图区域 # 替换成你实际的区域坐标roi = Noneresult_post=(0,0)big_img = cv2.imdecode(np.fromfile(file=image_path, dtype=np.uint8), cv2.IMREAD_COLOR) # 加载大图big_height, big_width, _ = big_img.shapebig_img_yt = big_img.copy()# height, width, _ = big_img.shape# window_height=500# window_width = int(window_height * width / height)# sj=str(random.randint(1000,9999))# # 创建窗口# cv2.namedWindow('Genshin navigation'+sj, cv2.WINDOW_NORMAL)# cv2.resizeWindow('Genshin navigation'+sj, window_width, window_height)# cv2.setWindowProperty("Genshin navigation"+sj, cv2.WND_PROP_TOPMOST, 1)logger.info("载入地图成功")while True:if self.get_window_handle_at_mouse_position() != hwnd:logger.info("鼠标离开游戏了!")time.sleep(0.5)continue# #time.sleep(interval)if state.开关_是否展预测结果:#cv2.waitKey(100)passif self.on_rec == False:# 分割路径为目录和文件名dir_path, filename = os.path.split(image_path)# 分割文件名和后缀name, extension = os.path.splitext(filename)new_file=image_path_lujincv2.circle(big_img, result_post, 2, (0,0, 255), -1)for point in self.interest_point:cv2.circle(big_img, point, 2, (0, 255, 0), -1)# 保存图像with open(new_file, 'wb') as f:f.write( cv2.imencode(extension, big_img)[1].tobytes())logger.info("录制完成! 保存在了:"+new_file)return# big_img = big_img_bk.copy()small_img = screenshot(hwnd, left, top, right, bottom, None)ret = find_img_all_sift(big_img_yt, small_img,roi)try:for result in ret:result_post = [result["result"][0], result["result"][1]]sm_width,sm_height= result["rectangle"][2][0]-result["rectangle"][0][0],result["rectangle"][2][1]-result["rectangle"][0][1]cv2.circle(big_img, result_post, 2, (255, 0, 0), -1)roi = [result["rectangle"][0][0]-40 ,result["rectangle"][0][1]-40, sm_width+80 , sm_height+80 ]if roi[0] < 0:roi[0] = 0if roi[1] < 0:roi[1] = 0if roi[2] > big_width:roi[0] = big_width - roi[2]if roi[3] > big_height:roi[1] = big_height - roi[3]self.now_point = result_post# 显示匹配结果breakexcept :logger.error(traceback.format_exc())roi=Noneif state.开关_是否展预测结果:if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()#cv2.imshow('Genshin navigation'+sj, big_img)if state.开关_是否展预测结果:pass#cv2.destroyAllWindows()return Truedef run_chuansong(self, image_path,maodian_path=""):'''传送:param image_path_map: 地图路径 有障碍物的:param image_path: 原图:return:'''# 获取窗口句柄hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom =0,0,1920,1080 # 替换成你实际的区域坐标p_left = rect[0] + w_p+leftp_top = rect[1] + h_p+toproi = Nonemoban_img = cv2.imdecode(np.fromfile(file=image_path, dtype=np.uint8), cv2.IMREAD_UNCHANGED)if maodian_path!="":moban_maodian_img = cv2.imdecode(np.fromfile(file=maodian_path, dtype=np.uint8), cv2.IMREAD_UNCHANGED)else:moban_maodian_img=Nonechuangsong_img=cv2.imdecode(np.fromfile(file="./datas/传送按钮.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)def ddd():paimeng_img = cv2.imdecode(np.fromfile(file="./datas/派蒙.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED)for _ in range(20):time.sleep(1)big_img = screenshot(hwnd, left, top, right, bottom, None)res = template_matching(big_img, paimeng_img, mask=None)if state.状态_循环开关 == False:logger.info("强制退出!")state.状态_需重新传送 = Falsereturn Falsefor result in res:result_post = [result["result"][0], result["result"][1]]logger.info("传送完成!")# 将数组从行向量转换为列向量# pts = pts.reshape((-1, 1, 2))if state.开关_是否展预测结果:# 转换为 NumPy 数组pts = np.array((result['rectangle'][0], result['rectangle'][1], result['rectangle'][3],result['rectangle'][2]),np.int32)cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, [pts], True, (0, 255, 0), 5)# cv2.imshow('Genshin navigation' , big_img)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()state.状态_传送中 = Falsestate.状态_需重新传送 = Falsereturn Truestate.状态_需重新传送 = Truelogger.info("需要重新传送! (注意检查一下 桌面缩放是否为100%)")return Falsebig_img = screenshot(hwnd, left, top, right, bottom, None)res = find_img_all_sift(big_img, moban_img,roi)time.sleep(1)hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄set_window_activate(hwnd)time.sleep(0.2)if state.状态_循环开关== False:logger.info("强制退出!")state.状态_需重新传送 = Falsereturn Falsetry:if len(res) == 0:logger.info(f"没定位到传送模板")state.状态_需重新传送 = Truereturn Falsefor result in res:logger.info("找到了传送位置")result_post = [result["result"][0], result["result"][1]]if state.开关_是否展预测结果:cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, result['dst_pot'], True, (0, 255, 0), 5)#cv2.imshow('Genshin navigation' , big_img)#cv2.imwrite("./output.jpg", big_img)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()#cv2.waitKey(100)# 显示匹配结果mouse_move(p_left +result_post[0], p_top + result_post[1])time.sleep(0.1)mouse_left_down()time.sleep(0.1)mouse_left_up()time.sleep(0.5)breakexcept Exception as err:logger.info(f"没找到传送位置{err}")state.状态_需重新传送 = Truereturn Falsetime.sleep(0.5)if state.状态_循环开关 == False:state.状态_需重新传送 = Falselogger.info("强制退出!")return Falsebig_img = screenshot(hwnd, left, top, right, bottom, None)res=template_matching(big_img, chuangsong_img, mask=None)ret = Falsetry:for result in res:result_post = [result["result"][0], result["result"][1]]logger.info(f"找到了传送按钮1{result_post} {result['confidence']}")# 转换为 NumPy 数组pts = np.array((result['rectangle'][0], result['rectangle'][1], result['rectangle'][3], result['rectangle'][2]), np.int32)# 将数组从行向量转换为列向量#pts = pts.reshape((-1, 1, 2))if state.开关_是否展预测结果:cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, [pts], True, (0, 255, 0), 5)# cv2.imshow('Genshin navigation' , big_img)#cv2.imwrite("./output.jpg", big_img)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()# cv2.waitKey(100)# 显示匹配结果mouse_move(p_left + result_post[0]+100, p_top + result_post[1])time.sleep(0.1)mouse_left_down()time.sleep(0.1)mouse_left_up()time.sleep(0.5)return ddd()except Exception as err:logger.info(f"没有找到传送按钮{err}")state.状态_需重新传送 = Truereturn Falseif ret != True:time.sleep(0.5)if state.状态_循环开关== False:state.状态_需重新传送 = Falselogger.info("强制退出!")return Falsebig_img = screenshot(hwnd, left, top, right, bottom, None)res = find_img_all_sift(big_img, moban_maodian_img, None)ret = Falsetry:if res == []:logger.info("传送失败")state.状态_需重新传送 = Truereturn Falsefor result in res:result_post = [result["result"][0], result["result"][1]]if result["result"][0]<581 and result["result"][1]<401:ret = Falsecontinueif result['confidence']<0.8:ret = Falsecontinuelogger.info(f"找到了锚点选项 {result_post} {result['confidence']}")if state.开关_是否展预测结果:cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, result['dst_pot'], True, (0, 255, 0), 5)# cv2.imshow('Genshin navigation' , big_img)#cv2.imwrite("./output.jpg", big_img)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()# cv2.waitKey(100)# 显示匹配结果mouse_move(p_left + result_post[0], p_top + result_post[1])time.sleep(0.1)mouse_left_down()time.sleep(0.1)mouse_left_up()time.sleep(0.5)ret = Truebreakexcept Exception as err:logger.info(f"没找到锚点选项{err}" )state.状态_需重新传送 = Truepyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")time.sleep(1)pyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")return Falseif ret == True:big_img = screenshot(hwnd, left, top, right, bottom, None)res = template_matching(big_img, chuangsong_img, mask=None)if state.状态_循环开关 == False:logger.info("强制退出!")state.状态_需重新传送 = Falsereturn Falsetry:if res==[]:logger.info("传送失败")state.状态_需重新传送 = Truepyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")time.sleep(1)pyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")return Falsefor result in res:result_post = [result["result"][0], result["result"][1]]logger.info(f"找到了传送按钮2{result_post} {result['confidence']}" )# 转换为 NumPy 数组pts = np.array((result['rectangle'][0], result['rectangle'][1], result['rectangle'][3],result['rectangle'][2]), np.int32)# 将数组从行向量转换为列向量# pts = pts.reshape((-1, 1, 2))if state.开关_是否展预测结果:cv2.circle(big_img, result_post, 10, (0, 0, 255), -1)cv2.polylines(big_img, [pts], True, (0, 255, 0), 5)# cv2.imshow('Genshin navigation' , big_img)#cv2.imwrite("./output.jpg", big_img)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()# cv2.waitKey(100)# 显示匹配结果mouse_move(p_left + result_post[0]+100, p_top + result_post[1])time.sleep(0.1)mouse_left_down()time.sleep(0.1)mouse_left_up()time.sleep(0.5)return ddd()except Exception as err:logger.info(f"传送失败{err}" )state.状态_需重新传送 = Truepyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")time.sleep(1)pyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")return Falseelse:logger.info("传送失败")state.状态_需重新传送 = Truepyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")time.sleep(1)pyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")return False#cv2.destroyAllWindows()return Truedef run_playback(self, image_path_map, image_path,wakuang=False,caiji=False,quickening_keys=None):'''执行寻路 直到移动到目标为止:param image_path_map: 地图路径 有障碍物的:param image_path: 原图:param isShow: 是否显示地图:param quickening_keys: 用来随机赶路的按钮 比如[{"probability":0.1,"keys":[ {"key":"1","time":0.2 }, {"key":"e","time":0.5}]}]代表概率10%(100毫秒一次判断) 按1 0.2秒 然后 按E 1秒 支持无数组合:return:'''self.isRunRec = Falsestate.状态_循环开关= Truestate.状态_已经有寻路了=Trueself.load_map(image_path_map)self.map_matrix_copy = self.map_matrix.copy()def setangle(angle_now, angle_target, fault=6):nonlocal best_angle,quickening_keysret=Trueangle_diff = abs(angle_now - angle_target)if angle_diff <= fault:ret= Falseif state.状态_循环开关== False:logger.info("强制退出1!")pyautogui.keyUp('w')ret= Falseif ret==False:return Falseangle_diff = int((angle_target - angle_now) % 360)if angle_diff > 180:fangxiang = -1angle_diff = 360 - angle_diffelse:fangxiang = 1if abs(angle_diff < fault):return True#print(f"调整视角 相差:{fangxiang * angle_diff}° 当前:{angle_now}° 目标:{angle_target}° ")mouse_moveR(int(fangxiang * 5 * angle_diff), 0)time.sleep(0.05)return Truestate.计次_定位失败=0# 获取窗口句柄hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄while True:time.sleep(1)if self.get_window_handle_at_mouse_position() == hwnd:breakif state.状态_循环开关== False:logger.info("强制退出!")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Falsereturn False# 设定保存截图的文件夹路径和文件名前缀#folder_path = "./datas/img/"# 设定定时器间隔(秒)roi=Nonebig_img_bk = cv2.imdecode(np.fromfile(file=image_path, dtype=np.uint8),cv2.IMREAD_UNCHANGED) # 加载大图big_height, big_width, big_td = big_img_bk.shapeif big_td==3:# # 将24位彩色图片转换为32位彩色图片big_img_bk = cv2.cvtColor(big_img_bk, cv2.COLOR_BGR2BGRA)#BGRA COLOR_BGR2BGRAtemplate = cv2.imdecode(np.fromfile(file=r"./datas/小图.png", dtype=np.uint8), cv2.IMREAD_UNCHANGED) # 加载透明图mask = template[:, :, 3] # 提取透明度通道作为掩码paimeng_img = cv2.imdecode(np.fromfile(file="./datas/派蒙.png", dtype=np.uint8),cv2.IMREAD_UNCHANGED)result_old = {"result": [0, 0]}state.计数_卡主次数=0state.状态_在爬墙=Falseinterest_coordinate2=Noneinterest_coordinate=Nonestate.游戏_打怪前坐标 = [0, 0]state.游戏_当前目标坐标 = [0, 0]state.计次_误差过大=0self.old_point= [0, 0]state.计次_识别次数=0state.计次_移动停滞 =0while True:#old_time = time.time()try:time.sleep(0.01)if state.状态_循环开关 == False:logger.info("强制退出!")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Falsereturn False# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)left, top, right, bottom = self.小地图区域 # 替换成你实际的区域坐标if state.状态_全局暂停:time.sleep(1)continueif not state.状态_YOLOV :time.sleep(1)continueif state.计次_误差过大 == 20:self.old_point = copy.copy(state.游戏_当前目标坐标)if state.计次_误差过大>= 30:logger.info("误差过大了30次,重新运行这个任务")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Truereturn Falseif state.计次_移动停滞>=20:logger.info("移动停滞了20次,重新运行这个任务")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Truereturn Falseif state.计次_定位失败>=30:logger.info("定位失败次数超过30次,重新运行这个任务")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Truereturn Falseself.quickening(quickening_keys)big_img = big_img_bk.copy()#filename = f"{folder_path}{int(time.time())}.png"height = bottom - topwidth = right - leftsmall_img = screenshot(hwnd, left, top, right, bottom, filename=None)#RGBAif type(small_img)==bool:hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄time.sleep(1)continue# 识别位置和方向_, max_similarity, best_angle, best_rotated_template = pyramid_template_matching(small_img[int(height / 2) - 30:int(height / 2) + 30, int(width / 2) - 30:int(width / 2) + 30],template,mask=mask)best_angle = int(best_angle)#cv2.imwrite(filename,small_img)#cv2.imwrite(f"{folder_path}{int(time.time())}_111.png",small_img[int(height / 2) - 30:int(height / 2) + 30, int(width / 2) - 30:int(width / 2) + 30])# 识别位置和方向state.游戏_当前视野角度= get_view(small_img[int(height / 2) - 70:int(height / 2) + 70, int(width / 2) - 70:int(width / 2) + 70],70)if state.游戏_当前视野角度==-1:state.游戏_当前视野角度=best_anglestate.游戏_当前导航角度=best_angle#print(state.游戏_当前视野角度,state.游戏_当前导航角度)# 裁剪图像,去除Alpha通道# small_img=small_img[:, :, :3]ret = find_img_all_sift(big_img, small_img,roi)if ret==None or ret == []:logger.info(f"定位失败{state.计次_定位失败}" )state.计数_卡主次数 += 1state.计次_定位失败 += 1if state.计次_定位失败>=15:pyautogui.keyDown('shift')time.sleep(0.3)pyautogui.keyUp('shift')# 如果定位失败,判断派蒙是否存在#如果派蒙不在 并且不是在连招过程中,则说明现在 在不明界面中,尝试按ESC键和点击指定位置跳过对话res__ = template_matching(screenshot(hwnd, 0, 0, 1920, 1080, filename=None), paimeng_img, mask=None)if res__==[] and state.状态_是否回放中==False:logger.info("发现不明界面,尝试按ESC键和点击指定位置跳过对话")rect = win32gui.GetWindowRect(hwnd)w_p, h_p = (rect[2] - rect[0]) - 1920, (rect[3] - rect[1]) - 1080# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)p_left = rect[0] + w_pp_top = rect[1] + h_pmouse_move(p_left+1400,p_top+584)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.2)mouse_move(p_left + 1412, p_top + 652)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.2)mouse_move(p_left + 1419, p_top + 729)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()time.sleep(0.2)mouse_move(p_left + 1380, p_top + 801)time.sleep(0.2)mouse_left_down()time.sleep(0.2)mouse_left_up()if random.randint(0,2)==1:time.sleep(0.2)pyautogui.keyDown("esc")time.sleep(0.2)pyautogui.keyUp("esc")if state.状态_寻路中:if roi==None:if result_old!={"result": [0, 0]}:ret = [result_old]else:roi = Nonecontinueelse:roi = Nonecontinueroi=Nonefor result in ret:if result["result"] !=result_old["result"] :state.计次_定位失败 = 0sm_width,sm_height= result["rectangle"][2][0]-result["rectangle"][0][0],result["rectangle"][2][1]-result["rectangle"][0][1]state.游戏_当前坐标 = [result["result"][0], result["result"][1]]roi = [result["rectangle"][0][0]-40 ,result["rectangle"][0][1]-40, sm_width+80 , sm_height+80 ]if roi[0]<0:roi[0]=0if roi[1]<0:roi[1]=0if roi[2]>big_width:roi[0]=big_width-roi[2]if roi[3] > big_height:roi[1] = big_height - roi[3]self.update_map_show(big_img)if state.状态_寻路中:if state.游戏_当前坐标[0] < 0 or state.游戏_当前坐标[1] < 0:roi = Nonebreakif abs(state.游戏_当前坐标[0] - self.end[0]) <= 120 and abs(state.游戏_当前坐标[1] - self.end[1]) <= 120 and quickening_keys != None:# 快到终点了,ban掉这些赶路的,防止走错地方quickening_keys = Noneif abs(state.游戏_当前坐标[0] - self.end[0]) <= 4 and abs(state.游戏_当前坐标[1] - self.end[1]) <= 4:logger.info("已经到达目的地")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Falsereturn Truestate.计次_识别次数+=1if state.计次_识别次数>=8:if state.状态_是否回放中==False and state.状态_寻路中:if abs(state.游戏_当前坐标[0] - result_old["result"][0]) <=3 and abs(state.游戏_当前坐标[1] - result_old["result"][1]) <= 3 :state.计次_移动停滞 += 1state.计数_卡主次数+=1if state.计数_卡主次数 >= 10:if abs(state.游戏_当前坐标[0] - self.end[0]) <= 20 and abs(state.游戏_当前坐标[1] - self.end[1]) <= 20:logger.info("非常接近目的地了!但是遇到意外,因此跳过这个任务!")pyautogui.keyUp('w')state.状态_已经有寻路了 = Falsestate.状态_需重新传送 = Falsereturn Trueif random.randint(0, 1):pyautogui.keyDown('a')aaa = -90else:pyautogui.keyDown('d')aaa = 90pyautogui.keyUp('w')mouse_moveR(6 * aaa, 0)state.计数_卡主次数 = 0logger.info("卡主太多次,尝试按X")pyautogui.keyDown('x')time.sleep(0.2)pyautogui.keyUp('x')time.sleep(1)pyautogui.keyUp('d')pyautogui.keyUp('a')pyautogui.keyDown('w')pyautogui.keyDown('x')time.sleep(0.2)pyautogui.keyUp('x')time.sleep(5)pyautogui.keyUp('w')else:logger.info("尝试脱困")pyautogui.keyDown('w')pyautogui.keyDown('space')time.sleep(0.2)pyautogui.keyUp('space')rrrr = random.randint(1, 20)if rrrr == 3:pyautogui.keyDown('a')time.sleep(1)pyautogui.keyUp('a')elif rrrr == 1:pyautogui.keyDown('d')time.sleep(1)pyautogui.keyUp('d')elif rrrr == 2:pyautogui.keyUp('w')pyautogui.keyDown('d')pyautogui.keyDown('s')time.sleep(1)pyautogui.keyUp('s')pyautogui.keyUp('d')time.sleep(2)pyautogui.keyDown('w')elif rrrr == 4:pyautogui.keyUp('w')pyautogui.keyDown('a')pyautogui.keyDown('s')time.sleep(1)pyautogui.keyUp('s')pyautogui.keyUp('a')time.sleep(2)pyautogui.keyDown('w')time.sleep(0.2)pyautogui.keyUp('w')else:state.计次_移动停滞 -=1if state.计次_移动停滞 <= 0:state.计次_移动停滞=0state.计数_卡主次数=0if state.计次_识别次数 >= 8:result_old = result.copy()state.计次_识别次数=0if state.开关_是否展预测结果:#cv2.polylines(big_img, result['dst_pot'], True, (0, 255, 0), 5)# 获取小图的宽度和高度small_height, small_width, small_channels = best_rotated_template.shapebest_rotated_template = cv2.resize(best_rotated_template,(int(small_width * 2), int(small_height * 2)))small_height, small_width, small_channels = best_rotated_template.shape# 计算放置小图像的左上角坐标,使其居中在result位置x = state.游戏_当前坐标[0] - int(small_width / 2)y = state.游戏_当前坐标[1] - int(small_height / 2)# 将小图叠加到大图上for c in range(small_channels):big_img[y:y + small_height, x:x + small_width, c] = \best_rotated_template[:, :, c] * (best_rotated_template[:, :, 3] / 255.0) + \big_img[y:y + small_height, x:x + small_width, c] * (1.0 - best_rotated_template[:, :, 3] / 255.0)self.draw_arrow( big_img, state.游戏_当前坐标[0] , state.游戏_当前坐标[1], length=200, angle=state.游戏_当前视野角度)if state.状态_寻路中:#如果刚刚打完怪.需要回到原位if state.游戏_打怪前坐标!=[0,0]:state.游戏_当前目标坐标= copy.copy(state.游戏_打怪前坐标)try:if abs(state.游戏_当前坐标[0] - state.游戏_打怪前坐标[0]) <= 55 and abs(state.游戏_当前坐标[1] - state.游戏_打怪前坐标[1]) <= 55:state.游戏_打怪前坐标 = [0, 0]logger.info("回到之前的位置了")continueexcept:state.游戏_打怪前坐标 = [0, 0]logger.info("回到之前的位置了")continueelse:state.游戏_当前目标坐标,interest_coordinate = self.find_nearest_coordinate(self.map_matrix_copy, state.游戏_当前坐标,50,distance_min=8)if state.游戏_当前目标坐标 == None:state.游戏_当前目标坐标,interest_coordinate = self.find_nearest_coordinate(self.map_matrix_copy,state.游戏_当前坐标, None, distance_min=8)if state.游戏_当前目标坐标 == None:state.游戏_当前目标坐标 = copy.copy(self.old_point)if self.old_point!=[0,0] and state.游戏_当前目标坐标!=[0,0]:if abs(state.游戏_当前坐标[0]-self.old_point[0])>=70 or abs(state.游戏_当前坐标[1]-self.old_point[1])>=70:state.游戏_当前坐标=copy.copy(self.old_point )logger.info("定位误差过大!!")state.计次_误差过大+=1else:state.计次_误差过大=0if state.计次_误差过大==0:self.old_point = copy.copy(state.游戏_当前坐标)if interest_coordinate!=None:interest_coordinate2=copy.copy(interest_coordinate)if interest_coordinate2!=None:if abs(state.游戏_当前坐标[0] - interest_coordinate2[0]) <= 5 and abs(state.游戏_当前坐标[1] - interest_coordinate2[1]) <= 5:logger.info("附近有兴趣点!")state.计时_未寻路 = int(time.time())state.状态_寻路中 = Falsestate.游戏_打怪前坐标 = copy.copy(state.游戏_当前目标坐标)state.计数_没找到任何目标 = 0state.计数_没找到怪物 = 0pyautogui.keyUp('w')state.计数_卡主次数 = 0state.计次_识别次数 = 0mouse_left_down()mouse_left_up()if wakuang or caiji:logger.info("在连招中....")pyautogui.keyDown('x')time.sleep(0.2)pyautogui.keyUp('x')state.状态_是否回放中 = Truetangbaowss.send_msg(" 脚本执行#@@#2")time.sleep(1)self.remove_coordinates_in_range_map(self.map_matrix_copy, interest_coordinate2, 5,state.游戏_当前目标坐标)if self.map_matrix_copy[interest_coordinate2[0]][interest_coordinate2[1]]==-1:interest_coordinate=Noneinterest_coordinate2=Noneself.map_matrix_copy = self.remove_coordinates_in_range_map(self.map_matrix_copy,state.游戏_当前坐标,8, state.游戏_当前目标坐标)if state.游戏_当前目标坐标 == None:for _ in range(30):if not state.状态_寻路中:breaksmall_img = screenshot(hwnd, left, top, right, bottom, filename=None) # RGBA# 识别位置和方向state.游戏_当前视野角度 = get_view(small_img[int(height / 2) - 70:int(height / 2) + 70,int(width / 2) - 70:int(width / 2) + 70], 70)rrr=setangle(state.游戏_当前视野角度, self.get_next_angle(state.游戏_当前坐标, self.end))if rrr==False:breakelse:for _ in range(30):if not state.状态_寻路中:breaksmall_img = screenshot(hwnd, left, top, right, bottom, filename=None) # RGBA# 识别位置和方向state.游戏_当前视野角度 = get_view(small_img[int(height / 2) - 70:int(height / 2) + 70,int(width / 2) - 70:int(width / 2) + 70], 70)rrr=setangle(state.游戏_当前视野角度, self.get_next_angle(state.游戏_当前坐标, state.游戏_当前目标坐标))if rrr==False:breakpyautogui.keyDown('w')if state.开关_是否展预测结果:cv2.circle(big_img, state.游戏_当前目标坐标, 10, (255, 24, 255), -1)if big_img.shape[2] == 4:state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)else:state.图片_找图 = big_imgstate.QT_信号.mysig_show_xunlu.emit()# # 显示匹配结果# cv2.imshow('Genshin navigation', big_img)#print(f"识别时间:{time.time() - old_time}")breakexcept :time.sleep(0.3)state.计数_卡主次数 += 1state.计次_定位失败 += 1if state.计次_定位失败 >= 15:pyautogui.keyDown('shift')time.sleep(0.3)pyautogui.keyUp('shift')roi = None#traceback.print_exc()def update_map_show(self,big_img):for rdex, rowitem in enumerate(self.map_matrix_copy):for cdex, column in enumerate(rowitem):# 只判断符合范围内的 并且是 >1则是路线范围的坐标if column > 0:point=(rdex,cdex)if column==1:cv2.circle(big_img, point, 2, (255, 0, 0), -1)elif column==2:cv2.circle(big_img, point, 4, (0, 255, 0), -1)elif column==3:cv2.circle(big_img, point, 4, (0, 0, 255), -1)def quickening(self,quickening_keys=None):''':param quickening_keys: 用来随机赶路的按钮 比如 [{"probability":0.1,"keys":[ {"key":"1","time":0.2 }, {"key":"e","time":0.5}]}] 代表概率10% 按1 0.2秒 然后 按E 1秒 支持无数组合:return:'''if quickening_keys==None:return Falsefor quickening_key in quickening_keys:if random.randint(1,100)<quickening_key["probability"]*100:for key in quickening_key["keys"]:if state.状态_循环开关== False:logger.info("强制退出!")pyautogui.keyUp('w')return Falselogger.info(f"触发赶路按键:{key['key']}")pyautogui.keyDown(key["key"])time.sleep(key["time"])pyautogui.keyUp(key["key"])time.sleep(0.1)return Truedef get_next_angle(self, start_pos, next_pos):# 计算方向向量direction_x = next_pos[0] - start_pos[0]direction_y = next_pos[1] - start_pos[1]# 使用反正切函数计算弧度角度值angle_rad = math.atan2(direction_y, direction_x)# 将弧度角度值转换为以北方向为0度的角度angle_deg = math.degrees(angle_rad)# 将角度值转换为顺时针方向angle_deg = int((angle_deg + 360 + 90) % 360)return angle_degdef find_nearest_coordinate(self,coordinates, my_position,range_limit=100,distance_min=0):'''寻找当前位置最近的坐标点 顺便返回最近的兴趣点:param coordinates::param my_position::param range_limit: # 搜索范围半径为50 提高效率:return:'''min_distance_coordinate = float('inf')nearest_coordinate =Noneinterest_coordinate=Nonefor rdex,rowitem in enumerate(coordinates) :for cdex,column in enumerate(rowitem):#只判断符合范围内的 并且是 >1则是路线范围的坐标if column>0:#x 附近 range_limit 范围的点 y 附近 range_limit 范围的点if range_limit!=None:if abs(my_position[0]-rdex)<=range_limit and abs(my_position[1]-cdex)<=range_limit :#计算两个坐标点之间的距离 但是不要判断和自己一样的坐标.那没意义if (my_position[0],my_position[1])!=(rdex,cdex):distance = math.sqrt((my_position[0] - rdex) ** 2 + (my_position[1] - cdex) ** 2)if distance>=distance_min:#要比最近小距离大.这样走位才不会老歪歪扭扭if distance < min_distance_coordinate:min_distance_coordinate = distanceif column==2:interest_coordinate=[rdex,cdex]nearest_coordinate = [rdex,cdex]else:# 计算两个坐标点之间的距离 但是不要判断和自己一样的坐标.那没意义if (my_position[0], my_position[1]) != (rdex, cdex):distance = math.sqrt((my_position[0] - rdex) ** 2 + (my_position[1] - cdex) ** 2)if distance >= distance_min: # 要比最近小距离大.这样走位才不会老歪歪扭扭if distance < min_distance_coordinate:min_distance_coordinate = distanceif column == 2:interest_coordinate = [rdex, cdex]nearest_coordinate = [rdex, cdex]return nearest_coordinate,interest_coordinatedef on_release(self, key: keyboard.KeyCode):"""定义释放时候的响应"""if key == Key.f9:#停止state.状态_循环开关= Falsestate.状态_全局暂停=Truestate.状态_已经有寻路了 = Falsestate.状态_是否禁止录制=Trueself.on_rec = Falseelif key == Key.f8:self.on_rec = Falseif state.状态_是否禁止录制:if state.状态_循环开关== False:returnstate.状态_全局暂停=not state.状态_全局暂停logger.info(f"state.状态_全局暂停{state.状态_全局暂停}")if state.状态_全局暂停:time.sleep(0.3)pyautogui.keyUp('w')elif key ==Key.f10:if self.isRunRec:self.interest_point.append(self.now_point)logger.info(f"标记坐标{self.now_point}")def load_map(self, image_path):'''加载地图 这个地图要障碍物地图:param image_path::return:'''logger.info("加载地图中...")# 读取图片self.image = Image.open(image_path)# 获取图片尺寸self.width, self.height = self.image.size# 定义地图self.map_matrix = []# 根据像素颜色生成地图数据for x in range(self.width):row=[]for y in range(self.height):pixel = self.image.getpixel((x, y))if pixel[2] >= 240 and pixel[0] < 20 and pixel[1] < 20: # 蓝色row.append(1)elif pixel[0] >= 240 and pixel[1] < 20 and pixel[2] < 20: # 红色self.end = (x, y)row.append(3)elif pixel[1] >= 240 and pixel[0] < 20 and pixel[2] < 20: # 绿色row.append(2)else:row.append(0)self.map_matrix.append(row)logger.info("地图加载完毕!")def get_window_handle_at_mouse_position(self, ):active_hwnd = ctypes.windll.user32.GetForegroundWindow()return active_hwnddef remove_coordinates_in_range_map(self, coordinates, center, range_limit,nex_pos):new_coordinates=coordinates.copy()for rdex, rowitem in enumerate(new_coordinates):for cdex, column in enumerate(rowitem):# 只判断符合范围内的 并且是 >1则是路线范围的坐标if column > 0:if (rdex,cdex) == self.end: # 终点不能删除continue#将附近的点标记为走过了elif abs(center[0]-rdex)<=range_limit and abs(center[1]-cdex)<=range_limit:new_coordinates[rdex][cdex]=-1# 将该点标记为已经走过的,标记为-1elif abs(center[0] - rdex) <= range_limit*2 and abs(center[1] - cdex) <= range_limit*2:angle_target = self.get_next_angle(center, nex_pos) # 当前位置对准兴趣点的角度angle_coordinate = self.get_next_angle(center, (rdex,cdex)) # 当前成员位置对准结束点的角度angle_diff = abs(angle_target - angle_coordinate)if angle_diff > 160 and angle_diff<=220 :new_coordinates[rdex][cdex]=-1# 将该点标记为已经走过的,标记为-1return new_coordinatesdef draw_arrow(self,img, x, y, length=50, angle=45):# 箭头的长度和角度(以北方向为0度,顺时针为正方向)# 将角度转换为以北方向为0度的角度degrees = (450 - angle) % 360# 计算箭头末端的坐标end_x = int(x + length * np.cos(np.radians(degrees)))end_y = int(y - length * np.sin(np.radians(degrees)))# 在图像上画线段cv2.line(img, (x, y), (end_x, end_y), (0, 0,255), 5)# 画箭头头部theta = np.radians(degrees)arrow_length = 10arrow_angle = np.radians(30)# 箭头头部的两个端点arrow_pt1 = (end_x - int(arrow_length * np.cos(theta + arrow_angle)),end_y + int(arrow_length * np.sin(theta + arrow_angle)))arrow_pt2 = (end_x - int(arrow_length * np.cos(theta - arrow_angle)),end_y + int(arrow_length * np.sin(theta - arrow_angle)))# 画箭头头部cv2.line(img, (end_x, end_y), arrow_pt1, (0, 0,255), 5)cv2.line(img, (end_x, end_y), arrow_pt2, (0, 0,255), 5)return img
四、添加ui
vi main.py
if __name__ == '__main__':....app = QApplication(sys.argv) # 初始化Qt应用ratio = screen_width / 2560 # 分辨率比例# 设置全局字体大小# 计算字体大小base_font_size = 13# 基准字体大小,适合1920*1080分辨率new_font_size = int(base_font_size * ratio)font = QFont("Arial", new_font_size)# 子控件的宽度item_width=320item_height=240item_height_min = 60window_main = MainWindow()window_main.show()sys.exit(app.exec_()) # 监听消息不关闭
ui略多下章看