背景简介
有时候我们需要进行游戏的简单完成日常任务,例如皇室战争这个手机游戏,这种卡牌游戏很吃卡牌等级,由于我是国际服新号,等级不太高,不想打到太高分数,避免碰到高等级高卡牌高强度对手,但手动降低杯数太麻烦,这里使用pyautogui完成一些基础操作:

requirements:

实际上并不需要那么多
opencv-python4.12.0.88
PyAutoGUI0.9.54
func-timeout
pyinstaller # 用来将程序打包成exe,不打包就不需要装,为了方便打包建议新建个虚拟环境!
图中看起来很多是因为装他们的时候会附带一些其他的,必装opencv,否则无法识别图像!

程序实际运行流程如下:
1.先人工手动进入mumu模拟器,进入游戏;
2.切换到点击开始主界面,此时程序开始接手,会将游戏窗口移动到最右侧,并限定图像匹配搜索范围到屏幕右下角,这样可以加快速度!
3.查找对战fight.png按钮,如果是英文版的应该是start,可以自己更换截图,替换掉即可,注意两个图和exe必须在同一文件夹中。
4.会要求输入对战的次数(此时会检查是否输入的数字,输其他的会要求重输)一般同一个竞技场基本是500杯的差距,一句输赢基本在30杯左右,所以设定个15基本差不多,如果是0则为死循环一直运行(不建议设为0);
5.会要求输入拖拽卡牌的次数,进游戏一个卡牌不放等输也不是办法,有时候对面以为你是玩空城计,双方就这样耗着都不放牌白白浪费时间,会选择四个卡牌的位置,隔三秒放一次牌,会使用拖动的方式释放,好处是避免瞬间点击,瞬间的话被检测的概率很可能比较高。【会随机选择四张里面的某一张卡,释放在左右随机的一个公主塔的后方,位置都有随机性小范围浮动,避免被检测】由于纯脚本刷数据刷伤害,听说会被封号,虽然该功能把拖拽卡牌的次数设高点的确可以用来刷伤害,但危险系数太大,所以一般设为10以内的数字即可。
6.对战结束后,检查是否出现“确定”图标,用来返回游戏主界面。
7.整个程序每次做完判定后,例如检查一轮是否有 fight,confirm图标后,会休眠5秒,在点进游戏后也会休眠一下,如果检查太频繁会占用太多系统资源。【感觉这游戏现在进入和退出每一局,都要花大几秒,非常怀疑在收集数据和上传数据,所以真的要悠着点!】
注意事项
1.仅在mumu模拟器上实测完美运行,其他模拟器不敢保证,源码在下面可以手动修改;
2.国际服会检查脚本,虽然可以用来刷卡牌大师伤害,但太过了可能被封号,我自己降杯弄了一两个星期都没啥事,我最多一次执行十几局。
3.我的分辨率为2560X1440,如果是其他分辨率,可能会找不到图像,特别是高度这块,我看到卡牌的中心位置大概在1440-180这样,如果是1080的话,应该相对少减去一些,不过懒得调了,可以自己改改!
4.程序到设定的对局数完成后,会自动关闭。
5.由于使用了图像匹配和点击,在执行操作的时候会跟人抢鼠标,所以比较适合在看视频的时候,给模拟器留出右边750宽度的空间,让程序慢慢降杯。
6.mumu模拟器实测正常玩偶尔会崩溃,跟脚本无关,该脚本无打开游戏和关闭模拟器功能。
import pyautogui as pa
import time
import random
import pygetwindow as gw
import func_timeout# 安全模式:当鼠标移动到屏幕左上角时自动中止程序
pa.FAILSAFE = True # 默认为True,强烈建议保持启用
global_count = 0
def move_to_side():# 打印当前所有窗口,给用户提示window = gw.getWindowsWithTitle('安卓设备')[0]print(f'找到了{window.title}')window.activate()window.resizeTo(750, pa.size().height)window.moveTo(pa.size().width - 750, 0)# 缩小图像匹配范围,为屏幕右侧750的下方区域nice_region = (window.left + 10, pa.size().height/2-1 ,window.width - 20, pa.size().height/2-1)# 浮点转整数nice_region = (int(nice_region[0]), int(nice_region[1]), int(nice_region[2]), int(nice_region[3]))return nice_region@func_timeout.func_set_timeout(5)
def get_drag_num():"""选择拖拽几次卡牌,如果5秒内无操作,则使用默认次数8"""try:drag_num = input("请输入拖拽次数,不输入则5秒后自动设为8次-->")except func_timeout.exceptions.FunctionTimedOut as e:drag_num = 8return int(drag_num)def low_trophy(in_region):# 让用户手动输入执行次数,为0则为死循环,输入非数字则让用户再次输入while True:exe_num = input('请输入执行次数,为0则无限循环:')if exe_num.isdigit() == False:print('检查到输入的非数字,请重新输入数字')continueif exe_num.isdigit():breakdrag_num = get_drag_num()exe_num = int(exe_num)print(f'执行次数为{exe_num},拖拽次数为{drag_num}')global global_countwhile True:if exe_num > 0:if global_count >= exe_num:print('程序运行结束')raise SystemExit("终止程序")try:location_confirm = pa.locateOnScreen('./confirm.png',confidence=.8,limit=1,region=in_region)if location_confirm:confirm_center = pa.center(location_confirm)# 左右略微移动new_center_1 = (confirm_center.x+random.randint(-5,5),confirm_center.y+random.randint(-5,5))pa.click(new_center_1)print('点击确认返回主界面,等待10秒')time.sleep(10)except:passtry:location_start = pa.locateOnScreen('./fight.png', confidence=.8, limit=1, region=in_region)if location_start:# print(pa.center(location))start_center = pa.center(location_start)# 随机左右移动new_center_2 = (start_center.x+random.randint(-5,5),start_center.y+random.randint(-5,5))pa.click(new_center_2)time.sleep(10)run_fun(drag_num)global_count += 1print(f"释放卡牌程序执行了{global_count}次")time.sleep(20)except:# print('未找到开始游戏,休眠5秒')time.sleep(5)def drag_to(first_po,second_po):pa.moveTo(first_po,duration=.5)pa.dragTo(second_po,duration=.5,tween=pa.easeInOutQuad)# print("完成一次卡牌释放")def get_two_po():"""该模块为2560X1440分辨率下,其他分辨率可能出问题,如果点不到则大概率是高度的原因1.找到四张卡牌对应位置的中央2.找到公主塔后方,上述两个位置都会随机小范围浮动"""# 以左边第一张牌的大概中心为基准fixed_x = pa.size().width - 750 + 230fixed_y = pa.size().height - 180# 四张卡牌的中心点位置first_center = (fixed_x, fixed_y)second_center = (fixed_x + 140, fixed_y)third_center = (fixed_x + 280, fixed_y)fourth_center = (fixed_x + 420, fixed_y)# 公主塔的后方target_position_1 = (fixed_x, fixed_y - 260)target_position_2 = (fixed_x + 280, fixed_y - 260)# 随机选择一张卡来释放choice_1 = random.choice([first_center, second_center, third_center, fourth_center])# 起始、目标位置都进行一定的浮动choice_1 = (choice_1[0]+random.randint(-5,5),choice_1[1]+random.randint(-5,5))choice_2 = random.choice([target_position_1,target_position_2])choice_2 = (choice_2[0]+random.randint(-5,5),choice_2[1]+random.randint(-5,5))return choice_1,choice_2def click_some():choice_1,choice_2 = get_two_po()drag_to(choice_1,choice_2)def run_fun(drag_num):print('进入游戏,准备拖拽卡牌')for i in range(drag_num):click_some()time.sleep(3)if __name__ == '__main__':low_trophy(move_to_side())# CMD打包命令 pyinstaller -F -c low_tro.py