C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn)

一、前言:

創(chuàng)新互聯(lián)公司專注于洛南網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供洛南營(yíng)銷型網(wǎng)站建設(shè),洛南網(wǎng)站制作、洛南網(wǎng)頁(yè)設(shè)計(jì)、洛南網(wǎng)站官網(wǎng)定制、小程序開(kāi)發(fā)服務(wù),打造洛南網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供洛南網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

前段時(shí)間微信更新了新版本后,帶來(lái)的一款H5小游戲“跳一跳”在各朋友圈里又火了起來(lái),類似以前的“打飛機(jī)”游戲,這游戲玩法簡(jiǎn)單,但加上了積分排名功能后,卻成了“裝逼”的地方,于是很多人花錢花時(shí)間的刷積分搶排名。后來(lái)越來(lái)越多的聰明的“程序哥們”弄出了不同方式不同花樣的跳一跳助手(外掛?),有用JS實(shí)現(xiàn)的、有JAVA實(shí)現(xiàn)的、有Python實(shí)現(xiàn)的,有直接物理模式的、有機(jī)械化的、有量尺子的等等,簡(jiǎn)直是百花齊放啊……

趕一下潮流,剛好有點(diǎn)時(shí)間,于是花了一個(gè)下午時(shí)間,我也弄了一個(gè)C#版本的簡(jiǎn)單實(shí)現(xiàn)。

C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn)

二、實(shí)現(xiàn):

簡(jiǎn)單的實(shí)現(xiàn)流程: 連接手機(jī) -> 獲取跳一跳游戲界面 -> 獲取位置(棋子位置和要跳躍的落腳點(diǎn)位置) -> 點(diǎn)擊棋子跳躍

1、連接手機(jī)

電腦要連接并操作安卓手機(jī),一般是通過(guò)ADB協(xié)議連接手機(jī)并進(jìn)行操作。連接手機(jī)前要求手機(jī)已開(kāi)啟USB調(diào)試模式,可通過(guò)USB線或者TCP方式連接手機(jī)。正常只要電腦安裝了adb sdk tools之類的工具包,就會(huì)自帶有adb命令,所以C#要能操作手機(jī),簡(jiǎn)單實(shí)現(xiàn)就是直接利用現(xiàn)成的adb命令。

手機(jī)通過(guò)USB線接入電腦后,在CMD窗口輸入以下adb devices命令,如果顯示有device列表則表示手機(jī)已連接成功可以對(duì)手機(jī)進(jìn)行操作了。

C:\Users\k>adb devices
List of devices attached
e832acb device

2、獲取游戲界面

獲取手機(jī)界面的截圖可通過(guò)以下adb命令獲?。?/p>

adb shell screencap -p [filename]

參數(shù) :

- p 表示截圖保存格式為PNG圖像格式。

filename: 截圖保存的路徑地址(手機(jī)路徑),如果不輸入則將截圖數(shù)據(jù)直接輸出到當(dāng)前控制臺(tái)會(huì)話,否則會(huì)將截圖保存到相關(guān)路徑地址(必須有寫權(quán)限)

為避免文件保存到手機(jī)后還要再執(zhí)行adb pull(拉文件到本地電腦)的操作,所以選擇不帶filename參數(shù)的命令。在C#代碼里通過(guò)Process這個(gè)類進(jìn)行adb命令的調(diào)用執(zhí)行,實(shí)現(xiàn)代碼如下:

var startInfo = new ProcessStartInfo("adb", "shell screencap -p");
startInfo.CreateNoWindow = true;
startInfo.ErrorDialog = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
var process = Process.Start(startInfo);
process.Start();
var memoStream = new MemoryStream();
process.StandardOutput.BaseStream.CopyTo(memoStream);

但由于adb client的原因,在它輸出的截圖數(shù)據(jù)流中會(huì)對(duì)'\n'(0A)這個(gè)字符替換為''\r\n'(0D0A)這兩個(gè)字符,并且在測(cè)試中還發(fā)現(xiàn)不同的手機(jī)替換次數(shù)還不相同的,有可能替換一次,也有可能替換二次!所以為解決這個(gè)問(wèn)題,先計(jì)算在最開(kāi)始的10字節(jié)里的數(shù)據(jù)出現(xiàn)了多少次'\r'(0D)字符后再出現(xiàn)‘\n'(0A)字符,因?yàn)檎5腜NG文件,在文件頭的第4,第5個(gè)字節(jié)位置里會(huì)有'\r\n'(0D0A)標(biāo)志,所以檢查出來(lái)的出現(xiàn)次數(shù)就表示'\n'(0A)被adb client替換了多少次,之后再對(duì)整個(gè)接收到的數(shù)據(jù)流進(jìn)行'\n'(0A)還原(刪除無(wú)用的'\r'(0D)字符)。

>>統(tǒng)計(jì)'\n'被替換了次

 private static int Find0DCount(MemoryStream stream)
 {
 int count = 0;
 stream.Position = 0;
 while(stream.Position < 10 && stream.Position < stream.Length)
 {
 int b = stream.ReadByte();
 if(b == '\r')
 {
  count++;
 }
 else if(b == '\n')
 {
  return count;
 }else if(count > 0)
 {
  count = 0;
 }
 }
 return 0;
 }

>>對(duì)接受到的截圖數(shù)據(jù)流進(jìn)行'\n'字符還原            

 var count = Find0DCount(memoStream);
 var newStream = new MemoryStream();
 memoStream.Position = 0;
 while (memoStream.Position != memoStream.Length)
 {
  var b = memoStream.ReadByte();
  if (b == '\r')
  {
  int c = 1;
  var b1 = memoStream.ReadByte();
  while(b1 == '\r' && memoStream.Position != memoStream.Length)
  {
  c++;
  b1 = memoStream.ReadByte();
  }
  if(b1 == '\n')
  {
  if(c == count)
  {
  newStream.WriteByte((byte)'\r');
  }
  newStream.WriteByte((byte)b1);
  }
  else
  {
  for(int i=0; i<c; i++) newStream.WriteByte((byte)'\r');
  newStream.WriteByte((byte)b1);
  }
  }
  else { 
  newStream.WriteByte((byte)b);
  }
 }
 return new Bitmap(newStream);

3、獲取棋子與跳躍落腳點(diǎn)位置

將獲取到的手機(jī)界面截圖顯示到軟件窗體上的PictureBox控件上,可用鼠標(biāo)的左右鍵分別點(diǎn)擊圖片位置標(biāo)示棋子位置和需要跳的落腳點(diǎn)位置,鼠標(biāo)點(diǎn)擊的坐標(biāo)位置即表示手機(jī)界面的坐標(biāo)位置。由于手機(jī)界面截圖在PictureBox控件顯示時(shí)為了能一屏全圖顯示,對(duì)圖片做了縮放處理,且圖片縮放后如果圖片的寬度小于PictureBox控件的寬度,PictureBox會(huì)將圖片居中后顯示。所以鼠標(biāo)點(diǎn)擊的坐標(biāo)位置還需要進(jìn)行坐標(biāo)轉(zhuǎn)換才可以映射為手機(jī)界面里的絕對(duì)坐標(biāo)位置。

轉(zhuǎn)換計(jì)算方法:先計(jì)算PictureBox控件的圖片縮放值和圖片顯示的左邊距,然后再對(duì)鼠標(biāo)點(diǎn)擊坐標(biāo)進(jìn)行縮放計(jì)算。代碼如下:      

private Point CalPoint(Point p)
 {
 if (this.cbZoom.Checked && this.pictureBox1.Image != null)
 {
 var zoom = (double)this.pictureBox1.Height / this.pictureBox1.Image.Height;
 var width = (int)(this.pictureBox1.Image.Width * zoom);
 var left = this.pictureBox1.Width / 2 - width / 2;
 return new Point((int)((p.X - left) / zoom), (int)(p.Y / zoom));
 }
 else
 {
 return p;
 }
 }

如全靠手動(dòng)鼠標(biāo)點(diǎn)擊坐標(biāo)位置來(lái)玩游戲,這和直接在手機(jī)里手動(dòng)玩游戲是沒(méi)有什么區(qū)別的,區(qū)別只在于能夠跳躍精準(zhǔn)些(跳躍力度能自動(dòng)計(jì)算出,下面會(huì)講),所以程序還要能夠?qū)崿F(xiàn)自動(dòng)化,就是要能夠自動(dòng)找出棋子與跳躍落腳點(diǎn)的位置。

A、找棋子的坐標(biāo)位置

棋子的位置非常的好找,對(duì)游戲界面里的棋子(圖2黃色塊)進(jìn)行放大可以發(fā)現(xiàn)棋子底部有一塊區(qū)域(圖3白色塊)的顏色值是固定的R(54)G(60)B(102)顏色,如下兩圖:

C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn)

(圖2)

C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn)

(圖3)

根據(jù)棋子的這一顏色特點(diǎn)在獲取到手機(jī)界面截圖時(shí),對(duì)圖片象素進(jìn)行掃描,查找R(54)G(60)B(102)這一顏色,找到的坐標(biāo)位置就是棋子的位置。為了能快速掃描圖片,不采用效率較低下的GetPixel方法獲取顏色值,而采用LockBits方法鎖定圖片數(shù)據(jù)到內(nèi)存,再采用指針移動(dòng)獲取象素顏色,由于采用了指針,代碼需要開(kāi)啟unsafe定義。且棋子正常情況下不會(huì)在最頂部和最底部出現(xiàn),所以不需要對(duì)整張界面圖片掃描,只掃描20%-63%區(qū)域的數(shù)據(jù),并且從底部開(kāi)始找起。

B、找跳躍的落腳點(diǎn)位置

寫此助手只是無(wú)聊時(shí)的產(chǎn)出物,所以我只是簡(jiǎn)單實(shí)現(xiàn)。游戲中如果連續(xù)跳到了目標(biāo)物的中間位置時(shí),新目標(biāo)物的中間部分會(huì)出現(xiàn)一個(gè)白色圈(如上圖2的紅色塊),如果再跳中此位置,會(huì)進(jìn)行加分。根據(jù)這一特點(diǎn),程序找出那一白色圈圈的位置即可做為落腳點(diǎn)位置,白色圈的顏色值為R(254)G(254)B(254),如果沒(méi)有此白色圈位置,則手動(dòng)鼠標(biāo)選擇落腳點(diǎn)位置。實(shí)現(xiàn)此功能后,程序基本上也能實(shí)現(xiàn)90%左右的自動(dòng)化跳躍了。

查找代碼實(shí)現(xiàn)如下:

private static Point FindPointImpl(Bitmap bitmap, out Point comboPoint)
 {
 var standPColor = Color.FromArgb(54, 60, 102);
 var comboPColor = Color.FromArgb(245, 245, 245);

 Point standPoint = Point.Empty;
 comboPoint = Point.Empty;

 int y1 = (int)(bitmap.Height * 0.2);
 int y2 = (int)(bitmap.Height * 0.63);

 PixelFormat pf = PixelFormat.Format24bppRgb;

 BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, y1, bitmap.Width, y2), ImageLockMode.ReadOnly, pf);
 try
 {
 unsafe
 {
  int w = 0;
  while (y2 > y1)
  {
  byte* p = (byte*)bitmapData.Scan0 + (y2 - y1 - 1) * bitmapData.Stride;
  w = bitmap.Width;
  int endColorCount = 0;
  while (w > 40)
  {
  ICColor* pc = (ICColor*)(p + w * 3);
  if (standPoint == Point.Empty &&
  pc->R == standPColor.R && pc->G == standPColor.G && pc->B == standPColor.B)
  {
  standPoint = new Point(w - 3, y2);
  if (comboPoint != Point.Empty) break;
  }
  else if (comboPoint == Point.Empty)
  {
  if (pc->R == comboPColor.R && pc->G == comboPColor.G && pc->B == comboPColor.B)
  {
   endColorCount++;
  }
  else
  {
   if (endColorCount > 0)
   {
   comboPoint = new Point(w + 5, y2 - 1);
   if (standPoint != Point.Empty) break;
   }
   endColorCount = 0;
  }
  }
  w--;
  }
  if (comboPoint == Point.Empty)
  {
  if (endColorCount > 10)
  {
  comboPoint = new Point(w + 5, y2 - 1);
  }
  }
  if (standPoint != Point.Empty && comboPoint != Point.Empty) break;
  y2--;
  }
 }
 return standPoint;
 }
 finally
 {
 bitmap.UnlockBits(bitmapData);
 }
 }

4、棋子跳躍

要能跳躍,首先需要知道一個(gè)蓄力時(shí)間,就是按住棋子多久的時(shí)間,此蓄力時(shí)間的計(jì)算公式如下:

蓄力時(shí)間 = 距離 * 力度系數(shù)

“距離”就是棋子位置與跳躍落腳點(diǎn)位置的距離,根據(jù)上面的方法得出這兩個(gè)位置的坐標(biāo)點(diǎn)后,根據(jù)直角三角形的勾股定理即可求出,代碼如下:      

 public double Distance
 {
 get
 {
 if (!this.CanDo) return -1;
 int w = Math.Abs(this.P2.X - this.P1.X);
 int h = Math.Abs(this.P2.Y - this.P1.Y);
 return Math.Sqrt((double)(w * w) + (h * h));
 }
 }

“力度系數(shù)”  是一個(gè)常量值,具體怎么定義沒(méi)去細(xì)查,我采用的計(jì)算公式是: “力度系數(shù) = 1495 / 手機(jī)分辨率的寬度值”, 如我的手機(jī)分辨率是1080*1920,則力度系數(shù)就是 1495 / 1080 = 1.3842....

算出了蓄力時(shí)間后通過(guò)以下adb命令發(fā)送到手機(jī)即可模擬點(diǎn)擊操作。

adb shell input swipe <x1> <y1> <x2> <y2> [duration(ms)]

x1, y1 就是棋子的坐標(biāo)位置

x2, y2 還是棋子的坐標(biāo)位置

duration 蓄力時(shí)間值,由距離*力度系數(shù)得出。

代碼如下:   

 public bool Do()
 {
 if (!this.CanDo) return false;
 var startInfo = new ProcessStartInfo("adb", string.Format("shell input swipe {0} {1} {0} {1} {2}", this.P1.X, this.P1.Y, this.Time));
 startInfo.CreateNoWindow = true;
 startInfo.ErrorDialog = true;
 startInfo.UseShellExecute = false;
 var process = Process.Start(startInfo);
 return process.Start();
 }

三、結(jié)束語(yǔ)

程序?qū)崿F(xiàn)很簡(jiǎn)單,都是通過(guò)adb命令與手機(jī)進(jìn)行交互操作。如果你認(rèn)為對(duì)你有幫助麻煩贊下即可:)積分別玩太過(guò)哦。

C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn)

可執(zhí)行文件下載地址:http://xiazai.jb51.net/201801/yuanma/JumperHelper.rar

代碼倉(cāng)庫(kù):https://github.com/kingthy/JumperHelper

總結(jié)

以上所述是小編給大家介紹的C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn),希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!

當(dāng)前標(biāo)題:C#實(shí)現(xiàn)微信跳一跳小游戲的自動(dòng)跳躍助手開(kāi)發(fā)實(shí)戰(zhàn)
文章起源:http://muchs.cn/article36/pgogsg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、標(biāo)簽優(yōu)化、面包屑導(dǎo)航、網(wǎng)站設(shè)計(jì)公司、定制開(kāi)發(fā)、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

綿陽(yáng)服務(wù)器托管