游戏开发笔记十六 讲解一个完整的回合制游戏demo

首先,我们来了解这种行为型AI的设计方法。

游戏程序中计算机角色的思考与行为,实际上是对各种不同事件进行分析思考,然后根据不同的情况作出相应的反应。但如何对发生的条件进行判断,并作出相应的反应呢?

对,我们可以利用“if-else”条件句以及“switch-case”语句这类的判断式来完成。

通常情况下,设计此类AI,会涉及到连串的条件判断句,简单数学运算,及一些数据结构的知识。



下面我们就来具体讲解这个demo涉及到的一些知识点:




一、AI怪物攻击与思考方式设计



例如今天我们要展示的这个回合制游戏demo里的AI,就有如下几种行为

(1)利爪攻击

(2)闪电链攻击

(3)致命一击

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

(5)逃跑


那么我们可以根据以上设计的怪物行为,设计以下一段算法,用来模拟怪物对战时的思考与行为的方式:

[cpp] view plain copy print ?
  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(monster.nHp > 20) //生命值大于20 { if(rand()%5!= 1) //进行利爪攻击概率4/5 else //进行闪电链攻击概率1/5 } else //生命值小于20 { switch(rand()%5) { case 0: //利爪攻击 break; case 1: //释放闪电链 break; case 2: //致命一击 break; case 3: //使用梅肯斯姆回复 ; break; case 4: //逃跑 if(1== rand()%3 ) //逃跑成功几率1/3 //逃跑成功 else //逃跑失败 break; } }

这段代码中,利用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里面,具体效果图在下面)


下面贴出实现人物技能的代码:

[cpp] view plain copy print ?
  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. }  
if (4==rand()%5) // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害 { damage = 4*(rand()%10 + player.lv*player.w); monster.nHp -= (int)damage; sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage); } else { damage = rand()%10 + player.lv*player.w; monster.nHp -= (int)damage; sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage); }




三、完整的回合制游戏源代码


基础部分就讲解完了,下面就贴出注释详细的,完整的回合制游戏demo的代码吧:

[cpp] view plain copy print ?
  1. #include "stdafx.h"   
  2. #include    
  3.   
  4. //定义一个结构体   
  5. struct chr  
  6. {  
  7.     int     nHp;  
  8.     int     fHp;  
  9.     int     lv;  
  10.     int     w;  
  11.     int     kind;  
  12. };  
  13.   
  14. //全局变量声明   
  15. HINSTANCE hInst;  
  16. HBITMAP bg,sheep,girl,skill,skillult,slash,magic,recover,game;  
  17. HDC     hdc,mdc,bufdc;  
  18. HWND    hWnd;  
  19. DWORD   tPre,tNow;  
  20. int     pNum,f,txtNum;  
  21. bool    attack,over;  
  22. chr     player,monster;  
  23. char    text[5][100];  
  24.   
  25.   
  26.   
  27.   
  28. //全局函数声明   
  29. ATOM                MyRegisterClass(HINSTANCE hInstance);  
  30. BOOL                InitInstance(HINSTANCEint);  
  31. LRESULT CALLBACK    WndProc(HWNDUINTWPARAMLPARAM);  
  32. void                MyPaint(HDC hdc);  
  33. void                MsgInsert(char*);  
  34. void                CheckDie(int hp,bool player);  
  35.   
  36. //****WinMain函数,程序入口点函数**************************************   
  37. int APIENTRY WinMain(HINSTANCE hInstance,  
  38.                      HINSTANCE hPrevInstance,  
  39.                      LPSTR     lpCmdLine,  
  40.                      int       nCmdShow)  
  41. {  
  42.     MSG msg;  
  43.   
  44.     MyRegisterClass(hInstance);  
  45.   
  46.     //初始化     
  47.     if (!InitInstance (hInstance, nCmdShow))   
  48.     {  
  49.         return FALSE;  
  50.     }  
  51.   
  52.     //消息循环   
  53.     GetMessage(&msg,NULL,NULL,NULL);            //初始化msg       
  54.     while( msg.message!=WM_QUIT )  
  55.     {  
  56.         if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) )  
  57.         {  
  58.             TranslateMessage( &msg );  
  59.             DispatchMessage( &msg );  
  60.         }  
  61.         else  
  62.         {  
  63.             tNow = GetTickCount();  
  64.             if(tNow-tPre >= 40)  
  65.                 MyPaint(hdc);  
  66.         }  
  67.     }  
  68.   
  69.     return msg.wParam;  
  70. }  
  71.   
  72. //***设计一个窗口类,类似填空题,使用窗口结构体*************************   
  73. ATOM MyRegisterClass(HINSTANCE hInstance)  
  74. {  
  75.     WNDCLASSEX wcex;  
  76.   
  77.     wcex.cbSize = sizeof(WNDCLASSEX);   
  78.     wcex.style          = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;  
  79.     wcex.lpfnWndProc    = (WNDPROC)WndProc;  
  80.     wcex.cbClsExtra     = 0;  
  81.     wcex.cbWndExtra     = 0;  
  82.     wcex.hInstance      = hInstance;  
  83.     wcex.hIcon          = NULL;  
  84.     wcex.hCursor        = NULL;  
  85.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  86.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  87.     wcex.lpszMenuName   = NULL;  
  88.     wcex.lpszClassName  = "canvas";  
  89.     wcex.hIconSm        = NULL;  
  90.   
  91.     return RegisterClassEx(&wcex);  
  92. }  
  93.   
  94. //****初始化函数************************************   
  95. //加载位图并设定各种初始值    
  96. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
  97. {  
  98.     HBITMAP bmp;  
  99.     hInst = hInstance;  
  100.   
  101.     hWnd = CreateWindow("canvas""浅墨的绘图窗口" , WS_OVERLAPPEDWINDOW,  
  102.         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);  
  103.   
  104.     if (!hWnd)  
  105.     {  
  106.         return FALSE;  
  107.     }  
  108.   
  109.     MoveWindow(hWnd,10,10,640,510,true);  
  110.     ShowWindow(hWnd, nCmdShow);  
  111.     UpdateWindow(hWnd);  
  112.   
  113.     hdc = GetDC(hWnd);  
  114.     mdc = CreateCompatibleDC(hdc);  
  115.     bufdc = CreateCompatibleDC(hdc);  
  116.   
  117.     bmp = CreateCompatibleBitmap(hdc,640,510);  
  118.     SelectObject(mdc,bmp);  
  119.   
  120.     bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,510,LR_LOADFROMFILE);  
  121.     sheep = (HBITMAP)LoadImage(NULL,"sheep.bmp",IMAGE_BITMAP,133,220,LR_LOADFROMFILE);  
  122.     girl = (HBITMAP)LoadImage(NULL,"girl.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE);  
  123.     skill = (HBITMAP)LoadImage(NULL,"skill.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE);  
  124.     skillult = (HBITMAP)LoadImage(NULL,"skillult.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE);  
  125.     slash = (HBITMAP)LoadImage(NULL,"slash.bmp",IMAGE_BITMAP,196,162,LR_LOADFROMFILE);  
  126.     magic = (HBITMAP)LoadImage(NULL,"magic.bmp",IMAGE_BITMAP,200,100,LR_LOADFROMFILE);  
  127.     recover = (HBITMAP)LoadImage(NULL,"recover.bmp",IMAGE_BITMAP,300,150,LR_LOADFROMFILE);  
  128.     game = (HBITMAP)LoadImage(NULL,"over.bmp",IMAGE_BITMAP,289,74,LR_LOADFROMFILE);  
  129.   
  130.     player.nHp = player.fHp = 50;   //设定玩家角色声明值及上限   
  131.     player.lv = 2;                  //设定玩家角色等级   
  132.     player.w  = 4;                  //设定攻击伤害加权值   
  133.   
  134.     monster.nHp = monster.fHp = 120;    //设定怪物角色生命值及上限   
  135.     monster.lv = 1;                     //设定怪物角色等级   
  136.     monster.w = 1;                      //设定攻击伤害加权值   
  137.   
  138.     txtNum = 0;     //显示消息数目   
  139.   
  140.     SetBkMode(mdc, TRANSPARENT);    //设置TextOut背景透明   
  141.   
  142.       
  143.   
  144.   
  145.     MyPaint(hdc);  
  146.   
  147.     return TRUE;  
  148. }  
  149.   
  150. //****自定义绘图函数*********************************   
  151. // 1.画面贴图与对战消息显示   
  152. // 2.怪物行为判断及各项数据处理与计算   
  153. void MyPaint(HDC hdc)  
  154. {  
  155.     char str[100];  
  156.     int i,damage;  
  157.   
  158.     //贴上背景图   
  159.     SelectObject(bufdc,bg);  
  160.     BitBlt(mdc,0,0,640,510,bufdc,0,0,SRCCOPY);  
  161.   
  162.     //显示对战消息   
  163.       
  164.       
  165.           
  166.     for(i=0;i
  167.         TextOut(mdc,0,360+i*18,text[i],strlen(text[i]));  
  168.   
  169.     //贴上怪物图   
  170.     if(monster.nHp>0)  
  171.     {  
  172.         SelectObject(bufdc,sheep);  
  173.         BitBlt(mdc,70,180,133,110,bufdc,0,110,SRCAND);  
  174.         BitBlt(mdc,70,180,133,110,bufdc,0,0,SRCPAINT);  
  175.         sprintf(str,"%d / %d",monster.nHp,monster.fHp);  
  176.         TextOut(mdc,100,320,str,strlen(str));  
  177.     }  
  178.   
  179.     //贴上玩家图   
  180.     if(player.nHp>0)  
  181.     {  
  182.         SelectObject(bufdc,girl);  
  183.         BitBlt(mdc,500,200,60,74,bufdc,pNum*60,74,SRCAND);  
  184.         BitBlt(mdc,500,200,60,74,bufdc,pNum*60,0,SRCPAINT);  
  185.         sprintf(str,"%d / %d",player.nHp,player.fHp);  
  186.         TextOut(mdc,510,320,str,strlen(str));  
  187.     }  
  188.   
  189.     if(over)                //贴上游戏结束图画   
  190.     {  
  191.         SelectObject(bufdc,game);  
  192.         BitBlt(mdc,200,200,289,37,bufdc,0,37,SRCAND);  
  193.         BitBlt(mdc,200,200,289,37,bufdc,0,0,SRCPAINT);  
  194.     }  
  195.     else if(!attack)        //贴上攻击命令图画   
  196.     {  
  197.         SelectObject(bufdc,skill);  
  198.         BitBlt(mdc,500,350,50,50,bufdc,0,0,SRCCOPY);  
  199.         SelectObject(bufdc,skillult);  
  200.         BitBlt(mdc,430,350,50,50,bufdc,0,0,SRCCOPY);  
  201.         //BitBlt(mdc,500,350,74,30,bufdc,0,30,SRCAND);   
  202.         //BitBlt(mdc,500,350,74,30,bufdc,0,0,SRCPAINT);   
  203.     }     
  204.     else  
  205.     {  
  206.         f++;  
  207.   
  208.         //第5~10个画面时显示玩家攻击图标   
  209.         if(f>=5 && f<=10)  
  210.         {  
  211.             SelectObject(bufdc,slash);  
  212.               
  213.             BitBlt(mdc,100,160,98,162,bufdc,98,0,SRCAND);  
  214.             BitBlt(mdc,100,160,98,162,bufdc,0,0,SRCPAINT);  
  215.   
  216.             //第10个画面时计算怪物受伤害程度并加入显示消息   
  217.             if(f == 10)  
  218.             {  
  219.   
  220.                 if (4==rand()%5)                   // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害   
  221.                 {  
  222.                     damage = 4*(rand()%10 + player.lv*player.w);  
  223.                     monster.nHp -= (int)damage;  
  224.   
  225.                     sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage);  
  226.                 }   
  227.                 else  
  228.                 {  
  229.                     damage = rand()%10 + player.lv*player.w;  
  230.                     monster.nHp -= (int)damage;  
  231.   
  232.                     sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage);  
  233.                 }  
  234.   
  235.   
  236.                   
  237.                 MsgInsert(str);  
  238.   
  239.                 CheckDie(monster.nHp,false);  
  240.             }  
  241.         }  
  242.   
  243.         srand(tPre);  
  244.   
  245.         //第15个画面时判断怪物进行哪项动作   
  246.         if(f == 15)  
  247.         {  
  248.             if(monster.nHp > 20)             //生命值大于20   
  249.             {  
  250.                 if(rand()%5 != 1)          //进行利爪攻击概率4/5   
  251.                     monster.kind = 0;  
  252.                 else                       //进行闪电链攻击概率1/5   
  253.                     monster.kind = 1;      
  254.             }  
  255.             else                                //生命值小于20   
  256.             {  
  257.                 switch(rand()%5)  
  258.                 {  
  259.                     case 0:                     //利爪攻击   
  260.                         monster.kind = 0;  
  261.                         break;  
  262.                     case 1:                     //释放闪电链   
  263.                         monster.kind = 1;  
  264.                         break;  
  265.                     case 2:                     //致命一击   
  266.                         monster.kind = 2;  
  267.                         break;  
  268.                     case 3:                     //使用梅肯斯姆回复   
  269.                         monster.kind = 3;  
  270.                         break;  
  271.                     case 4:                     //逃跑   
  272.                         monster.kind = 4;  
  273.                         break;  
  274.                 }  
  275.             }  
  276.         }  
  277.   
  278.         //第26~30个画面时显示玩家攻击图标   
  279.         if(f>=26  && f<=30)  
  280.         {  
  281.             switch(monster.kind)  
  282.             {  
  283.                 case 0:                         //利爪攻击   
  284.                     SelectObject(bufdc,slash);  
  285.                     BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND);  
  286.                     BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT);  
  287.   
  288.                     //第30个画面时计算玩家受伤害程度并加入显示消息   
  289.                     if(f == 30)  
  290.                     {  
  291.                         damage = rand()%10 + monster.lv*monster.w;  
  292.                         player.nHp -= (int)damage;  
  293.                           
  294.                         sprintf(str,"怪物利爪攻击...对玩家照成 %d 点伤害",damage);  
  295.                         MsgInsert(str);  
  296.   
  297.                         CheckDie(player.nHp,true);  
  298.                     }  
  299.                     break;  
  300.                 case 1:                         //释放闪电链   
  301.                     SelectObject(bufdc,magic);  
  302.                     BitBlt(mdc,480,190,100,100,bufdc,100,0,SRCAND);  
  303.                     BitBlt(mdc,480,190,100,100,bufdc,0,0,SRCPAINT);  
  304.   
  305.                     //第30个画面时计算玩家受伤害程度并加入显示消息   
  306.                     if(f == 30)  
  307.                     {  
  308.                         damage = rand()%10 + 3*monster.w;  
  309.                         player.nHp -= (int)damage;    
  310.                           
  311.                         sprintf(str,"怪物释放闪电链...对玩家照成 %d 点伤害",damage);  
  312.                         MsgInsert(str);  
  313.   
  314.                         CheckDie(player.nHp,true);  
  315.                     }  
  316.                     break;  
  317.                 case 2:                         //致命一击   
  318.                     SelectObject(bufdc,slash);  
  319.                     BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND);  
  320.                     BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT);  
  321.   
  322.                     //第30个画面时计算玩家受伤害程度并加入显示消息   
  323.                     if(f == 30)  
  324.                     {  
  325.                         damage = rand()%10 + monster.lv*monster.w*5;  
  326.                         player.nHp -= (int)damage;  
  327.                           
  328.                         sprintf(str,"怪物致命一击...对玩家照成 %d 点伤害.",damage);  
  329.                         MsgInsert(str);  
  330.   
  331.                         CheckDie(player.nHp,true);  
  332.                     }  
  333.                     break;  
  334.                 case 3:                         //使用梅肯斯姆补血   
  335.                     SelectObject(bufdc,recover);  
  336.                     BitBlt(mdc,60,160,150,150,bufdc,150,0,SRCAND);  
  337.                     BitBlt(mdc,60,160,150,150,bufdc,0,0,SRCPAINT);  
  338.   
  339.                     //第30个画面时怪物回复生命值并加入显示消息   
  340.                     if(f == 30)  
  341.                     {  
  342.                         monster.nHp += 30;  
  343.                           
  344.                         sprintf(str,"怪物使用梅肯斯姆...恢复了30点生命值",damage);  
  345.                         MsgInsert(str);  
  346.                     }  
  347.                     break;  
  348.                 case 4:  
  349.                     //在第30个画面时判断怪物是否逃跑成功   
  350.                     if(f == 30)  
  351.                     {  
  352.                         if(1== rand()%3 )   //逃跑几率1/3   
  353.                         {  
  354.                             over = true;  
  355.                             monster.nHp = 0;  
  356.   
  357.                             sprintf(str,"怪物逃跑中...逃跑成功");  
  358.                             MsgInsert(str);  
  359.                         }  
  360.                         else  
  361.                         {  
  362.                             sprintf(str,"怪物逃跑中...逃跑失败");  
  363.                             MsgInsert(str);  
  364.                         }  
  365.                     }  
  366.                     break;  
  367.             }  
  368.         }  
  369.   
  370.         if(f == 30)         //回合结束   
  371.         {  
  372.             attack = false;  
  373.             f = 0;  
  374.         }  
  375.     }  
  376.   
  377.     BitBlt(hdc,0,0,640,510,mdc,0,0,SRCCOPY);  
  378.   
  379.     tPre = GetTickCount();  
  380.   
  381.     pNum++;  
  382.     if(pNum == 8)  
  383.         pNum = 0;  
  384. }  
  385.   
  386. //****新增的对战消息函数********************************   
  387. void MsgInsert(char* str)  
  388. {  
  389.     if(txtNum < 5)  
  390.     {  
  391.         sprintf(text[txtNum],str);  
  392.         txtNum++;  
  393.     }  
  394.     else  
  395.     {  
  396.         for(int i=0;i
  397.             sprintf(text[i],text[i+1]);  
  398.   
  399.         sprintf(text[4],str);  
  400.     }  
  401. }  
  402.   
  403. //****生命值判断函数*************************   
  404. void CheckDie(int hp,bool player)  
  405. {  
  406.     char str[100];  
  407.   
  408.     if(hp <= 0)  
  409.     {  
  410.         over = true;  
  411.         if(player)  
  412.         {  
  413.             sprintf(str,"胜败乃兵家常事,大侠请重新来过......");  
  414.             MsgInsert(str);  
  415.         }  
  416.         else  
  417.         {  
  418.             sprintf(str,"少年,你赢了,有两下子啊~~~~~!!!!");  
  419.             MsgInsert(str);  
  420.         }  
  421.     }  
  422. }  
  423.   
  424. //****消息处理函数***********************************   
  425. //    
  426. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  427. {  
  428.     int x,y;  
  429.   
  430.     switch (message)  
  431.     {  
  432.         case WM_KEYDOWN:                //键盘消息   
  433.             if(wParam==VK_ESCAPE)       //按下Esc键   
  434.                 PostQuitMessage(0);  
  435.             break;  
  436.         case WM_LBUTTONDOWN:            //鼠标左键消息   
  437.             if(!attack)  
  438.             {  
  439.                 x = LOWORD(lParam);     //X坐标   
  440.                 y = HIWORD(lParam);     //Y坐标   
  441.               
  442.                 if(x >= 500 && x <= 550 && y >= 350 && y <= 400)  
  443.                     attack = true;  
  444.             }  
  445.             break;  
  446.         case WM_DESTROY:                //窗口结束消息   
  447.             DeleteDC(mdc);  
  448.             DeleteDC(bufdc);  
  449.             DeleteObject(bg);  
  450.             DeleteObject(sheep);  
  451.             DeleteObject(girl);  
  452.             DeleteObject(skill);  
  453.             DeleteObject(skillult);  
  454.             DeleteObject(slash);  
  455.             DeleteObject(magic);  
  456.             DeleteObject(recover);  
  457.             DeleteObject(game);  
  458.   
  459.             ReleaseDC(hWnd,hdc);  
  460.   
  461.             PostQuitMessage(0);  
  462.             break;  
  463.         default:                        //默认消息   
  464.             return DefWindowProc(hWnd, message, wParam, lParam);  
  465.    }  
  466.    return 0;  
  467. }  
#include "stdafx.h" #include //定义一个结构体 struct chr { int nHp; int fHp; int lv; int w; int kind; }; //全局变量声明 HINSTANCE hInst; HBITMAP bg,sheep,girl,skill,skillult,slash,magic,recover,game; HDC hdc,mdc,bufdc; HWND hWnd; DWORD tPre,tNow; int pNum,f,txtNum; bool attack,over; chr player,monster; char text[5][100]; //全局函数声明 ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void MyPaint(HDC hdc); void MsgInsert(char*); void CheckDie(int hp,bool player); //****WinMain函数,程序入口点函数************************************** 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 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; } //***设计一个窗口类,类似填空题,使用窗口结构体************************* ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 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); } //****初始化函数************************************ //加载位图并设定各种初始值 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,510,true); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hdc = GetDC(hWnd); mdc = CreateCompatibleDC(hdc); bufdc = CreateCompatibleDC(hdc); bmp = CreateCompatibleBitmap(hdc,640,510); SelectObject(mdc,bmp); bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,510,LR_LOADFROMFILE); sheep = (HBITMAP)LoadImage(NULL,"sheep.bmp",IMAGE_BITMAP,133,220,LR_LOADFROMFILE); girl = (HBITMAP)LoadImage(NULL,"girl.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE); skill = (HBITMAP)LoadImage(NULL,"skill.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE); skillult = (HBITMAP)LoadImage(NULL,"skillult.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE); slash = (HBITMAP)LoadImage(NULL,"slash.bmp",IMAGE_BITMAP,196,162,LR_LOADFROMFILE); magic = (HBITMAP)LoadImage(NULL,"magic.bmp",IMAGE_BITMAP,200,100,LR_LOADFROMFILE); recover = (HBITMAP)LoadImage(NULL,"recover.bmp",IMAGE_BITMAP,300,150,LR_LOADFROMFILE); game = (HBITMAP)LoadImage(NULL,"over.bmp",IMAGE_BITMAP,289,74,LR_LOADFROMFILE); player.nHp = player.fHp = 50; //设定玩家角色声明值及上限 player.lv = 2; //设定玩家角色等级 player.w = 4; //设定攻击伤害加权值 monster.nHp = monster.fHp = 120; //设定怪物角色生命值及上限 monster.lv = 1; //设定怪物角色等级 monster.w = 1; //设定攻击伤害加权值 txtNum = 0; //显示消息数目 SetBkMode(mdc, TRANSPARENT); //设置TextOut背景透明 MyPaint(hdc); return TRUE; } //****自定义绘图函数********************************* // 1.画面贴图与对战消息显示 // 2.怪物行为判断及各项数据处理与计算 void MyPaint(HDC hdc) { char str[100]; int i,damage; //贴上背景图 SelectObject(bufdc,bg); BitBlt(mdc,0,0,640,510,bufdc,0,0,SRCCOPY); //显示对战消息 for(i=0;i0) { SelectObject(bufdc,sheep); BitBlt(mdc,70,180,133,110,bufdc,0,110,SRCAND); BitBlt(mdc,70,180,133,110,bufdc,0,0,SRCPAINT); sprintf(str,"%d / %d",monster.nHp,monster.fHp); TextOut(mdc,100,320,str,strlen(str)); } //贴上玩家图 if(player.nHp>0) { SelectObject(bufdc,girl); BitBlt(mdc,500,200,60,74,bufdc,pNum*60,74,SRCAND); BitBlt(mdc,500,200,60,74,bufdc,pNum*60,0,SRCPAINT); sprintf(str,"%d / %d",player.nHp,player.fHp); TextOut(mdc,510,320,str,strlen(str)); } if(over) //贴上游戏结束图画 { SelectObject(bufdc,game); BitBlt(mdc,200,200,289,37,bufdc,0,37,SRCAND); BitBlt(mdc,200,200,289,37,bufdc,0,0,SRCPAINT); } else if(!attack) //贴上攻击命令图画 { SelectObject(bufdc,skill); BitBlt(mdc,500,350,50,50,bufdc,0,0,SRCCOPY); SelectObject(bufdc,skillult); BitBlt(mdc,430,350,50,50,bufdc,0,0,SRCCOPY); //BitBlt(mdc,500,350,74,30,bufdc,0,30,SRCAND); //BitBlt(mdc,500,350,74,30,bufdc,0,0,SRCPAINT); } else { f++; //第5~10个画面时显示玩家攻击图标 if(f>=5 && f<=10) { SelectObject(bufdc,slash); BitBlt(mdc,100,160,98,162,bufdc,98,0,SRCAND); BitBlt(mdc,100,160,98,162,bufdc,0,0,SRCPAINT); //第10个画面时计算怪物受伤害程度并加入显示消息 if(f == 10) { if (4==rand()%5) // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害 { damage = 4*(rand()%10 + player.lv*player.w); monster.nHp -= (int)damage; sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage); } else { damage = rand()%10 + player.lv*player.w; monster.nHp -= (int)damage; sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage); } MsgInsert(str); CheckDie(monster.nHp,false); } } srand(tPre); //第15个画面时判断怪物进行哪项动作 if(f == 15) { if(monster.nHp > 20) //生命值大于20 { if(rand()%5 != 1) //进行利爪攻击概率4/5 monster.kind = 0; else //进行闪电链攻击概率1/5 monster.kind = 1; } else //生命值小于20 { switch(rand()%5) { case 0: //利爪攻击 monster.kind = 0; break; case 1: //释放闪电链 monster.kind = 1; break; case 2: //致命一击 monster.kind = 2; break; case 3: //使用梅肯斯姆回复 monster.kind = 3; break; case 4: //逃跑 monster.kind = 4; break; } } } //第26~30个画面时显示玩家攻击图标 if(f>=26 && f<=30) { switch(monster.kind) { case 0: //利爪攻击 SelectObject(bufdc,slash); BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND); BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT); //第30个画面时计算玩家受伤害程度并加入显示消息 if(f == 30) { damage = rand()%10 + monster.lv*monster.w; player.nHp -= (int)damage; sprintf(str,"怪物利爪攻击...对玩家照成 %d 点伤害",damage); MsgInsert(str); CheckDie(player.nHp,true); } break; case 1: //释放闪电链 SelectObject(bufdc,magic); BitBlt(mdc,480,190,100,100,bufdc,100,0,SRCAND); BitBlt(mdc,480,190,100,100,bufdc,0,0,SRCPAINT); //第30个画面时计算玩家受伤害程度并加入显示消息 if(f == 30) { damage = rand()%10 + 3*monster.w; player.nHp -= (int)damage; sprintf(str,"怪物释放闪电链...对玩家照成 %d 点伤害",damage); MsgInsert(str); CheckDie(player.nHp,true); } break; case 2: //致命一击 SelectObject(bufdc,slash); BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND); BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT); //第30个画面时计算玩家受伤害程度并加入显示消息 if(f == 30) { damage = rand()%10 + monster.lv*monster.w*5; player.nHp -= (int)damage; sprintf(str,"怪物致命一击...对玩家照成 %d 点伤害.",damage); MsgInsert(str); CheckDie(player.nHp,true); } break; case 3: //使用梅肯斯姆补血 SelectObject(bufdc,recover); BitBlt(mdc,60,160,150,150,bufdc,150,0,SRCAND); BitBlt(mdc,60,160,150,150,bufdc,0,0,SRCPAINT); //第30个画面时怪物回复生命值并加入显示消息 if(f == 30) { monster.nHp += 30; sprintf(str,"怪物使用梅肯斯姆...恢复了30点生命值",damage); MsgInsert(str); } break; case 4: //在第30个画面时判断怪物是否逃跑成功 if(f == 30) { if(1== rand()%3 ) //逃跑几率1/3 { over = true; monster.nHp = 0; sprintf(str,"怪物逃跑中...逃跑成功"); MsgInsert(str); } else { sprintf(str,"怪物逃跑中...逃跑失败"); MsgInsert(str); } } break; } } if(f == 30) //回合结束 { attack = false; f = 0; } } BitBlt(hdc,0,0,640,510,mdc,0,0,SRCCOPY); tPre = GetTickCount(); pNum++; if(pNum == 8) pNum = 0; } //****新增的对战消息函数******************************** void MsgInsert(char* str) { if(txtNum < 5) { sprintf(text[txtNum],str); txtNum++; } else { for(int i=0;i= 500 && x <= 550 && y >= 350 && y <= 400) attack = true; } break; case WM_DESTROY: //窗口结束消息 DeleteDC(mdc); DeleteDC(bufdc); DeleteObject(bg); DeleteObject(sheep); DeleteObject(girl); DeleteObject(skill); DeleteObject(skillult); DeleteObject(slash); DeleteObject(magic); DeleteObject(recover); DeleteObject(game); ReleaseDC(hWnd,hdc); PostQuitMessage(0); break; default: //默认消息 return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }


每一回合开始的时候,我们点击画面上“无敌斩”的技能图标,就可以进行攻击,对怪物造成伤害,人品好的话,还可以触发强力被动技能“恩赐解脱”,对怪物造成4倍暴击伤害,这里我们设定的暴击概率为20%



浅墨在截图的时候,人品挺好的,恩赐解脱的暴击概率为20%,但是浅墨的4次攻击里,有3次都打出了“恩赐解脱”的暴击效果,直接果断地把这只小绵羊带走了,呵呵。


下面就是游戏运行的截图:


游戏开始



第一刀就出暴击了,48点伤害



运气不错,又一刀暴击,68点伤害



最后一刀又出了暴击,小绵羊被“秒杀”,游戏结束




我们还可以调节怪物等级,怪物攻击加权值,怪物血量上限以及玩家角色等级,玩家角色攻击加权值,玩家角色血量上限来让游戏更具挑战性。


当然,我们也可以增加更多的代码,来使怪物的思考与行动方式更具真实性和多样性,来使玩家的技能更加丰富。



这个回合制游戏demo可以说是目前市场上回合制游戏的本体,《仙剑奇侠传》(三代以前的,三代及以后的仙剑都是进度条模式了),《梦幻西游》《问道》等经典的回合制游戏,无非就是在这种风格的demo基础上,写更多的代码,丰富内容而已,或为游戏引擎的核心代码。


最后浅墨再提一点,以结束这篇笔记,其实就是为了给大家提供一些实现思路:


可以在这个demo的基础上,增加剧情,世界观,游戏地图,等级系统,经验值系统,宠物系统,道具系统,符文系统,五行相克系

统,天气系统等,让这个回合制游戏更加有趣更加吸引人。

你可能感兴趣的:(游戏开发,游戏,null,image,hp,数据结构,callback)