跳一跳實訓
1 手機和電腦用數據線連接
在電腦上下載360手機助手,並通過數據線連接手機。首次連接需要安裝對應的手機驅動程序。(手機最好也下一個連接助理以保證連接的通暢),進入到保存微信跳一跳資源包的路徑並復制下來
進入cmd命令窗口
輸入adb命令
adb devices |
可以查看連接的Android設備的信息
運行結果:
2 獲取手機相關的信息
通過如下命令可以查看連接電腦的Android手機相關的信息
adb shell dumpsys window displays |
|
運行結果:
在第3行可以看到手機的分辨率(自己加個紅框框)
獲取屏幕密度
|
運行結果:
獲取手機型號
adb shell getprop ro.product.device |
運行結果:
獲取Android系統的版本
adb shell getprop ro.build.version.release |
運行結果:
3 截屏
輸入如下命令:
adb shell screencap -p /sdcard/auto.png |
此時,截屏的圖片就保存到 /sdcard/auto.png文件中。
註意:/sdcard/和/data/目錄是可以寫入的。
一般手機是內置了sdcard的,而且data是沒有權限的(反正我的沒有),所以大膽寫進sdcard
可以通過命令
adb shell ls /sdcard/ -l |
查看sdcard目錄下所有的文件。
通過如下命令把手機上的文件拷貝到電腦上
adb pull /sdcard/auto.png d:\ |
此時,圖片就會被拷貝到d:\根目錄下了。打開即可看到當前手機的屏幕信息。
註意:這時候手機最好是調到跳一跳遊戲開始的界面,下一步模擬微信跳一跳的時候才有效果。
4 屏幕點擊事件
通過如下命令模擬點擊手機屏幕的事件
adb shell input swipe x1 x2 y1 y2 duration |
通過adb shell input swipe命令進行滑動
X1、x1:滑動開始的點
Y1、y2:滑動結束的點
Duration:持續的時間(單位ms)
特殊情況:如果不寫duration參數,就理解為點擊事件。如果寫duration,然後x1x2和y1y2是相同的點,就表示長按
跳一跳的關鍵是:duration的計算
嘗試:
adb shell input swipe 100 100 100 700 |
這個700是改變的值,(跳一跳的第一步),改變700這個參數值試出得2分的範圍
註意一個問題: 手機要進入usb調試,將usb模擬點擊的按鈕打開,不然會不成功
值 |
得分 |
716 |
2 |
710 |
2 |
700 |
1 |
705 |
1 |
708 |
2 |
720 |
2 |
724 |
2 |
728 |
2 |
730 |
2 |
742 |
1 |
741 |
2 |
最大範圍是 [708,741] 最中間值為:724.5
5 duration值的計算
假設我們截屏的效果是如下:
從圖中可以看到,時間的值跟開始位置到結束位置的距離有關。
假設時間是t,距離是s。公式應該是s = at
基本思路:兩點之間的距離乘以一個時間系數。
所以要從截圖上識別出起跳位置的坐標(x1,y1)和目標位置的坐標(x2,y2)。
起跳位置的坐標:小人的底座中心點
目標位置的坐標:目標菱形的中心點
然後計算這兩點之間的距離(歐氏距離):sqrt((x1-x2)2+(y1-y2)2)
6 截屏的代碼
創建img目錄,後面把所有截屏的圖片都放到該目錄下(原則上每跳一步都需要截屏一次)
operation.py
import os
|
main.py
from .operation import * |
# 測試截屏
|
運行結果:
7 顯示圖片的代碼
需要安裝matplotlib庫
pip install matplotlib |
draw.py
import matplotlib.pyplot as plt # 繪圖
|
main.py
# 測試顯示圖片
|
運行結果:
8 計算兩點間的歐氏距離
Algorithm.py
#-*- coding:utf-8 -*-
|
測試偶是歐氏距離main.py
測試歐式距離
|
運行結果:
9 尋找關鍵坐標——框架
在alogrithm.py加入方法fine_point()
def fine_point(self):
|
測試關鍵坐標
Main.py
#測試尋找關鍵坐標
|
運行結果:
10 獲取每一個點的RGB值
# 尋找關鍵坐標 # 返回值1,2 piece_x, piece_y 起跳點的坐標 170,555 # 返回值3,4 board_x, board_y 目標點的坐標 395,425 def find_point(self,im): piece_x = piece_y = 0 board_x = board_y = 0
# 圖像的大小 w,h = im.size # (540, 960) # 加載圖像 im_pixel = im.load()
# 遍歷圖像中的每一個點 # 遍歷每一行 for i in range(h): # 遍歷每一列 for j in range(w):
pixel = im_pixel[j,i] print("i = ", i, ",j = ", j, "pixel = ", pixel)
|
測試代碼如下:
# 測試尋找關鍵坐標 def test_find_point(): op = Operation() im = op.screen_cap() algorithm = Algorithm() start_x, start_y, end_x, end_y = algorithm.find_point(im) print("start_point:", start_x, start_y) print("end_point:", end_x, end_y)
|
11 尋找關鍵坐標——起跳坐標
算法策略:獲取小人的底座中心點的值作為起跳點。
1 獲取小人的所有像素點中y坐標的最大值
2 在小人y坐標的最大值那些像素點中,計算出x的平均值,作為小人底座的x的值。
3 y坐標的最大值減去一個偏移值,就作為小人底座的y值。(註意:該偏移值不同的設備是不同的,同一臺設備不同場景下是一樣的)
比如教師機的設備中最低點的值是(168,565),中心值是 (168,555),從而計算出偏移值為565-555=10
11.1 獲取小人y坐標的最大值
需要從上往下一行行掃描像素點,直到找到小人位置。
# 尋找關鍵坐標 # 返回值1,2 piece_x, piece_y 起跳點的坐標 170,555 # 返回值3,4 board_x, board_y 目標點的坐標 395,425 def find_point(self,im): piece_x = piece_y = 0 board_x = board_y = 0
# 圖像的大小 w,h = im.size # (540, 960) # 加載圖像 im_pixel = im.load()
# 記錄y的最大值 piece_y_max = 0
# 1 計算出起跳點 就是小人底座的中心點 # 1.1 獲取小人的所有像素點中y坐標的最大值 # 遍歷圖像中的每一個點 # 遍歷每一行 for i in range(h): # 遍歷每一列 for j in range(w):
pixel = im_pixel[j,i] #print("i = ", i, ",j = ", j, "pixel = ", pixel)
# 判斷pixel是否小人所在的位置 # 當該點的RGB值約為55,59,102的時候就可以認為是小人所在的像素點了 if(50 < pixel[0] < 60 and 53 < pixel[1] < 63 and 95 < pixel[2] < 110): # 記錄下y的值 if i > piece_y_max: piece_y_max = i
print("piece_y_max = %d" % (piece_y_max,))
# 1.2 在小人y坐標的最大值那些像素點中,計算出x的平均值,作為小人底座的x的值。
# 1.3 y坐標的最大值減去一個偏移值,就作為小人底座的y值。(註意:該偏移值不同的設備是不同的,同一臺設備不同場景下是一樣的)
return piece_x, piece_y, board_x, board_y
|
11.2 獲取小人底座的x坐標
記錄下小人所有的點。
# 尋找關鍵坐標 # 返回值1,2 piece_x, piece_y 起跳點的坐標 170,555 # 返回值3,4 board_x, board_y 目標點的坐標 395,425 def find_point(self,im): piece_x = piece_y = 0 board_x = board_y = 0
# 圖像的大小 w,h = im.size # (540, 960) # 加載圖像 im_pixel = im.load() # 記錄小人所有的點 points = []
# 記錄y的最大值 piece_y_max = 0
# 1 計算出起跳點 就是小人底座的中心點 # 1.1 獲取小人的所有像素點中y坐標的最大值 # 遍歷圖像中的每一個點 # 遍歷每一行 for i in range(h): # 遍歷每一列 for j in range(w):
pixel = im_pixel[j,i] #print("i = ", i, ",j = ", j, "pixel = ", pixel)
# 判斷pixel是否小人所在的位置 # 當該點的RGB值約為55,59,102的時候就可以認為是小人所在的像素點了 if(50 < pixel[0] < 60 and 53 < pixel[1] < 63 and 95 < pixel[2] < 110): # 把當前的點添加到points數組中 points.append((j,i)) # (x,y) # 記錄下y的值 if i > piece_y_max: piece_y_max = i
print("piece_y_max = %d" % (piece_y_max,))
# 1.2 在小人y坐標的最大值那些像素點中,計算出x的平均值,作為小人底座的x的值。 bottom_x = [] for x,y in points: if y == piece_y_max: bottom_x.append(x)
piece_x = sum(bottom_x) // len(bottom_x) print("piece_x = %d" % (piece_x,))
piece_y=piece_y_max-self.piece_base_height
# 1.3 y坐標的最大值減去一個偏移值,就作為小人底座的y值。(註意:該偏移值不同的設備是不同的,同一臺設備不同場景下是一樣的)
return piece_x, piece_y, board_x, board_y
|
|
運行結果:
12 優化程序
無論是起跳位置,還有目標位置。都只取垂直的中間的1/3樣式進行掃描。
13 尋找關鍵坐標——目標坐標
#-*-
coding:utf-8 -*-
|
運行結果:
13. 1 獲取目標坐標的y值
取屏幕寬和高的一半(x=560和y=960)
我們會發現,目標格子的邊沿(x=568,y=980)和這個是差不多的(y的偏差是20,x的偏差是8)
以後每次跳動的時候,假如已經知道目標格子的邊沿,和目標坐標的x值,就可以很輕松計算出目標坐標的y值。
註意:每個格子的寬和高的比例是相同的。
左:(568,850)
右:(1243,850)
上:(1023,715)
下:(1023,980)
中:(1023,850)
高和寬的比例:(980-715)/(1243-568) =265/675=53/135。假設該值為p
已經知道目標坐標的x值,求目標坐標的y值
# 2.2計算目標格式子y值 # 屏幕中心的值 center_x = w / 2 + 8 # x的偏差是8 center_y = h / 2 + 20 # y的偏差是20 # 格子高和寬的比例 height_per_width = 265 / 675 # 計算出目標格子的y值(需要轉換成整數) board_y = int(center_y - height_per_width * (board_x - center_x)) print("board_y = %d" % (board_y,))
|
運行結果:
13.3 區分從左往右跳和從右往左跳
Algorithm.py 方法:find_point
def fine_point(self,img): … … …
|
14 初步估算距離與時間的比例
algorithm.py
# 距離與時間的轉換 def distance_to_time(self, distance): # 當0分的時候 距離為 261.222128 時間為730 p = 730 / 261.222128 # 該算法後面待優化 press_time = distance * p return press_time
|
15 控制屏幕進行跳動
operation.py
# 控制屏幕進行跳動 def jump(self, src, dst, press_time): press_time = int(press_time) cmd = "adb shell input swipe %d %d %d %d %d" % ( int(src[0]), int(src[1]), int(dst[0]), int(dst[1]), press_time ) print(cmd) os.system(cmd)
|
main.py
def test_jump(): algorithm = Algorithm() op = Operation() im = op.screen_cap() start_x, start_y, end_x, end_y = algorithm.find_point(im) start_point = (start_x, start_y) end_point = (end_x, end_y) distance = algorithm.euclidean_distance(start_point, end_point) press_time = algorithm.distance_to_time(distance) op.jump(start_point, end_point, press_time) |
END
遇到的問題
問題1:pychram運行main.py時出現“adb的亂碼”
解決:將adb的目錄添加到電腦的系統環境變量中,之後重啟pycharm
問題2:運行是operation時候img目錄沒有圖像生成
解決:
def screen_cap(self):
filename = time = datetime.datetime.now().strftime("%H%M%S") + ".png"
# 截屏並保存到手機的目錄上
cmd = "adb shell screencap -p /sdcard/" + filename
os.system(cmd)
# 把手機目錄上的文件拷貝到PC上
cmd = "adb pull /sdcard/" + filename + "img/" + filename
os.system(cmd)
#打開圖像
return
Image.open(‘img/‘+filename)
在cmd = "adb pull /sdcard/" + filename + "img/" + filename中 img前面沒有添加空格
更改如下:cmd = "adb
pull /sdcard/" +
filename + " img/"
+ filename
跳一跳實訓