用Python實現跳一跳自動跳躍。

  • 2019 年 10 月 8 日
  • 筆記

經由前兩期的介紹,對於「跳一跳」自動化的實現,基本差不多了。

本期就來完整的跑一遍,快樂學習。

1. OpenCV:模板匹配。 獲得小跳棋中心位置

2. OpenCV:邊緣檢測。 獲得下一方塊中心位置

Python+ADB+OpenCv,實現「跳一跳」自動化。

/ 01 / ADB

ADB工具即Android Debug Bridge(Android調試橋) tools。

ADB是一個命令行窗口,用於通過電腦端與模擬器或者真實設備交互。

與之前小F接觸過的Appium有點相似。

ADB的安裝很簡單,就是將安裝包解壓後,將路徑添加到系統的環境變數中即可。

然後使用Python的os模組執行ADB命令。

def get_screenshot():      # 截取手機的螢幕      os.system('adb shell /system/bin/screencap -p /sdcard/screencap.png')      # 把模擬器裡面的文件或文件夾傳到電腦上      os.system('adb pull /sdcard/screencap.png screencap.png')      def jump(distance):      # 設置按壓時間,係數為1.35      press_time = int(distance * 1.35)        # 生成隨機手機螢幕模擬觸摸點,防止成績無效      # 生成隨機整數(0-9),最終數值為(0-90)      rand = random.randint(0, 9) * 10        # adb長按操作,即在手機螢幕上((320-410),(410-500))坐標處長按press_time毫秒      cmd = ('adb shell input swipe %i %i %i %i ' + str(press_time)) % (320 + rand, 410 + rand, 320 + rand, 410 + rand)        # 輸出adb命令      print(cmd)        # 執行adb命令      os.system(cmd)

本次涉及到的ADB命令,就只有三個,不多。

一個截屏,一個推送手機截圖到電腦上,最後模擬長按手機螢幕。

/ 02 / 跳動實現

先檢測遊戲結束畫面。

判斷是否需要結束遊戲程式。

# 遊戲結束的模板影像  temp_end = cv2.imread('end.jpg', 0)      def game_over(img):      """      模板匹配,檢測是否要將程式結束      """      # 如果在遊戲截圖中匹配到帶"再玩一局"字樣的模板,則循環中止      res_end = cv2.matchTemplate(img, temp_end, cv2.TM_CCOEFF_NORMED)      if cv2.minMaxLoc(res_end)[1] > 0.95:          print('Game over!')          return True

模板匹配原理圖如下。

當返回的最大矩陣值大於0.95時,則認為原始影像中肯定出現了再玩一局字樣。

則遊戲結束,程式也隨之結束。

小跳棋的模板匹配程式碼如下。

主要是獲取小跳棋的位置,即「跳一跳」起點位置參數。

# 讀取小跳棋模板影像  temple = cv2.imread('temple.png', 0)  # 獲取小跳棋模板影像的高和寬  th, tw = temple.shape[:2]      def get_start(img):      """      模板匹配,獲取跳一跳起點的位置參數(小跳棋)      """      # 使用標準相關係數匹配,1表示完美匹配,-1表示糟糕的匹配,0表示沒有任何相關性      result = cv2.matchTemplate(img, temple, cv2.TM_CCOEFF_NORMED)      # 使用函數minMaxLoc,確定匹配結果矩陣的最大值和最小值(val),以及它們的位置(loc)      min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)      # 得到小跳棋的中心位置參數      return max_loc[0] + 47, max_loc[1] + 208

得到結果如下。

下面通過OpenCV的邊緣檢測獲取「跳一跳」的終點位置。

def get_end(img):      """      邊緣檢測,獲取跳一跳終點的位置參數(方塊)      """      # 高斯模糊      img_rgb = cv2.GaussianBlur(img, (5, 5), 0)      # 邊緣檢測      canny_img = cv2.Canny(img_rgb, 1, 10)      # 獲得邊緣檢測影像的高和寬      H, W = canny_img.shape        # 第一個頂點的高度      y_top = np.nonzero([max(row) for row in canny_img[400:]])[0][0] + 400      # 第一個頂點的寬度      x_top = int(np.mean(np.nonzero(canny_img[y_top])))        # 跳過小白圈,然後遍歷      y_bottom = y_top + 80      for row in range(y_bottom, H):          if canny_img[row, x_top] != 0:              y_bottom = row              break        # 得到方塊的中心點      x_center, y_center = x_top, (y_top + y_bottom) // 2      return x_center, y_center

邊緣檢測原理圖如下。

最後便是主程式啦。

# 循環直到遊戲失敗結束  for i in range(10000):      # 將Android手機上的截圖移到電腦當前文件夾下      get_screenshot()      # 讀取截圖影像      img = cv2.imread('screencap.png', 0)        # 遊戲結束      if game_over(img):          break        # 得到起點位置參數      x_start, y_start = get_start(img)      # 獲取終點位置參數      x_end, y_end = get_end(img)        # 將起點位置繪製出來,一個圓      cv2.circle(img, (x_start, y_start), 10, 255, -1)      # 將終點位置繪製出來,一個圓      img_end = cv2.circle(img, (x_end, y_end), 10, 255, -1)      # 保存圖片      cv2.imwrite('end.png', img_end)        # 計算起點和終點的直線距離,勾三股四弦五      distance = (x_start - x_end) ** 2 + (y_start - y_end) ** 2      distance = distance ** 0.5        # 根據獲得的距離來設置按壓時長      jump(distance)      time.sleep(1.3)

下面就來看一下「跳一跳」自動跳躍的影片。

輕輕鬆鬆得分,毫無問題。

/ 03 / 總結

相關工具及程式碼已上傳網盤,公眾號回復「跳一跳」即可獲取。

安裝好ADB工具,然後通過數據線將Android手機和電腦連接。

最後運行程式碼,親測有效。

當然程式碼還是有待優化的,如下圖~

一方面是得分不高,另一方面就是會被檢測到作弊…

所以還有待改進呢!!!