【Visual C++】游戏编程学习笔记之八:回合制游戏demo(剑侠客VS

作者:ZeeCoder 微博链接:

我的邮箱:michealfloyd@126.com 欢迎大家发邮件来和我交流编程心得

you are what you read!与大家共勉!

————————————————-分割线:ZeeCoder——————————————–

本笔记借鉴了经典回合制游戏大作—梦幻西游的战斗场景和音效。

回合制游戏主要利用行为性AI,通常都依靠连续的if….else…语句来判断和运算,或者利用一些数据结构的概念

一、计算机角色的思考和行为

在本游戏demo中,巡游天神(计算机)的行为主要有以下:

1.普通攻击

2.泰山压顶

3.地狱烈火

4.回血

5.逃跑

那么在游戏编程设计中,我们可以定义一个int型kind(表示攻击类型),然后通过switch…case语句来做出相应判断

if (s_monster.nHp > 200)//当怪物血量小于200时{if (rand()%3 != 1){//普通攻击,2/3几率}else{//进行泰山压顶攻击,1/3几率}}else{switch(rand()%5){case 0://使用普通攻击break;case 1://使用泰山魔法攻击break;case 2://使用地狱烈火break;case 3://回血break;case 4://逃跑break;}}

二、玩家角色攻击和行为

本游戏中玩家为剑侠客,他主要有两个技能加一个暴击:

1、剑击

2、雷电诀

3、暴击(十分之一概率)

判断剑侠客释放哪个技能主要通过鼠标左键单击。

case WM_LBUTTONDOWN:if (!attack_1 && !attack_2) //当没有按下攻击按钮{x = LOWORD(lParam);//获取游戏鼠标坐标y = HIWORD(lParam);if ( x >= 80 && y >= 150 && x <= 113 && y <= 183 )//判断是否按下了剑击按钮{attack_1 = true ;attack_2 = false ;}if ( x >= 80 && y >= 200 && x <= 113 && y <= 233 )//判断是否按下了雷神诀按钮{attack_1 = false ;attack_2 = true ;}}玩家按下技能按钮后,需要判断行为。if (attack_1 && !attack_2) //释放剑击技能{//添加行为}else if (!attack_1 && attack_2)//释放雷击技能{//添加行为}if ( frame ==10)//第十个画面统计怪物收到的伤害值并显示{if ( 4 == rand()%10)//百分之十几率释放暴击技能{//添加行为}else{if (!attack_1 && attack_2) //释放雷击技能{//添加行为}else if (attack_1 && !attack_2)//释放剑击技能{//添加行为} }}

三、下面来看整个demo的完整代码。

1、为了偷懒使用了TransparentBlt进行透明贴图,效果感觉和bitblt差不多。可能是我P图没P好的原因吧。

2、本demo利用msi类,,添加了战斗音效。效果还是很酷炫的,代码也不叫简单。

#include "stdafx.h"#include "GameDemo.h"#define MAX_LOADSTRING 100#pragma comment (lib , "msimg32.lib")#include <Mmsystem.h>#pragma comment(lib, "Winmm.lib")//定义结构struct state{int nHp;//目前生命值int mHp;//最大生命值int lv;//怪物等级int Weight;//加权值int kind;//怪物的行为代号};//全局变量HINSTANCE hInst;HBITMAP bg , h_monster , h_player , skill1 , skill2 , sword , thunder, stick ,hill ,blaze ,recover,gameover,baoji;HDC hdc , mdc , bufdc;HWND hWnd;DWORD tNow , tPre;state s_player , s_monster;bool attack_1,attack_2 , over ;TCHAR text[5][100];int pNum , mNum ,frame , textNum;//函数声明ATOMMyRegisterClass(HINSTANCE hInstance);BOOLInitInstance(HINSTANCE, int);LRESULT CALLBACKWndProc(HWND, UINT, WPARAM, LPARAM);INT_PTR CALLBACKAbout(HWND, UINT, WPARAM, LPARAM);voidGameRun(HDC hdc);voidMsgInsert(TCHAR* str);voidCheckDie(int hp , bool player);//***************************WinMain主函数**********************************int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,intnCmdShow){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))//PM_REMOVE消息从队列里除掉{TranslateMessage(&msg);DispatchMessage(&msg);}else{tNow = GetTickCount();//获取当前时间if (tNow – tPre >= 100)//实现游戏循环{GameRun(hdc);//循环贴图}}}return (int) msg.wParam;}//***************************窗口类函数**********************************ATOM MyRegisterClass(HINSTANCE hInstance){WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;wcex.lpfnWndProc= WndProc;wcex.cbClsExtra= 0;wcex.cbWndExtra= 0;wcex.hInstance= hInstance;wcex.hIcon= NULL;wcex.hCursor= NULL;wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName= MAKEINTRESOURCE(IDC_GAMEDEMO);wcex.lpszClassName= _T("Game");wcex.hIconSm= NULL;return RegisterClassEx(&wcex);}//***************************初始化函数**********************************//实现功能:加载位图并给各变量设定初始值BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){ HBITMAP bmp; hInst = hInstance; // Store instance handle in our global variablehWnd = CreateWindow(_T("Game"), _T("致经典–梦幻西游"), 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);bmp = CreateCompatibleBitmap(hdc , 640 , 480); SelectObject(mdc , bmp);//载入位图 bg=(HBITMAP)LoadImage(NULL , _T("Res\\bg.bmp") , IMAGE_BITMAP , 640 ,480 , LR_LOADFROMFILE);//背景图 h_monster=(HBITMAP)LoadImage(NULL , _T("Res\\monster.bmp") , IMAGE_BITMAP , 640 , 144 , LR_LOADFROMFILE);//怪物图 h_player=(HBITMAP)LoadImage(NULL , _T("Res\\player.bmp") , IMAGE_BITMAP , 448 , 91 , LR_LOADFROMFILE);//玩家图 skill1=(HBITMAP)LoadImage(NULL , _T("Res\\skill1.bmp") , IMAGE_BITMAP , 33 ,33 , LR_LOADFROMFILE);//技能1 skill2=(HBITMAP)LoadImage(NULL , _T("Res\\skill2.bmp") , IMAGE_BITMAP , 33 ,33 , LR_LOADFROMFILE);//技能2 baoji= (HBITMAP)LoadImage(NULL , _T("Res\\baoji.bmp") , IMAGE_BITMAP , 160 ,84 , LR_LOADFROMFILE);//暴击 sword=(HBITMAP)LoadImage(NULL , _T("Res\\sword.bmp") , IMAGE_BITMAP , 200 ,102 , LR_LOADFROMFILE);//玩家使用剑击 thunder=(HBITMAP)LoadImage(NULL , _T("Res\\thunder.bmp") , IMAGE_BITMAP , 160 ,210 , LR_LOADFROMFILE);//雷击效果图 stick=(HBITMAP)LoadImage(NULL , _T("Res\\stick.bmp") , IMAGE_BITMAP , 1100 ,720 , LR_LOADFROMFILE);//巡游棍击 blaze=(HBITMAP)LoadImage(NULL , _T("Res\\blaze.bmp") , IMAGE_BITMAP , 88 ,79 , LR_LOADFROMFILE);//火焰效果图 hill=(HBITMAP)LoadImage(NULL , _T("Res\\hill.bmp") , IMAGE_BITMAP , 80 ,64 , LR_LOADFROMFILE);//泰山压顶 //recover=(HBITMAP)LoadImage(NULL , _T("Res\\recover.bmp") , IMAGE_BITMAP , 1100 ,720 , LR_LOADFROMFILE);//回血效果图 gameover=(HBITMAP)LoadImage(NULL , _T("Res\\gameover.bmp") , IMAGE_BITMAP , 400 ,100 , LR_LOADFROMFILE);//游戏结束s_player.nHp = s_player.mHp = 130; s_player.lv = 3 ; s_player.Weight = 5 ;s_monster.nHp = s_monster.mHp = 400; s_monster.lv = 1; s_monster.Weight = 1;textNum = 0 ;SetBkMode(mdc , TRANSPARENT); //设置TextOut背景透明GameRun(hdc);return TRUE;}//***************************消息响应函数**********************************// 判断玩家是否使用攻击命令LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){int x , y ;switch (message){case WM_KEYDOWN:if (wParam == VK_ESCAPE)//【Esc】退出游戏PostQuitMessage(0);break;case WM_LBUTTONDOWN:if (!attack_1 && !attack_2) //当没有按下攻击按钮{x = LOWORD(lParam);y = HIWORD(lParam);if ( x >= 80 && y >= 150 && x <= 113 && y <= 183 )//判断是否按下了剑击按钮{attack_1 = true ;attack_2 = false ;mciSendString(L"open Audio\\sword.mp3 alias sword", NULL, 0, NULL);mciSendString(L"play sword ", NULL, 0, NULL);}if ( x >= 80 && y >= 200 && x <= 113 && y <= 233 )//判断是否按下了雷击按钮{attack_1 = false ;attack_2 = true ;mciSendString(L"open Audio\\thunder.mp3 alias thunder", NULL, 0, NULL);mciSendString(L"play thunder ", NULL, 0, NULL);}}break;case WM_DESTROY:DeleteDC(mdc);DeleteDC(bufdc);PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}//********************************自定义游戏回合开始函数***********************//实现功能://1.贴战斗实时战况消息//2.怪物及玩家行为判断及血量等各项数据的计算void GameRun(HDC hdc){TCHAR str[100] ;memset(str , 0 ,sizeof(TCHAR)*100);int i , damage;//播放背景音乐mciSendString(L"open Audio\\bgm.mp3 alias bgm", NULL, 0, NULL);mciSendString(L"play bgm repeat", NULL, 0, NULL);//贴上背景图SelectObject(bufdc , bg);BitBlt(mdc , 0 , 0 , 640 , 480 , bufdc , 0 , 0 ,SRCCOPY);//显示战况消息for (i = 0 ; i < 6 ; i++){TextOut(mdc , 0 , 300 + i*18, text[i] , _tcslen(text[i]));}//贴上怪物图if (s_monster.nHp > 0)//如果怪物没死{SelectObject(bufdc , h_monster);// BitBlt(mdc,700,250,78,94,bufdc,mNum*78,94,SRCAND); // BitBlt(mdc,700,250,78,94,bufdc,mNum*78,0,SRCPAINT);TransparentBlt(mdc , 400 , 100 ,160 , 144 , bufdc , mNum*160 , 0 ,160 , 144 , RGB(62,91,167));_stprintf_s(str , TEXT("%d / %d") , s_monster.nHp , s_monster.mHp);SetTextColor(mdc , RGB(0,135,0));TextOut(mdc , 430 , 90 , str , _tcslen(str));}//贴上玩家图if (s_player.nHp > 0){SelectObject(bufdc , h_player);// BitBlt(mdc,200,200,53,137,bufdc,pNum*53,137,SRCAND);// BitBlt(mdc,200,200,53,137,bufdc,pNum*53,0,SRCPAINT); TransparentBlt(mdc , 130 , 135 , 56 , 91 , bufdc , pNum*56 , 0 ,56 , 91 , RGB(62,91,167));//透明贴图_stprintf_s(str , TEXT("%d / %d") , s_player.nHp , s_player.mHp);TextOut(mdc , 130 , 120 , str , _tcslen(str));}//贴上游戏结束图画if (over){SelectObject(bufdc , gameover);TransparentBlt(mdc , 120 , 130 , 400 , 100 , bufdc , 0 , 0 ,400 , 100 , RGB(255,255,255));//透明贴图}//贴上技能图else if (!attack_1 && !attack_2)//没有攻击命令{SelectObject(bufdc , skill1);BitBlt(mdc , 80 , 150 , 33 , 33 , bufdc , 0 , 0 , SRCCOPY);SelectObject(bufdc , skill2);BitBlt(mdc , 80 , 200 , 33 , 33 , bufdc , 0 , 0 , SRCCOPY);}else{frame++;//第五到十个画面显示玩家攻击状态if ( frame >=5 && frame <= 10){if (attack_1 && !attack_2) //释放剑击技能{SelectObject(bufdc , sword);mciSendString(L"close sword ", NULL, 0, NULL);//剑击效果图TransparentBlt(mdc , 200 , 130 , 200 , 102 , bufdc , 0 , 0 ,200 , 102 , RGB(255,0,0));//透明贴图}else if (!attack_1 && attack_2)//释放雷击技能{SelectObject(bufdc , thunder);mciSendString(L"close thunder ", NULL, 0, NULL);//雷击效果图TransparentBlt(mdc , 400 , 80 , 160 , 210 , bufdc , 0 , 0 ,160 , 210 , RGB(255,255,255));//透明贴图}if ( frame ==10)//第十个画面统计怪物收到的伤害值并显示{if ( 4 == rand()%10)//百分之十几率释放暴击技能{damage = 4*(rand()%10 + s_player.lv * s_player.Weight);s_monster.nHp -= (int)damage;//贴上暴击效果图_stprintf_s(str , TEXT("剑侠客使出浑身力气,4倍暴击。。。!!巡游天兵血量瞬间下降%d") , damage);}else{if (!attack_1 && attack_2) //释放雷击技能{damage = 1.5*(rand()%10 + s_player.lv * s_player.Weight);s_monster.nHp -= (int)damage;_stprintf_s(str , TEXT("剑侠客释放雷击技能,巡游天兵被从天而降的雷劈掉了%d伤害") , damage);}else if (attack_1 && !attack_2)//释放剑击技能{damage = rand()%10 + s_player.lv * s_player.Weight;s_monster.nHp -= (int)damage;_stprintf_s(str , TEXT("剑侠客一剑刺向巡游天兵,巡游天兵躲闪不及,生命值减少%d") , damage);}}MsgInsert(str);CheckDie(s_monster.nHp , false);}}srand(tPre);//使得每次产生的随机数不一样//第十五个画面判断怪物进行哪项动作if (frame == 15){if (s_monster.nHp > 200){if (rand()%3 != 1){s_monster.kind = 0; //普通攻击,2/3mciSendString(L"open Audio\\stick.mp3 alias stick", NULL, 0, NULL);//播放音效mciSendString(L"play stick ", NULL, 0, NULL);}else{s_monster.kind = 1;//进行泰山压顶攻击,1/3mciSendString(L"open Audio\\hill.mp3 alias hill", NULL, 0, NULL);//播放音效mciSendString(L"play hill ", NULL, 0, NULL);}}else{switch(rand()%5){case 0://使用普通攻击s_monster.kind = 0 ;break;case 1://使用泰山魔法攻击s_monster.kind = 1 ;break;case 2://使用地狱烈火mciSendString(L"open Audio\\blaze.mp3 alias blaze", NULL, 0, NULL);//播放音效mciSendString(L"play blaze ", NULL, 0, NULL);s_monster.kind = 2 ;break;case 3://回血s_monster.kind = 3 ;break;case 4://逃跑s_monster.kind = 4 ;break;}}}if ( frame >= 26 && frame <= 30)//第26~30个画面显示巡游攻击状态动画{switch (s_monster.kind){case 0://使用普通攻击SelectObject(bufdc , stick);mciSendString(L"close stick ", NULL, 0, NULL);//第30个画面时计算玩家受到的伤害程序并显示消息if (frame == 30){damage = rand()%10 + s_monster.lv*s_monster.Weight;s_player.nHp -= (int)damage;_stprintf_s(str , TEXT("巡游一棍打向剑侠客,正中额头,伤害-%d") , damage);MsgInsert(str);CheckDie(s_player.nHp , true);}break;case 1://使用泰山压顶攻击SelectObject(bufdc , hill);mciSendString(L"close hill ", NULL, 0, NULL);TransparentBlt(mdc , 120 , 135 , 80 , 64 , bufdc , 0 , 0 ,80 , 64 , RGB(0,0,0));//透明贴图//第30个画面时计算玩家受到的伤害程序并显示消息if (frame == 30){damage = 1.5*(rand()%10 + s_monster.lv*s_monster.Weight);s_player.nHp -= (int)damage;_stprintf_s(str , TEXT("巡游怒喝一声,泰山压顶!!呀哈哈哈!,剑侠客血量-%d") , damage);MsgInsert(str);CheckDie(s_player.nHp , true);}break;case 2://使用地狱烈火SelectObject(bufdc , blaze);mciSendString(L"close blaze ", NULL, 0, NULL);TransparentBlt(mdc , 120 , 135 , 88 , 79 , bufdc , 0 , 0 ,88 , 79 , RGB(0,0,0));//透明贴图//第30个画面时计算玩家受到的伤害程序并显示消息if (frame == 30){damage = 3*(rand()%10 + s_monster.lv*s_monster.Weight);s_player.nHp -= (int)damage;_stprintf_s(str , TEXT("巡游口里念着咒语,地狱烈火!剑侠客在熊熊烈火中痛不欲生,伤害-%d") , damage);MsgInsert(str);CheckDie(s_player.nHp , true);}break;case 3://回血//SelectObject(bufdc , recover);//第30个画面时计算玩家受到的伤害程序并显示消息if (frame == 30){s_monster.nHp += 50;_stprintf_s(str , TEXT("普渡众生,巡游回复了50点生命值") );MsgInsert(str);}break;case 4://逃跑//第30个画面时计算玩家受到的伤害程序并显示消息if (frame == 30){if (1 == rand()%3){over = true;s_monster.nHp = 0;_stprintf_s(str , TEXT("巡游落荒而逃….逃跑成功") );MsgInsert(str);}else{_stprintf_s(str , TEXT("巡游落荒而逃….逃跑失败") );MsgInsert(str);}}break;}}if (frame == 30)//第30个画面回合结束{attack_1 = attack_2 = false;frame = 0;}}BitBlt(hdc , 0 ,0 ,640 ,480 ,mdc , 0 , 0 ,SRCCOPY);tPre = GetTickCount();//动画循环变量pNum++;mNum++;if(pNum == 8)pNum = 0;if (mNum == 4)mNum = 0;}//对战消息显示函数void MsgInsert(TCHAR* str){if (textNum < 5){_stprintf_s(text[textNum] , str);textNum ++;}else{for (int i = 0 ; i < textNum ; i++){_stprintf_s(text[i],text[i+1]);}_stprintf_s(text[4] , str);}}void CheckDie(int hp , bool player){TCHAR str[100] ;memset(str , 0 ,sizeof(TCHAR)*100);if (hp <= 0){over = true;if (player){_stprintf_s(str,TEXT("胜败乃兵家常事,少侠请重新来过….."));MsgInsert(str);}else{_stprintf_s(str,TEXT("恭喜少侠,你赢了,巡游被你打死了…."));MsgInsert(str);}}}

四、效果图

所有的赏赐都只是被用来奖励工作成果的。

【Visual C++】游戏编程学习笔记之八:回合制游戏demo(剑侠客VS

相关文章:

你感兴趣的文章:

标签云: