微信跳一跳也出來一段時間了,各種插件版本也是層出不窮,基于學習的目的,寫了這個demo,完全是android原生實現。
目前的輔助程序大致原理都是一樣的,只是實現方式不同。
運行效果
原理
1.截取當前屏幕
2.根據圖片確定幾個關鍵坐標點的位置(如下圖所示)
3.計算棋子底部和下一個直線跳臺的距離
4.根據距離模擬觸摸屏幕
jump1.png
一開打算使用AccessibilityService來實現(比如之前比較火熱的搶紅包插件),因為不需用root系統,但是后來在截取屏幕的時候發現不太好實現,就放棄了。
后來選擇使用adb shell命令來截屏和模擬點擊,但是可惜的是需要root,所以只能root手機了(魅族自帶root功能,非常方便)。
root之后一切就好辦了,直接用service就能實現我們想要的功能了。
思路
1.啟動一個服務
2.在服務里重復執行:截屏——計算——點擊屏幕的操作就行了
注意點
1.確保服務不會輕易被系統殺死,這邊我用了前臺服務(優先級稍高于后臺服務)
2.圖片的掃描是bitmap對象,這邊注意bitmap的內存回收
關鍵代碼
截屏和模擬點擊
獲取了root權限后:
主要是兩條指令:
screencap -p /sdcard/跳一跳助手/wxjump.png"http://截屏
input swipe 100 100 100 100 100//觸摸屏幕
本地圖像識別確定關鍵坐標點
首先掃描確定目標跳臺的上頂點,因為微信跳一跳的背景幾乎都為純色背景,
可以通過逐行掃描,根據顏色差來確定上頂點。
這邊不需要掃描全部圖片,跳臺的高度基本在屏幕的上半部分,可以掃描(1/3——2/3或者2/5——3/5)
大概差不多就行,可以自行調整。
Bitmap src = BitmapFactory.decodeFile("/sdcard/跳一跳助手/wxjump.png");//獲取本地圖像
int R, G, B;
int pixelColor;
int pixelColorTop=0;//臺面上頂點顏色
int pixelBottomColor=0;//臺面下頂點顏色
int height = src.getHeight();
int width = src.getWidth();
Point chessPoint=new Point();//棋子坐標點
Point tableTopPoint=new Point();//目標跳臺上頂點
Point tableMiddlePoint=new Point();//目標跳臺中心點
Point tableBottomPoint=new Point();//目標跳臺下頂點
//尋找跳臺上頂點
searchTop:
for (int y = height/4; y < 2*height/3; y++) {
int pixelColorBorder=src.getPixel(50, y);//邊界對照顏色
int R_BORDER= Color.red(pixelColorBorder);
int G_BORDER= Color.green(pixelColorBorder);
int B_BORDER= Color.blue(pixelColorBorder);
for (int x = 50; x < width-50; x++) {
pixelColor = src.getPixel(x, y);
R = Color.red(pixelColor);
G = Color.green(pixelColor);
B = Color.blue(pixelColor);
//根據顏色值差異判斷上頂點
if(Math.abs(R_BORDER-R)>10||Math.abs(G_BORDER-G)>10||Math.abs(B_BORDER-B)>10){
pixelColorTop=pixelColor;
tableTopPoint.x=x;
tableTopPoint.y=y;
Log.e("tableTopPointColor:","("+R+"|"+G+"|"+B+")");
break searchTop;
}
}
}
逐行掃描,首先是獲取邊界對照點的顏色值,然后逐一比對。我看過其他的一些版本,有些是根據只要顏色值不同
就默認為是上頂點。但是實際提取顏色的過程中,我發現同一行每一個像素點背景的顏色存在細小的誤差,比如兩個相鄰點
的顏色值可能是RGB[255,255,255]和[254,254,255]。
所以我這邊判斷上頂點是根據檢測點與邊界對照點RGB的誤差超過10就可以認為是上頂點。(目前測試沒有發現問題)
//尋找棋子坐標點
searchChess:
for(int y=tableTopPoint.y;y<2*height/3; y++){
for (int x = 50; x < width-50; x++) {
pixelColor = src.getPixel(x, y);
R = Color.red(pixelColor);
G = Color.green(pixelColor);
B = Color.blue(pixelColor);
//根據顏色值判斷棋子上定頂點
if(50 < R&&R< 60&&53 < G &&G< 63&&95 < B&&B< 110){
chessPoint.x=x;
chessPoint.y=y+130;
Log.e("chess:",chessPoint.x+"|"+chessPoint.y);
Log.e("chess:",R+"|"+G+"|"+B);
break searchChess;
}
}
}
棋子的坐標點相對好找,這邊為了節省掃描開支,y軸可以從跳臺的上頂點往下掃描
直接判定顏色值得RGB范圍即可確定。和其他版本不同的是,我這邊只掃描了棋子的上頂點。因為
棋子的大小是固定的,確定了上頂點就可以確定棋子跳臺的中心位置了。我這邊默認加了130(可能有一些誤差,實際測試暫時沒發現問題)
//尋找跳臺下頂點,從最大方塊往上計算,尋找與上頂點相同的點
for(int y=tableTopPoint.y+274;y>tableTopPoint.y; y--){
pixelBottomColor = src.getPixel(tableTopPoint.x, y);
if(pixelBottomColor==pixelColorTop){
tableBottomPoint.x=tableTopPoint.x;
tableBottomPoint.y=y;
tableMiddlePoint.x=tableBottomPoint.x;
tableMiddlePoint.y=(tableBottomPoint.y+tableTopPoint.y)/2;
Log.e("bottom:",tableTopPoint.x+"|"+y);
Log.e("middle:",tableMiddlePoint.x+"|"+tableMiddlePoint.y);
break;
}
}
這邊也是其他版本提供的思路,獲取到上頂點后,因為跳一跳跳臺的大小不會超過某一個值,所以直接從上頂點的y
坐標加274往上掃描,顏色值與上頂點相同的點就是下頂點。這里的274大約是最大方塊的對角線長度。
計算距離s;確定觸摸時間t
因為距離和觸摸時間是一個一次函數t=k*s,所以只需要確定k的值就行
float SpaceTimeConfig=0.92f;
Log.e("distance",(int)(SpaceTimeConfig*distance)+"");
try {
CommandExecution.execCommand("input swipe 100 100 100 100 "+(int)(SpaceTimeConfig*distance),true);
} catch (Exception e) {
e.printStackTrace();
Log.e("error",e.getMessage()+"");
}
我這邊k的值取得0.92 (測試后,每次都跳的中心點)這個值試幾次就可以調出來,當然你也可以自己計算出
不同的分辨率的屏幕需要調整一下
其他優化待優化的地方
1.圖像識別的地方還可以優化,主要是跳臺的上下頂點位置,某些特殊的紋路會識別失?。炯y之類的)
2.代碼里的一些常量(k的系數,棋子高度,跳臺的最大面寬等),也不需要手動替換,可以獲取屏幕的分辨率來做適配
3.讀取本地圖像的權限問題,我沒有做適配,因為測試用的魅族手機,6.0以上的權限不需要特別適配,如果是其他手機還要增加讀取權限。