Win32游戏制作之---SwordsMan

来源:互联网 发布:算法的发展成果 编辑:程序博客网 时间:2024/06/07 01:44

前言  

       今天是NBA总决赛的最后一场,还是抢七大战,伟大的James终于圆梦了,比赛之中突然发现詹姆斯胡子都有点白了,原来原先那些陪伴自己众多篮球明星都已经老了,Kobe也退役了,他们每个人都有梦想并且为之奋斗,詹姆斯为了自己的梦想奋斗了这么多年才圆梦,那么自己也应该努力实现自己心中的梦想,当然只是想实现自己心中的一个小小的梦想而已,不是每个人都可以说出为自己家乡带来总冠军那样的豪言壮语。

               

                                                          ---这句话送给伟大的詹姆斯,也送给我自己,更送给所有在梦想道路上前进的人

       前段时间由于忙着准备学校的考试,没有时间好好做自己想做的事情,再加上今天早上还有NBA总决赛,所以好久没有写博客,考试考完之后,我就开始了自己做了一个小游戏---SwordsMan,当然这只是我给取得一个名字,并不是那个SwordsMan,和真正的SwordsMan游戏还是有很大的差距的。

       游戏之中的场景图是我用Photoshop拼接而成,自己也感觉做的不好,大家看看就好。游戏中的小鸟以及Boss小龙也是用Photoshop制作的,下面来看一下实际的效果图:

游戏初始状态:


 游戏技能释放:

每次用技能击中小鸟,会获得积分,当积分达到100的时候,Boss就会出现,如图:


当然Boss会释放技能,来看一下:


       Boss的技能是一组动态图,范围是以它为中心的150个像素之内,当然,Boss会先判断玩家的位置,然后在释放技能,不过这里只是初步判断一下,人工智能是一个很大的话题。现在总算明白为什么为什么AI难以设计了,以前还总是嫌弃AI不行,比如我在玩三国志系列游戏的时候总是觉得AI不够智能,现在总算明白AI的难度之大。大家在玩dota或者LoL的时候,可能会感觉电脑弱(不过话说我从来没有感觉到dota之中的AI弱,我表示经常打不过电脑o(╯□╰)o)。

       其实我感觉最难的还是碰撞检测,我在代码之中也是做了一些比较简单地检测,其实这一块真的挺难的,首先你得想到各种情况,哪怕是边边角角碰到了,可能也是一种有效的碰撞。这样的碰撞检测在FPS类型的游戏之中显得格外的重要,比如子弹是否击中人,以及击中人的什么部位。这些方面,国外的做的很好,尤其是一些3A级大作。在他们的游戏引擎之中就有专门的检测机制。

          下面来看一下代码,为了节省篇幅,我给出重要的代码,生成窗口的代码就省略了。

//头文件#pragma once#include "GameEngine.h"#include <Windows.h>#include <tchar.h>                    //使用swprintf_s函数所需的头文件#include "resource.h"#define KEY_DOWN(vk_code)              ((GetAsyncKeyState(vk_code)&0x8000)?1:0)#define KEY_UP(vk_code)                ((GetAsyncKeyState(vk_code)&0x8000)?0:1)struct SwordBullets                     //代表主人公的剑气 {    int x, y;                       //人物剑气的坐标    bool isExist;                       //剑气是否存在};struct BirdMaster{    int x, y;                       //代表经过的小鸟的坐标    bool isExist;                       //表示小鸟是否存在    int picNum;                       //图的标号(一共5个)    HBITMAP hbird;                       //小鸟的位图句柄};  struct BossMaster{    int x, y;                      //代表boss的贴图坐标    int picNum;                      //boss图的标号(一共6个)    int ipicNum;                      //boss技能图的标号    HBITMAP hBoss;                      //boss的位图句柄    int boss_blood;                      //boss的血量    HBITMAP hBossSkill;                      //boss技能句柄    int Skill_x, Skill_y;                      //boss的技能坐标    int boss_x, boss_y;                      //boss的中心坐标};int g_iX = 0, g_iY = 0;            //鼠标所在位置<pre name="code" class="cpp"> //全局设备句柄、全局内存DC句柄、以及缓冲DCHDC g_hdc = NULL, g_mdc = NULL, g_bufdc = NULL; //定义五个位图句柄,分别用来存储背景、人物、剑气、失败、胜利                                                               HBITMAP g_hBackGround = NULL, g_hSwordMan = NULL, g_hSwordBlade = NULL, g_hGameOver = NULL, g_hvictory = NULL; int g_iXSwordMan = 0, g_iYSwordMan = 0; //人物所在位置记录当前人物的坐标,也就是贴图坐标int g_iBGOffset = 0, g_iBulletNum = 0, g_ibirdNum = 0; //g_iBGOffset为滚动背景所要裁剪的区域宽度,g_iBulletNum记录剑气的数量SwordBullets Bullet[30];                            BirdMaster Bird[10];                                BossMaster Boss;                                     int g_Score = 0;                                    //玩家得分int g_life = 0;                                     //玩家血量bool g_Isboss = false;                              //boss是否存在//时间计数DWORD g_tPre = 0, g_tNow = 0;                       GameEngine * g_pGame = NULL;//游戏中检测函数,(传参看似没有必要,主要是为了便于日后代码的维护)bool Is_in_Randge(BirdMaster &bir);bool Is_hit_bird(BirdMaster &bir, SwordBullets &bullet);void bird_hit_man(BirdMaster &bir);void Boss_appear();void Boss_Initalize();void Is_hit_boss(BossMaster &bos, SwordBullets &bullet);bool Boss_hit_man(BossMaster &bos);#pragma

#include "Swordsman.h"BOOL GameInitalize(HINSTANCE hInstance)         //游戏初始化函数{PlaySound(L"仙剑三·原版主题曲", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);try{g_pGame = new GameEngine(hInstance, L"SwordsMan", L"SwordMan", IDI_SWORDMAN, IDI_SWORDMAN_SM);}catch (...){return FALSE;}g_pGame->SetFrameDelay(20);g_life = 100;g_Score = 0;Boss_Initalize();                                //初始化boss的一些信息,满足一定条件出现return TRUE;}void GameStart(HWND hWindow)                        //游戏开始函数{HBITMAP bmp;g_hdc = GetDC(hWindow);g_mdc = CreateCompatibleDC(g_hdc);g_bufdc = CreateCompatibleDC(g_hdc);bmp = CreateCompatibleBitmap(g_hdc, WINDOW_WIDTH, WINDOW_HEIGHT);g_iX = 300;g_iY = 100;g_iXSwordMan = 300;g_iYSwordMan = 100;SelectObject(g_mdc, bmp);                     //选择空位图//加载各种位图g_hSwordMan = (HBITMAP)LoadImage(NULL, L"GameMedia\\swordman.bmp", IMAGE_BITMAP, 317, 283, LR_LOADFROMFILE);g_hSwordBlade = (HBITMAP)LoadImage(NULL, L"GameMedia\\swordblade.bmp", IMAGE_BITMAP, 100, 26, LR_LOADFROMFILE);g_hBackGround = (HBITMAP)LoadImage(NULL, L"GameMedia\\bg.bmp", IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT, LR_LOADFROMFILE);g_hGameOver = (HBITMAP)LoadImage(NULL, L"GameMedia\\gameover.bmp", IMAGE_BITMAP, 1086, 396, LR_LOADFROMFILE);g_hvictory = (HBITMAP)LoadImage(NULL, L"GameMedia\\victory.bmp", IMAGE_BITMAP, 800, 600, LR_LOADFROMFILE);POINT pt, lt, rb;RECT rect;//设置光标的位置pt.x = 300;pt.y = 100;ClientToScreen(hWindow, &pt);SetCursorPos(pt.x, pt.y);ShowCursor(false);                             //隐藏鼠标GetClientRect(hWindow, &rect);lt.x = rect.left;lt.y = rect.top;rb.x = rect.right;rb.y = rect.bottom;ClientToScreen(hWindow, <);ClientToScreen(hWindow, &rb);rect.left = lt.x;rect.top = lt.y;rect.right = rb.x;rect.bottom = rb.y;//限制鼠标的移动区域ClipCursor(&rect);GamePaint(hWindow);}void GamePlay(){GamePaint(g_pGame->GetWindow());if (g_ibirdNum <= 10){g_tPre = GetTickCount();if (g_tPre - g_tNow > 1500){for (int j = 0; j < 10; j++){if (!Bird[j].isExist){Bird[j].x = 0;Bird[j].y = rand() % WINDOW_HEIGHT;Bird[j].isExist = true;Bird[j].picNum = 0;Bird[j].hbird = (HBITMAP)LoadImage(NULL, L"GameMedia\\bird.bmp", IMAGE_BITMAP, 1250, 360, LR_LOADFROMFILE);g_ibirdNum++;g_tNow = g_tPre;break;}}}}}void GamePaint(HWND hWindow)                                     //游戏绘制函数{//先在内存DC之中贴上背景图SelectObject(g_bufdc, g_hBackGround);BitBlt(g_mdc, 0, 0, g_iBGOffset, WINDOW_HEIGHT, g_bufdc, WINDOW_WIDTH - g_iBGOffset, 0, SRCCOPY);BitBlt(g_mdc, g_iBGOffset, 0, WINDOW_WIDTH - g_iBGOffset, WINDOW_HEIGHT, g_bufdc, 0, 0, SRCCOPY);TCHAR str[20] = {};//计算剑侠的贴图坐标,设定每次进行贴图时,剑侠都会慢慢以10个像素的单位向目标接近if (g_iX > g_iXSwordMan)                         //若当前鼠标X坐标大于人物的坐标,则前进(往左){g_iXSwordMan += 10;if (g_iXSwordMan > g_iX){g_iXSwordMan = g_iX;}}else                                             //若当前的鼠标X坐标小于人物的坐标,则后退(往右){g_iXSwordMan -= 10;if (g_iXSwordMan < g_iX){g_iXSwordMan = g_iX;}}if (g_iY > g_iYSwordMan){g_iYSwordMan += 10;if (g_iYSwordMan > g_iY){g_iYSwordMan = g_iY;}}else{g_iYSwordMan -= 10;if (g_iYSwordMan < 0){g_iYSwordMan = 0;}}//贴上剑侠的图SelectObject(g_bufdc, g_hSwordMan);TransparentBlt(g_mdc, g_iXSwordMan, g_iYSwordMan, 317, 283, g_bufdc, 0, 0, 317, 283, RGB(0, 0, 0));//剑气(子弹)的贴图,先判断剑气(子弹)数目“g_iBulletNum”的值是否为“0”。若不为0,则对剑气(子弹)数组中各个还存在的剑气(子弹)按照其所在的坐标(b[i].x,b[i].y)循环进行贴图操作SelectObject(g_bufdc, g_hSwordBlade);if (g_iBulletNum != 0){for (int i = 0; i < 30; i++){if (Bullet[i].isExist){//检测有无击中bossIs_hit_boss(Boss, Bullet[i]);if (Boss.boss_blood <= 0)                         //若boss死亡,就贴出胜利的图片{g_Isboss = false;SelectObject(g_bufdc, g_hvictory);TransparentBlt(g_mdc, 250, 50, 800, 600, g_bufdc, 0, 0, 800, 600, RGB(0, 0, 0));g_pGame->SetGameState(TRUE);}//依次判断有无击中小鸟for (int j = 0; j < 10; j++){if (Is_hit_bird(Bird[j], Bullet[i])){Bullet[i].isExist = false;g_iBulletNum--;Bird[j].isExist = false;g_ibirdNum--;g_Score += 10;if (g_Score >= 100){g_Isboss = true;}}}if (Bullet[i].isExist){TransparentBlt(g_mdc, Bullet[i].x - 70, Bullet[i].y + 100, 100, 33, g_bufdc, 0, 0, 100, 26, RGB(0, 0, 0));}//设置下一个剑气(子弹)的坐标。剑气(子弹)是从右向左发射的,因此,每次其X轴上的坐标值递减10个单位,这样贴图会产生往左移动的效果。而如果剑气(子弹)下次的坐标已超出窗口的可见范围(h[i].x<0),那么剑气(子弹)设为不存在,并将剑气(子弹)总数g_iBulletNum变量值减1.Bullet[i].x -= 10;if (Bullet[i].x < 0){g_iBulletNum--;Bullet[i].isExist = false;}}}}//判断小鸟的数量“g_ibirdNum”的值是否为“0”。若不为0,则对剑气(子弹)数组中各个还存在的剑气(子弹)按照其所在的坐标,循环进行贴图操作if (g_ibirdNum != 0){for (int i = 0; i < 10; i++){if (Bird[i].isExist){if (Bird[i].picNum >= 5){Bird[i].picNum = 0;}SelectObject(g_bufdc, Bird[i].hbird);BitBlt(g_mdc, Bird[i].x, Bird[i].y, 250, 180, g_bufdc, Bird[i].picNum * 250, 180, SRCAND);BitBlt(g_mdc, Bird[i].x, Bird[i].y, 250, 180, g_bufdc, Bird[i].picNum * 250, 0, SRCPAINT);if (Is_in_Randge(Bird[i])){if (Bird[i].y < g_iYSwordMan){Bird[i].y += 10;}else{Bird[i].y -= 10;}Bird[i].x += 10;}else{Bird[i].x += 10;                                   //设定小鸟的前进路线}if (Bird[i].x > WINDOW_WIDTH){Bird[i].isExist = false;g_ibirdNum--;}Bird[i].picNum++;bird_hit_man(Bird[i]);}}}//判断boss是否存在if (g_Isboss){Boss_appear();}//创建字体HFONT hfont;hfont = CreateFont(35, 20, 0, 0, 300, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("微软雅黑"));//把字体选入g_mdcSelectObject(g_mdc, hfont);//设置背景颜色透明SetBkMode(g_mdc, TRANSPARENT);//设置文字颜色SetTextColor(g_mdc, RGB(255, 0, 0));//在右上角进行文字输出swprintf_s(str, L"玩家血量(life): %d%%", g_life);TextOut(g_mdc, 850, 0, str, wcslen(str));swprintf_s(str, L"玩家得分(score): %d", g_Score);TextOut(g_mdc, 50, 0, str, wcslen(str));//贴上图BitBlt(g_hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, g_mdc, 0, 0, SRCCOPY);//背景的移动g_iBGOffset += 5;if (g_iBGOffset >= WINDOW_WIDTH)                    //如果大于窗口宽度,就变为0{g_iBGOffset = 0;}}void GameActivate(HWND hWindow)                   //游戏被激活{HDC hdc;RECT rect;GetClientRect(hWindow, &rect);hdc = GetDC(hWindow);DrawText(hdc, L"Welcome to back", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);ReleaseDC(hWindow, hdc);}void GameDeactivate(HWND hWindow)                   //游戏未被激活{HDC hdc;RECT rect;GetClientRect(hWindow, &rect);hdc = GetDC(hWindow);DrawText(hdc, L"Pause", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);ReleaseDC(hWindow, hdc);}void GameEnd()                                       //游戏结束函数{//资源的清理DeleteObject(g_hBackGround);DeleteDC(g_bufdc);DeleteDC(g_mdc);ReleaseDC(g_pGame->GetWindow(), g_hdc);}void HandleKeys()                                         //按键处理函数{if (KEY_DOWN(VK_ESCAPE))                              //按下Esc退出{DestroyWindow(g_pGame->GetWindow());return;}if (KEY_DOWN(VK_UP))                                 //按下up(向上的箭头)玩家向上移动{g_iYSwordMan -= 50;if (g_iYSwordMan < 0){g_iYSwordMan = 0;}}if (KEY_DOWN(VK_SHIFT))                              //按下shift进行加速{g_iBGOffset += 15;}if (KEY_DOWN(VK_LBUTTON))                            //按下鼠标左键{for (int i = 0; i < 30; i++){if (!Bullet[i].isExist){Bullet[i].x = g_iXSwordMan;Bullet[i].y = g_iYSwordMan + 30;Bullet[i].isExist = true;g_iBulletNum++;break;}}}if (KEY_DOWN(VK_DOWN))                            //按下up(向上的箭头)玩家向上移动{g_iYSwordMan += 50;if (g_iYSwordMan > WINDOW_HEIGHT){g_iYSwordMan = WINDOW_HEIGHT;}}}bool Is_in_Randge(BirdMaster &bir)                             //判断玩家是否在小鸟的视野半径内,若是,则飞向玩家{if (bir.isExist){if (bir.x < g_iXSwordMan){if (((bir.x - g_iXSwordMan)*(bir.x - g_iXSwordMan) + (bir.y - g_iYSwordMan)*(bir.y - g_iYSwordMan)) <= 90000)   //检测是否在一个圆内{return true;}}return false;}else{return false;}}bool Is_hit_bird(BirdMaster &bir, SwordBullets &bullet)               //判断技能有没有击中小鸟{if (bir.isExist){if ((bullet.y + 13 > bir.y + 25) && (bullet.y + 13 < bir.y + 45)){if ((bullet.x < bir.x + 220) && (bullet.x > bir.x + 125)){return true;}}}return false;}void bird_hit_man(BirdMaster &bir)                                 //小鸟与玩家的碰撞{if (bir.isExist){if (((bir.y + 45) <= g_iYSwordMan + 250) && ((bir.y + 45) >= g_iYSwordMan + 80)){if (((bir.x + 220) > (g_iXSwordMan + 35)) && ((bir.x + 220) < (g_iXSwordMan + 125))){bir.isExist = false;g_life -= 10;if (g_life == 0){SelectObject(g_bufdc, g_hGameOver);                    //玩家死亡,贴出失败的图片BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 543, 0, SRCAND);BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 0, 0, SRCPAINT);g_pGame->SetGameState(TRUE);}}}}}void Boss_appear()                                             //Boss出现{ if (Boss.picNum >= 6){Boss.picNum = 0;}SelectObject(g_bufdc, Boss.hBoss);                       //贴上Boss的图片BitBlt(g_mdc, Boss.x, Boss.y, 220, 200, g_bufdc, Boss.picNum * 220, 200, SRCAND);BitBlt(g_mdc, Boss.x, Boss.y, 220, 200, g_bufdc, Boss.picNum * 220, 0, SRCPAINT);Boss.picNum++;if (((Boss.boss_x - g_iXSwordMan)*(Boss.boss_x - g_iXSwordMan) + (Boss.boss_y - g_iYSwordMan)*(Boss.boss_y - g_iYSwordMan))<360000){if (Boss.ipicNum >= 6){Boss.ipicNum = 0;}if (Boss.Skill_x - Boss.x >= 380){Boss.Skill_x = Boss.x + 200;}if (Boss.Skill_y - Boss.y >= 280){Boss.Skill_y = Boss.y + 100;}SelectObject(g_bufdc, Boss.hBossSkill);                     //贴上Boss的技能图BitBlt(g_mdc, Boss.Skill_x, Boss.Skill_y, 150, 150, g_bufdc, Boss.ipicNum * 150, 150, SRCAND);BitBlt(g_mdc, Boss.Skill_x, Boss.Skill_y, 150, 150, g_bufdc, Boss.ipicNum * 150, 0, SRCPAINT);Boss.ipicNum++;if (Boss_hit_man(Boss)){g_life -= 20;if (g_life <= 0)                                       //判断玩家是否还有血量,若没有,则贴出失败的图片{g_life = 0;SelectObject(g_bufdc, g_hGameOver);                 BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 543, 0, SRCAND);BitBlt(g_mdc, 400, 100, 543, 396, g_bufdc, 0, 0, SRCPAINT);g_pGame->SetGameState(TRUE);}}}else{if (Boss.x >= g_iXSwordMan - 400){Boss.x -= rand() % 40;}else{if (Boss.y > g_iYSwordMan){Boss.y -= 15;if (Boss.x < g_iXSwordMan){Boss.x += 10;}else{Boss.x -= 20;}}else if (Boss.y < g_iYSwordMan){Boss.y += 15;if (Boss.x < g_iXSwordMan){Boss.x += 10;}else{Boss.x -= 20;}}else{if (Boss.x < g_iXSwordMan){Boss.x += 10;}else{Boss.x -= 20;}}}}}void Boss_Initalize()                                      //Boss的初始化{g_Isboss = false;Boss.boss_blood = 100;Boss.hBoss = (HBITMAP)LoadImage(NULL, L"GameMedia\\boss.bmp", IMAGE_BITMAP, 1320, 400, LR_LOADFROMFILE);    //加载boss位图Boss.hBossSkill = (HBITMAP)LoadImage(NULL, L"GameMedia\\bossSkill.bmp", IMAGE_BITMAP, 900, 300, LR_LOADFROMFILE);  //加载boss技能图Boss.picNum = 0;Boss.ipicNum = 0;Boss.x = 0;Boss.y = rand() % WINDOW_HEIGHT;Boss.Skill_x = Boss.x + 200;                     //技能设置的初始位置Boss.Skill_y = Boss.y + 100;Boss.boss_x = Boss.x + 150;                      //Boss的中心位置Boss.boss_y = Boss.y + 100;}bool Boss_hit_man(BossMaster &bos)                   //判断Boss是否碰撞到玩家,或者Boss技能是否击中玩家{bos.Skill_x += 30;if (bos.Skill_y + 75 < g_iYSwordMan){bos.y += 5;bos.Skill_y += 20;}else if (bos.Skill_y + 75 > g_iYSwordMan){bos.y -= 5;bos.Skill_y -= 20;}else{bos.y = bos.y;bos.Skill_y = bos.Skill_y;}//判断boss是否碰到玩家if ((bos.boss_y <= g_iYSwordMan + 250) && (bos.boss_y >= g_iYSwordMan + 80)){if ((bos.boss_x > (g_iXSwordMan + 35)) && (bos.boss_x < (g_iXSwordMan + 125))){g_life -= 15;}}//判断boss技能是否击中玩家if ((bos.Skill_y + 75 <= g_iYSwordMan + 250) && ((bos.Skill_y + 75) >= g_iYSwordMan + 80)){if (((bos.Skill_x + 75) >(g_iXSwordMan + 35)) && ((bos.Skill_x + 75) < (g_iXSwordMan + 125))){return true;}}return false;}void Is_hit_boss(BossMaster &bos, SwordBullets &bullet)           //玩家是否击中Boss{if ((bullet.y + 13 > (bos.boss_y - 30)) && (bullet.y + 13 < bos.boss_y + 15)){if ((bullet.x < bos.boss_x + 50) && (bullet.x > bos.boss_x - 30)){bos.boss_blood -= 15;g_Score += 20;bullet.isExist = false;                               //子弹击中之后消失g_iBulletNum--;}}}

           在代码之中基本都有注释,这里就不分析了,Boss有500点血量,还真不是那么简单地:

            不过要想简单也很容易,把Boss血量降到1就可以了,O(∩_∩)O,来一张胜利作为结束:


         当然这只是一个很简单的游戏,大家可以看看,也欢迎大家给出一些建议!(PS:窗口生成那一部分WIN32的代码是一个固定的格式,为了节省篇幅所以没有贴出)


1 0
原创粉丝点击