微信小游戏之跳一跳-电脑自动跳跃

应用环境

  • android手机,需要启用开发者模式。
  • 电脑端的代码采用matlab编写。
  • 手机端和电脑端使用usb数据线连接。
  • 电脑端通过adb命令向手机发送相应的命令。
  • 苹果手机不支持(越狱的话也许可以,不过需要找到相应的 adb 命令)

本人手机为中兴BA910,屏幕分辨率 1280720 ,屏幕尺寸为 5.1 英寸,其他分辨率以及尺寸的手机需要修改相应的代码参数以进行适配。

完整的代码我放在了github上了,github地址:
https://github.com/lpstudy/jump-wechat-game

请注意:
上述的代码是我一个下午加上晚上前2个小时所写,包括想法,设计,代码调试,以及游戏测试,因此我基本上是以 C 的语法在写matlab,代码实在是丑陋不堪,自己由于科研方面的压力也没有精力去完善它,甚至于优化它。真的请见谅,我只想记录一下自己当时的想法,也希望能够给其他想做这个的一点哪怕小小的帮助,我就很满足了。

游戏的效果(跑了几十分钟,得了4096分,程序依然在运行,不得已手动挂掉它):

背景介绍

大约几天以前,听说微信更新了版本,小程序中增加了一个小游戏叫“跳一跳”,于是乎,我也更新了一个,打开玩了玩,一直都是 100 分左右,那叫个难受。

游戏界面大概是这个样子的:




游戏规则

游戏的过程就是通过按住屏幕,使得立着的那个小人距离向目标块上跳跃,如果跳不到目标方块上,那么游戏就失败了,分数是根据小人跳跃的块数逐步累加的。游戏者按住屏幕的时间越长,小人跳跃的越远,我们在游戏中要根据小人距离目标块的距离,来控制按住屏幕的时间,以尽可能的跳跃到目标块的中心,这样不仅有额外的加分,还因为有些目标块很小,不在中心的话,很容易掉下来。
我当时想着操作那么简单,是不是可能能够借助电脑帮我弄呢?

我的一些思路

连接手机

adb命令

既然要用电脑去分析,那么首先需要能够通过电脑给手机发送按压屏幕的指令,我原来简单玩过android,因此知道有个adb的东东,可以连接到手机端的shell,进行各种各样的操作。我于是下载了adb,并把它加入到windows 系统的环境变量里面,这样就可以通过命令行使用adb命令了。

adb命令的一个帮助界面:
这里写图片描述

查看手机设备是否连接

当adb命令可以正常使用后,还需要将手机通过usb数据线与电脑连接起来,手机端需要启用开发者模式,开启usb调试选项,电脑端需要安装手机的驱动程序(我一般都是直接安装一个手机管家之类的电脑端软件,只要它能够识别手机,那么驱动程序可认为已经安装OK了),这个时候使用以下命令,查看设备:
adb devices
如果你的手机正常连接手机,那么会看到有一行设备,如图
这里写图片描述

与游戏相关的adb指令

经过搜索之后,发现下面的两个关键命令

  • 截取屏幕
    adb shell screencap -p /sdcard/screen.png
    上述命令截取屏幕并保存到sdcard中,可以使用
    adb pull /sdcard/screen.png来将图片下载到电脑中

  • 触摸屏幕命令
    adb shell input swipe 100 500 100 500 ms
    其中的ms表示触摸屏幕的毫秒数,这个在判断好距离之后,得到相应的ms数,就可以向手机发送触摸屏幕命令。

程序思路框架

截取手机屏幕

利用上述的adb命令将手机屏幕的图片截取下来,这样就可以放在电脑上分析一下当前图片,为了方便,我写了一个bat脚本来对手机进行截屏,并将图片下载到电脑端。

@echo off
:: capture screen
adb shell screencap -p /sdcard/%1
adb pull /sdcard/%1

将上述代码命名为capture.bat,然后就可以在cmd命令下执行它了,如下图:
这里写图片描述
图中的命令表示将手机端的屏幕图片下载到电脑端,并保存为1.png

处理图片

当拿到游戏屏幕后,我的目标分为两个步骤,(1)是计算出小人距离目标方块的距离,即dis;(2)根据距离dis来计算出要触摸的毫秒数。

由于步骤1和2是整个工作的核心内容,因此单列2个大节分别讲述。

执行触摸命令

根据前面步骤计算出的毫秒数,假定为666ms,然后执行触屏命令:
adb shell input swipe 100 500 100 500 666
这样手机端就像手按压屏幕一样,小人开始发起跳跃。

%向手机发送触摸屏幕指定时间y的命令,并停顿1s
command = ['adb shell input swipe 100 500 100 500', ' ', num2str(round(y))];
fprintf('%s\n', command);
system(command);
pause(1)

图片分析核心步骤

计算出小人距离目标方块的距离

为了计算出小人距离目标方块的距离,需要两个要素,一个是小人的位置source,一个是目标方块的位置target。在知道两个要素的坐标之后,可以直接使用两个点的距离公式代入计算(target-source)^2

图片预处理之边缘检测-图片二值化

屏幕截取的图片是RGB的,不仅计算量很大,还复杂,因此首先将其灰度化,然后寻找其边缘,这样不仅寻找目标简单,而且整个图片都可以转换为一个二值图片。

下图是原始图片和边缘化之后的对比图:





可以看出这个出来的边缘还是非常清晰的,为了分析的方便,我将此图的上面的分数部分和下面的一段切掉,如下图所示:

这样看起来是不是很清楚了,有小人,有目标方块,你肯定想问我,别说了,代码拿回来(其实我想说,代码我是从网上随便找的一份,简单修改了一下,已经不知道它的出处了)

filename = 'ori_2.png';
ori=imread(filename);
ori = specialcase(ori);
ori=rgb2gray(ori);
%转化成灰度图 
ori=im2double(ori);
%函数im2double 
%使用垂直Sobcl箅子.自动选择阈值 
[VSFAT Threshold]=edge(ori, 'sobel','vertical');
%边缘探测 
f=edge(ori,'sobel',Threshold/6);
f = f(400:1000, :);
imwrite(f, strcat('result\edge_cut', filename)) 

上述的代码自动选择阈值Threshold,用来分辨边缘,但是我实践下来感觉分辨率还不够,因此我随便写了一个Threshold/6作为阈值,进行边缘检测,随后使用400:1000进行切割图片(这个值你可能需要改动,因为我的屏幕分辨率是1280*720的,因此可以这样写死)。

上面的代码还调用了一个specialcase的函数,这主要是因为有两种颜色的盒子与背景很相似,每次都不能正确找到边缘。对于这两个类型颜色的盒子,我直接暴力修改它的颜色,specialcase的代码如下:

function [f] = specialcase(f)
    [a,b,c] = size(f);
    for i = 1:a
        for j = 1:b
            if f(i,j, 1) == 255 && f(i,j,2) == 238 && f(i,j,3) == 97 || f(i,j, 1) == 186 && f(i,j,2) == 240 && f(i,j,3) == 68
                f(i,j,1) = 100;
                f(i,j,2) = 149;
                f(i,j,3) = 105;
            end
        end
    end
end
小人的位置

经过上述预处理之后的图片,就是二值图片了。小人就是一个轮廓而已。为了寻找它在图片中的位置,我采用了模式匹配的思想,首先挖出几个小人图片作为模板,然后利用图片滑动思想,将小人图片在大图片中滑动,逐个对比小人图片与大图片相应的方框,查看其相似度,如果相似的话,那么就找到了大图片中的小人了。

小人模板图片:

大图的其中几个框:

事实上每个框的size都和小人是完全相同的(我是手画的,可能看起来不完全一样),小人图片如上图所示,在上图中逐个像素滑动。滑动到一个方框后就比较小人和方框的相似度。

假定小人图片为 p ,大图中的一个框为 s p 中黑色点的个数为 total ,且黑色点对应的索引位置为

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值