前言
微信跳一跳目前很火信柿,然后由于大家想獲得朋友圈較高的排名,這和一開始的微信運動步數(shù)一樣,出現(xiàn)了手機掛狗脖子上刷步數(shù)的現(xiàn)象宏胯,這回微信跳一跳也有外掛了,本文和你分享一下本人制作輔助思路
分析
首先玩過游戲的都知道本姥,小人會根據(jù)按壓的時長來跳相應的距離胳嘲,這個按壓時長和距離一定是成正比的,為啥這么說扣草?應該這是這個游戲的核心玩法,就是要通過玩家掌握好這個按壓時長才能得到較準確的跳躍距離颜屠,若不成正比辰妙,那這個游戲簡直太難掌握了,根本不能很好的操作小人甫窟。
這樣的話密浑,只要能夠準確計算出兩個點之間的距離,既能計算出按壓時長粗井,計算距離尔破,那就要找到起點和終點,這是做這個輔助的重點浇衬。
與游戲交互
做為一個Android開發(fā)者一定知道:ADB懒构,我們則可以通過ADB對手機進行截屏即拿到了游戲界面:
進行屏幕截圖
screencap 命令是一個用于對設備顯示屏進行屏幕截圖的 shell 實用程序。在 shell 中耘擂,此語法為:
screencap filename
要從命令行使用 screencap胆剧,請輸入以下命令:
$ adb shell screencap /sdcard/screen.png
以下屏幕截圖會話示例向您展示使用 adb shell 捕獲屏幕截圖,并使用 pull 命令從設備下載此文件:
$ adb shell
shell@ $ screencap /sdcard/screen.png
shell@ $ exit
$ adb pull /sdcard/screen.png
游戲界面拿到之后需要去分析圖片醉冤,拿到像素點信息秩霍,很顯然在python里要用到了PIL(python imaging library)這個庫,通過PIL去獲取各個像素點信息:
im = Image.open('image_file_path')
pix = im.load()
r,g,b,a = pix[x,y]
圖像中一個像素點用32位表示蚁阳,RGBA 各8位铃绒,所以我們用0~255來表示色值,RGBA組合呈現(xiàn)不同的顏色螺捐,但是一般常見的PNG是有alpha通道颠悬,而JPG是沒有alpha通道的。
基礎講完了归粉,我們開始來找起點和終點了椿疗!
找起點
首先我們都知道方塊的中心就是一個終點,觀察一下起始狀態(tài):
小人起始時肯定是在中心點的糠悼,通過對角線可以看出小人圓臺底部圓心即為起點届榄。那么小人的起點就是:紫色最寬的區(qū)域的中點即為小人的橫坐標;紫色的最低點即為小人的縱坐標倔喂。
代碼:
im = Image.open('./temp_screen.png')
pix = im.load()
min_point_x = 65535
max_point_x = 0
max_point_y = 0
global start_x
global start_y
for x in range(190,910):
for y in range(1000,1250):
r,g,b,a = pix[x,y]
if abs(r - 56) < 5 and abs(g-59) < 5 and abs(b-102) < 5:
min_point_x = min(min_point_x,x)
max_point_x = max(max_point_x,x)
start_y = y
start_x = (min_point_x + max_point_x) / 2
return start_x,start_y
找終點
對于終點我們可以觀察得出一個簡單的規(guī)律铝条,終點和起點總是在一條直線上靖苇,那么我們就可以算出直線函數(shù):
(x0,y0)為起點,斜率 k 算出來是1.6(PS量一下兩個點的坐標班缰,縱坐標之差與橫坐標之差比)贤壁,這樣的話終點在小人的左側直線斜率即為-1.6,在右側直線斜率即為1.6埠忘。然后只通過斜率算出終點的橫縱坐標是不現(xiàn)實的脾拆,至少知道橫坐標或者縱坐標。
那我們分析一下橫坐標好算還是縱坐標好算呢莹妒?顯然是橫坐標名船!
- 對于菱形來說終點的橫坐標不就是最高的那個點么?
- 對于圓來說最上面可能是一條線段旨怠,那橫坐標不就是線段的中點么渠驼?
- 好像沒有其它圖形了...
至此我們的問題轉換成查找方塊最上方的點的橫坐標,繼而計算出終點的縱坐標鉴腻。
從上至下一排排的掃描迷扇,直至找到最高點。而背景是一個漸變色爽哎,就需要我們每一排都要去更新一次背景色蜓席,再找這一行是否存在與背景色不一樣的像素點。
因為方塊顏色和背景圖相差較大倦青,這里用灰度值來進行比較瓮床,RGB轉灰度圖:
gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
如果被比較的像素點與背景的灰度值之差小于5即認為非背景點,并保持本行所有非背景點产镐,算出橫坐標隘庄,進而算出縱坐標即可,代碼如下:
im = Image.open('./temp_screen.png')
img_L = im.convert("L")
list_x = []
is_end = False
for y in xrange(400,1100):
base_pix = img_L.getpixel((1,y))
for x in xrange(100,910):
pix = img_L.getpixel((x,y))
if abs(base_pix - pix) > 5 :
if abs(x - start_x) > 100:
list_x.append(x)
is_end = True
if is_end:
end_x = sum(list_x)/len(list_x)
if end_x < start_x:
end_y = start_y-(start_x-end_x)/1.6
else:
end_y = start_y-(end_x-start_x)/1.6
return end_x,end_y
操作
既然我們已經(jīng)算出起點和終點了癣亚,則可以算出兩點之間的距離:
sx,sy = cal_start()
ex,ey = cal_end()
distance = (sx - ex)**2 + (sy - ey)**2
distance = distance ** 0.5
操作小人
這個還是利用了ADB的shell命令:
def jump(distance):
press_time = int(distance * 1.35)
cmd = 'adb shell input swipe 320 410 320 410 ' + str(press_time)
os.system(cmd)
循環(huán)連續(xù)跳
最終加上循環(huán)代碼:
def start():
while True:
pull_screenshot()
time.sleep(1.0)
im = Image.open('./temp_screen.png')
img_L = im.convert("L")
ll = img_L.getpixel((5,5))
if abs(ll - 47) < 5:
return
sx,sy = cal_start()
ex,ey = cal_end()
distance = (sx - ex)**2 + (sy - ey)**2
distance = distance ** 0.5
print 'distance =', distance
jump(distance)
time.sleep(2.0)
結果
寫在最后:
第一次想到做輔助腳本還是看到了 wangshub 在知乎上發(fā)布的第一個版本丑掺,那時候還利用了matplotlib去手動點擊起點和終點,實現(xiàn)跳一跳述雾,由于在上班并沒有去改代碼街州,但是做為一個有圖像知識基礎的Android開發(fā)者,很快想好思路玻孟,回去即著手寫本文的代碼唆缴,晚上寫好后,朋友說人家已經(jīng)更新了黍翎,刷新一看面徽,各個版本已經(jīng)push上去了。然后看star數(shù)一天一天漲到了8K+,并且圖片識別趟紊、AI技術層出不窮氮双,完全看到一個python學習熱潮。比對發(fā)現(xiàn)本文方法略為簡單霎匈,深深感受到學習的道路很長很長戴差,還需不斷的去努力,爭取做那個引領風騷的人铛嘱!