1.不使用外部硬件模块,仅使用Python库实现。
2.有些代码可以再优化,处理一些异常的情况。
我会在文章最后附上在win10下使用pyinstaller打包好的可执行程序。如果和我的电脑屏幕分辨率一样的,可以直接下载下来测试使用。
本文下面构建的Python脚本都有详细的配套教程以及源码,都已经打包好上传到百度云了,链接在文章结尾处!
扫码此处领取大家自行获取即可~~~
[[CSDN大礼包:《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 kb
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 True
def 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 kb
AutoPalyFlag = 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 True
def slow_key_press(key, down_time = 0.2):
kb.press(key)
time.sleep(down_time)
kb.release(key)
return True
def record_and_print_log(log):
global log_file
print(log)
log_file.write(log)
log_file.write('\n')
return True
def 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_box
if index > 4:
index = 4
if index < 0:
index = 0
box = 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 True
def show_emoji():
kb.press_and_release('t')
return True
# 提升等级
def upgrade_champ():
kb.press_and_release('f')
return True
def get_lol_hwnd():
title = 'League of Legends'
w = pg.getWindowsWithTitle(title)
for i in w:
if i.title == title:
w = i
break
return w
def 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):
return
center = 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 False
if not pg.locateCenterOnScreen(pic_find_match, confidence=0.9):
return True
return 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 False
time.sleep(1)
return click_find_match(lol_hwnd)
# 等待并接受对局
def accept_match(lol_hwnd):
# 5分钟未进入游戏,直接重新匹配
timeout = 5 * 60
start_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 True
time.sleep(1)
# 等待太久,重新匹配
if not stop_and_start_a_new_match(lol_hwnd):
return False
record_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 True
time.sleep(2)
return True
WaitStageFlag = True
def change_wait_flag_callback():
global WaitStageFlag
record_and_print_log("17分钟未检测到3-2回合,直接进行下一步操作")
WaitStageFlag = False
return True
def pg_wait_stage_3_2():
global WaitStageFlag
pic = 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,停止Timer
t.cancel()
return True
case = 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 True
def 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 True
def pg_wait_surrender_finish(lol_hwnd):
client_title = 'League of Legends (TM) Client'
times = 0
while True:
time.sleep(1)
times += 1
if pg.getWindowsWithTitle(client_title) == []:
break
if 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 True
def pg_play_again(lol_hwnd):
global box_play_again, box_empty_area
center = 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 True
def pg_stop_play():
global AutoPalyFlag
AutoPalyFlag = False
return True
# 手动结束脚本命令,按Ctrl+Alt+q即可设置停止运行标志
kb.add_hotkey('ctrl+alt+q', pg_stop_play)
def pg_main():
global AutoPalyFlag, log_file
w = get_lol_hwnd()
play_times = 0
start_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 += 1
record_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:
break
pg_play_again(w)
# 统计每局时间,写入到文件
for i in range(len(game_time_list)):
seconds = game_time_list[i] % 60
minutes = 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) % 60
diff_time_sec /= 60
# 总共所用分钟数
total_minutes = diff_time_sec
t_m = int(diff_time_sec) % 60
diff_time_sec /= 60
t_h = int(diff_time_sec)
average_minutes = total_minutes / play_times
t_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 True
if __name__ == '__main__':
pg_main()
os.system("pause")
复制代码
注意:此代码是在电脑分辨率1920x1080,缩放100%,LOL客户端分辨率1600x900时测试的。 使用方法 首先把脚本文件夹scs中的end.jpg替换为你自己召唤师ID的截图(注意:最好是一把云顶打完后结算界面时,去截左上角自己ID的图片,否则有可能导致最后检测不到你的召唤师ID图片,然后脚本卡住,不能自动下一局。),图片文件名不要改变。创建一个云顶之弈的房间,然后以管理员权限启动脚本,然后切换到LOL客户端。这时候脚本会去检测“寻找对局”按钮,如果找到后,脚本就会控制鼠标点击这个按钮,然后就不要动鼠标和键盘了。开始挂机之旅。如果这把挂完不想挂机了,可以切换到桌面(游戏中按键,脚本检测不到),然后按 Ctrl+Alt+Q,然后切回游戏。脚本会在这把打完后不再挂机,并输出挂机统计结果。