原作者在1.85版本的时候将代码重构了,有很多步骤发生了变更,重新瞅瞅
一、main入口函数 (不带事件循环及ui)
和第一章一样,先取main,然后去找对应的函数在哪里,是做什么的
import ctypes
import os
import subprocess
import sys
import time
import traceback
import webbrowser
import 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_position
if __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.kernel32
kernel32.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_LAYERED
win32gui.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.user32
user32.SetProcessDPIAware()
dpi = user32.GetDpiForSystem()
# 计算缩放比例
scale = round(dpi / 96.0, 2)
return scale
except 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 = 0
while 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_child
else:
return 0
def get_windows_screen_scale():
""" 调用 Windows API 函数获取缩放比例 """
try:
user32 = ctypes.windll.user32
user32.SetProcessDPIAware()
dpi = user32.GetDpiForSystem()
# 计算缩放比例
scale = round(dpi / 96.0, 2)
return scale
except 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 = True
angle_diff = abs(angle_now - angle_target)
if angle_diff <= fault:
ret = False
if ret == False:
return False
angle_diff = int((angle_target - angle_now) % 360)
if angle_diff > 180:
fangxiang = -1
angle_diff = 360 - angle_diff
else:
fangxiang = 1
if 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_SHOW
else:
win32gui.ShowWindow(hwnd, 0) # 0对应SW_HIDE
3、检查版本更新
lanrenauto/jianchaupdate.py
import html
import requests
def 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 False
except:
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、全局变量
# -*- 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=800
CMD_HEIGHT = 400
if CMD_HEIGHT<100:
CMD_WIDTH = 800
CMD_HEIGHT = 400
ON_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 YolovYuanShen
ysyl = 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.状态_开伞时间=__t
logger.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.计次_误差过大 += 1
state.状态_开伞时间=-1
class YolovYuanShen():
def __init__(self, weights, providers=["CUDAExecutionProvider",'CPUExecutionProvider'],dic_labels={},model_h=320, model_w=320):
pyautogui.PAUSE = 0.01
pyautogui.FAILSAFE = False
self.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 = True
self.小地图区域 = (87 - 10, 45 - 10, 246 + 10, 204 + 10)
state.状态_YOLOV = True
def run_fight(self,wakuang=False,caiji=False,daguai=False):
global loop
state.计时_未寻路 = 0
# 获取窗口句柄
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
while True:
if state.状态_循环开关 == False:
logger.info("强制退出!")
pyautogui.keyUp('w')
state.状态_是否回放中 = False
state.计数_没找到任何目标 = 0
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_检测中=False
return
if get_window_handle_at_mouse_position() == hwnd:
break
state.计数_没找到任何目标 = 0
state.计数_没找到怪物 = 0
state.状态_在爬墙 = False
state.状态_检测中 = True
state.状态_开伞时间 = -1
已经_F了=False
历史_上次玩家高度 = 0.1
玩家高度 = 0.1
计数_没有玩家 = 0
isIn_wxl = False
set_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.状态_是否回放中 = False
state.计数_没找到任何目标 = 0
state.状态_检测中 = False
tangbaowss.send_msg( "是否回放#@@#假")
return
time.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_p
p_top = rect[1] + h_p
计数_没有玩家 += 1
if state.状态_寻路中 == False:
state.计数_没找到任何目标 += 1
state.计数_没找到怪物 += 1
state.状态_在爬墙 = False
if state.状态_已经有寻路了 == False or state.状态_检测中==False:
state.状态_是否回放中 = False
state.计数_没找到任何目标 = 0
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_检测中 = False
return
if state.状态_全局暂停:
tangbaowss.send_msg( "是否回放#@@#假")
time.sleep(2)
continue
if get_window_handle_at_mouse_position() != hwnd:
tangbaowss.send_msg( "是否回放#@@#假")
time.sleep(0.1)
# 激活hwnd
hwnd = 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)
continue
datas, 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)
continue
if datas == []:
if state.状态_是否回放中 == False:
if state.状态_在爬墙 == False and state.状态_寻路中 == True:
计数_没有玩家 = 0
ttt = threading.Thread(target=开伞)
if state.python_var > 9:
ttt.daemon = True
else:
ttt.setDaemon(True)
ttt.start()
else:
# 将离屏幕中心最近的点排在前面
datas = sorted(datas, key=self.distance_to_target)
isIn, idex = find_dict_index(datas, 'label', '往上跳')
if isIn:
state.状态_在爬墙 = True
else:
state.状态_在爬墙 = False
isIn_wxl, _ = find_dict_index(datas, 'label', '往下落')
isIn, idex = find_dict_index(datas, 'label', '被控了')
if isIn:
logger.info("被控了!")
pyautogui.press('space')
pyautogui.press('space')
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
continue
isIn_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了 =True
state.计次_识别次数 = 0
state.计数_卡主次数 = 0
logger.info(f"顺手捡采集物!{datas[idex]}")
state.状态_有目标 = True
pyautogui.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.计次_识别次数 = 0
state.计数_卡主次数 = 0
ret_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 = False
isIn, idex = find_dict_index(datas, 'label', '在水面')
if isIn:
logger.info("在水面!")
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_寻路中 = True
self.key_w_down = True
if 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.状态_寻路中 = False
state.状态_有目标 = True
state.状态_是否回放中 = False
if self.key_w_down:
self.key_w_down = False
pyautogui.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.计数_卡主次数 = 0
state.计次_识别次数 = 0
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:
if state.状态_寻路中:
state.状态_寻路中 = False
state.状态_是否回放中 = False
if self.key_w_down:
self.key_w_down = False
pyautogui.keyUp('w')
# 判断是否死亡
ret_sw = self.find_zhuyidiren(hwnd, left, top, right, bottom, p_left, p_top)
state.状态_有目标 = True
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')
state.计数_卡主次数 = 0
state.计次_识别次数 = 0
tangbaowss.send_msg( "是否暂停#@@#假")
if ret_sw:
state.计次_移动停滞 = 1200
state.计次_定位失败 = 1200
state.状态_是否回放中 = False
state.计数_没找到任何目标 = 0
logger.info("全军覆没了!跳过该任务!")
pyautogui.keyDown('esc')
time.sleep(0.1)
pyautogui.keyUp('esc')
state.状态_检测中 = False
return
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 isIn:
logger.info("在派蒙界面,点返回")
pyautogui.keyUp('w')
self.key_w_down = False
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
time.sleep(2)
pyautogui.keyDown('shift')
time.sleep(0.2)
pyautogui.keyUp('shift')
continue
isIn, idex = find_dict_index(datas, 'label', '玩家')
if isIn:
玩家高度 = datas[idex]["points"][1][1] - datas[idex]["points"][0][1]
计数_没有玩家 = 0
isIn, idex = find_dict_index(datas, 'label', '关闭')
if isIn:
if state.状态_寻路中:
state.状态_寻路中 = False
state.状态_有目标 = True
logger.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 = False
ret_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.计数_卡主次数 = 0
state.计次_识别次数 = 0
break
# 发现怪物则进行视角移动 以及 接近目标
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:
continue
state.计数_没找到任何目标 = 0
state.计数_没找到怪物 = 0
# 如果怪物就再旁边则直接连招
if self.centre_point[1] - data["points"][1][1] <= 20:
if self.key_w_down:
self.key_w_down = False
pyautogui.keyUp('w')
state.计数_卡主次数 = 0
state.计次_识别次数 = 0
if not state.状态_是否回放中:
mouse_moveR(0, 800)
logger.info("在连招中....")
pyautogui.keyDown('x')
time.sleep(0.2)
pyautogui.keyUp('x')
state.状态_是否回放中 = True
tangbaowss.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 = True
break # 只判断第一只怪
elif data["label"] == "矿石":
if not wakuang:
continue
state.计数_没找到任何目标 = 0
state.计数_没找到怪物 = 0
set_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 = False
time.sleep(0.2)
pyautogui.keyUp('w')
logger.info("在挖矿中....")
state.计数_卡主次数 = 0
state.计次_识别次数 = 0
if not state.状态_是否回放中:
logger.info("在挖矿中....")
pyautogui.press("x")
state.状态_是否回放中 = True
tangbaowss.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 = True
break # 只判断第1个矿
else: # '发现采集物'或 "掉落物"
state.计数_没找到任何目标 = 0
angle_now = 0
angle_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 = True
if r_caiji==False:
pyautogui.keyDown('shift')
time.sleep(0.2)
pyautogui.keyUp('shift')
break # 只判断第一个采集物
else:
if data["sim"]>=0.6:
if state.状态_在爬墙==True:
continue
if data["label"] == "矿石":
if not wakuang:
continue
logger.info("发现有矿石.顺手挖一下吧!")
已经_F了 = False
elif data["label"] == "怪物" and data["points"][1][
0] - data["points"][0][0] >= 90:
if not daguai:
continue
已经_F了 = False
logger.info("发现有怪拦路.顺手消灭吧!")
elif data["label"] == "采集物":
if not caiji:
continue
已经_F了 = False
logger.info("发现有采集物.顺手捡一下吧!")
elif data["label"] =="掉落物":
if daguai or wakuang or caiji:
已经_F了 = False
logger.info("发现有掉落物.顺手捡一下吧!")
else:
continue
else:
continue
set_angle(0, get_angle(self.centre_point, (data["x"], data["y"])))
state.计时_未寻路 = int(time.time())
state.状态_寻路中 = False
state.游戏_打怪前坐标 = copy.copy(state.游戏_当前目标坐标)
state.计数_没找到任何目标 = 0
state.计数_没找到怪物 = 0
pyautogui.keyUp('w')
self.key_w_down = False
state.计数_卡主次数 = 0
state.计次_识别次数 = 0
mouse_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):
计数_没有玩家 = 0
ttt = threading.Thread(target=开伞)
if state.python_var > 9:
ttt.daemon = True
else:
ttt.setDaemon(True)
ttt.start()
if 计数_没有玩家 == 0:
历史_上次玩家高度 = copy.copy(玩家高度)
if state.状态_寻路中 == False:
# 如果按下了W键了,并且当前没有任何目标等于4次了,则松开w键
if state.计数_没找到任何目标 == 6:
self.key_w_down = False
state.计次_识别次数 = 0
state.计数_卡主次数 = 0
pyautogui.keyUp('w')
if state.计次_定位失败 > 0:
state.计数_没找到怪物 = 0
if state.ON_LIANZHAOBUJIANCE==0 and wakuang==False and state.计数_没找到怪物 >= 25 and state.计次_定位失败 == 0 and state.状态_是否回放中 == True:
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
# 如果连续8次没找到目标,则进行旋转
if 20 > state.计数_没找到任何目标 >= 15 or (
state.计数_没找到任何目标 >= 1 and state.计数_没找到怪物 >= 10 and state.状态_是否回放中 == True):
state.计数_卡主次数 = 0
state.计次_识别次数 = 0
历史_上次玩家高度 = 0
if state.计数_没找到任何目标 == 15:
if state.计次_定位失败 == 0:
if state.状态_是否回放中 == False:
logger.info("重置视角")
pyautogui.keyUp('w')
self.key_w_down = False
pyautogui.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.计次_识别次数 = 0
state.计数_卡主次数 = 0
else:
state.计数_没找到任何目标 = 0
else:
state.计数_没找到任何目标 = 0
else:
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次没找到目标,则继续寻路X
elif state.计数_没找到任何目标 >= 20:
if not state.状态_是否回放中:
logger.info("附近没有怪和采集物了.继续寻路吧!")
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_寻路中 = True
state.计次_识别次数 = 0
state.计数_卡主次数 = 0
state.计数_没找到任何目标 = 0
mouse_left_up()
pyautogui.keyUp('w')
continue
time_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.状态_检测中 = False
state.状态_已经有寻路了 = False
state.状态_需重新传送 = False
state.计次_移动停滞 = 1200
state.计次_定位失败 = 1200
state.状态_是否回放中 = False
state.计数_没找到任何目标 = 0
return False
elif kkk == -1:
logger.info("全军覆没了!")
time.sleep(3)
state.计次_移动停滞 = 1200
state.计次_定位失败 = 1200
state.状态_是否回放中 = False
state.计数_没找到任何目标 = 0
state.状态_检测中 = False
state.状态_已经有寻路了 = False
state.状态_需重新传送 = True
return False
if state.计时_未寻路 != 0:
logger.info("附近可能还有怪,但是打不到.继续寻路吧!")
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_寻路中 = True
state.计数_卡主次数 = 0
mouse_left_up()
pyautogui.keyUp('w')
state.计数_没找到任何目标 = 0
state.游戏_打怪前坐标 = [0, 0]
time.sleep(5)
else:
state.状态_是否回放中 = False
tangbaowss.send_msg("是否回放#@@#假")
state.状态_寻路中 = True
state.计数_卡主次数 = 0
mouse_left_up()
pyautogui.keyUp('w')
state.计数_没找到任何目标 = 0
state.游戏_打怪前坐标 = [0, 0]
continue
elif 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.状态_是否回放中 = True
tangbaowss.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.状态_是否回放中 = True
tangbaowss.send_msg( "脚本执行#@@#2")
except:
time.sleep(0.5)
logger.error(traceback.format_exc())
def run_fuben(self, cishu=99, isFanxiang=False):
global loop
state.状态_已经有寻路了 = True
# 获取窗口句柄
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
set_window_activate(hwnd)
while True:
if state.状态_循环开关 == False:
logger.info("强制退出!")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_需重新传送 = False
return False
if get_window_handle_at_mouse_position() == hwnd:
break
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
p_top = rect[1] + h_p
paimeng_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.状态_需重新传送 = False
state.状态_已经有寻路了 = False
return False
elif kkk == -1:
logger.info("全军覆没了!")
time.sleep(3)
state.状态_需重新传送 = True
state.状态_已经有寻路了 = False
return False
计数_没有玩家 = 0
jiaoben = (os.path.abspath(os.path.join(state.PATH_JIAOBEN, state.LIANZHAO)))
# 临时发送消息给指定客户端
message = f"解析脚本#@@#{jiaoben}"
tangbaowss.send_msg(message)
try:
# 往前走或者往后走,直到找到 F
if 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.状态_已经有寻路了 = False
logger.info("没体力了 成功退出副本!")
time.sleep(10)
state.状态_需重新传送 = False
return 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.状态_需重新传送 = True
state.状态_已经有寻路了 = False
return False
# 开始yolov自动进入副本
except:
time.sleep(0.5)
logger.error(traceback.format_exc())
state.状态_需重新传送 = True
state.状态_已经有寻路了 = False
return False
# 0 镜头对准北方向 走过去开副本
# 1 开始打怪阶段
# 2 打完了 开始找生命树 并且把自己移动到0° 对准生命树的位置
# 3 走路到 领取奖励 直到点击 继续挑战为止 完成一轮
阶段 = 0
计次_阶段0 = 0
计时_阶段3_开始时间 = 0
state.计数_没找到怪物=0
isinF = False
for i in range(cishu):
logger.info(f"刷副本中 {i + 1}/{cishu}")
while True:
try:
time.sleep(0.01)
if state.状态_循环开关 == False:
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_需重新传送 = False
state.状态_已经有寻路了 = False
return False
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
p_top = rect[1] + h_p
if state.状态_全局暂停:
tangbaowss.send_msg( "是否回放#@@#假")
time.sleep(2)
continue
if get_window_handle_at_mouse_position() != hwnd:
# 激活hwnd
hwnd = 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_small
width_small = right_small - left_small
small_img = screenshot(hwnd, left_small, top_small, right_small, bottom_small,
filename=None) # RGBA
if type(small_img) == bool:
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
time.sleep(2)
continue
state.游戏_当前视野角度 = 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)
continue
if datas == []:
pass
else:
# 将离屏幕中心最近的点排在前面
datas = sorted(datas, key=self.distance_to_target)
if 阶段 == 0: # 0 镜头对准北方向 走过去开副本
计次_阶段0 += 1
if 计次_阶段0 >= 2000:
pyautogui.keyDown('esc')
time.sleep(0.1)
pyautogui.keyUp('esc')
time.sleep(1)
state.状态_已经有寻路了 = False
logger.info("没体力了 成功退出副本!")
time.sleep(10)
state.状态_需重新传送 = False
return True
isIn, 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)
continue
if self.click_dimaiyichang(hwnd, left, top, right, bottom, p_left, p_top):
time.sleep(1)
continue
if 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): continue
pyautogui.keyUp("w")
阶段 = 1
isIn, 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.状态_已经有寻路了 = False
logger.info("没体力了 成功退出副本!")
time.sleep(10)
state.状态_需重新传送 = True
return True
elif 阶段 == 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 = False
pyautogui.keyUp('w')
mouse_left_up()
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("打完了")
time.sleep(2)
# 重置镜头
mouse_middle_down()
time.sleep(0.1)
mouse_middle_up()
阶段 = 2
continue
else:
self.key_w_down = False
pyautogui.keyUp('w')
mouse_left_up()
state.状态_是否回放中 = False
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("打完了")
time.sleep(2)
# 重置镜头
mouse_middle_down()
time.sleep(0.1)
mouse_middle_up()
阶段 = 2
continue
isIn, 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.计数_没找到怪物+=1
if random.randint(1, 50) == 2:
mouse_middle_down()
time.sleep(0.2)
mouse_middle_up()
if state.计数_没找到怪物>=20:
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_是否回放中 = False
pyautogui.keyUp('w')
self.key_w_down=False
mouse_moveR(int(6 * 120), 0)
time.sleep(0.1)
if template_matching(ret_scr, paimeng_img, mask=None) != []:
logger.info("全军覆没了!跳重试该任务!")
state.状态_需重新传送 = True
state.状态_已经有寻路了 = False
return False
else:
state.计数_没找到怪物=0
for 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 = False
pyautogui.keyUp('w')
if not state.状态_是否回放中:
logger.info("在连招中....")
pyautogui.press("x")
state.状态_是否回放中 = True
tangbaowss.send_msg( "脚本执行#@@#2")
else:
# 不在则继续跟着怪物方向走
pyautogui.keyDown('w')
self.key_w_down = True
break
elif 阶段 == 2: # 2 打完了 开始找生命树 并且把自己移动到0° 对准生命树的位置
调整视角 = set_angle(state.游戏_当前视野角度, 90, fault=6)
发现了目标了 = False
isIn, idex = find_dict_index(datas, 'label', '生命树')
for data in datas:
if data["label"] == "生命树":
发现了目标了 = True
x_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()
break
logger.info("树正对 但是角度还不对")
elif data["label"] == "副本楼梯":
发现了目标了 = True
x_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()
break
logger.info("副本楼梯正对 但是角度还不对")
elif data["label"] == "副本门框" and isIn == False:
发现了目标了 = True
x_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:
continue
if 发现了目标了 == 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 = True
continue
isIn, idex = find_dict_index(datas, 'label', '捡东西')
if isIn: # 路过的顺手捡了
logger.info("发现 F 交互")
pyautogui.keyDown('f')
time.sleep(0.1)
pyautogui.keyUp('f')
isinF = True
continue
isIn, 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)
continue
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)
continue
isIn, 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.状态_已经有寻路了 = False
logger.info("没体力了 成功退出副本!")
time.sleep(10)
state.状态_需重新传送 = False
return True
else:
isinF = False
阶段 = 0
break
else:
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.状态_已经有寻路了 = False
logger.info("没体力了 成功退出副本!")
time.sleep(10)
state.状态_需重新传送 = False
return True
else:
isinF = False
阶段 = 0
break
if 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.状态_已经有寻路了 = False
logger.info("刷副本全部完成!")
def run_chuansongjiaoben(self, jiaoben,cbb_address="手动录制"):
global new_msg
try:
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
if state.状态_循环开关 == False:
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("强制退出!")
return False
if 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]) - 1080
p_left = rect[0] + w_p
p_top = rect[1] + h_p
set_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.状态_需重新传送 = False
state.状态_已经有寻路了 = False
return False
elif kkk == -1:
logger.info("全军覆没了!")
time.sleep(3)
state.状态_需重新传送 = True
state.状态_已经有寻路了 = False
return False
# 临时发送消息给指定客户端
message = f"解析脚本#@@#{jiaoben}"
tangbaowss.send_msg(message)
time.sleep(2)
state.状态_是否回放中 = True
tangbaowss.send_msg( "脚本执行#@@#1")
while True:
try:
time.sleep(0.1)
if state.状态_是否回放中 == False:
state.状态_需重新传送 = False
return True
if state.状态_循环开关 == False:
state.状态_需重新传送 = False
tangbaowss.send_msg( "是否回放#@@#假")
return False
if get_window_handle_at_mouse_position() != hwnd:
# 激活hwnd
hwnd = 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.状态_需重新传送 = False
tangbaowss.send_msg( "是否回放#@@#假")
return False
def 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_p
p_top = rect[1] + h_p
set_window_activate(hwnd)
if state.状态_循环开关 == False:
logger.info("强制退出!")
state.状态_已经有寻路了 = False
return False
message = f"解析脚本#@@#{jiaoben}"
tangbaowss.send_msg(message)
time.sleep(2)
tangbaowss.send_msg( "脚本执行#@@#1")
state.状态_是否回放中 = True
try:
while True:
try:
time.sleep(0.1)
if state.状态_是否回放中 == False:
state.状态_需重新传送 = False
state.状态_已经有寻路了 = False
logger.info("脚本运行完毕!")
return True
if state.状态_循环开关 == False:
state.状态_需重新传送 = False
state.状态_已经有寻路了 = False
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("强制退出!")
return False
if get_window_handle_at_mouse_position() != hwnd:
# 激活hwnd
hwnd = 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.状态_有目标 = True
pyautogui.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.状态_需重新传送 = False
state.状态_已经有寻路了 = False
tangbaowss.send_msg( "是否回放#@@#假")
return False
def run_jixing(self):
'''
领取纪行奖励
:return:
'''
global new_msg
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
p_top = rect[1] + h_p
hwnd = 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');return
time.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(
"领取纪行任务经验失败");return
time.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(
"没有奖励能领取");return
time.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_msg
state.状态_检测中 = False
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
p_top = rect[1] + h_p
hwnd = 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.状态_检测中 = False
state.状态_已经有寻路了=False
state.状态_需重新传送 = False
return False
elif kkk == -1:
logger.info("全军覆没了!")
time.sleep(3)
state.状态_检测中 = False
state.状态_已经有寻路了 = False
state.状态_需重新传送 = True
return False
#按了后看有没有打开成功
n=0
while True:
if n==0:
n=0
kkk = self.处理异常情况(hwnd, left, top, right, bottom, p_left, p_top)
if kkk == 0:
logger.info("在意外界面 超时!")
time.sleep(3)
state.状态_检测中 = False
state.状态_已经有寻路了 = False
state.状态_需重新传送 = False
return False
elif kkk == -1:
logger.info("全军覆没了!")
time.sleep(3)
state.状态_检测中 = False
state.状态_已经有寻路了 = False
state.状态_需重新传送 = True
return False
pyautogui.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 = 0
time.sleep(1)
if state.状态_循环开关 == False:
state.状态_需重新传送 = False
state.状态_已经有寻路了 = False
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("强制退出!")
return False
if get_window_handle_at_mouse_position() != hwnd:
# 激活hwnd
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
set_window_activate(hwnd)
time.sleep(0.2)
if state.状态_全局暂停:
tangbaowss.send_msg( "是否回放#@@#假")
time.sleep(2)
continue
if self.click_kuaisubiandui(hwnd, left, top, right, bottom , p_left, p_top):
time.sleep(2)
break
n += 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):
break
logger.info("清空角色成功!")
# 循环选择角色
a = 0
for i,img in enumerate(img_datas):
ret =False
while not ret:
logger.info(str(a))
if state.状态_循环开关 == False:
state.状态_需重新传送 = False
state.状态_已经有寻路了 = False
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("强制退出!")
return False
if get_window_handle_at_mouse_position() != hwnd:
# 激活hwnd
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
set_window_activate(hwnd)
time.sleep(0.2)
if state.状态_全局暂停:
tangbaowss.send_msg( "是否回放#@@#假")
time.sleep(2)
continue
ret= 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)
break
if 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+=1
if a==11:
a=0
time.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.状态_需重新传送 = False
state.状态_已经有寻路了 = False
tangbaowss.send_msg( "是否回放#@@#假")
logger.info("强制退出!")
return False
pyautogui.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.状态_需重新传送 = False
state.状态_已经有寻路了 = False
return True
def 处理异常情况(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.状态_已经有寻路了 = False
state.状态_是否回放中 = False
state.状态_检测中=False
tangbaowss.send_msg( "是否回放#@@#假")
state.状态_需重新传送 = False
return 1
big_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.状态_检测中 = False
return -1
else:
if rrrr != []:
return 1
time.sleep(1)
return 0
def 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 True
else:
return False
def 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_duihuahuopengren
return 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_pianpianhua
return 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]) ** 2
angle = get_angle(self.centre_point, [d["x"], d["y"]])
if angle >= 300 or angle <= 60:
dist *= 999
if w < 60:
dist *= 0.1
return dist + w * h
if __name__ == '__main__':
while True:
time.sleep(0.0001)
pass
2、添加通讯模块
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_msg
try:
# 发送连接成功消息
await websocket.send("连接成功")
# 添加新连接的客户端到集合中
connected_clients.append(websocket)
while True:
# 接收客户端发送的消息
message = await websocket.recv()
new_msg = message
# print(f"收到消息:{message}")
# 处理接收到的消息
if message == "是否回放中 真":
state.状态_是否回放中 = True
elif message == "是否回放中 假":
state.状态_是否回放中 = False
elif message == "是否暂停 真":
state.状态_是否暂停 = True
elif message == "是否暂停 假":
state.状态_是否暂停 = False
elif message == "是否开始录制 假":
state.状态_是否开始录制 = False
elif message == "是否开始录制 真":
if state.状态_是否禁止录制 == False:
logger.info("请按F8结束录制")
state.QT_信号.mysig_tishi.emit(f"请按F8结束录制")
state.状态_是否开始录制 = True
elif message == "是否禁止录制 假":
state.状态_是否禁止录制 = False
elif message == "是否禁止录制 真":
state.状态_是否禁止录制 = True
elif 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.kernel32
kernel32.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:
pass
sys.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.78
import copy
import cv2
import os
import numpy as np
from ..moni.moni import *
from ..logger_module import logger
from demo.lanrenauto.tools.screenshot import screenshot #这里的demo是你的项目目录名
from demo import state
def 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,其他设为0
mask = np.zeros_like(alpha3)
mask[alpha3 >= 151] = 255
mask[alpha3 < 151] = 0
mask[alpha3 > 229] = 0
img = 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 = overlap
max_angle = angle
start_angle = max_angle - 10
if start_angle < 0:
start_angle = 0
end_angle = start_angle + 10
else:
end_angle = start_angle + 20
for 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 = overlap
max_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,其他设为0
mask = np.zeros_like(alpha3)
mask[alpha3 >= 9] = 255
mask[alpha3 < 9] = 0
mask[alpha3 > 190] = 0
img = 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 = overlap
max_angle = angle
start_angle = max_angle - 10
if start_angle < 0:
start_angle = 0
end_angle = start_angle + 10
else:
end_angle = start_angle + 20
for 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 = overlap
max_angle = angle
return 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 =1
best_angle = -1 # 最佳匹配角度
max_similarity = 0 # 最大相似度
best_res = 0
best_rotated_template = ""
h, w = template.shape[:2]
scaled_mask = mask.astype(np.uint8)
scaled_image = image
scaled_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 = similarity
best_angle = angle
best_res = res
best_rotated_template = rotated_template
best_angle_bk = copy.copy(best_angle)
start_angle = best_angle_bk - 10
if start_angle < 0:
start_angle = 0
end_angle = start_angle + 10
else:
end_angle = start_angle + 20
for 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 = similarity
best_angle = angle
best_res = res
best_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 = mask
scaled_image = img
scaled_template = template_img
if 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_loc
else:
top_left = max_loc
if max_val < threshold or max_val == np.inf:
break
middle_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:
break
cv2.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: 窗口偏移top
label: 输出的日志
times: 找图次数
step: 查找间隔
is_click: 是否点击
threshold: 最小相似度
keepKey: 需要一直按住哪个键? 不填 就不按 None
mask: 掩码
double_click: 是否双击
big_img: None 自动截图 非None 则提供大图np图 提供大图为了保证点击成功 left 和 top也要准确给
is_show: False 是否展示
Returns:
'''
for _ in range(times):
if not state.状态_循环开关:
return False
if 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_img
state.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 True
if times > 1:
time.sleep(step)
big_img=None
return 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 = None
ret = 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 True
if times > 1:
time.sleep(step)
return False
def 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 = roi
roi_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 None
kp_src, des_src = sift.detectAndCompute(g2, None)
if len(kp_src) < min_match_count:
return None
result = []
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:
break
sch_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:
break
pypts = []
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]) // 4
Y = (pypts[0][1] + pypts[1][1] + pypts[2][1] + pypts[3][1]) // 4
middle_point = (X, Y)
if 0 > middle_point[0] > w or 0 > middle_point[1] > h:
continue
result.append(dict(
result=middle_point,
rectangle=pypts,
confidence=sim,
dst_pot=[np.int32(dst)]
))
if maxcnt and len(result) >= maxcnt:
break
qindexes, 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 result
except:
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: 窗口偏移top
label: 输出的日志
times: 找图次数
step: 查找间隔
is_click: 是否点击
threshold: 最小相似度
keepKey: 需要一直按住哪个键? 不填 就不按 None
roi: 感兴趣的区域 默认为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 False
if 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:
continue
result_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_img
state.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 True
if times > 1:
time.sleep(step)
big_img=None
return 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: 窗口偏移top
label: 输出的日志
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 True
if 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 = 0
index = None
for i, dictionary in enumerate(dict_list):
if dictionary.get(key) == value:
if dictionary.get('sim') > max_sim:
max_sim = dictionary.get('sim')
index = i
return index != None, index
def find_dict_num(dict_list, key, value):
'''
列表中 字典里指定key 存在某个值的次数
:param dict_list:
:param key:
:param value:
:return:
'''
num_ = 0
for i, dictionary in enumerate(dict_list):
if dictionary.get(key) == value:
num_ += 1
return 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/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 Image
from ..logger_module import logger
def screenshot(hwnd, left=0, top=0, right=0, bottom=0, filename=None):
try:
#old_time=time.time()
width = right-left
height = bottom - top
# 判断窗口是否可见
if not win32gui.IsWindowVisible(hwnd):
return False
# 创建设备描述表
hwnd_dc = win32gui.GetDC(hwnd) #GetWindowDC
mfc_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 img
except :
logger.error(traceback.format_exc())
return False
if __name__ == '__main__':
try:
step=int(input("多少秒截一张图?输入数字按回车确认:"))
except:
step=2
input("请将游戏设置为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)
5、添加模型推理
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, ImageFont
def 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_path
else:
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
# 转换图像以便使用PIL
pil_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 = position
y_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=weights
self.model_h=model_h
self.model_w=model_w
self.thred_nms=thred_nms
self.thred_cond=thred_cond
self.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_labels
big_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_labels
big_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 object
color: color to draw rectangle, such as (0,255,0)
label: str
line_thickness: int
return:
img
"""
tl = (
line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
) # line/font thickness
color = 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 thickness
t_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_w
c_y = outputs[:, 1] / self.model_h * img_h
w = outputs[:, 2] / self.model_w * img_w
h = outputs[:, 3] / self.model_h * img_h
p_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=320
model_w=320
dic_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() # 启动寻路类
添加寻路类
# -*- 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.01
pyautogui.FAILSAFE=False
self.on_rec=False
self.map_matrix = []
self.now_point=(0,0)#当前的位置
self.old_point=(0,0)#上一次的位置
self.isRunRec=False
self.小地图区域=(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 = True
self.isRunRec=True
self.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 = None
result_post=(0,0)
big_img = cv2.imdecode(np.fromfile(file=image_path, dtype=np.uint8), cv2.IMREAD_COLOR) # 加载大图
big_height, big_width, _ = big_img.shape
big_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)
pass
if self.on_rec == False:
# 分割路径为目录和文件名
dir_path, filename = os.path.split(image_path)
# 分割文件名和后缀
name, extension = os.path.splitext(filename)
new_file=image_path_lujin
cv2.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] = 0
if roi[1] < 0:
roi[1] = 0
if 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
# 显示匹配结果
break
except :
logger.error(traceback.format_exc())
roi=None
if state.开关_是否展预测结果:
if big_img.shape[2] == 4:
state.图片_找图 = cv2.cvtColor(big_img, cv2.COLOR_BGRA2BGR)
else:
state.图片_找图 = big_img
state.QT_信号.mysig_show_xunlu.emit()
#cv2.imshow('Genshin navigation'+sj, big_img)
if state.开关_是否展预测结果:
pass
#cv2.destroyAllWindows()
return True
def 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+left
p_top = rect[1] + h_p+top
roi = None
moban_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=None
chuangsong_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.状态_需重新传送 = False
return False
for 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_img
state.QT_信号.mysig_show_xunlu.emit()
state.状态_传送中 = False
state.状态_需重新传送 = False
return True
state.状态_需重新传送 = True
logger.info("需要重新传送! (注意检查一下 桌面缩放是否为100%)")
return False
big_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.状态_需重新传送 = False
return False
try:
if len(res) == 0:
logger.info(f"没定位到传送模板")
state.状态_需重新传送 = True
return False
for 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_img
state.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)
break
except Exception as err:
logger.info(f"没找到传送位置{err}")
state.状态_需重新传送 = True
return False
time.sleep(0.5)
if state.状态_循环开关 == False:
state.状态_需重新传送 = False
logger.info("强制退出!")
return False
big_img = screenshot(hwnd, left, top, right, bottom, None)
res=template_matching(big_img, chuangsong_img, mask=None)
ret = False
try:
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_img
state.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.状态_需重新传送 = True
return False
if ret != True:
time.sleep(0.5)
if state.状态_循环开关== False:
state.状态_需重新传送 = False
logger.info("强制退出!")
return False
big_img = screenshot(hwnd, left, top, right, bottom, None)
res = find_img_all_sift(big_img, moban_maodian_img, None)
ret = False
try:
if res == []:
logger.info("传送失败")
state.状态_需重新传送 = True
return False
for result in res:
result_post = [result["result"][0], result["result"][1]]
if result["result"][0]<581 and result["result"][1]<401:
ret = False
continue
if result['confidence']<0.8:
ret = False
continue
logger.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_img
state.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 = True
break
except Exception as err:
logger.info(f"没找到锚点选项{err}" )
state.状态_需重新传送 = True
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
time.sleep(1)
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
return False
if 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.状态_需重新传送 = False
return False
try:
if res==[]:
logger.info("传送失败")
state.状态_需重新传送 = True
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
time.sleep(1)
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
return False
for 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_img
state.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.状态_需重新传送 = True
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
time.sleep(1)
pyautogui.keyDown("esc")
time.sleep(0.2)
pyautogui.keyUp("esc")
return False
else:
logger.info("传送失败")
state.状态_需重新传送 = True
pyautogui.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 True
def 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 = False
state.状态_循环开关= True
state.状态_已经有寻路了=True
self.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_keys
ret=True
angle_diff = abs(angle_now - angle_target)
if angle_diff <= fault:
ret= False
if state.状态_循环开关== False:
logger.info("强制退出1!")
pyautogui.keyUp('w')
ret= False
if ret==False:
return False
angle_diff = int((angle_target - angle_now) % 360)
if angle_diff > 180:
fangxiang = -1
angle_diff = 360 - angle_diff
else:
fangxiang = 1
if 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 True
state.计次_定位失败=0
# 获取窗口句柄
hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE) # 替换成你实际的窗口句柄
while True:
time.sleep(1)
if self.get_window_handle_at_mouse_position() == hwnd:
break
if state.状态_循环开关== False:
logger.info("强制退出!")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = False
return False
# 设定保存截图的文件夹路径和文件名前缀
#folder_path = "./datas/img/"
# 设定定时器间隔(秒)
roi=None
big_img_bk = cv2.imdecode(np.fromfile(file=image_path, dtype=np.uint8),cv2.IMREAD_UNCHANGED) # 加载大图
big_height, big_width, big_td = big_img_bk.shape
if big_td==3:
# # 将24位彩色图片转换为32位彩色图片
big_img_bk = cv2.cvtColor(big_img_bk, cv2.COLOR_BGR2BGRA)#BGRA COLOR_BGR2BGRA
template = 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.计数_卡主次数=0
state.状态_在爬墙=False
interest_coordinate2=None
interest_coordinate=None
state.游戏_打怪前坐标 = [0, 0]
state.游戏_当前目标坐标 = [0, 0]
state.计次_误差过大=0
self.old_point= [0, 0]
state.计次_识别次数=0
state.计次_移动停滞 =0
while True:
#old_time = time.time()
try:
time.sleep(0.01)
if state.状态_循环开关 == False:
logger.info("强制退出!")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = False
return False
# 设定截图区域的左上角坐标 (x, y) 和右下角坐标 (x, y)
left, top, right, bottom = self.小地图区域 # 替换成你实际的区域坐标
if state.状态_全局暂停:
time.sleep(1)
continue
if not state.状态_YOLOV :
time.sleep(1)
continue
if state.计次_误差过大 == 20:
self.old_point = copy.copy(state.游戏_当前目标坐标)
if state.计次_误差过大>= 30:
logger.info("误差过大了30次,重新运行这个任务")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = True
return False
if state.计次_移动停滞>=20:
logger.info("移动停滞了20次,重新运行这个任务")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = True
return False
if state.计次_定位失败>=30:
logger.info("定位失败次数超过30次,重新运行这个任务")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = True
return False
self.quickening(quickening_keys)
big_img = big_img_bk.copy()
#filename = f"{folder_path}{int(time.time())}.png"
height = bottom - top
width = right - left
small_img = screenshot(hwnd, left, top, right, bottom, filename=None)#RGBA
if 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_angle
state.游戏_当前导航角度=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.计数_卡主次数 += 1
state.计次_定位失败 += 1
if 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_p
p_top = rect[1] + h_p
mouse_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 = None
continue
else:
roi = None
continue
roi=None
for result in ret:
if result["result"] !=result_old["result"] :
state.计次_定位失败 = 0
sm_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]=0
if roi[1]<0:
roi[1]=0
if 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 = None
break
if abs(state.游戏_当前坐标[0] - self.end[0]) <= 120 and abs(
state.游戏_当前坐标[1] - self.end[1]) <= 120 and quickening_keys != None:
# 快到终点了,ban掉这些赶路的,防止走错地方
quickening_keys = None
if abs(state.游戏_当前坐标[0] - self.end[0]) <= 4 and abs(state.游戏_当前坐标[1] - self.end[1]) <= 4:
logger.info("已经到达目的地")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = False
return True
state.计次_识别次数+=1
if 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.计次_移动停滞 += 1
state.计数_卡主次数+=1
if state.计数_卡主次数 >= 10:
if abs(state.游戏_当前坐标[0] - self.end[0]) <= 20 and abs(
state.游戏_当前坐标[1] - self.end[1]) <= 20:
logger.info("非常接近目的地了!但是遇到意外,因此跳过这个任务!")
pyautogui.keyUp('w')
state.状态_已经有寻路了 = False
state.状态_需重新传送 = False
return True
if random.randint(0, 1):
pyautogui.keyDown('a')
aaa = -90
else:
pyautogui.keyDown('d')
aaa = 90
pyautogui.keyUp('w')
mouse_moveR(6 * aaa, 0)
state.计数_卡主次数 = 0
logger.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.计次_移动停滞 -=1
if state.计次_移动停滞 <= 0:
state.计次_移动停滞=0
state.计数_卡主次数=0
if state.计次_识别次数 >= 8:
result_old = result.copy()
state.计次_识别次数=0
if state.开关_是否展预测结果:
#cv2.polylines(big_img, result['dst_pot'], True, (0, 255, 0), 5)
# 获取小图的宽度和高度
small_height, small_width, small_channels = best_rotated_template.shape
best_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("回到之前的位置了")
continue
except:
state.游戏_打怪前坐标 = [0, 0]
logger.info("回到之前的位置了")
continue
else:
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.计次_误差过大+=1
else:
state.计次_误差过大=0
if 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.状态_寻路中 = False
state.游戏_打怪前坐标 = copy.copy(state.游戏_当前目标坐标)
state.计数_没找到任何目标 = 0
state.计数_没找到怪物 = 0
pyautogui.keyUp('w')
state.计数_卡主次数 = 0
state.计次_识别次数 = 0
mouse_left_down()
mouse_left_up()
if wakuang or caiji:
logger.info("在连招中....")
pyautogui.keyDown('x')
time.sleep(0.2)
pyautogui.keyUp('x')
state.状态_是否回放中 = True
tangbaowss.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=None
interest_coordinate2=None
self.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.状态_寻路中:
break
small_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:
break
else:
for _ in range(30):
if not state.状态_寻路中:
break
small_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:
break
pyautogui.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_img
state.QT_信号.mysig_show_xunlu.emit()
# # 显示匹配结果
# cv2.imshow('Genshin navigation', big_img)
#print(f"识别时间:{time.time() - old_time}")
break
except :
time.sleep(0.3)
state.计数_卡主次数 += 1
state.计次_定位失败 += 1
if 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 False
for 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 False
logger.info(f"触发赶路按键:{key['key']}")
pyautogui.keyDown(key["key"])
time.sleep(key["time"])
pyautogui.keyUp(key["key"])
time.sleep(0.1)
return True
def 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_deg
def 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 =None
interest_coordinate=None
for 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 = distance
if 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 = distance
if column == 2:
interest_coordinate = [rdex, cdex]
nearest_coordinate = [rdex, cdex]
return nearest_coordinate,interest_coordinate
def on_release(self, key: keyboard.KeyCode):
"""定义释放时候的响应"""
if key == Key.f9:#停止
state.状态_循环开关= False
state.状态_全局暂停=True
state.状态_已经有寻路了 = False
state.状态_是否禁止录制=True
self.on_rec = False
elif key == Key.f8:
self.on_rec = False
if state.状态_是否禁止录制:
if state.状态_循环开关== False:
return
state.状态_全局暂停=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_hwnd
def 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# 将该点标记为已经走过的,标记为-1
elif 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# 将该点标记为已经走过的,标记为-1
return new_coordinates
def 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 = 10
arrow_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=320
item_height=240
item_height_min = 60
window_main = MainWindow()
window_main.show()
sys.exit(app.exec_()) # 监听消息不关闭
ui略多下章看