寫在前面
分享一下今天下午用python寫的“跳一跳”小游戲的輔助程序。之前是準備用樹莓派操控一個“機械手指”來代替人的觸摸操作,但該方案還在醞釀中,實現了再分享。接下來要分享的是用“純軟件”的方法來玩“跳一跳”。
原理
原理其實很簡單,按如下步驟操作即可:
- 每次跳躍之前,截取一下手機屏幕,并將截圖保存到本地電腦中;
- 計算截圖中人偶的位置與將要跳至的臺面中心的距離dd;
- 將以上距離dd換算成相應的觸摸時間ss;
- 發送模擬觸摸的命令至手機,觸摸時間為以上時間ss;
實現
本人只做過Android開發,因此下面只給出Android平臺下的實現方法。
步驟1
可以用Android官方提供的adb工具來完成。首先需要搜索并下載對應操作系統下adb工具。其次需要將手機連接電腦, 并將手機的 設置 > 開發人員選項 > USB調試打開。現在在命令行調用一下adb工具,看是否檢查到手機:
adb devices
PS:若將adb路徑添加到了PATH環境變量中,則可直接在命令行調用adb;否則以上命令需要輸入adb的全路徑。
若執行以上命令后,輸出了設備相關信息,則說明手機連接成功,可繼續以下操作。
用如下命令可截取手機屏幕圖片至SD卡保存:
1
|
adb shell screencap -p /mnt/sdcard/screencap.png |
然后可用如下命令pull圖片到電腦:
1
|
adb pull /mnt/sdcard/screencap.png C:/screencap.png |
步驟2
是整個問題的關鍵。要計算出人偶與將要跳至的臺面中心的距離,需要分別識別出人偶的位置(坐標)和臺面中心的位置(坐標)。
我們以人偶最底部的一行的中心作為人偶的位置,如下圖所示:
至于怎么識別出人偶的最底部,可以這樣來操作。通過觀察可發現,人偶底部的顏色的rgb值在(53, 57, 95)到(59, 61, 103)之間,因此我們逐行掃描各個像素點,找到rbg值在該區間的各行,最后一行即為人偶的底部了。得到了最底部的一行,自然就能算出該行的中心坐標。
接下來需要識別人偶將要跳至的平臺的中心。要想得到該中心的坐標,我們只需要識別得到下圖中的兩個頂點vertex1和vertex2的坐標即可:
我們同樣用從左往右,從上往下的順序掃描各個像素點的方法來找出vertex1的坐標。掃描之前先獲取整個背景的顏色的rgb值,取任意“空白”處即可(例如本人手機截圖大小為1920x1080,可斷定坐標為(40, 500)的點一定處于“空白”處。)。在掃描過程中一旦發現某處的顏色與背景色不一致,發生了“突變”,可斷定該點即為vertex1。
我們把vertex1點的rgb值記錄下來作為臺面的背景色。在接下去的掃描過程中,我們開始關心當前掃描的點的rgb值是否和該記錄值“相似”。“相似”則說明該點“屬于”臺面,而通過上圖可發現,頂點vertex2是所有“屬于”臺面的點中,橫坐標最小的點,這樣vertex2的坐標也找到了。
顯然,臺面中心的橫坐標等于vertex1的橫坐標,而縱坐標等于vertex2的縱坐標。
步驟3
通過多次嘗試,發現用如下公式轉換距離dd(單位:px)為時間ss(單位:毫秒)比較合適:
1
2
|
s=d∗ 1.35 s=d∗ 1.35 |
步驟4
得到了觸摸時間,我們還是借助adb工具來模擬觸摸屏幕的行為,以下是相關命令:
1
|
adb shell input swipe 0 0 0 0 1000 |
以上命令的最后一個參數即為需要模擬按壓屏幕的時長,單位是毫秒。
實現效果
成功連接手機至電腦(手機需開啟USB調試),并進入“跳一跳”游戲,然后到電腦上運行該代碼即可自動“跳一跳”。
上一張截圖:
完整代碼
以下是完整代碼,在本人手機(1920 * 1080 )下測試發現大多數情況都能正中靶心,少數情況不能命中靶心,極少數情況會跳出臺面以外。其他分辨率的手機可能需要適當修改BACKGROUND_POS和DISTANCE_TO_TIME_RATIO參數大小。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
import math import os import tempfile import time from functools import reduce from PIL import Image BACKGROUND_POS = ( 40 , 500 ) DISTANCE_TO_TIME_RATIO = 1.35 SCREENSHOT_PATH = tempfile.gettempdir() + "/screenshot.png" def calculate_jump_distance(): im = Image.open(SCREENSHOT_PATH) background_rgb = im.getpixel(BACKGROUND_POS) role_pos_list = None vertex1_pos = None block_background_rgb = None vertex2_pos = None role_line_flag = True for y in range(BACKGROUND_POS[ 1 ], im.height): if role_pos_list and role_line_flag: break role_line_flag = True vertex2_line_flag = True for x in range(BACKGROUND_POS[ 0 ], im.width): current_rgb = im.getpixel((x, y)) next_rgb = im.getpixel((x + 1 , y)) if x + 1 < im.width else ( 0 , 0 , 0 ) # 識別頂點 1 if x > BACKGROUND_POS[ 0 ] and y > BACKGROUND_POS[ 1 ] and not vertex1_pos \ and not is_similar(background_rgb, current_rgb) and is_similar(current_rgb, next_rgb): vertex1_pos = (x, y) block_background_rgb = current_rgb # 識別頂點 2 if block_background_rgb and vertex2_line_flag and is_similar(current_rgb, block_background_rgb, 5 ): vertex2_line_flag = False if vertex2_pos: if x < vertex2_pos[ 0 ] and vertex2_pos[ 0 ] - x < 20 and y - vertex2_pos[ 1 ] < 20 : vertex2_pos = (x, y) else : vertex2_pos = (x, y) # 識別小人 if is_part_of_role(current_rgb): if role_line_flag: role_pos_list = [] role_line_flag = False role_pos_list.append((x, y)) if len(role_pos_list) == 0 : raise Exception( '無法識別小人位置!!!' ) pos_sum = reduce((lambda o1, o2: (o1[ 0 ] + o2[ 0 ], o1[ 1 ] + o2[ 1 ])), role_pos_list) role_pos = ( int (pos_sum[ 0 ] / len(role_pos_list)), int (pos_sum[ 1 ] / len(role_pos_list))) destination_pos = (vertex1_pos[ 0 ], vertex2_pos[ 1 ]) return int (linear_distance(role_pos, destination_pos)) def is_part_of_role(rgb): return 53 < rgb[ 0 ] < 59 and 57 < rgb[ 1 ] < 61 and 95 < rgb[ 2 ] < 103 def linear_distance(xy1, xy2): return math.sqrt(pow(xy1[ 0 ] - xy2[ 0 ], 2 ) + pow(xy1[ 1 ] - xy2[ 1 ], 2 )) def is_similar(rgb1, rgb2, degree= 10 ): return abs(rgb1[ 0 ] - rgb2[ 0 ]) <= degree and abs(rgb1[ 1 ] - rgb2[ 1 ]) <= degree and abs(rgb1[ 2 ] - rgb2[ 2 ]) <= degree def screenshot(): os.system( "adb shell screencap -p /mnt/sdcard/screencap.png" ) os.system( "adb pull /mnt/sdcard/screencap.png {} >> {}/jump.out" .format(SCREENSHOT_PATH, tempfile.gettempdir())) def jump(touch_time): os.system( "adb shell input swipe 0 0 0 0 {}" .format(touch_time)) def distance2time(distance): return int (distance * DISTANCE_TO_TIME_RATIO) if __name__ == '__main__' : count = 1 while True: screenshot() distance = calculate_jump_distance() touch_time = distance2time(distance) jump(touch_time) print( "#{}: distance={}, time={}" .format(count, distance, touch_time)) count += 1 time.sleep( 1 ) |
總結
以上所述是小編給大家介紹的100行python代碼實現跳一跳輔助程序,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:https://www.cnblogs.com/dongkuo/p/8285162.html