Python脚本实现云顶之弈自动挂机刷局数!这操作怎么说?

news/2024/12/28 20:07:04/

前言

1.不使用外部硬件模块,仅使用Python库实现。

2.有些代码可以再优化,处理一些异常的情况。

我会在文章最后附上在win10下使用pyinstaller打包好的可执行程序。如果和我的电脑屏幕分辨率一样的,可以直接下载下来测试使用。

本文下面构建的Python脚本都有详细的配套教程以及源码,都已经打包好上传到百度云了,链接在文章结尾处!

扫码此处领取大家自行获取即可~~~

Python环境

Python 3.7.3 库:PIL,random,threading,pyautogui,keyboard 更新 2021/3/14 更新:测试了奖励界面关闭函数的功能,测试结果是可以正常关闭奖励界面。但是鼠标点的有点偏下,现在提供一个新的关闭奖励界面函数,鼠标能点到按钮中间了:

def close_award_interface2(lol_hwnd):box = (721, 822, 161, 50) # (x,y,width,height) 按钮相对于LOL客户端的坐标center = convert_lol_to_destop_point(pg.center(box), lol_hwnd)slow_click(center)move_to_empty_area(lol_hwnd)return True
复制代码

主要改动

1.不使用外部硬件模块,但是代码必须使用管理员权限运行才可以。将原来的硬件上报鼠标事件,键盘事件换成了pyautogui和keyboard库上报。因为实际测试的时候,pyautogui库模拟鼠标没遇到什么问题,但是模拟键盘的时候,在游戏中响应不了,所以使用keyboard库来模拟键盘。

import pyautogui as pg
import keyboard as kbdef slow_click(p, button='PRIMARY', move_time = 0.2, down_time = 0.2, up_time = 0.2):pg.moveTo(p[0], p[1], duration=move_time)pg.mouseDown(p[0], p[1], button=button, duration = down_time)pg.mouseUp(p[0], p[1], button=button, duration = up_time)return Truedef slow_key_press(key, down_time = 0.2):kb.press(key)time.sleep(down_time)kb.release(key)return True
复制代码

比如,上面的代码中,鼠标动作是采用pyautogui库实现的,键盘动作是keyboard库实现的。直接调用这两个模块的鼠标点击,或者模拟按键。游戏内不会响应,所以要在每个动作加一小段的延迟,游戏中才能正常响应。 2.增加了匹配过长时间后,重新匹配的功能,防止出现以下的情况: 当时晚上睡觉前开始挂机,一觉醒来,给我看懵逼了(手动捂脸)。 3.增加检测关闭奖励界面功能,防止出现以下情况,然后脚本卡住: 但是这个界面我一周只遇到过一次,没机会验证我新增的功能是否正常。 4.优化了一下匹配时候点击按钮的参数。 5.新增了快捷键让脚本不再继续挂机的功能(按了快捷键后,这局挂完机,脚本不再继续循环挂机,同时输出挂机的统计信息,比如:总共挂机多长时间,挂了多少局,每局用时多久,平均每局的时间):

完整代码

import time
import pyautogui as pg
from PIL import Image
import random
import datetime
import threading
import os
import keyboard as kbAutoPalyFlag = True# 设置 图标 box
box_settings = (1890, 870, 1920, 900)
# 发起投降 按钮 box
box_surrender = (700, 820, 840, 860)
# 确认投降 按钮 box
box_confirm = (720, 460, 940, 520)
# 空白区域
box_empty_area = (700, 0, 800, 10)
# 再玩一次
box_play_again = (530, 830, 780, 880)# 商店5个怪的box
champ_box = [(480, 930, 670, 1070),(680, 930, 870, 1070),(880, 930, 1070, 1070),(1080, 930, 1270, 1070),(1290, 930, 1480, 1070),
]log_path = 'log\\%s.log'%time.strftime('%Y_%m_%d_%H_%M_%S')
log_file = open(log_path, 'w', encoding='utf8')def slow_click(p, button='PRIMARY', move_time = 0.2, down_time = 0.2, up_time = 0.2):pg.moveTo(p[0], p[1], duration=move_time)pg.mouseDown(p[0], p[1], button=button, duration = down_time)pg.mouseUp(p[0], p[1], button=button, duration = up_time)return Truedef slow_key_press(key, down_time = 0.2):kb.press(key)time.sleep(down_time)kb.release(key)return Truedef record_and_print_log(log):global log_fileprint(log)log_file.write(log)log_file.write('\n')return Truedef get_time_fmt_str(t = None):if not t:t = time.localtime()return time.strftime("%Y/%m/%d %H:%M:%S", t)def stray():# 在棋盘小范围内随机游荡,防止鼠标指针挡住关卡图片导致检测不到3-2关卡x = random.randint(400, 1500)y = random.randint(200, 500)slow_click((x,y), button='right', move_time=0.5)return True# 买一个怪
def buy_single_champ(index):global champ_boxif index > 4:index = 4if index < 0:index = 0box = champ_box[index]center = get_box_center(box)slow_click(center, move_time=0.3)return True# 刷新商店
def refresh_shop():kb.press_and_release('d')return Truedef show_emoji():kb.press_and_release('t')return True# 提升等级
def upgrade_champ():kb.press_and_release('f')return Truedef get_lol_hwnd():title = 'League of Legends'w = pg.getWindowsWithTitle(title)for i in w:if i.title == title:w = ibreakreturn wdef get_box_center(box):x = int((box[2] - box[0]) / 2) + box[0]y = int((box[3] - box[1]) / 2) + box[1]return (x, y)def move_to_empty_area(lol_hwnd):global  box_empty_area# 客户端已经最小化,不移动鼠标if lol_hwnd.size < (1024, 768):returncenter = convert_lol_to_destop_point(get_box_center(box_empty_area), lol_hwnd)pg.moveTo(center[0], center[1], duration = 0.1)return True# 将LOL客户端相对坐标根据LOL客户端位置转换为实际桌面的绝对坐标
def convert_lol_to_destop_point(p, lol_hwnd):x = lol_hwnd.topleft[0] + p[0]y = lol_hwnd.topleft[1] + p[1]return (x, y)def surrender():global box_settings, box_surrender, box_confirm# center = get_box_center(box_settings)# 点击设置# slow_click(center, move_time=0.5)slow_key_press('esc')center = get_box_center(box_surrender)# 发起投降slow_click(center, move_time=0.5)center = get_box_center(box_confirm)# 确定离开slow_click(center, move_time=0.5)return True# 开始匹配
def click_find_match(lol_hwnd):pic_find_match = Image.open(r'scs\find_match.jpg')p = pg.locateCenterOnScreen(pic_find_match, confidence=0.9)if p:slow_click(p)move_to_empty_area(lol_hwnd)else:return Falseif not pg.locateCenterOnScreen(pic_find_match, confidence=0.9):return Truereturn False# 等待太久,重新匹配
def stop_and_start_a_new_match(lol_hwnd):pic = Image.open(r'scs\stop.jpg')p = pg.locateCenterOnScreen(pic, confidence=0.9)if p:slow_click(p)move_to_empty_area(lol_hwnd)else:record_and_print_log('找不到停止按钮!')return Falsetime.sleep(1)return  click_find_match(lol_hwnd)# 等待并接受对局
def accept_match(lol_hwnd):# 5分钟未进入游戏,直接重新匹配timeout = 5 * 60start_time = time.time()pic_accept = Image.open(r'scs\accept.jpg')while time.time() - start_time < timeout * 10:while time.time() - start_time < timeout:p = pg.locateCenterOnScreen(pic_accept, confidence = 0.93)if p:slow_click(p)move_to_empty_area(lol_hwnd)# 客户端已最小化,正在启动游戏if lol_hwnd.size < (1024, 768):return Truetime.sleep(1)# 等待太久,重新匹配if not stop_and_start_a_new_match(lol_hwnd):return Falserecord_and_print_log("10次超时未找到对局!")return False# 寻找对局 -> 队列中 -> 接受 -> 开始加载
# 寻找对局 -> 队列中 -> 接受 -> 有人拒绝 -> 队列中
def pg_find_match(lol_hwnd):click_find_match(lol_hwnd)return accept_match(lol_hwnd)def pg_wait_loading(lol_hwnd):pic = Image.open(r'scs\1_1.jpg')while True:# pyautogui 模块检测关卡box = pg.locateOnScreen(pic, confidence=0.9)if box:return Truetime.sleep(2)return TrueWaitStageFlag = Truedef change_wait_flag_callback():global WaitStageFlagrecord_and_print_log("17分钟未检测到3-2回合,直接进行下一步操作")WaitStageFlag = Falsereturn Truedef pg_wait_stage_3_2():global WaitStageFlagpic = Image.open(r'scs\3_2.jpg')WaitStageFlag = True# 启动一个Timer,17分钟未检测到3-2回合,直接进行下一步t = threading.Timer(17 * 60, change_wait_flag_callback)t.start()while WaitStageFlag:# pyautogui 模块检测关卡box = pg.locateOnScreen(pic, confidence=0.9)if box:# 检测到3-2,停止Timert.cancel()return Truecase = random.randint(1,100)# %40 概率游荡if case <= 40:stray()case = random.randint(1,100)# %25 概率买1个英雄if case >= 25 and case < 50:buy_single_champ(random.randint(0, 4))case = random.randint(1,100)# %0 概率刷新商店if case == 0:refresh_shop()case = random.randint(1,100)# %30 概率发表情if case <= 30:show_emoji()case = random.randint(1,100)# %5 概率升级if case >= 85 and case < 90:upgrade_champ()time.sleep(2)return Truedef close_award_interface(lol_hwnd):ok_box = (lol_hwnd.centerx - 100, lol_hwnd.bottom - 60, 200, 40)c = pg.center(ok_box)slow_click(c)move_to_empty_area(lol_hwnd)return Truedef pg_wait_surrender_finish(lol_hwnd):client_title = 'League of Legends (TM) Client'times = 0while True:time.sleep(1)times += 1if pg.getWindowsWithTitle(client_title) == []:breakif times % 10 == 0:print("pg_wait_surrender_finish %d sec, still wait client over!"%times)# 检测结算界面是否打开while True:pic = Image.open(r'scs\end.jpg')box = pg.locateOnScreen(pic, confidence = 0.9)if box:break# 结算界面已打开,但被奖励界面遮挡if lol_hwnd.size == (1600, 900):close_award_interface(lol_hwnd)time.sleep(1)return Truedef pg_play_again(lol_hwnd):global box_play_again, box_empty_areacenter = convert_lol_to_destop_point(get_box_center(box_play_again), lol_hwnd)pg.click(center[0], center[1], duration = 0.3)  # 点击再玩一次move_to_empty_area(lol_hwnd)time.sleep(1)pic = Image.open(r'scs\end.jpg')pic_find_match = Image.open(r'scs\find_match.jpg')box = pg.locateOnScreen(pic_find_match, confidence=0.9)while not box:if pg.locateOnScreen(pic, confidence = 0.9) != []:pg.click(center[0], center[1], duration = 0.3)  # 点击再玩一次move_to_empty_area(lol_hwnd)else:# 奖励界面遮挡close_award_interface(lol_hwnd)time.sleep(1)box = pg.locateOnScreen(pic_find_match, confidence=0.9)return Truedef pg_stop_play():global AutoPalyFlagAutoPalyFlag = Falsereturn True# 手动结束脚本命令,按Ctrl+Alt+q即可设置停止运行标志
kb.add_hotkey('ctrl+alt+q', pg_stop_play)def pg_main():global AutoPalyFlag, log_filew = get_lol_hwnd()play_times = 0start_time = time.localtime()game_time_list = []while AutoPalyFlag:# 游戏开始时间game_start = time.time()record_and_print_log("[%s] pg_find_match"%get_time_fmt_str())pg_find_match(w)record_and_print_log("[%s] pg_wait_loading"%get_time_fmt_str())pg_wait_loading(w)record_and_print_log("[%s] pg_wait_stage_3_2"%get_time_fmt_str())pg_wait_stage_3_2()record_and_print_log("[%s] surrender"%get_time_fmt_str())surrender()record_and_print_log("[%s] pg_wait_surrender_finish"%get_time_fmt_str())pg_wait_surrender_finish(w)play_times += 1record_and_print_log("[%s] pg_play_again"%get_time_fmt_str())# 游戏结束时间game_end = time.time()game_time = int(game_end - game_start)game_time_list.append(game_time)if not AutoPalyFlag:breakpg_play_again(w)# 统计每局时间,写入到文件for i in range(len(game_time_list)):seconds = game_time_list[i] % 60minutes = int(game_time_list[i] / 60)info = "第%d局用时[%d]分[%d]秒"%(i + 1, minutes, seconds)record_and_print_log(info)# 输出统计信息end_time = time.localtime()record_and_print_log("[%s] 开始挂机"%get_time_fmt_str(start_time))record_and_print_log("[%s] 结束挂机"%get_time_fmt_str(end_time))diff_time_sec = time.mktime(end_time) - time.mktime(start_time)t_s = int(diff_time_sec) % 60diff_time_sec /= 60# 总共所用分钟数total_minutes = diff_time_sect_m = int(diff_time_sec) % 60diff_time_sec /= 60t_h = int(diff_time_sec)average_minutes = total_minutes / play_timest_ave_m = int(average_minutes)t_ave_s = int((average_minutes - t_ave_m) * 60)record_and_print_log("共挂机[%d]小时[%d]分钟[%d]秒,挂机[%d]局,平均[%d]分钟[%d]秒一局"%(t_h, t_m, t_s, play_times, t_ave_m, t_ave_s))log_file.close()return Trueif __name__ == '__main__':pg_main()os.system("pause")
复制代码

注意:此代码是在电脑分辨率1920x1080,缩放100%,LOL客户端分辨率1600x900时测试的。 使用方法 首先把脚本文件夹scs中的end.jpg替换为你自己召唤师ID的截图(注意:最好是一把云顶打完后结算界面时,去截左上角自己ID的图片,否则有可能导致最后检测不到你的召唤师ID图片,然后脚本卡住,不能自动下一局。),图片文件名不要改变。创建一个云顶之弈的房间,然后以管理员权限启动脚本,然后切换到LOL客户端。这时候脚本会去检测“寻找对局”按钮,如果找到后,脚本就会控制鼠标点击这个按钮,然后就不要动鼠标和键盘了。开始挂机之旅。如果这把挂完不想挂机了,可以切换到桌面(游戏中按键,脚本检测不到),然后按 Ctrl+Alt+Q,然后切回游戏。脚本会在这把打完后不再挂机,并输出挂机统计结果。


http://www.ppmy.cn/news/918018.html

相关文章

华为OD机考真题-- | 200分-宜居星球改造计划-带答案

题目描述: 宜居星球改造计划 2XXX年&#xff0c;人类通过对火星的大气进行宜居改造分析&#xff0c;使得火星已在理论上具备人类宜居的条件&#xff1b; 由于技术原因&#xff0c;无法一次性将火星大气全部改造&#xff0c;只能通过局部处理形式&#xff1b; 假设将火星待改造…

关于tft 7寸屏幕调试硬件记录

TFT屏幕分为显示与触摸部分&#xff1b;显示部分采用565数据格式&#xff0c;触摸部分由iic控制。 显示部分出现图片亮度不够的问题。后发现是由于VCOM电压过大&#xff0c;参考手册给的数据不够可靠&#xff0c;修改电压之后图片色彩显示为正常&#xff1b; 出现问题&#x…

传感器实验——3.5寸串口屏幕

传感器实验——3.5寸串口屏幕 MR开发板3.5触屏型号&#xff1a;DC48320N035SHT20温湿度传感器引脚说明示例代码:串口屏显示获取的温湿度效果 MR开发板 3.5触屏型号&#xff1a;DC48320N035 使用方法之前在ESP32中写过&#xff0c;今天来试试STM32的。使用方法如下建议康康。 …

android大屏适配_Android屏幕大小适配问题解决

一、一些基本概念 1、长度(真实长度):英寸、inch 2、分辨率:density 每英寸像素数 dpi(密度) 3、像素:px 4、dip的公式:px /dip=dpi/160 所以 dip 类似于英寸、长度(dp=dip,sp类似于dip) 5、相对分辨率=长px*宽px 二、平时我们一些概念的混淆 1、平时我们说 手机的分辨率…

可视化大屏设计尺寸_UI设计中大屏可视化设计尺寸指南

大屏可视化的设计尺寸定义,一直是很多设计师苦恼的一件事,很多时候大屏出现的问题,都是因为对设计尺寸没有一个正确的认识导致。 比如大屏内容呈现不全、拉伸、压缩、字号小的看不见等等,出现这样的问题就会浪费时间调整返工,今天我就带大家来认认真真的讨论一下大屏的设计…

基于PyQt5的UI界面开发——信号与槽

信号与槽的机制 PyQt5采用了一种被称为“信号与槽”机制的编程模式&#xff0c;用于处理对象间的通信和事件处理。在PyQt5中&#xff0c;信号&#xff08;signal&#xff09;是对象发出的特定事件&#xff0c;例如按钮被点击、文本被修改等。而槽&#xff08;slot&#xff09;…

个人博客系统(二)

该博客系统共有八个页面,即注册页面、登录页面、添加文章页面、修改文章页面、我的博客列表页面、主页、查看文章详情页面、个人中心页面。 1 注册页面 该页面如图所示: 首先,要先判断注册的用户名、密码、确认密码以及验证码是否为空,若有一个为空,点击提交,则会提醒 …

AD如何画多图纸原理图

很多东西都讲究模块化&#xff0c;比如&#xff1a;程序模块化。原理图也不例外&#xff0c;模块化后的原理图更能直观的显示其原理。文章中操作的图标是什么功能可以先看下参考文献中的博客在来看看我的这篇博客&#xff0c;效果会更好。 原本以为原理图多图纸很难&#xff0c…