微信小游戏之跳一跳-电脑自动跳跃
应用环境
-
android
手机,需要启用开发者模式。 - 电脑端的代码采用
matlab
编写。 - 手机端和电脑端使用
usb
数据线连接。 - 电脑端通过
adb
命令向手机发送相应的命令。 - 苹果手机不支持(越狱的话也许可以,不过需要找到相应的
adb 命令)
本人手机为中兴BA910,屏幕分辨率
完整的代码我放在了github上了,github地址:
https://github.com/lpstudy/jump-wechat-game
请注意:
上述的代码是我一个下午加上晚上前2个小时所写,包括想法,设计,代码调试,以及游戏测试,因此我基本上是以
游戏的效果(跑了几十分钟,得了4096分,程序依然在运行,不得已手动挂掉它):
背景介绍
大约几天以前,听说微信更新了版本,小程序中增加了一个小游戏叫“跳一跳”,于是乎,我也更新了一个,打开玩了玩,一直都是
游戏界面大概是这个样子的:
游戏规则
游戏的过程就是通过按住屏幕,使得立着的那个小人距离向目标块上跳跃,如果跳不到目标方块上,那么游戏就失败了,分数是根据小人跳跃的块数逐步累加的。游戏者按住屏幕的时间越长,小人跳跃的越远,我们在游戏中要根据小人距离目标块的距离,来控制按住屏幕的时间,以尽可能的跳跃到目标块的中心,这样不仅有额外的加分,还因为有些目标块很小,不在中心的话,很容易掉下来。
我当时想着操作那么简单,是不是可能能够借助电脑帮我弄呢?
我的一些思路
连接手机
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都和小人是完全相同的(我是手画的,可能看起来不完全一样),小人图片如上图所示,在上图中逐个像素滑动。滑动到一个方框后就比较小人和方框的相似度。
假定小人图片为
有可能一个小人模板在整个图片中都找不到
%使用多个模板进行匹配,直到有一个匹配成功
res_i = 0;
res_j = 0;
finish = 0;
for sample = 1:4
if finish == 1
break
end
%template person
t = imread(['samples\persons\', num2str(sample), '.bmp']);
[a, b] = find( t == 0); %find the edge
t = t(min(a):max(a), min(b):max(b));
[m,n] = size(t);
[row, col] = find(t==0);
for i = 100:M-m
if finish == 1
break
end
for j = 1:N-n
if finish == 1
break
end
total = length(row);
cur = 0;
for k = 1:total
if f(i+row(k), j+col(k)) == 1
cur = cur + 1;
end
end
ratio = cur * 1.0/total;
if(ratio > 0.5)
fprintf('ratio=%.2f, i=%d, j=%d, filename=%s', cur * 1.0 / total, i, j, filename)
res_i = i;
res_j = j;
finish = 1;
end
end
end
end
if finish == 0
i = randi(3000000);
fprintf('I can not find the little person, please refer the hardpics directory to check %d.png\n', i)
figure(3), imshow(f), title('f.png')
imwrite(f, strcat('hardpics\', filename));
return
end
代码中使用了多个模板,与大图中逐个进行框,计算出
计算目标方块的位置
我们知道小人是向左上或者右上跳跃的,也就是说,肯定是向上跳跃的,因此,从上向下逐行扫描找到大图中第一个白色的点,那就是目标方块的上边角,然后将其向下平移几十个像素,就基本到达它的中心位置了。
它的原始大图如下图所示:
基于以上思路,先寻找它的上边角。我们知道小人的头有点高,有可能它的头有可能超过目标方块的上边角,因此先把小人扣出去。
%remove the little person, in case it made some mistakes
remove_f = f;
remove_f(res_i:res_i+m, res_j:res_j+n) = 0;
在扣除小人后,我们得到remove_f
如下图所示:
然后计算它的目标方块的上边角,就是上图中圆柱体最上边的那个点(我用白色的三角形进行了标记)。
% get top i and top j
[x, y] = find(remove_f == 1);
top_i = min(x);
top_j = round(mean(find(remove_f(top_i, :) == 1)));
随后,需要判定上图中的白色三角形需要向下平移多少个像素才到达目标方块的中心点。我首先设置了两个阈值
举例:假定我们找到的
%from top_i to down, it is x axis.
wids = zeros(1,top_i+200);
center = -1;
for i = top_i : top_i + 200
index = find(remove_f(i,:) == 1);
width = max(index)-min(index);
if isempty(width)
width = wids(i-1);
end
wids(i) = width;
if i-top_i > 0 && width < wids(i-1)
last = i-1;
start = 0;
for j = i-1:-1:top_i
if not(wids(j) == wids(i-1))
start = j+1;
break
end
end
if last-start>8
last = start + 4;
end
center = round((start+last)/2)-top_i;
break;
end
end
if center < 18
center = 18;
end
if center > 50
center = 50;
end
计算小人的中心点:
小人的中心点可以根据小人左上角的位置
source_i = res_i + m - 15;
source_j = res_j+round(n/2); %脚跟距离脚的中心,需要向上提15个像素
计算目标方块的中心点:
目标方块的中心点,列已经确定了,只需要行向下平移
%calculate distance
target_i = top_i + center;
target_j = top_j; %最上方的那个点,向下平移50个像素,认为是center。
如下图所示:
然后根据
%计算距离
dis = sqrt( (target_i-source_i)^2 + (target_j-source_j)^2 ) ;
fprintf(' dis = %.2f\n', dis)
根据距离dis来计算出要触摸的毫秒数
我们需要制定一个字典,来根据距离adb
给手机发送触摸屏幕
有了这些点,我画了一个点图,感觉像是线性的,因此就简单使用了matlab中自带的一元线性拟合的代码,拟合的一条直线(猜测是最小二乘法)。这样,随后给出任意的
线性拟合的代码如下:
%根据距离,以及拟合的直线,得到相应的毫秒数 y
dict = [
[163,350], [197.04, 420], [207, 430], [270, 550], [287, 570], [310, 640], [321, 670], [348.63, 730], [367.27, 769],[374, 780],[393.61, 800], [409.93, 841]
];
x = dict(1:2:end);
y = dict(2:2:end);
p = polyfit(x,y,1);
y1=polyval(p,x); %计算出拟合的y值
%figure(3),plot(x,y,'k*',x,y1,'r-'); %画出数据对比图,黑点是原始数据,红线是拟合曲线
y = polyval(p, dis);
本人lpstudy,转载请注明出处,谢谢。
上一篇: 微信跳一跳游戏助手
下一篇: python邮件发送 STMP