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

前言

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

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

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

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

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

[[CSDN大礼包:《python安装包&全套学习资料》免费分享]]安全链接,放心点击

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

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,然后切回游戏。脚本会在这把打完后不再挂机,并输出挂机统计结果。

你可能感兴趣的:(python入门,Python编程,python,python,开发语言,python入门,Python教程,Python开发)