跳一跳实训

  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

原文地址:https://www.cnblogs.com/zjinwei/p/9245831.html