前言:
1.开发工具:Auto js
2.平台:安卓6.0以上
3.开发语言 :javascript
效果演示:
这里附上效果展示视频:
开发步骤:
1.建模,将游戏环境转化为数据
2.使用算法,寻找路径
3.模拟屏幕点击,完成自动闯关
建模过程:
这里只说下思路,具体实现有很多的方法,需要大家自己去写。
1.先截取当前屏幕,如图一
2.所有有用的地图信息都在图二中我用红框框出来的区域里面,所以第一步先把框框里面的图先截取出来
3.截取后的图片如下图三所示:
4.去掉四周多余的空白区域后,也就是最终的处理后的图片如图四:
5.把图片转化为二维数组,可以走的地方值为1(灰色方块),不能走的地方值为0(白色的位置)
例如上面这幅地图转化为数组就是:
1 0 1 1 1 1
1 1 1 1 1 1
1 1 1 1 0 1
1 1 1 1 1 1
0 1 1 0 1 1
1 1 1 1 0 1
1 1 1 1 1 1
其中有一个特殊的点就是起点,起点位置需要单独记录下来,后面需要用到,并且规定起点位置的值为1。
路径算法:
思路:DFS搜索算法,从起点开始,对当前点的上下左右进行遍历,看该位置是否能够走,如果能就把该位置作为当前点,继续进行搜索,如果不能走就接着遍历,直到地图上所有能够走的地方都被走过了,此时代表你找到了一条一笔画完的路径,记录下该路径。
剪枝:开始没有意识到剪枝的重要性,就直接写的暴力搜索,这样的算法在电脑上面搜索没什么问题,但是换到手机上在地图比较大的时候就会比较卡,有时候需要计算1分钟才能找到路径,后来我就加了一个剪枝,速度马上就提上来了,一般都在3秒之内就能找到路径。
路径算法 代码如下:
//回溯法寻找路径
function backtrack(x,y)
{
//如果该点无效 超出界限或者该处的值为0
if(isValid(x,y)==false)
return false;
//记录该点被走过 所以该处的值为0
tem[x][y]=0;
//剪枝 如果出现这种情况 代表不可能走完,直接return
if(condition1())
{
return true;
}
//如果所有的格子都已经走过 代表找到路径
if(over())
{
toast ("找到路径");
//sleep(1000);
for(var i=0;i<nx;i++)
for(var j=0;j<ny;j++)
act[i][j]=action[i][j];
goByAct();
toast ("finished");
//sleep (1000);
exit ();
return true;
}
//action记录路径 1代表往上 2-下 3-左 4-右
//往上寻找
action[x][y]=1;
if(backtrack(x,y-1))
{
tem[x][y-1]=1;
}
//往下寻找
action[x][y]=2;
if(backtrack(x,y+1))
{
tem[x][y+1]=1;
}
//往左寻找
action[x][y]=3;
if(backtrack(x-1,y))
{
tem[x-1][y]=1;
}
//往右寻找
action[x][y]=4;
if(backtrack(x+1,y))
{
tem[x+1][y]=1;
}
action[x][y]=0;
return true;
}
屏幕点击:
计算出每个方块中心的坐标,利用刚才保存下来的路径,编写函数进行屏幕点击 。
源码:
这里给出源码,由于没有做手机适配,所以这个代码不能直接拿来就用,仅供参考学习。
//一些常用的颜色我要记录下来,利用图片进行获取
var img1=images.read("/sdcard/2.jpg");
var gray=img1.pixel(50,50);
var white=img1.pixel(120,120);
//var black=img1.pixel(23,348);
//获取地图 大致截取
function getMap()
{
requestScreenCapture();
var img = captureScreen();
var tar=images.clip(img,5,575,1045,1120);
x0p+=5;
y0p+=575;
return tar;
}
//消除空白边缘
function removeBack(tar)
{
//转化图 片为数据
var w=tar.getWidth();
var h=tar.getHeight();
var x1,x2,y1,y2;
x1=y1=0;
x2=w,y2=h;
//上 y1改变
while(1)
{
//灰色
var point=findColor(tar,gray,{
region:[0,y1,w,2],
threshold:1
});
y1+=2;
if(point)
break;
}
//左 x1
while(1)
{
var point=findColor(tar,gray,{
region:[x1,0,2,h],
threshold:1
});
x1+=2;
if(point)
break;
}
//右 x1
while(1)
{
var point=findColor(tar,gray,{
region:[x2-2,0,2,h],
threshold:1
});
x2-=2;
if(point)
break;
}
//下 y2
while(1)
{
var point=findColor(tar,gray,{
region:[0,y2-2,w,2],
threshold:1
});
y2-=2;
if(point)
break;
}
x0p+=x1;
y0p+=y1;
return images.clip(tar,x1,y1,x2-x1,y2-y1);
}
//得到二维数组
function getData(img)
{
//img 是边缘剪裁完毕后的图像
var x=0,y=0;
var w=img.getWidth();
var h=img.getHeight();
while(1)
{
//找灰色的点
var point=findColor(img,gray,{
region:[0,y,w,1],
threshold:1
});
//如果没找到了
if(!point)
break;
y+=1;
}
while(1)
{
//找灰色的点
var point=findColor(img,gray,{
region:[x,0,1,h],
threshold:1
});
//如果没找到了
if(!point)
break;
x+=1;
}
dx=x;
dy=y;
//var space=0;
while(1)
{
//找灰色的点
var point=findColor(img,gray,{
region:[x,0,1,h],
threshold:1
});
//如果找到了
if(point)
break;
x+=1;
}
space=x-dx;
nx=parseInt(w/(dx+space))+1;
ny=parseInt(h/(dy+space))+1;
toast(nx+","+ny);
sleep(1000);
//画地图
var arr=new Array();
for(var i=0;i<nx;i++)
{
arr[i]=new Array();
for(var j=0;j<ny;j++)
{
if(img.pixel(dx/2+i*(dx+space),dy/2+j*(dy+space))==gray)
{
arr[i][j]=1;
//toast(dx/2+i*(dx+space)+","+(dy/2+j*(dy+space)));
}
else
{
//找起点
if(img.pixel(dx/2+i*(dx+space),dy/2+j*(dy+space))==white)
{
arr[i][j]=0;
}
else
{
arr[i][j]=-1;
bx=i;
by=j;
//toast ("找到起点"+i+","+j);
//sleep (1000);
}
//toast(i+","+j);
}
//sleep(1000);
}
}
return arr;
}
function over()
{
for(var i=0;i<nx;i++)
for(var j=0;j<ny;j++)
if(tem[i][j]==0)
continue;
else return false;
return true;
}
function isValid(x,y)
{
if(x<0||x>=nx||y<0||y>=ny)
return false;
if(tem[x][y]==0)
return false;
return true;
}
//如果这种情况出现就返回true 如果没出现就返回false
function condition1()
{
var tarn=0;
var n0=0;
for(var i=0;i<nx;i++)
{
for(var j=0;j<ny;j++)
{
n0=0;
if(tem[i][j]!=0)
{
if(!isValid(i-1,j))
n0++;
if(!isValid(i+1,j))
n0++;
if(!isValid(i,j-1))
n0++;
if(!isValid(i,j+1))
n0++;
}
if(n0>=3)
tarn++;
if(tarn>=3)
return true;
}
}
return false;
}
function backtrack(x,y)
{
//console.log("(%d,%d):%d",x,y,tem[x][y]);
//sleep(1000);
/*
if(x<0||x>=nx||y<0||y>=ny)
return false;
if(tem[x][y]==0)
return false;
*/
//如果该点无效
if(isValid(x,y)==false)
return false;
tem[x][y]=0;
//如果这种情况出现 代表不可能走完,直接return
if(condition1())
{
return true;
}
if(over())
{
toast ("找到路径");
//sleep(1000);
for(var i=0;i<nx;i++)
for(var j=0;j<ny;j++)
act[i][j]=action[i][j];
goByAct();
toast ("end");
//sleep (1000);
exit ();
return true;
}
//console.log("%d",tem[x][y]);
action[x][y]=1;
if(backtrack(x,y-1))
{
tem[x][y-1]=1;
}
action[x][y]=2;
if(backtrack(x,y+1))
{
tem[x][y+1]=1;
}
action[x][y]=3;
if(backtrack(x-1,y))
{
tem[x-1][y]=1;
}
action[x][y]=4;
if(backtrack(x+1,y))
{
tem[x+1][y]=1;
}
action[x][y]=0;
return true;
}
function myclick(x,y)
{
//console.log("点击:%d,%d",x,y);
click(x0p+dx/2+x*(dx+space),y0p+dy/2+y*(dy+space));
sleep(50);
}
function goByAct()
{
//toast(act[bx][by]);
var x=bx,y=by;
while(act[x][y]!=0)
{
switch(act[x][y])
{
case 1:
{
y--;
myclick(x,y);
};break;
case 2:
{
y++;
myclick(x,y);
};break;
case 3:
{
x--;
myclick(x,y);
};break;
case 4:
{
x++;
myclick(x,y);
};break;
default:break;
}
}
}
//记录数组的宽度和开始坐标
var nx,ny,bx,by;
//记录左上角坐标
var x0p=0,y0p=0;
//记录格子的宽度和高度,以及中间的间隙
var dx,xy,space;
var img0=getMap();
var img=removeBack(img0);
var arr=getData(img);
var tem=new Array;//临时数组
var action=new Array;//行动路线
var act=new Array;
//init
for(var i=0;i<nx;i++)
{
tem[i]=new Array;
action[i]=new Array;
act[i]=new Array;
for(var j=0;j<ny;j++)
{
tem[i][j]=arr[i][j];
action[i][j]=0;
act[i][j]=0;
}
}
//console.show();
toast ("找到起点("+bx+","+by+") 1秒后开始寻找路径");
sleep (1000);
backtrack(bx,by);
toast ("没找到");
写在最后:
至此,一笔画完的游戏辅助就写好了 。刚开始写博客,有不足的地方还请大家包涵,也请大家多给我提点意见,谢谢大家。