最近和跟着同事一起玩阴阳师,发现这个游戏有太多重复操作了,这完全就是浪费生命啊;所以想到用python写一个自动挂机脚本。
最开始想得很简单,就是一直去找相应得按钮,然后点击就可以了。所以直接用pyautogui的图片定位和点击功能就行了,也确实实现了,代码如下:
import pyautogui,time pyautogui.FAILSAFE = True \'\'\'PyAutoGUI提供了一个保护措施。当pyautogui.FAILSAFE = True时,如果把鼠标光标在屏幕左上角, PyAutoGUI函数就会产生pyautogui.FailSafeException异常,用于在程序失控时退出\'\'\' time.sleep(2) def get_point(picture): \'\'\'精确匹配某个按钮的位置;通过传入图片获取图片在屏幕上的定位,一旦获取到值则退出,否则继续尝试\'\'\' picture = \'./img/\' + picture count = 5 while count > 0: point = pyautogui.locateCenterOnScreen(picture) if point is not None: return point else: count -= 1 def get_range(picture): \'\'\'用模糊匹配得到某个按钮的大概位置,一般是在那种点击屏幕任意位置的情况,或只需要知道某个按钮在不在当前屏幕的情况使用 要动的图片采用模糊匹配,否则精确匹配的话,图片一直在动像素也在变化,就不能定位到了\'\'\' picture = \'./img/\' + picture count = 5 while count > 0: range = pyautogui.locateCenterOnScreen(picture, grayscale=True, confidence=0.5) if range is not None: return range else: count -= 1 def click_button(picture,accurate=True): \'\'\'点击按钮的函数,默认精确度为True,即默认为精确点击,如果accurate=False,则为模糊点击,用于动态图形按钮\'\'\' if accurate==True: action = get_point(picture) else: action = get_range(picture) if action is not None: pyautogui.click(action,duration=0.5) def cycle_fight(): \'\'\'用户循环重复的战斗场景,如果刷觉醒材料,刷御魂\'\'\' while True: click_button(\'tiaozhan.PNG\') click_button(\'zhunbei.PNG\') click_button(\'over.PNG\') cycle_fight()
最后发现这样似乎不科学,不仅慢还浪费资源;然后又想到在准备完成和战斗结束这段时间,有很长的空白期,完全可以让程序停止啊,所以又出现了下面的代码:
import pyautogui,time
pyautogui.FAILSAFE = True
\'\'\'PyAutoGUI提供了一个保护措施。当pyautogui.FAILSAFE = True时,如果把鼠标光标在屏幕左上角,
PyAutoGUI函数就会产生pyautogui.FailSafeException异常,用于在程序失控时退出\'\'\'
time.sleep(2)
def get_point(picture):
\'\'\'精确匹配某个按钮的位置;通过传入图片获取图片在屏幕上的定位,一旦获取到值则退出,否则继续尝试\'\'\'
picture = \'./img/\' + picture
count = 5
while count > 0:
point = pyautogui.locateCenterOnScreen(picture)
if point is not None:
return point
else:
count -= 1
def get_range(picture):
\'\'\'用模糊匹配得到某个按钮的大概位置,一般是在那种点击屏幕任意位置的情况,或只需要知道某个按钮在不在当前屏幕的情况使用
要动的图片采用模糊匹配,否则精确匹配的话,图片一直在动像素也在变化,就不能定位到了\'\'\'
picture = \'./img/\' + picture
count = 5
while count > 0:
range = pyautogui.locateCenterOnScreen(picture, grayscale=True, confidence=0.5)
if range is not None:
return range
else:
count -= 1
def click_button(picture,accurate=True):
\'\'\'点击按钮的函数,默认精确度为True,即默认为精确点击,如果accurate=False,则为模糊点击,用于动态图形按钮\'\'\'
if accurate==True:
action = get_point(picture)
else:
action = get_range(picture)
if action is not None:
pyautogui.click(action,duration=0.5)
return True #点击成功则返回True
else:
return None #通过这个返回值判定有没有点击成功
def on_fight(sec):
\'\'\'这个函数用于模拟从【准备】到【战斗结束】这段时间,在这段时间里程序应该是阻塞的,这样就不用一直去找对应的按钮,从而消耗大量的系统资源\'\'\'
fight_run = click_button(\'zhunbei.PNG\') #点击准备按钮则战斗开始
if fight_run is not None: #如果成功点击了准备按钮则代表战斗开始,程序进入寻找【战斗结束按钮】的状态
count = 0
while True:
time.sleep(sec) #设定每几秒检测一次战斗是否结束,这个值可以具体情况设置
fight_over = click_button(\'over.PNG\')
if fight_over is not None:
return True
else:
count +=1 #记录循环次数
if count*sec > 600:
\'\'\'用循环次数乘以中断时间,大约等于战斗过程的时间,这里的意思是战斗过程大于10分钟,一般这种情况,肯定
是在点击了准备之后,战斗过程中异常中断,这时候程序会一直陷入这个寻找战斗结束的死循环中,但这是没有意义的,
所以直接退出整个程序\'\'\'
exit(1)
else:
return None #如果没有点击【准备】按钮,则返回为空,继续进入下一次寻找进入战斗起始按钮的过程
def cycle_fight(sec):
\'\'\'用户循环重复的战斗场景,如果刷觉醒材料,刷御魂\'\'\'
while True:
click_button(\'tiaozhan.PNG\')
on_fight(sec)
def story_task():
\'\'\'用于过废话连篇的剧情\'\'\'
while True:
click_button(\'tiaoguo.PNG\')
click_button(\'dialogue.PNG\')
click_button(\'storyJump.PNG\')
def explore_task(sec):
\'\'\'用于过探索副本\'\'\'
while True:
click_button(\'fight.PNG\',accurate=False)
click_button(\'masterFight.PNG\',accurate=False)
on_fight(sec)
#explore_task()
# cycle_fight(10)
story_task()
这样一来虽然降低了系统资源消耗但代码逻辑变得极为复杂,然后便想到用多线程封装,之后效率确实极大的提升了:
import pyautogui,time
import threading
pyautogui.FAILSAFE = True
\'\'\'PyAutoGUI提供了一个保护措施。当pyautogui.FAILSAFE = True时,如果把鼠标光标在屏幕左上角,
PyAutoGUI函数就会产生pyautogui.FailSafeException异常,用于在程序失控时退出\'\'\'
time.sleep(2)
class FindButton(threading.Thread):
def __init__(self, picture):
super(FindButton, self).__init__()
self.picture = \'./img/\' + picture
def run(self):
while True:
self.point = pyautogui.locateCenterOnScreen(self.picture, confidence=0.8)
if self.point is not None:
pyautogui.click(self.point, duration=0.5)
challenge = FindButton(\'tiaozhan.PNG\')
prepare = FindButton(\'zhunbei.PNG\')
over = FindButton(\'over.PNG\')
challenge.start()
prepare.start()
over.start()
但是这样一来,系统资源的消耗也成倍的增加了,相当于每多一个点击操作,就要多使用一倍的系统资源。那不如用协程来解决吧,既是单线程,又可以异步进行;
最后证明协程比多线程稍慢(毕竟协程是需要排队的,协程直接的切换也是需要消耗时间的),比单线程则是快了太多了
而且消耗的系统资源也极大的降低了(甚至比单线程的情况还低),并且代码也简单了太多了!
import gevent import pyautogui def click(picture): picture = \'./img/\' + picture while True: point = pyautogui.locateCenterOnScreen(picture, confidence=0.8) if point is None: gevent.sleep(1) else: pyautogui.click(point, duration=0.5) def cycle_fight(): \'\'\'用于循环重复的战斗场景,如果刷觉醒材料,刷御魂\'\'\' gevent.joinall([ #利用joinall方法将每一步操作加入协程池中 gevent.spawn(click,\'tiaozhan.PNG\'), #每一个协程的加入方法是:(函数名,参数) gevent.spawn(click,\'zhunbei.PNG\'), gevent.spawn(click,\'over.PNG\') ]) def story_task(): \'\'\'\'用于过废话连篇的剧情任务\'\'\' gevent.joinall([ gevent.spawn(click, \'dialogue.PNG\'), gevent.spawn(click, \'tiaoguo.PNG\'), gevent.spawn(click, \'storyJump.PNG\'), gevent.spawn(click, \'fight.PNG\'), gevent.spawn(click, \'zhunbei.PNG\'), gevent.spawn(click, \'over.PNG\') ]) def explore_task(): \'\'\'用于过探索副本\'\'\' gevent.joinall([ gevent.spawn(click, \'fight.PNG\'), gevent.spawn(click, \'masterFight.PNG\'), gevent.spawn(click, \'zhunbei.PNG\'), gevent.spawn(click, \'over.PNG\') ]) cycle_fight()