【Visual C++】遊戲開發筆記十六 講解一個完整的回合制遊戲demo

本系列文章由zhmxy555編寫,轉載請註明出處。 

作者:毛星雲    郵箱: [email protected]    歡迎郵件交流編程心得



這節筆記的主要內容是介紹一個完整的回合制遊戲demo,而這個demo裏面主要突出了遊戲裏AI的各種思考與行爲的方式,這樣的AI稱作行爲型AI。

如果對AI基礎不太瞭解的朋友,請移步:


【Visual C++】遊戲開發筆記十五 遊戲人工智能(一) 運動型遊戲AI


首先,我們來了解這種行爲型AI的設計方法。

遊戲程序中計算機角色的思考與行爲,實際上是對各種不同事件進行分析思考,然後根據不同的情況作出相應的反應。但如何對發生的條件進行判斷,並作出相應的反應呢?

對,我們可以利用“if-else”條件句以及“switch-case”語句這類的判斷式來完成。

通常情況下,設計此類AI,會涉及到連串的條件判斷句,簡單數學運算,及一些數據結構的知識。



下面我們就來具體講解這個demo涉及到的一些知識點:




一、AI怪物攻擊與思考方式設計



例如今天我們要展示的這個回合制遊戲demo裏的AI,就有如下幾種行爲

(1)利爪攻擊

(2)閃電鏈攻擊

(3)致命一擊

(4)使用梅肯斯姆回覆生命值

(5)逃跑


那麼我們可以根據以上設計的怪物行爲,設計以下一段算法,用來模擬怪物對戰時的思考與行爲的方式:

  1. if(monster.nHp > 20)  //生命值大於20  
  2. {  
  3. if(rand()%5!= 1)             
  4.  //進行利爪攻擊概率4/5  
  5. else  
  6.        //進行閃電鏈攻擊概率1/5  
  7. }  
  8. else     //生命值小於20  
  9. {  
  10. switch(rand()%5)  
  11. {  
  12. case 0:  //利爪攻擊  
  13. break;  
  14. case 1:  //釋放閃電鏈  
  15. break;  
  16. case 2:  //致命一擊  
  17. break;  
  18. case 3:  //使用梅肯斯姆回覆  ;  
  19. break;  
  20. case 4:  //逃跑  
  21. if(1== rand()%3 )     //逃跑成功機率1/3  
  22. //逃跑成功  
  23. else  
  24. //逃跑失敗  
  25. break;  
  26. }  
  27. }  


這段代碼中,利用if-else判斷式判斷怪物生命值,然後怪物有4/5的機率釋放普通的利爪攻擊,有1/5的機率釋放閃電鏈魔法攻擊,當怪物重傷生命值小於20點時,也有一定的機率逃跑。

以上的利用“if-else”、“switch”語句,使計算機角色進行事件情況判斷,然後寫出相應的動作實現代碼,這就是行爲型遊戲AI

設計的核心精神。





二,玩家角色攻擊方式設計


然後我們再來設計一下玩家的攻擊技能。

今天放出的這個demo裏我給人物設定了兩個技能,一個主動的普通攻擊技能“無敵斬”,傷害計算公式爲damage = rand()%10 + player.lv*player.w(player.lv爲角色等級,player.w爲攻擊係數)。

而被動技能爲可以有一定機率打出4倍暴擊傷害的“恩賜解脫”,這個技能是Dota裏面幻影刺客的大招(呵呵,淺墨玩dota時可是超級幻刺控~~)。

其實暴擊的實現方式很簡單,就是用if條件句進行概率的判斷(淺墨在這裏利用4==rand( )%5來設定暴擊概率爲20%),如果判斷成功就將“倍率x普通攻擊”作爲damage的值。

(哈哈,淺墨專門找到了Dota裏面這兩個技能的圖標以及用到了這個demo裏面,具體效果圖在下面)


下面貼出代碼人物技能的代碼:

  1. if (4==rand()%5)                   // 20%機率觸發幻影刺客的大招,恩賜解脫,4倍暴擊傷害  
  2. {  
  3. damage = 4*(rand()%10 + player.lv*player.w);  
  4. monster.nHp -= (int)damage;  
  5. sprintf(str,"恩賜解脫觸發,這下牛逼了,4倍暴擊...對怪物照成了%d點傷害",damage);  
  6. }   
  7. else  
  8. {  
  9. damage = rand()%10 + player.lv*player.w;  
  10. monster.nHp -= (int)damage;  
  11. sprintf(str,"玩家使用了無敵斬,傷害一般般...對怪物照成了%d點傷害",damage);  
  12. }  




三、完整的回合制遊戲源代碼


基礎部分就講解完了,下面就貼出註釋詳細的,完整的回合制遊戲demo的代碼吧:

  1. #include "stdafx.h"  
  2. #include <stdio.h>  
  3. //定義一個結構體  
  4. struct chr  
  5. {  
  6. int  nHp;  
  7. int  fHp;  
  8. int  lv;  
  9. int  w;  
  10. int  kind;  
  11. };  
  12. //全局變量聲明  
  13. HINSTANCE hInst;  
  14. HBITMAP bg,sheep,girl,skill,skillult,slash,magic,recover,game;  
  15. HDC  hdc,mdc,bufdc;  
  16. HWND    hWnd;  
  17. DWORD   tPre,tNow;  
  18. int  pNum,f,txtNum;  
  19. bool    attack,over;  
  20. chr  player,monster;  
  21. char    text[5][100];  
  22. //全局函數聲明  
  23. ATOM     MyRegisterClass(HINSTANCE hInstance);  
  24. BOOL     InitInstance(HINSTANCEint);  
  25. LRESULT CALLBACK    WndProc(HWNDUINTWPARAMLPARAM);  
  26. void     MyPaint(HDC hdc);  
  27. void     MsgInsert(char*);  
  28. void     CheckDie(int hp,bool player);  
  29. //****WinMain函數,程序入口點函數**************************************  
  30. int APIENTRY WinMain(HINSTANCE hInstance,  
  31.                      HINSTANCE hPrevInstance,  
  32.                      LPSTR     lpCmdLine,  
  33.                      int       nCmdShow)  
  34. {  
  35. MSG msg;  
  36. MyRegisterClass(hInstance);  
  37. //初始化    
  38. if (!InitInstance (hInstance, nCmdShow))   
  39. {  
  40. return FALSE;  
  41. }  
  42. //消息循環  
  43. GetMessage(&msg,NULL,NULL,NULL);            //初始化msg      
  44.     while( msg.message!=WM_QUIT )  
  45.     {  
  46.         if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) )  
  47.         {  
  48.             TranslateMessage( &msg );  
  49.             DispatchMessage( &msg );  
  50.         }  
  51. else  
  52. {  
  53. tNow = GetTickCount();  
  54. if(tNow-tPre >= 40)  
  55. MyPaint(hdc);  
  56. }  
  57.     }  
  58. return msg.wParam;  
  59. }  
  60. //***設計一個窗口類,類似填空題,使用窗口結構體*************************  
  61. ATOM MyRegisterClass(HINSTANCE hInstance)  
  62. {  
  63. WNDCLASSEX wcex;  
  64. wcex.cbSize = sizeof(WNDCLASSEX);   
  65. wcex.style   = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;  
  66. wcex.lpfnWndProc    = (WNDPROC)WndProc;  
  67. wcex.cbClsExtra  = 0;  
  68. wcex.cbWndExtra  = 0;  
  69. wcex.hInstance   = hInstance;  
  70. wcex.hIcon   = NULL;  
  71. wcex.hCursor     = NULL;  
  72. wcex.hCursor     = LoadCursor(NULL, IDC_ARROW);  
  73. wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  74. wcex.lpszMenuName   = NULL;  
  75. wcex.lpszClassName  = "canvas";  
  76. wcex.hIconSm     = NULL;  
  77. return RegisterClassEx(&wcex);  
  78. }  
  79. //****初始化函數************************************  
  80. //加載位圖並設定各種初始值   
  81. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
  82. {  
  83. HBITMAP bmp;  
  84. hInst = hInstance;  
  85. hWnd = CreateWindow("canvas""淺墨的繪圖窗口" , WS_OVERLAPPEDWINDOW,  
  86. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);  
  87. if (!hWnd)  
  88. {  
  89. return FALSE;  
  90. }  
  91. MoveWindow(hWnd,10,10,640,510,true);  
  92. ShowWindow(hWnd, nCmdShow);  
  93. UpdateWindow(hWnd);  
  94. hdc = GetDC(hWnd);  
  95. mdc = CreateCompatibleDC(hdc);  
  96. bufdc = CreateCompatibleDC(hdc);  
  97. bmp = CreateCompatibleBitmap(hdc,640,510);  
  98. SelectObject(mdc,bmp);  
  99. bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,510,LR_LOADFROMFILE);  
  100. sheep = (HBITMAP)LoadImage(NULL,"sheep.bmp",IMAGE_BITMAP,133,220,LR_LOADFROMFILE);  
  101. girl = (HBITMAP)LoadImage(NULL,"girl.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE);  
  102. skill = (HBITMAP)LoadImage(NULL,"skill.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE);  
  103. skillult = (HBITMAP)LoadImage(NULL,"skillult.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE);  
  104. slash = (HBITMAP)LoadImage(NULL,"slash.bmp",IMAGE_BITMAP,196,162,LR_LOADFROMFILE);  
  105. magic = (HBITMAP)LoadImage(NULL,"magic.bmp",IMAGE_BITMAP,200,100,LR_LOADFROMFILE);  
  106. recover = (HBITMAP)LoadImage(NULL,"recover.bmp",IMAGE_BITMAP,300,150,LR_LOADFROMFILE);  
  107. game = (HBITMAP)LoadImage(NULL,"over.bmp",IMAGE_BITMAP,289,74,LR_LOADFROMFILE);  
  108. player.nHp = player.fHp = 50;   //設定玩家角色聲明值及上限  
  109. player.lv = 2;   //設定玩家角色等級  
  110. player.w  = 4;   //設定攻擊傷害加權值  
  111. monster.nHp = monster.fHp = 120;    //設定怪物角色生命值及上限  
  112. monster.lv = 1;  //設定怪物角色等級  
  113. monster.w = 1;   //設定攻擊傷害加權值  
  114. txtNum = 0;  //顯示消息數目  
  115. SetBkMode(mdc, TRANSPARENT);    //設置TextOut背景透明  
  116. MyPaint(hdc);  
  117. return TRUE;  
  118. }  
  119. //****自定義繪圖函數*********************************  
  120. // 1.畫面貼圖與對戰消息顯示  
  121. // 2.怪物行爲判斷及各項數據處理與計算  
  122. void MyPaint(HDC hdc)  
  123. {  
  124. char str[100];  
  125. int i,damage;  
  126. //貼上背景圖  
  127. SelectObject(bufdc,bg);  
  128. BitBlt(mdc,0,0,640,510,bufdc,0,0,SRCCOPY);  
  129. //顯示對戰消息  
  130. for(i=0;i<txtNum;i++)  
  131. TextOut(mdc,0,360+i*18,text[i],strlen(text[i]));  
  132. //貼上怪物圖  
  133. if(monster.nHp>0)  
  134. {  
  135. SelectObject(bufdc,sheep);  
  136. BitBlt(mdc,70,180,133,110,bufdc,0,110,SRCAND);  
  137. BitBlt(mdc,70,180,133,110,bufdc,0,0,SRCPAINT);  
  138. sprintf(str,"%d / %d",monster.nHp,monster.fHp);  
  139. TextOut(mdc,100,320,str,strlen(str));  
  140. }  
  141. //貼上玩家圖  
  142. if(player.nHp>0)  
  143. {  
  144. SelectObject(bufdc,girl);  
  145. BitBlt(mdc,500,200,60,74,bufdc,pNum*60,74,SRCAND);  
  146. BitBlt(mdc,500,200,60,74,bufdc,pNum*60,0,SRCPAINT);  
  147. sprintf(str,"%d / %d",player.nHp,player.fHp);  
  148. TextOut(mdc,510,320,str,strlen(str));  
  149. }  
  150. if(over)     //貼上游戲結束圖畫  
  151. {  
  152. SelectObject(bufdc,game);  
  153. BitBlt(mdc,200,200,289,37,bufdc,0,37,SRCAND);  
  154. BitBlt(mdc,200,200,289,37,bufdc,0,0,SRCPAINT);  
  155. }  
  156. else if(!attack)     //貼上攻擊命令圖畫  
  157. {  
  158. SelectObject(bufdc,skill);  
  159. BitBlt(mdc,500,350,50,50,bufdc,0,0,SRCCOPY);  
  160. SelectObject(bufdc,skillult);  
  161. BitBlt(mdc,430,350,50,50,bufdc,0,0,SRCCOPY);  
  162. //BitBlt(mdc,500,350,74,30,bufdc,0,30,SRCAND);  
  163. //BitBlt(mdc,500,350,74,30,bufdc,0,0,SRCPAINT);  
  164. }  
  165. else  
  166. {  
  167. f++;  
  168. //第5~10個畫面時顯示玩家攻擊圖標  
  169. if(f>=5 && f<=10)  
  170. {  
  171. SelectObject(bufdc,slash);  
  172. BitBlt(mdc,100,160,98,162,bufdc,98,0,SRCAND);  
  173. BitBlt(mdc,100,160,98,162,bufdc,0,0,SRCPAINT);  
  174. //第10個畫面時計算怪物受傷害程度並加入顯示消息  
  175. if(f == 10)  
  176. {  
  177. if (4==rand()%5)                   // 20%機率觸發幻影刺客的大招,恩賜解脫,4倍暴擊傷害  
  178. {  
  179. damage = 4*(rand()%10 + player.lv*player.w);  
  180. monster.nHp -= (int)damage;  
  181. sprintf(str,"恩賜解脫觸發,這下牛逼了,4倍暴擊...對怪物照成了%d點傷害",damage);  
  182. }   
  183. else  
  184. {  
  185. damage = rand()%10 + player.lv*player.w;  
  186. monster.nHp -= (int)damage;  
  187. sprintf(str,"玩家使用了無敵斬,傷害一般般...對怪物照成了%d點傷害",damage);  
  188. }  
  189. MsgInsert(str);  
  190. CheckDie(monster.nHp,false);  
  191. }  
  192. }  
  193. srand(tPre);  
  194. //第15個畫面時判斷怪物進行哪項動作  
  195. if(f == 15)  
  196. {  
  197. if(monster.nHp > 20)  //生命值大於20  
  198. {  
  199. if(rand()%5 != 1)          //進行利爪攻擊概率4/5  
  200. monster.kind = 0;  
  201. else                       //進行閃電鏈攻擊概率1/5  
  202. monster.kind = 1;      
  203. }  
  204. else     //生命值小於20  
  205. {  
  206. switch(rand()%5)  
  207. {  
  208. case 0:  //利爪攻擊  
  209. monster.kind = 0;  
  210. break;  
  211. case 1:  //釋放閃電鏈  
  212. monster.kind = 1;  
  213. break;  
  214. case 2:  //致命一擊  
  215. monster.kind = 2;  
  216. break;  
  217. case 3:  //使用梅肯斯姆回覆  
  218. monster.kind = 3;  
  219. break;  
  220. case 4:  //逃跑  
  221. monster.kind = 4;  
  222. break;  
  223. }  
  224. }  
  225. }  
  226. //第26~30個畫面時顯示玩家攻擊圖標  
  227. if(f>=26  && f<=30)  
  228. {  
  229. switch(monster.kind)  
  230. {  
  231. case 0:  //利爪攻擊  
  232. SelectObject(bufdc,slash);  
  233. BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND);  
  234. BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT);  
  235. //第30個畫面時計算玩家受傷害程度並加入顯示消息  
  236. if(f == 30)  
  237. {  
  238. damage = rand()%10 + monster.lv*monster.w;  
  239. player.nHp -= (int)damage;  
  240. sprintf(str,"怪物利爪攻擊...對玩家照成 %d 點傷害",damage);  
  241. MsgInsert(str);  
  242. CheckDie(player.nHp,true);  
  243. }  
  244. break;  
  245. case 1:  //釋放閃電鏈  
  246. SelectObject(bufdc,magic);  
  247. BitBlt(mdc,480,190,100,100,bufdc,100,0,SRCAND);  
  248. BitBlt(mdc,480,190,100,100,bufdc,0,0,SRCPAINT);  
  249. //第30個畫面時計算玩家受傷害程度並加入顯示消息  
  250. if(f == 30)  
  251. {  
  252. damage = rand()%10 + 3*monster.w;  
  253. player.nHp -= (int)damage;  
  254. sprintf(str,"怪物釋放閃電鏈...對玩家照成 %d 點傷害",damage);  
  255. MsgInsert(str);  
  256. CheckDie(player.nHp,true);  
  257. }  
  258. break;  
  259. case 2:  //致命一擊  
  260. SelectObject(bufdc,slash);  
  261. BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND);  
  262. BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT);  
  263. //第30個畫面時計算玩家受傷害程度並加入顯示消息  
  264. if(f == 30)  
  265. {  
  266. damage = rand()%10 + monster.lv*monster.w*5;  
  267. player.nHp -= (int)damage;  
  268. sprintf(str,"怪物致命一擊...對玩家照成 %d 點傷害.",damage);  
  269. MsgInsert(str);  
  270. CheckDie(player.nHp,true);  
  271. }  
  272. break;  
  273. case 3:  //使用梅肯斯姆補血  
  274. SelectObject(bufdc,recover);  
  275. BitBlt(mdc,60,160,150,150,bufdc,150,0,SRCAND);  
  276. BitBlt(mdc,60,160,150,150,bufdc,0,0,SRCPAINT);  
  277. //第30個畫面時怪物回覆生命值並加入顯示消息  
  278. if(f == 30)  
  279. {  
  280. monster.nHp += 30;  
  281. sprintf(str,"怪物使用梅肯斯姆...恢復了30點生命值",damage);  
  282. MsgInsert(str);  
  283. }  
  284. break;  
  285. case 4:  
  286. //在第30個畫面時判斷怪物是否逃跑成功  
  287. if(f == 30)  
  288. {  
  289. if(1== rand()%3 )   //逃跑機率1/3  
  290. {  
  291. over = true;  
  292. monster.nHp = 0;  
  293. sprintf(str,"怪物逃跑中...逃跑成功");  
  294. MsgInsert(str);  
  295. }  
  296. else  
  297. {  
  298. sprintf(str,"怪物逃跑中...逃跑失敗");  
  299. MsgInsert(str);  
  300. }  
  301. }  
  302. break;  
  303. }  
  304. }  
  305. if(f == 30)  //回合結束  
  306. {  
  307. attack = false;  
  308. f = 0;  
  309. }  
  310. }  
  311. BitBlt(hdc,0,0,640,510,mdc,0,0,SRCCOPY);  
  312. tPre = GetTickCount();  
  313. pNum++;  
  314. if(pNum == 8)  
  315. pNum = 0;  
  316. }  
  317. //****新增的對戰消息函數********************************  
  318. void MsgInsert(char* str)  
  319. {  
  320. if(txtNum < 5)  
  321. {  
  322. sprintf(text[txtNum],str);  
  323. txtNum++;  
  324. }  
  325. else  
  326. {  
  327. for(int i=0;i<txtNum;i++)  
  328. sprintf(text[i],text[i+1]);  
  329. sprintf(text[4],str);  
  330. }  
  331. }  
  332. //****生命值判斷函數*************************  
  333. void CheckDie(int hp,bool player)  
  334. {  
  335. char str[100];  
  336. if(hp <= 0)  
  337. {  
  338. over = true;  
  339. if(player)  
  340. {  
  341. sprintf(str,"勝敗乃兵家常事,大俠請重新來過......");  
  342. MsgInsert(str);  
  343. }  
  344. else  
  345. {  
  346. sprintf(str,"少年,你贏了,有兩下子啊~~~~~!!!!");  
  347. MsgInsert(str);  
  348. }  
  349. }  
  350. }  
  351. //****消息處理函數***********************************  
  352. //   
  353. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  354. {  
  355. int x,y;  
  356. switch (message)  
  357. {  
  358. case WM_KEYDOWN:     //鍵盤消息  
  359. if(wParam==VK_ESCAPE)    //按下Esc鍵  
  360. PostQuitMessage(0);  
  361. break;  
  362. case WM_LBUTTONDOWN:     //鼠標左鍵消息  
  363. if(!attack)  
  364. {  
  365. x = LOWORD(lParam);  //X座標  
  366. y = HIWORD(lParam);  //Y座標  
  367. if(x >= 500 && x <= 550 && y >= 350 && y <= 400)  
  368. attack = true;  
  369. }  
  370. break;  
  371. case WM_DESTROY:     //窗口結束消息  
  372. DeleteDC(mdc);  
  373. DeleteDC(bufdc);  
  374. DeleteObject(bg);  
  375. DeleteObject(sheep);  
  376. DeleteObject(girl);  
  377. DeleteObject(skill);  
  378. DeleteObject(skillult);  
  379. DeleteObject(slash);  
  380. DeleteObject(magic);  
  381. DeleteObject(recover);  
  382. DeleteObject(game);  
  383. ReleaseDC(hWnd,hdc);  
  384. PostQuitMessage(0);  
  385. break;  
  386. default:     //默認消息  
  387. return DefWindowProc(hWnd, message, wParam, lParam);  
  388.    }  
  389.    return 0;  
  390. }  


每一回合開始的時候,我們點擊畫面上“無敵斬”的技能圖標,就可以進行攻擊,對怪物造成傷害,人品好的話,還可以觸發強力被動技能“恩賜解脫”,對怪物造成4倍暴擊傷害,這裏我們設定的暴擊概率爲20%



淺墨在截圖的時候,人品挺好的,恩賜解脫的暴擊概率爲20%,但是淺墨的4次攻擊裏,有3次都打出了“恩賜解脫”的暴擊效果,直接果斷地把這隻小綿羊帶走了,呵呵。


下面就是遊戲運行的截圖:


遊戲開始



第一刀就出暴擊了,48點傷害



運氣不錯,又一刀暴擊,68點傷害



最後一刀又出了暴擊,小綿羊被“秒殺”,遊戲結束




我們還可以調節怪物等級,怪物攻擊加權值,怪物血量上限以及玩家角色等級,玩家角色攻擊加權值,玩家角色血量上限來讓遊戲更具挑戰性。


當然,我們也可以增加更多的代碼,來使怪物的思考與行動方式更具真實性和多樣性,來使玩家的技能更加豐富。



這個回合制遊戲demo可以說是目前市場上回合制遊戲的本體,《仙劍奇俠傳》(三代以前的,三代及以後的仙劍都是進度條模式了),《夢幻西遊》《問道》等經典的回合制遊戲,無非就是在這種風格的demo基礎上,寫更多的代碼,豐富內容而已,或爲遊戲引擎的核心代碼。


最後淺墨再提一點,以結束這篇筆記,其實就是爲了給大家提供一些實現思路:


可以在這個demo的基礎上,增加劇情,世界觀,遊戲地圖,等級系統,經驗值系統,寵物系統,道具系統,符文系統,五行相剋系

統,天氣系統等,讓這個回合制遊戲更加有趣更加吸引人。

而這些系統,我在以後的筆記裏面會盡量全部涵蓋進行講解的,希望大家繼續關注我的博客。




本節筆記到這裏就結束了。


本節筆記的源代碼請點擊這裏下載:  【Visual C++】Note_Code_16



感謝一直支持【Visual C++】遊戲開發筆記系列專欄的朋友們,也請大家繼續關注我的專欄,我一有時間就會把自己的學習心得,覺得比較好的知識點寫出來和大家一起分享。

精通遊戲開發的路還很長很長,非常希望能和大家一起交流,共同學習,共同進步。

大家看過後覺得值得一看的話,可以頂一下這篇文章,你們的支持是我繼續寫下去的動力~

如果文章中有什麼疏漏的地方,也請大家指正。也希望大家可以多留言來和我探討編程相關的問題。

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


——————————淺墨於2012年4月10日

原文地址:http://blog.csdn.net/zhmxy555/article/details/7447864

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章