最近和跟着同事一起玩陰陽師,發現這個游戲有太多重復操作了,這完全就是浪費生命啊;所以想到用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()