我的家園

          我的家園

          本系列文章由zhmxy555編寫(xiě),轉(zhuǎn)載請(qǐng)注明出處。  http://blog.csdn.net/zhmxy555/article/details/7434317

          作者:毛星云    郵箱: happylifemxy@qq.com    歡迎郵件交流編程心得


          我們常常聽(tīng)聞AI(Artificial Intelligence人工智能)這個(gè)名詞,比如Dota里面的AI地圖。寫(xiě)這篇文章的時(shí)候,最新版的Dota AI是6.72f,估計(jì)過(guò)幾天6.73的AI也要出來(lái)了。很多Dota玩家喜歡玩AI地圖練練感覺(jué)和補(bǔ)刀,可以這樣說(shuō),Dota 地圖成功的加入了AI元素,是近幾年Dota風(fēng)靡全球不可缺少的因素之一。


          一、知識(shí)點(diǎn)講解


          那么,到底什么是AI呢?首先我們來(lái)了解一下人工智能(AI)的具體定義。“人工智能”(Artificial Intelligence)簡(jiǎn)稱(chēng)AI。它是研究、開(kāi)發(fā)用于模擬、延伸和擴(kuò)展人的智能的理論、方法、技術(shù)及應(yīng)用系統(tǒng)的一門(mén)新的技術(shù)科學(xué)。人工智能研究如何用計(jì)算機(jī)去模擬、延伸和擴(kuò)展人的智能;如何把計(jì)算機(jī)用得更聰明;如何設(shè)計(jì)和建造具有高智能水平的計(jì)算機(jī)應(yīng)用系統(tǒng);如何設(shè)計(jì)和制造更聰明的計(jì)算機(jī)以及智能水平更高的智能計(jì)算機(jī)等。人工智能是計(jì)算機(jī)科學(xué)的一個(gè)分支,人工智能是計(jì)算機(jī)科學(xué)技術(shù)的前沿科技領(lǐng)域。人工智能與計(jì)算機(jī)軟件有密切的關(guān)系。一方面,各種人工智能應(yīng)用系統(tǒng)都要用計(jì)算機(jī)軟件去實(shí)現(xiàn),另一方面,許多聰明的計(jì)算機(jī)軟件也應(yīng)用了人工智能的理論方法和技術(shù)。


          而我們要講解的游戲人工智能,只是淵博的人工智能領(lǐng)域里面的冰山一角。我們不會(huì)用到那些類(lèi)似于神經(jīng)網(wǎng)絡(luò),基因算法,模糊邏輯等復(fù)雜的人工智能理論,我們只需利用自己本身的思考模式去賦予游戲中角色判斷的能力,來(lái)進(jìn)行某些特定的行為。


          今天我們主角是運(yùn)動(dòng)型的AI,下面就開(kāi)始正題吧。


          凡是在游戲中會(huì)移動(dòng)的物體,幾乎都涉及到了運(yùn)動(dòng)型的游戲AI,例如游戲中怪物的追逐或者躲避玩家和游戲中NPC角色的移動(dòng)都是移動(dòng)型AI的例子。


          <1>追逐移動(dòng)

          下面我們以移動(dòng)型AI里的追逐移動(dòng)型AI來(lái)作為例子講解。

          追逐移動(dòng)一般是通過(guò)控制一角色朝某一目標(biāo)接近來(lái)實(shí)現(xiàn),簡(jiǎn)單點(diǎn)說(shuō),就是兩個(gè)物體的空間坐標(biāo)相互接近。比如我們要設(shè)計(jì)一個(gè)怪物追逐玩家的游戲,只要在每次進(jìn)行貼圖時(shí),將怪物坐在坐標(biāo)與玩家角色所在的坐標(biāo)進(jìn)行比較,自增或者自減怪物X,Y軸上的貼圖坐標(biāo),就可產(chǎn)生追逐移動(dòng)的效果。下面就是一個(gè)典型的怪物追逐外加的移動(dòng)AI算法,其中“梟獸X”、“梟獸Y”,“幻影刺客X”,“幻影刺客Y”分別用來(lái)表示怪物及玩家在X與Y軸上的貼圖坐標(biāo)。


          【算法1】

          If(梟獸X>幻影刺客X)
          梟獸X--;
          else
          梟獸X++;
          If(梟獸Y<幻影刺客Y)
          梟獸Y++;
          else
          梟獸Y--;

          下面我們?cè)賮?lái)看一個(gè)例子,這段算法是以上面的【算法1】為核心代碼,賦予了怪物更多的“思考”空間。追逐移動(dòng)的怪物會(huì)按照自身生命值的多寡來(lái)決定是否進(jìn)行追逐,每次計(jì)算下次的位置坐標(biāo)時(shí),也只有二分之三的幾率能正確地朝向玩家,以其中以“梟獸HP”來(lái)表示怪物當(dāng)前的生命值。


          【算法2】

          If(梟獸HP>200)              //生命值大于200時(shí)才追
          (
          P=rand()%3;                   //取隨機(jī)數(shù)除以3的余數(shù)
          If(p!=1)                        //余數(shù)不為1時(shí)進(jìn)行追逐
          {
          If(梟獸X>幻影刺客X)
          梟獸X--;
          else
          梟獸X++;
          If(梟獸Y<幻影刺客Y)
          梟獸Y++;
          else
          梟獸Y--;
          }
          else
            	梟獸HP+=5           //怪物不動(dòng),自動(dòng)補(bǔ)5點(diǎn)血
          )


          這樣的怪物就比較有靈性了,要繼續(xù)創(chuàng)造出更聰明的AI,只要繼續(xù)完善代碼,寫(xiě)出更多的功能就行了。




          <2>躲避移動(dòng)

          其實(shí)躲避移動(dòng)和追逐移動(dòng)的算法差不多,就是把++的地方和--對(duì)調(diào)就行了,讓怪物與人物的空間坐標(biāo)相互遠(yuǎn)離。

          具體代碼如下:


          【算法3】

          If(梟獸X>幻影刺客X)
          梟獸X++;
          else
          梟獸X--;
          If(梟獸Y<幻影刺客Y)
          梟獸Y--;
          else
          梟獸Y++;


          二、在實(shí)例中將知識(shí)融會(huì)貫通


          依舊,我們看一個(gè)實(shí)例,來(lái)將本節(jié)的知識(shí)融會(huì)貫通。


          這是一個(gè)小鳥(niǎo)追逐小女孩的場(chǎng)景,我們需要用鍵盤(pán)的【↑】【↓】【←】【→】鍵來(lái)躲避小鳥(niǎo)的追擊,具體鍵盤(pán)輸入消息的知識(shí)點(diǎn)還

          不太了解的朋友,請(qǐng)移步筆記十二,這里給出鏈接:


          【Visual C++】游戲開(kāi)發(fā)筆記十二 游戲輸入消息處理(一) 鍵盤(pán)消息處理



          下面依舊是貼圖詳細(xì)注釋的源代碼:

          #include "stdafx.h"
          #include <stdio.h>
          
          //全局變量聲明
          HINSTANCE hInst;
          HBITMAP girl[4],bg,bird;                
          HDC		hdc,mdc,bufdc;
          HWND	hWnd;
          
          DWORD	tPre,tNow,nowX,nowY;
          POINT	p[3];               //用于記錄3只小鳥(niǎo)的貼圖坐標(biāo)
          int		num,dir,x,y;       //x,y變量為人物貼圖坐標(biāo),dir為人物移動(dòng)方向,這里我們中以0,1,2,3代表人物上,下,左,右方向上的移動(dòng):num為連續(xù)貼圖中的小圖編號(hào)
          
          //全局函數(shù)聲明
          ATOM				MyRegisterClass(HINSTANCE hInstance);
          BOOL				InitInstance(HINSTANCE, int);
          LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
          void				MyPaint(HDC hdc);
          
          //****WinMain函數(shù),程序入口點(diǎn)函數(shù)***********************
          int APIENTRY WinMain(HINSTANCE hInstance,
                               HINSTANCE hPrevInstance,
                               LPSTR     lpCmdLine,
                               int       nCmdShow)
          {
          	MSG msg;
          
          	MyRegisterClass(hInstance);
          
          	//初始化
          	if (!InitInstance (hInstance, nCmdShow)) 
          	{
          		return FALSE;
          	}
          
          	 GetMessage(&msg,NULL,NULL,NULL);            //初始化msg  
          	//消息循環(huán)
              while( msg.message!=WM_QUIT )
              {
                  if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) )
                  {
                      TranslateMessage( &msg );
                      DispatchMessage( &msg );
                  }
          		else
          		{
          			tNow = GetTickCount();
          			if(tNow-tPre >= 40)
          				MyPaint(hdc);
          		}
              }
          
          	return msg.wParam;
          }
          
          //****設(shè)計(jì)一個(gè)窗口類(lèi),類(lèi)似填空題,使用窗口結(jié)構(gòu)體*******************
          ATOM MyRegisterClass(HINSTANCE hInstance)
          {
          	WNDCLASSEX wcex;
          
          	wcex.cbSize = sizeof(WNDCLASSEX); 
          	wcex.style			= CS_HREDRAW | CS_VREDRAW;
          	wcex.lpfnWndProc	= (WNDPROC)WndProc;
          	wcex.cbClsExtra		= 0;
          	wcex.cbWndExtra		= 0;
          	wcex.hInstance		= hInstance;
          	wcex.hIcon			= NULL;
          	wcex.hCursor		= NULL;
          	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
          	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
          	wcex.lpszMenuName	= NULL;
          	wcex.lpszClassName	= "canvas";
          	wcex.hIconSm		= NULL;
          
          	return RegisterClassEx(&wcex);
          }
          
          //****初始化函數(shù)*************************************
          // 加載位圖并設(shè)定各種初始值
          BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
          {
          	HBITMAP bmp;
          	hInst = hInstance;
          
          	hWnd = CreateWindow("canvas", "淺墨的繪圖窗口" , WS_OVERLAPPEDWINDOW,
          		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
          
          	if (!hWnd)
          	{
          		return FALSE;
          	}
          
          	MoveWindow(hWnd,10,10,640,480,true);
          	ShowWindow(hWnd, nCmdShow);
          	UpdateWindow(hWnd);
          
          	hdc = GetDC(hWnd);
          	mdc = CreateCompatibleDC(hdc);
          	bufdc = CreateCompatibleDC(hdc);
          
          
          	//建立空的位圖并置入mdc中
          	bmp = CreateCompatibleBitmap(hdc,640,480);
          	SelectObject(mdc,bmp);
          
          
          	//設(shè)定人物貼圖初始位置和移動(dòng)方向
          	x = 300;
          	y = 250;
          	dir = 0;
          	num = 0;
          	nowX = 300;
          	nowY = 300;
          
          
          	//載入各連續(xù)移動(dòng)位圖及背景圖
          	girl[0] = (HBITMAP)LoadImage(NULL,"girl0.bmp",IMAGE_BITMAP,440,148,LR_LOADFROMFILE);
          	girl[1] = (HBITMAP)LoadImage(NULL,"girl1.bmp",IMAGE_BITMAP,424,154,LR_LOADFROMFILE);
          	girl[2] = (HBITMAP)LoadImage(NULL,"girl2.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE);
          	girl[3] = (HBITMAP)LoadImage(NULL,"girl3.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE);
          	bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,480,LR_LOADFROMFILE);\
          
          
          	bird = (HBITMAP)LoadImage(NULL,"bird.bmp",IMAGE_BITMAP,122,122,LR_LOADFROMFILE);
          
          	p[0].x = 30;
          	p[0].y = 100;
          
          	p[1].x = 250;
          	p[1].y = 250;
          
          	p[2].x = 500;
          	p[2].y = 400;
          
          
          	MyPaint(hdc);
          
          	return TRUE;
          }
          
          //****自定義繪圖函數(shù)*********************************
          // 1.人物貼圖坐標(biāo)修正及窗口貼圖
          //進(jìn)行AI行為判斷并貼圖
          void MyPaint(HDC hdc)
          {
          	int w,h,i;
          
          	//先在mdc中貼上背景圖
          	SelectObject(bufdc,bg);
          	BitBlt(mdc,0,0,640,480,bufdc,0,0,SRCCOPY);
          
          	//按照目前的移動(dòng)方向取出對(duì)應(yīng)人物的連續(xù)走動(dòng)圖,并確定截取人物圖的寬度與高度
          	SelectObject(bufdc,girl[dir]);
          	switch(dir)
          	{
          		case 0:
          			w = 55;
          			h = 74;
          			break;
          		case 1:
          			w = 53;
          			h = 77;
          			break;
          		case 2:
          			w = 60;
          			h = 74;
          			break;
          		case 3:
          			w = 60;
          			h = 74;
          			break;
          	}
          	//按照目前的X,Y的值在mdc上進(jìn)行透明貼圖,然后顯示在窗口畫(huà)面上
          	BitBlt(mdc,x,y,w,h,bufdc,num*w,h,SRCAND);
          	BitBlt(mdc,x,y,w,h,bufdc,num*w,0,SRCPAINT);
          	
          	BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);
          
          
          
          	//貼出鳥(niǎo)的圖片
          	SelectObject(bufdc,bird);
          
          
          	for(i=0;i<3;i++)
          	{
          
          		if(rand()%3 != 1)		//有2/3幾率進(jìn)行追蹤
          		{
          			if(p[i].y > y-16)
          				p[i].y -= 5;
          			else
          				p[i].y += 5;
          
          			if(p[i].x > x-25)
          				p[i].x -= 5;
          			else
          				p[i].x += 5;
          		}
          
          		if(p[i].x > x-25)    //判斷小鳥(niǎo)的移動(dòng)方向,從而選擇合適的位圖朝向
          		{
          			BitBlt(mdc,p[i].x,p[i].y,61,61,bufdc,61,61,SRCAND);
          			BitBlt(mdc,p[i].x,p[i].y,61,61,bufdc,0,61,SRCPAINT);
          		}
          		else
          		{
          			BitBlt(mdc,p[i].x,p[i].y,61,61,bufdc,61,0,SRCAND);
          			BitBlt(mdc,p[i].x,p[i].y,61,61,bufdc,0,0,SRCPAINT);
          		}
          	}
          
          	BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);
          
          
          
          
          	tPre = GetTickCount();         //記錄此次繪圖時(shí)間
          
          	num++;
          	if(num == 8)
          		num = 0;
          
          }
          
          //****消息處理函數(shù)***********************************
          // 1.按下【Esc】鍵結(jié)束程序
          // 2.按下方向鍵重設(shè)貼圖坐標(biāo)
          LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
          {
          	switch (message)
          	{
          		case WM_KEYDOWN:	     //按下鍵盤(pán)消息
          			//判斷按鍵的虛擬鍵碼
          			switch (wParam) 
          			{
          				case VK_ESCAPE:           //按下【Esc】鍵
          					PostQuitMessage( 0 );  //結(jié)束程序
          					break;
          				case VK_UP:				  //按下【↑】鍵
          					//先按照目前的移動(dòng)方向來(lái)進(jìn)行貼圖坐標(biāo)修正,并加入人物往上移動(dòng)的量(每次按下一次按鍵移動(dòng)10個(gè)單位),來(lái)決定人物貼圖坐標(biāo)的X與Y值,接著判斷坐標(biāo)是否超出窗口區(qū)域,若有則再次修正
          					switch(dir)
          					{
          						case 0:	
          							y -= 10;
          							break;
          						case 1:
          							x -= 1;
          							y -= 8;
          							break;
          						case 2:	
          							x += 2;
          							y -= 10;
          							break;
          						case 3:
          							x += 2;
          							y -= 10;
          							break;
          					}
          					if(y < 0)
          						y = 0;
          					dir = 0;
          					break;
          				case VK_DOWN:			  //按下【↓】鍵
          					switch(dir)
          					{
          						case 0:
          							x += 1;
          							y += 8;
          							break;
          						case 1:
          							y += 10;
          							break;
          						case 2:
          							x += 3;
          							y += 6;
          							break;
          						case 3:
          							x += 3;
          							y += 6;
          							break;
          					}
          
          					if(y > 375)
          						y = 375;
          					dir = 1;
          					break;
          				case VK_LEFT:			  //按下【←】鍵
          					switch(dir)
          					{
          						case 0:
          							x -= 12;
          							break;
          						case 1:
          							x -= 13;
          							y += 4;
          							break;
          						case 2:
          							x -= 10;
          							break;
          						case 3:
          							x -= 10;
          							break;
          					}
          					if(x < 0)
          						x = 0;
          					dir = 2;
          					break;
          				case VK_RIGHT:			   //按下【→】鍵
          					switch(dir)
          					{
          						case 0:
          							x += 8;
          							break;
          						case 1:
          							x += 7;
          							y += 4;
          							break;
          						case 2:
          							x += 10;
          							break;
          						case 3:
          							x += 10;
          							break;
          					}
          					if(x > 575)
          						x = 575;
          					dir = 3;
          					break;
          			}
          			break;
          		case WM_DESTROY:			    	//窗口結(jié)束消息
          			int i;
          
          			DeleteDC(mdc);
          			DeleteDC(bufdc);
          			for(i=0;i<4;i++)
          				DeleteObject(girl[i]);
          			DeleteObject(bg);
          			ReleaseDC(hWnd,hdc);
          
          			PostQuitMessage(0);
          			break;
          		default:							//其他消息
          			return DefWindowProc(hWnd, message, wParam, lParam);
             }
             return 0;
          }
          



          運(yùn)行截圖如下:


          以及

          運(yùn)行這個(gè)小游戲,我們要用鍵盤(pán)的【↑】【↓】【←】【→】鍵來(lái)躲避小鳥(niǎo)的追擊,小鳥(niǎo)則會(huì)不斷向人物靠近。



          貼圖這方面,我只是把效果做了出來(lái),由于最近實(shí)在是有些忙,這個(gè)demo提供只是希望給大家一個(gè)實(shí)現(xiàn)AI的思路,具體的bug沒(méi)有進(jìn)一步修復(fù)和完善,這個(gè)例子里面會(huì)出現(xiàn)小鳥(niǎo)閃爍的小問(wèn)題,希望大家不要見(jiàn)事說(shuō)事,呵呵。


          至于貼圖的方式,之前嘗試了CImage的draw方法,圖像閃爍得很?chē)?yán)重,由于這是消息循環(huán)產(chǎn)生的動(dòng)畫(huà)效果,圖像閃爍的原因估計(jì)和CImage類(lèi)的貼圖效率有關(guān)。之后還采用過(guò)用CImage的detach方法將某png的句柄附給HBITMAP,然后調(diào)用bitblt進(jìn)行貼圖,卻得到了一個(gè)失真的矩形。

          如果有解決動(dòng)畫(huà)顯示里CImage貼圖會(huì)閃爍的方法,請(qǐng)與我討論,這是一個(gè)相互學(xué)習(xí)提高的過(guò)程,非常希望能和大家交流。

          由于CImage類(lèi)的采用沒(méi)起到一個(gè)好的效果,所以依舊采用的傳統(tǒng)的bitblt貼圖方式。這種貼圖方式的優(yōu)點(diǎn)是貼圖效率非常的高。





          本節(jié)筆記到這里就結(jié)束了,由于近期在做一個(gè)純flash的網(wǎng)站,更新速度和評(píng)論的回復(fù)都不像往常那么及時(shí),而且文章末尾demo的質(zhì)量有些下滑,不過(guò)這不會(huì)影響整體的學(xué)習(xí)效果,希望大家能夠體諒。


          本節(jié)筆記的源代碼請(qǐng)點(diǎn)擊這里下載:   【Visual C++】Code_Note_15


          感謝一直支持【Visual C++】游戲開(kāi)發(fā)筆記系列專(zhuān)欄的朋友們,也請(qǐng)大家繼續(xù)關(guān)注我的專(zhuān)欄,我一有時(shí)間就會(huì)把自己的學(xué)習(xí)心得,覺(jué)得比較好的知識(shí)點(diǎn)寫(xiě)出來(lái)和大家一起分享。


          精通游戲開(kāi)發(fā)的路還很長(zhǎng)很長(zhǎng),非常希望能和大家一起交流,共同學(xué)習(xí),共同進(jìn)步。


          大家看過(guò)后覺(jué)得值得一看的話(huà),可以頂一下這篇文章,你們的支持是我繼續(xù)寫(xiě)下去的動(dòng)力~


          如果文章中有什么疏漏的地方,也請(qǐng)大家指正。也希望大家可以多留言來(lái)和我探討編程相關(guān)的問(wèn)題。


          最后,謝謝你們一直的支持~~~

          ——————————淺墨于2012年4月7日

          作者:zhmxy555 發(fā)表于2012-4-7 5:44:36 原文鏈接
          閱讀:3780 評(píng)論:47 查看評(píng)論

          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 青州市| 当阳市| 巴青县| 西乌珠穆沁旗| 弥渡县| 台北县| 新营市| 柳江县| 铁岭市| 洛扎县| 凤城市| 康保县| 定陶县| 五莲县| 年辖:市辖区| 怀柔区| 和政县| 历史| 山东| 融水| 忻州市| 景谷| 金寨县| 桂阳县| 宜阳县| 虹口区| 团风县| 武夷山市| 建宁县| 公主岭市| 阿荣旗| 定边县| 云阳县| 湟源县| 卢湾区| 木兰县| 赣州市| 翁源县| 台安县| 合肥市| 醴陵市|