一、獲取指定座標的顏色
在遊戲中,怪物的血是顯示在一個固定的地方的,所以,只要這個點沒有血,那麼表示當前沒有怪可攻擊,意思就是可以找怪了,但是這個座標是多少我都不知道。於是,我在遊戲中截了下屏,並保存了圖片,把它設爲桌面背景色,哈哈,我真是太聰明啦~~在程序中BUTTON下寫一行:lbpoint.Text = Cursor.Position.X.ToString() + "," + Cursor.Position.Y.ToString();把鼠標放到血是最左邊,也就是怪物血最少的那裏,按了下空格,嘿嘿,坐標出來了:293, 35,當然是不是一次完成的,多幾下就差不多了。
現在坐標出來了,怎麼取它的色呢?經過一陣“擺渡”,原來windows的一個API可以實現這樣的功能,OK,那就用它吧,代碼如下:
[DllImport("gdi32.dll")]
static public extern uint GetPixel(IntPtr hDC, int XPos, int YPos);
[DllImport("gdi32.dll")]
static public extern IntPtr CreateDC(string driverName, string deviceName, string output, IntPtr lpinitData);
[DllImport("gdi32.dll")]
static public extern bool DeleteDC(IntPtr DC);
static public byte GetRValue(uint color)
{
return (byte)color;
}
static public byte GetGValue(uint color)
{
return ((byte)(((short)(color)) >> 8));
}
static public byte GetBValue(uint color)
{
return ((byte)((color) >> 16));
}
static public byte GetAValue(uint color)
{
return ((byte)((color) >> 24));
}
static public Color GetColorOfScreen(Point screenPoint)
{
IntPtr displayDC = CreateDC("DISPLAY", null, null, IntPtr.Zero);
uint colorref = GetPixel(displayDC, screenPoint.X, screenPoint.Y);
DeleteDC(displayDC);
byte Red = GetRValue(colorref);
byte Green = GetGValue(colorref);
byte Blue = GetBValue(colorref);
return Color.FromArgb(Red, Green, Blue);
}
我現在只要調用GetColorOfScreen這個方法就可以得到相應座標點的色了,作了下測試,OK!遊戲中怪的血條色爲:(255, 162, 2, 5)可以Color redColor = Color.FromArgb(255, 162, 2, 5);這可構造出色。
二、獲取遊戲窗口
如果按邏輯的順序的話,這一步應該放在第一的,這裏也是應用的API
[DllImport("user32.dll")]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
IntPtr 這個東西,其實說白了就是窗口的句柄,
同時,我用進程名稱要獲取這個遊戲窗口,如下:
Process[] pros = Process.GetProcessesByName(txtProName.Text.Trim());
if (pros.Length != 0)
{
IntPtr pGame = pros[0].MainWindowHandle;
SetActiveWindow(pGame);
SetForegroundWindow(pGame);
Thread.Sleep(2000);
BeginGame();
}
else
{
MessageBox.Show("找不到相應的窗口");
}
三、開始遊戲
先是定義一個色表示爲怪物的血條Color redColor = Color.FromArgb(255, 162, 2, 5);
再定義一個點,並獲取它的色:
Point p = new Point(293, 35);
Color gameColor = GetColorOfScreen(p);
當這兩個色相同時,那麼就打怪:
不相等時,再找怪
方法是這樣的:
private void BeginGame()
{
Color redColor = Color.FromArgb(255, 162, 2, 5);//(255, 255, 123, 104);
Point p = new Point(293, 35);
Color gameColor = GetColorOfScreen(p);
if (gameColor == redColor)
{
Fight();
BeginGame();
}
else
{
FindMonster();
BeginGame();
}
}
四、找怪
核心: SendKeys.SendWait("{Tab}");
向指定的窗口發送消息,SendKeys還有一個方法Send,有什麼區別呢,自己試了就知道了,輸出到文本文件裏看就明白了,這裏表示按了一下Tab這個鍵,關於其它鍵的用法如下:
鍵 代碼
BACKSPACE {BACKSPACE}、{BS} 或 {BKSP}
BREAK {BREAK}
CAPS LOCK {CAPSLOCK}
DEL 或 DELETE {DELETE} 或 {DEL}
DOWN ARROW(下箭頭鍵) {DOWN}
END {END}
ENTER {ENTER} 或 ~
ESC {ESC}
HELP {HELP}
HOME {HOME}
INS 或 INSERT {INSERT} 或 {INS}
LEFT ARROW(左箭頭鍵) {LEFT}
NUM LOCK {NUMLOCK}
PAGE DOWN {PGDN}
PAGE UP {PGUP}
PRINT SCREEN {PRTSC}(保留,以備將來使用)
RIGHT ARROW(右箭頭鍵) {RIGHT}
SCROLL LOCK {SCROLLLOCK}
TAB {TAB}
UP ARROW(上箭頭鍵) {UP}
F1 {F1}
F2 {F2}
F3 {F3}
F4 {F4}
F5 {F5}
F6 {F6}
F7 {F7}
F8 {F8}
F9 {F9}
F10 {F10}
F11 {F11}
F12 {F12}
F13 {F13}
F14 {F14}
F15 {F15}
F16 {F16}
數字鍵盤加號 {ADD}
數字鍵盤減號 {SUBTRACT}
數字鍵盤乘號 {MULTIPLY}
數字鍵盤除號 {DIVIDE}
若要指定與 SHIFT、CTRL 和 ALT 鍵的任意組合一起使用的鍵,請在這些鍵代碼之前加上以下一個或多個代碼:
鍵 代碼
SHIFT + (SHIFT="+")
CTRL ^ (CTRL="^") 如果輸入
ALT %
如果是一般的數據和字母,就只要寫進去就可以了,如:SendKeys.SendWait("A");
五、打怪
找怪都做好了,那打怪就好辦了,我想(事實不是這樣的)
把攻擊鍵放到3這個位置,
SendKeys.SendWait("3");
SendKeys.SendWait("3");
SendKeys.Flush();
OK。這樣就差不多了,再加個線程進去,不然,怎麼停止呢~~,完整的代碼如下(主要部分):
Thread th = null;
//開始掛機
private void btStart_Click(object sender, EventArgs e)
{
th = new Thread(new ThreadStart(StartGame));
th.Start();
btStart.Enabled = false;
btStop.Enabled = true;
}
//停止掛機
private void btStop_Click(object sender, EventArgs e)
{
th.Abort();
btStart.Enabled = true;
btStop.Enabled = false;
}
private void StartGame()
{
Process[] pros = Process.GetProcessesByName(txtProName.Text.Trim());//在窗體上輸入遊戲進程名
if (pros.Length != 0)
{
IntPtr pGame = pros[0].MainWindowHandle;
SetActiveWindow(pGame);
SetForegroundWindow(pGame);
Thread.Sleep(2000);
BeginGame();
}
else
{
MessageBox.Show("找不到相應的窗口");
}
}
private void BeginGame()
{
Color redColor = Color.FromArgb(255, 162, 2, 5);//(255, 255, 123, 104);
Point p = new Point(293, 35);
Color gameColor = GetColorOfScreen(p);
if (gameColor == redColor)
{
Fight();
BeginGame();
}
else
{
FindMonster();
BeginGame();
}
}
private void Fight()
{
SendKeys.SendWait("3");
SendKeys.SendWait("3");
SendKeys.Flush();
Thread.Sleep(2000);
}
private void FindMonster()
{
SendKeys.SendWait("{Tab}");
Thread.Sleep(2000);
}
到這裏爲止,我認爲就差不多了,測試:
把遊戲圖片設爲桌面背景,打開一個記事本,在窗體中輸入notepad,點開始掛機,
找到記事本,OK
由於桌面那張圖片是有怪物的,輸入33 OK
再把記事本最大化,找不到怪物血條了,輸入tab OK。。
哈哈,,說明測試成功!!
但是,問題也出來了,進入遊戲後,找怪都可以正常,就是不能按3這個鍵,,不知道爲什麼,我在自己寫的窗體上都可以的~~~
於是,再做了個窗體;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
lbMessage.Text=KeyData.ToString();
return base.ProcessCmdKey(ref msg, keyData);
}
這樣,窗體上輸入的按鍵都會顯示到控件是去,然後用剛剛那個窗口,輸入這個程的名稱,點開始掛機,在沒有怪血條的時候是輸入的Tab,OK。但有怪的時候是輸入的D3而不是3,啊。。這是怎麼回事呢,於是我把打怪那一行改爲:SendKeys.SendWait("D3");但是由於下午上班了,沒時間測試,晚上回去再試試吧。
後記:
我還用鼠標點示來實現觸發打怪,取到攻擊技能的座標,
[Flags]
enum MouseEventFlag : uint
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800,
VirtualDesk = 0x4000,
Absolute = 0x8000
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo);
SetCursorPos(418, 744);
Thread.Sleep(1000);
mouse_event(MouseEventFlag.RightDown, 418, 744, 0, UIntPtr.Zero);
mouse_event(MouseEventFlag.RightUp, 418, 744, 0, UIntPtr.Zero);
MouseEventFlag 這個枚舉基本上所有的鼠標事件都有了,不錯,可以做很多事啦,但在遊戲中還是不行,我懷疑是遊戲處理了這種消息的發送的,不讓這種模擬的消息傳入,那樣的話,做外掛可以麻煩了,不過。今天還是有不少的體會的,因爲我一開始什麼都不懂,到後來還是學到點東西啦~
[DllImport("user32.dll")]
表示調用Windows的一個DLL,它是Windows自帶的,不過要對它有所瞭解才能用它,我們用的時候只要定義好它的方法就行了,實現交給它自身去處理,就是extern