1. 程式人生 > >跳一跳實訓

跳一跳實訓

euc utf resize UC 像素點 dumpsys 自己 calc 截圖

  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
import datetime

from PIL import Image
# 實現控制Android設備等相關的操作

class Operation:
# 構造方法
def __init__(self):
pass

# 截屏
def screen_cap(self):
filename = time = datetime.datetime.now().strftime("%H%M%S") + ".png"
# 截屏並保存到手機的目錄上
cmd = "adb shell screencap -p /sdcard/auto.png"
os.system(cmd)
# 把手機目錄上的文件拷貝到PC上
cmd = "adb pull /sdcard/auto.png" + " img/" + filename
os.system(cmd)

# 打開圖像文件
return Image.open(filename)

main.py

from  .operation import *
# 測試截屏
def test_screen_cap():
op = Operation()
im = op.screen_cap()

運行結果:

技術分享圖片

7 顯示圖片的代碼

需要安裝matplotlib庫

pip install matplotlib

draw.py

import matplotlib.pyplot as plt # 繪圖
import cv2 # 讀取圖片文件

# 實現顯示圖片 繪制圖片等功能
class Draw:
# 構造器
def __init__(self):
# 初始化圖像plt對象
self.fig = plt.figure()

# 顯示圖片
def show_pic(self, filename,scale=1):
# 讀取圖像
img = cv2.imread(filename)
# 調整顯示的比例
img = cv2.resize(img, (0,0), fx=scale, fy=scale)
# 顯示圖像
plt.imshow(img)
plt.show()

main.py

# 測試顯示圖片
def test_show_pic():
draw = Draw()
draw.show_pic("img/155900.png")

運行結果:

技術分享圖片

8 計算兩點間的歐氏距離

Algorithm.py

#-*- coding:utf-8 -*-
#author:zhengjinwei
#data: 2018.6.28
#
計算兩點之間的歐式距離
import math

class Algorithm:
def __init__(self):
pass

def
calculate_distance(self,p1,p2):
#計算歐式距離,用兩點p1,p2表示距離
return ((p2[0]-p1[0])**2+(p2[1]-p1[1])**2)*0.5

def fine_point(self):
"""
#
尋找關鍵坐標
# 返回值1,2 start_x, start_y 起跳點的坐標 170,555
# 返回值3,4 end_x, end_y 目標點的坐標 395,425
:return:
"""
start_x=170
start_y=555
end_x=395
end_y=425
return start_x,start_y,end_x,end_y

測試偶是歐氏距離main.py

測試歐式距離
algorithm = Algorithm()
p1 = (3, 4)
p2 = (6, 8)
d = algorithm.calculate_distance(p1, p2)
print(d)

運行結果:

技術分享圖片

9 尋找關鍵坐標——框架

在alogrithm.py加入方法fine_point()

def fine_point(self):
"""
#
尋找關鍵坐標
# 返回值1,2 start_x, start_y 起跳點的坐標 170,555
# 返回值3,4 end_x, end_y 目標點的坐標 395,425
:return:
"""
start_x=170
start_y=555
end_x=395
end_y=425
return start_x,start_y,end_x,end_y

測試關鍵坐標

Main.py

#測試尋找關鍵坐標
def text_find_point():
algorithm=Algorithm()
start_x,start_y,end_x,end_y=algorithm.fine_point()
print(‘{0} {1} {2} {3}‘.format(start_x,start_y,end_x,end_y))

運行結果:

技術分享圖片

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
print("piece_y = %d" % (piece_y,))

# 1.3 y坐標的最大值減去一個偏移值,就作為小人底座的y值。(註意:該偏移值不同的設備是不同的,同一臺設備不同場景下是一樣的)

return piece_x, piece_y, board_x, board_y

運行結果:

技術分享圖片

12 優化程序

無論是起跳位置,還有目標位置。都只取垂直的中間的1/3樣式進行掃描。

13 尋找關鍵坐標——目標坐標

#-*- coding:utf-8 -*-
#author:zhengjinwei
#data: 2018.6.28
#
計算兩點之間的歐式距離

class Algorithm:
#底座中心與小人最低點的偏移值
piece_base_height=10
def __init__(self):
pass

def
calculate_distance(self,p1,p2):
#計算歐式距離,用兩點p1,p2表示距離
return ((p2[0]-p1[0])**2+(p2[1]-p1[1])**2)*0.5

def fine_point(self,img):
"""
#
尋找關鍵坐標
# 返回值1,2 piece_x, piece_y 起跳點的坐標 170,555
# 返回值3,4 board_x, board_y 目標點的坐標 395,425
:return:
"""
piece_x=0
piece_y=0
board_x=0
board_y=0
# 圖像的大小
w,h=img.size#(1080,1920)
#
加載圖形
img_pixel=img.load()
# print(w,h,img_pixel)
#
記錄小人所有的點
points = []

# 記錄y的最大值
piece_y_max = 0

# 1 計算出起跳點 就是小人底座的中心點
# 1.1 獲取小人的所有像素點中y坐標的最大值
# 遍歷圖像中的每一個點
# 遍歷每一行
for i in range(h // 3, h * 2 // 3):
#遍歷每一列
for j in range(w):
pixel=img_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
print("piece_y = %d" % (piece_y,))
# 1.3 y坐標的最大值減去一個偏移值,就作為小人底座的y值。(註意:該偏移值不同的設備是不同的,同一臺設備不同場景下是一樣的)


"""
取目標點的位置
"""
points = []
# 只取中間1/3進行掃描
for i in range(h // 3, h * 2 // 3):
if len(points) > 0:
break
# 取坐標的一個點作為背景的參照物
last_pixel = img_pixel[0, i]
# 逐個掃描右邊的點
for j in range(w):
pixel = img_pixel[j, i]
# 把當前點與最左邊的點比較 如果RGB差異比較大 則認為是目標點
if (abs(pixel[0] - last_pixel[0])
+ abs(pixel[1] - last_pixel[1])
+ abs(pixel[2] - last_pixel[2]) > 10):
points.append((j, i))

top_x = []
for x, y in points:
top_x.append(x)

board_x = sum(top_x) // len(top_x)
print("board_x = %d" % (board_x,))


return piece_x,piece_y,board_x,board_y

運行結果:

技術分享圖片

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):
..


# 2.2計算目標格式子y值

# 屏幕中心的值
center_x = w / 2 + 8 # x的偏差是8
center_y = h / 2 + 20 # y的偏差是20

# 格子高和寬的比例
height_per_width = 265 / 675

# 計算出目標格子的y值(需要轉換成整數)
# 計算出目標格子的y值(需要轉換成整數)
# 從piece_x調到board_x 如果piece_x < board_x則表示從左往右跳
# 如果piece_x > board_x 則表示從右往左跳
if piece_x < board_x:
board_y = int(center_y - height_per_width * (board_x - center_x))
else: # 從右往左跳
board_y = int(center_y + height_per_width * (board_x - center_x))

print("board_y = %d" % (board_y,))

return piece_x,piece_y,board_x,board_y

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

跳一跳實訓