网站建设的关键词,网络营销推广哪家比较好,建立网站兴田德润电话多少,广州 网站 建设文章目录 《C 游戏代码解析#xff1a;简易飞机大战游戏的实现》一、游戏整体结构与功能概述二、各个类和函数的功能分析#xff08;一#xff09;BK类 - 背景类#xff08;二#xff09;hero_plane类 - 玩家飞机类#xff08;三#xff09;plane_bullet类 - 玩家飞机发… 文章目录 《C 游戏代码解析简易飞机大战游戏的实现》一、游戏整体结构与功能概述二、各个类和函数的功能分析一BK类 - 背景类二hero_plane类 - 玩家飞机类三plane_bullet类 - 玩家飞机发射的子弹类四enemy_plane类 - 敌方飞机类五enemy_bullet类 - 敌方飞机发射的子弹类六其他重要函数 三、完整代码Plane.hPlane.cppmain.cpp 《C 游戏代码解析简易飞机大战游戏的实现》
在这篇博客中将简单实现并深入解析一个C 飞机大战游戏的代码该小游戏界面由EasyX图形库实现相关内容可以参考EasyX图形库基础使用教程快速上手小游戏运行演示效果见飞机大战效果演示源码和相关资源文件课件已上传giteePLANE · TTKun/Project - 码云
一、游戏整体结构与功能概述
这个飞机大战游戏主要由多个类和函数构成整体功能包括游戏的初始化、玩家操作、敌机与子弹的生成和管理、碰撞检测以及游戏的开始和结束界面展示等。
二、各个类和函数的功能分析
一BK类 - 背景类
构造函数 这个类用于实现背景滚动效果。构造函数接受一个IMAGE类型的引用同时初始化背景图像的y坐标为-WIN_HEIGHT这是为了让背景从屏幕上方开始滚动。 show函数 该函数实现了背景的滚动效果。首先判断y坐标是否为0如果是则重置为-WIN_HEIGHT然后每次将y坐标增加2最后使用putimage函数在新的坐标位置绘制背景图像。
代码
class BK //背景方便实现背景滚动效果
{
public:BK(IMAGE img) :img(img),y(-WIN_HEIGHT){}void show() {if (!y) y -WIN_HEIGHT;y 2;putimage(0, y, img);}
private:IMAGE img;int y;
};二hero_plane类 - 玩家飞机类
构造函数 构造函数接受多个IMAGE类型的指针用于不同状态下的玩家飞机图像同时初始化玩家飞机的初始位置、血量等属性。例如通过计算将飞机初始位置设置在屏幕下方中央。 MouseControl函数 此函数用于实现玩家飞机的鼠标控制。通过获取鼠标消息如果鼠标的y坐标小于等于450则限制为450然后根据鼠标的x坐标重新计算并更新飞机的位置矩形。 show函数 根据玩家飞机的血量值使用不同的飞机图像进行绘制。如果血量大于等于5使用一种图像血量在3 - 4之间使用另一种血量在1 - 2之间使用第三种图像进行绘制。 GetRect函数 简单地返回表示飞机位置和大小的矩形对象的引用这个矩形对象在碰撞检测等功能中会被用到。
代码
class hero_plane //玩家飞机
{
public:hero_plane(IMAGE* hero_planey, IMAGE* hero_planeb, IMAGE* hp_down1y, IMAGE* hp_down1b, IMAGE* hp_down2y, IMAGE* hp_down2b):point(0),hpy(hero_planey), hpb(hero_planeb), hpd1y(hp_down1y), hpd1b(hp_down1b), hpd2y(hp_down2y), hpd2b(hp_down2b), HP(HERO_HP){rect.left WIN_WIDTH / 2 - (*hpy).getwidth() / 2;rect.top WIN_HEIGHT - (*hpy).getheight();rect.right rect.left (*hpy).getwidth();rect.bottom WIN_HEIGHT;}void MouseControl() {ExMessage mouse_mess;if (peekmessage(mouse_mess, EM_MOUSE)){if (mouse_mess.y 450) {mouse_mess.y 450;}rect.left mouse_mess.x - (*hpy).getwidth() / 2;rect.top mouse_mess.y - (*hpy).getheight() / 2;rect.right rect.right rect.left (*hpy).getwidth();rect.bottom rect.top (*hpy).getheight();}}void show() {//putimage(185, 550, imgy, SRCAND);//putimage(185, 550, imgb, SRCPAINT);if (HP 5) {putimage(rect.left, rect.top, hpy, SRCAND);putimage(rect.left, rect.top, hpb, SRCPAINT);}else if (HP 3 HP 4) {putimage(rect.left, rect.top, hpd1y, SRCAND);putimage(rect.left, rect.top, hpd1b, SRCPAINT);}else if (HP 1 HP 2) {putimage(rect.left, rect.top, hpd2y, SRCAND);putimage(rect.left, rect.top, hpd2b, SRCPAINT);}}RECT GetRect() { return rect; }long long point;int HP;
private:IMAGE* hpy;IMAGE* hpb;IMAGE* hpd1y;IMAGE* hpd1b;IMAGE* hpd2y;IMAGE* hpd2b;RECT rect;//int HP;
};三plane_bullet类 - 玩家飞机发射的子弹类
构造函数 接受一个IMAGE类型的指针和一个hero_plane类型的指针。根据玩家飞机的位置初始化子弹的位置矩形确保子弹从飞机中间位置发射。 show函数 实现子弹的移动显示。如果子弹的顶部超出屏幕顶部rect.top 0则返回false表示子弹已出界。每次将子弹的top和bottom坐标减少3实现向上移动移动距离move增加3然后使用putimage函数在新位置绘制子弹。 getleft函数 返回子弹位置矩形的左边坐标这可能在某些碰撞检测场景中有用。 GetRect函数 返回子弹位置矩形的引用用于碰撞检测等操作。
代码
class plane_bullet //玩家飞机发射的子弹
{
public:plane_bullet(IMAGE* img, hero_plane* hp): imgPtr(img), move(0){rect.left hp-GetRect().left (hp-GetRect().right - hp-GetRect().left - imgPtr-getwidth()) / 2;rect.right rect.left imgPtr-getwidth();rect.top hp-GetRect().top;rect.bottom rect.top imgPtr-getheight();}bool show() {if (rect.top 0) return false;rect.top - 3;rect.bottom - 3;move 3;putimage(rect.left, rect.top, imgPtr);return true;}LONG getleft() {return rect.left;}RECT GetRect() { return rect; }int move;
private:IMAGE* imgPtr;RECT rect;
};四enemy_plane类 - 敌方飞机类
构造函数 接受多个IMAGE类型的指针用于不同状态下的敌机图像以及一个x坐标值。初始化敌机的位置、血量等属性将敌机的初始位置设置在屏幕上方的指定x坐标处。 show函数 根据敌机的血量值使用不同的敌机图像进行绘制。同时每次将敌机的top和bottom坐标增加1实现向下移动如果敌机的top坐标超出屏幕底部则返回false。 CreateBullet函数代码中未实现具体函数体 从函数名推测应该是用于创建敌机发射的子弹。 GetRect函数 返回敌机位置矩形的引用用于碰撞检测等操作。
代码
class enemy_plane //敌方飞机
{
public:enemy_plane(IMAGE* enemy1y, IMAGE* enemy1b, IMAGE* enemy1_down1y, IMAGE* enemy1_down1b, IMAGE* enemy1_down2y, IMAGE* enemy1_down2b, int x);bool show();void CreateBullet(IMAGE* enemy_bullet);RECT GetRect();int enemy1_hp;int width() {return imgenemy1b-getwidth();}vectorenemy_bullet enemy_bul_vec;
private:IMAGE* imgenemy1y;IMAGE* imgenemy1b;IMAGE* imgenemy1_down1y;IMAGE* imgenemy1_down1b;IMAGE* imgenemy1_down2y;IMAGE* imgenemy1_down2b;IMAGE* imgenemy_bullet;RECT rect;
};
// 实现 enemy_plane 的成员函数,在类外声明enemy_plane::enemy_plane(IMAGE* enemy1y, IMAGE* enemy1b, IMAGE* enemy1_down1y, IMAGE* enemy1_down1b, IMAGE* enemy1_down2y, IMAGE* enemy1_down2b, int x)//初始化: imgenemy1y(enemy1y), imgenemy1b(enemy1b), imgenemy1_down1y(enemy1_down1y), imgenemy1_down1b(enemy1_down1b),imgenemy1_down2y(enemy1_down2y), imgenemy1_down2b(enemy1_down2b), enemy1_hp(ENEMY_HP)
{rect.left x;rect.right x imgenemy1b-getwidth();rect.top -imgenemy1b-getheight();rect.bottom 0;
}bool enemy_plane::show() //敌方飞机的移动显示
{if (rect.top WIN_HEIGHT) return false;rect.top 1;rect.bottom 1;if (enemy1_hp 3) {putimage(rect.left, rect.top, imgenemy1y, SRCAND);putimage(rect.left, rect.top, imgenemy1b, SRCPAINT);}else if (enemy1_hp 2) {putimage(rect.left, rect.top, imgenemy1_down1y, SRCAND);putimage(rect.left, rect.top, imgenemy1_down1b, SRCPAINT);}else if (enemy1_hp 1) {putimage(rect.left, rect.top, imgenemy1_down2y, SRCAND);putimage(rect.left, rect.top, imgenemy1_down2b, SRCPAINT);}return true;
}RECT enemy_plane::GetRect() //获取敌方飞机贴图相关信息
{return rect;
}五enemy_bullet类 - 敌方飞机发射的子弹类
构造函数 接受一个IMAGE类型的指针和一个enemy_plane类型的指针。根据敌机的宽度和位置初始化子弹的位置矩形确保子弹从敌机中间位置发射。 show函数 实现子弹的移动显示。如果子弹的顶部超出屏幕底部bullet_rect.top 0则返回false表示子弹已出界。每次将子弹的top和bottom坐标增加2实现向下移动移动距离move增加2然后使用putimage函数在新位置绘制子弹。 GetRect函数 返回子弹位置矩形的引用用于碰撞检测等操作。
class enemy_bullet //敌方飞机发射的子弹
{
public:enemy_bullet(IMAGE* img, enemy_plane* ep);bool show();RECT GetRect();int move;
private:IMAGE* bullet_ptr;RECT bullet_rect;enemy_plane* plane_ref;
};// 实现 enemy_bullet 的成员函数类外声明
enemy_bullet::enemy_bullet(IMAGE* img, enemy_plane* ep)//初始化: bullet_ptr(img), move(0), plane_ref(ep)
{bullet_rect.left (ep-width() - img-getwidth()) / 2 ep-GetRect().left;bullet_rect.right bullet_rect.left img-getwidth();bullet_rect.top ep-GetRect().bottom;bullet_rect.bottom bullet_rect.top img-getheight();
}bool enemy_bullet::show() //敌方飞机子弹的移动显示
{if (bullet_rect.top 0) return false;bullet_rect.top 2;bullet_rect.bottom 2;move 2;putimage(bullet_rect.left, bullet_rect.top, bullet_ptr);return true;
}RECT enemy_bullet::GetRect() //获取敌方飞机发射的子弹的贴图相关信息
{return bullet_rect;
}六其他重要函数 transparentimage透明背景贴图函数 这个函数用于实现透明背景贴图的功能相对复杂一些。它首先创建一个新的IMAGE对象获取其图像缓冲区的指针。然后遍历图像的每个像素将亮度低于0.03的像素设置为白色其他非白色像素设置为黑色最后通过SRCAND和SRCPAINT模式将处理后的图像与原始图像合成从而实现透明背景的效果。 代码 void transparentimage(int x, int y, IMAGE img) //便于直接使用透明背景贴图的函数可用于功能拓展
{IMAGE img1;DWORD* d1;img1 img;d1 GetImageBuffer(img1);float h, s, l;for (int i 0; i img1.getheight() * img1.getwidth(); i) {RGBtoHSL(BGR(d1[i]), h, s, l);if (l 0.03) {d1[i] BGR(WHITE);}if (d1[i] ! BGR(WHITE)) {d1[i] 0;}}putimage(x, y, img1, SRCAND);putimage(x, y, img, SRCPAINT);
}is_click判定鼠标点击函数函数 判断给定的坐标(x,y)是否在指定的矩形区域r内通过比较坐标与矩形的边界坐标来实现。 代码 bool is_click(int x, int y, RECT r) //判断是否点击指定区域
{return (r.left x r.right x r.top y r.bottom y);
}RectCrashRect判定碰撞函数 用于判断两个矩形的碰撞。它首先计算一个新的矩形r这个矩形的坐标是通过将r1的坐标根据r2的大小进行调整得到的。然后通过比较新矩形与r2的坐标关系来判断是否碰撞这种计算方式是一种比较特殊的碰撞检测算法需要仔细理解坐标的计算逻辑。 代码 bool RectCrashRect(RECT r1, RECT r2) //判断两个贴图的碰撞
{RECT r;r.left r1.left - (r2.right - r2.left);r.right r1.right;r.top r1.top - (r2.bottom - r2.top);r.bottom r1.bottom;return (r.left r2.left r2.left r.right r.top r2.top r2.top r.bottom);
}Welcome欢迎界面函数 这个函数用于创建游戏的初始化界面。它首先加载背景图像并绘制然后设置文字的颜色、字体等属性在屏幕上绘制游戏标题、开始游戏和退出游戏的按钮。接着通过一个循环获取鼠标消息判断鼠标左键是否按下如果按下并且点击在开始游戏按钮区域则返回如果点击在退出游戏按钮区域则直接退出程序。 代码 void Welcome() //初始化界面
{LPCTSTR title _T(打飞机);LPCTSTR title_play _T(开始游戏);LPCTSTR title_exit _T(退出游戏);IMAGE background; RECT title_playr, title_exitr;BeginBatchDraw();loadimage(background, ./images/bk.png);putimage(0, 0, background);setbkcolor(WHITE);//cleardevice();settextstyle(40, 0, _T(黑体));settextcolor(BLACK);outtextxy(WIN_WIDTH / 2 - textwidth(title) / 2,WIN_HEIGHT/5,title);outtextxy(WIN_WIDTH / 2 - textwidth(title_play) / 2, WIN_HEIGHT *2 / 5, title_play);title_playr.left WIN_WIDTH / 2 - textwidth(title_play) / 2;title_playr.right title_playr.left textwidth(title_play);title_playr.top WIN_HEIGHT *2/ 5;title_playr.bottom title_playr.top textheight(title_play);outtextxy(WIN_WIDTH / 2 - textwidth(title_exit) / 2, WIN_HEIGHT *3 / 5, title_exit);title_exitr.left WIN_WIDTH / 2 - textwidth(title_exit) / 2;title_exitr.right title_exitr.left textwidth(title_exit);title_exitr.top WIN_HEIGHT *3/ 5;title_exitr.bottom title_exitr.top textheight(title_exit);EndBatchDraw();//判断鼠标是否点击了开始或结束while (true) {ExMessage mouse_mess;getmessage(mouse_mess, EM_MOUSE);if (mouse_mess.lbutton) //判断鼠标左键是否按下{if (is_click(mouse_mess.x, mouse_mess.y, title_playr)) {return;//如果在title_playr范围内点击则返回,return后接着主函数继续运行游戏。}else if (is_click(mouse_mess.x, mouse_mess.y, title_exitr)) {exit(0);//如果如果在title_exitr范围内点击直接退出程序}}}
}Over函数 用于游戏结束时的结算界面。它首先打印一个字符这里可能是用于调试目的然后将击杀数转换为字符串并在屏幕上显示设置文字颜色为红色并显示在屏幕中央。接着通过一个循环获取键盘消息当按下回车键时返回。 代码 void Over(long long kill)//游戏结束结算
{printf_s(o);TCHAR* str new TCHAR[128];_stprintf_s(str, 128, _T(击杀数%llu), kill);settextcolor(RED);outtextxy(WIN_WIDTH / 2 - textwidth(str) / 2, WIN_HEIGHT / 5, str);// 键盘事件 按Enter返回LPCTSTR info _T(按Enter返回);settextstyle(20, 0, _T(黑体));outtextxy(WIN_WIDTH - textwidth(info), WIN_HEIGHT - textheight(info), info);while (true){ExMessage mess;getmessage(mess, EM_KEY);if (mess.vkcode 0x0D){return;}}
}CreateEnemy创建敌机函数 功能比较简单向敌机向量ep_vec中添加一个新的敌机对象敌机的初始位置是在屏幕宽度范围内随机生成的。 代码 void CreateEnemy(vectorenemy_plane ep_vec, IMAGE enemy1y, IMAGE enemy1b,IMAGE enemy1_down1y, IMAGE enemy1_down1b, IMAGE enemy1_down2y, IMAGE enemy1_down2b)
//创建敌机
{ep_vec.push_back(enemy_plane(enemy1y, enemy1b, enemy1_down1y, enemy1_down1b, enemy1_down2y, enemy1_down2b, abs(rand()) % (WIN_WIDTH - enemy1y.getwidth())));
}EnemyShow敌机移动显示函数 遍历敌机向量ep_vec调用每个敌机的show函数来显示敌机。 代码 void EnemyShow(vectorenemy_planeep_vec) //敌机移动显示
{for (int i 0; i ep_vec.size(); i) {ep_vec[i].show();}
}CreatePlaneBullet创建玩家飞机弹药函数 向玩家飞机子弹向量plane_bul_vec中添加一个新的子弹对象子弹的初始位置和属性根据玩家飞机来确定。 代码 void CreatePlaneBullet(vectorplane_bullet plane_bul_vec,hero_plane hp,IMAGE img) //创建玩家飞机子弹
{plane_bul_vec.push_back(plane_bullet(img, hp));
}BulletShow玩家飞机弹药移动显示函数 遍历玩家飞机子弹向量plane_bul_vec调用每个子弹的show函数来显示子弹。 代码 void BulletShow(vectorplane_bullet bul_vec) //玩家飞机子弹移动显示{for (int i 0; i bul_vec.size();i) {bul_vec[i].show();}}DeleteEnemy删除出界敌机函数
如果敌机向量不为空并且第一个敌机的顶部超出屏幕底部则删除这个敌机这样可以提高程序效率避免处理已经出界的敌机。
代码 void DeleteEnemy(vectorenemy_plane ep_vec) //删除敌方出界的飞机提高程序效率{if (ep_vec.empty()) return;if (ep_vec[0].GetRect().top WIN_HEIGHT) {ep_vec.erase(ep_vec.begin());}}DeleteBullet删除玩家飞机出界子弹函数
使用迭代器遍历玩家飞机子弹向量bul_vec如果子弹的移动距离超过屏幕高度则删除这个子弹。
代码 void DeleteBullet(vectorplane_bullet bul_vec) //删除玩家飞机出界的子弹提高程序效率{auto it bul_vec.begin();while (it ! bul_vec.end()) {if (it-move WIN_HEIGHT) {it bul_vec.erase(it);}else {it;}}}DeleteEnemyBullet删除敌机出界子弹函数
类似DeleteBullet函数使用迭代器遍历敌方飞机子弹向量enemy_bul_vec如果子弹的移动距离超过屏幕高度则删除这个子弹。
代码 void DeleteEnemyBullet(vectorenemy_bullet enemy_bul_vec) //删除敌方飞机出界的子弹提高程序效率{auto it enemy_bul_vec.begin();while (it ! enemy_bul_vec.end()) {if (it-move WIN_HEIGHT) {it enemy_bul_vec.erase(it);}else {it;}}}DestroyEnemy摧毁敌机函数
重难点这个函数实现了敌机与玩家飞机、子弹与敌机的碰撞处理。首先处理敌机与玩家飞机的碰撞如果碰撞则删除敌机并减少玩家飞机的血量。然后处理子弹与敌机的碰撞使用嵌套的迭代器遍历子弹向量和敌机向量如果碰撞则删除子弹并减少敌机的血量当敌机血量小于等于0时删除敌机并增加玩家飞机的得分。这里需要注意迭代器的有效性在删除敌机后需要正确处理迭代器以避免出现未定义行为。
代码 void DestroyEnemy(vectorplane_bullet bul_vec, vectorenemy_plane ep_vec, hero_plane hp) //成功击中敌机后的操作{// 处理敌机与我方飞机的碰撞for (auto ep ep_vec.begin(); ep ! ep_vec.end();ep) {if (RectCrashRect((*ep).GetRect(), hp.GetRect())) {ep ep_vec.erase(ep);hp.HP - 3;break;}}// 处理子弹与敌机的碰撞auto bul bul_vec.begin();while (bul ! bul_vec.end()) {auto ep ep_vec.begin();while (ep ! ep_vec.end()) {if (RectCrashRect((*bul).GetRect(), (*ep).GetRect())) {bul bul_vec.erase(bul);(*ep).enemy1_hp--;if ((*ep).enemy1_hp 0) {ep ep_vec.erase(ep);hp.point;if (ep ep_vec.end()) {break;}//当删除一个敌机后ep迭代器可能已经失效后续的循环可能会出现未定义的行为。//解决方法可以是在删除敌机后正确地更新迭代器确保循环的正确性}break;}else {ep;}}if (bul ! bul_vec.end()) {bul;}}}
RunGame游戏主体运行函数
这是游戏的主体运行函数。首先进行一些初始化工作如设置背景颜色、清屏、加载各种图像资源并创建游戏对象如背景对象、玩家飞机对象、敌机向量、子弹向量等。然后进入一个主循环只要玩家飞机的血量大于0就一直循环。在循环中先进行批量绘制的准备工作刷新消息队列。然后依次进行背景显示、玩家飞机的鼠标控制和显示、敌机的生成、显示和删除出界敌机、玩家飞机子弹的生成、显示和删除出界子弹、敌机子弹的生成、显示、击中判定和删除出界子弹、以及碰撞检测等操作。最后当玩家飞机血量小于等于0时调用Over函数进行游戏结束结算。
代码 void RunGame() //主体运行{setbkcolor(WHITE);cleardevice();IMAGE background;IMAGE my_plane[2];IMAGE hpd1[2];IMAGE hpd2[2];IMAGE enemy1[2];IMAGE enemy1_down1[2];IMAGE enemy1_down2[2];IMAGE enemy2[2];IMAGE bullet1;IMAGE bullet2;loadimage(background, _T(./images/bk2.png),WIN_WIDTH);putimage(0, 0, background);loadimage(my_plane[0], _T(./images/me1y.png),80,88);loadimage(my_plane[1], _T(./images/me1b.png), 80, 88);loadimage(hpd1[0], _T(./images/hpd1y.png), 80, 88);loadimage(hpd1[1], _T(./images/hpd1b.png), 80, 88);loadimage(hpd2[0], _T(./images/hpd2y.png), 80, 88);loadimage(hpd2[1], _T(./images/hpd2b.png),80,88);loadimage(bullet1, _T(./images/bullet1.png));loadimage(bullet2, _T(./images/bullet2.png));loadimage(enemy1[0], _T(./images/enemy1y.png));loadimage(enemy1[1], _T(./images/enemy1b.png));loadimage(enemy1_down1[0], _T(./images/enemy1_down1y.png));loadimage(enemy1_down1[1], _T(./images/enemy1_down1b.png));loadimage(enemy1_down2[0], _T(./images/enemy1_down2y.png));loadimage(enemy1_down2[1], _T(./images/enemy1_down2b.png));BK bk BK(background);hero_plane hp(my_plane[0], my_plane[1],hpd1[0],hpd1[1],hpd2[0],hpd2[1]);vectorenemy_planeep_vec;vectorplane_bulletplane_bul_vec; //我方飞机子弹vectorenemy_bulletenemy_bul_vec; //敌方飞机子弹ep_vec.push_back(enemy_plane(enemy1[0], enemy1[1], enemy1_down1[0], enemy1_down1[1], enemy1_down2[0], enemy1_down2[1], 50));int count 0;while (hp.HP0) {count;BeginBatchDraw();flushmessage();//开始准备bk.show();Sleep(8);hp.MouseControl();hp.show();//生成敌方飞机if (ep_vec.empty() || ep_vec[ep_vec.size() - 1].GetRect().top50) {CreateEnemy(ep_vec, enemy1[0], enemy1[1], enemy1_down1[0], enemy1_down1[1], enemy1_down2[0], enemy1_down2[1]);}EnemyShow(ep_vec);DeleteEnemy(ep_vec);//生成我方飞机子弹if (!plane_bul_vec.size()) {CreatePlaneBullet(plane_bul_vec, hp, bullet1);}else if (plane_bul_vec[plane_bul_vec.size() - 1].move 36) {CreatePlaneBullet(plane_bul_vec, hp, bullet1);}//生成敌机子弹auto ep ep_vec.begin();while (ep ! ep_vec.end()) {if ((*ep).GetRect().bottom % 150 20) {//enemy_plane epn *ep;enemy_bullet eb(bullet2, (*ep));enemy_bul_vec.push_back(eb);}ep;}//敌机子弹数组移动和击中判定auto eb enemy_bul_vec.begin();while (eb ! enemy_bul_vec.end()) {if (RectCrashRect((*eb).GetRect(), hp.GetRect())) {hp.HP - 1;eb enemy_bul_vec.erase(eb);}(*eb).show();eb;}BulletShow(plane_bul_vec);DeleteBullet(plane_bul_vec);DeleteEnemyBullet(enemy_bul_vec);DestroyEnemy(plane_bul_vec, ep_vec, hp);EndBatchDraw();}Over(hp.point);}三、完整代码
Plane.h
#pragma once
#includeiostream
#includegraphics.h
#includevector
#includequeue
#includemmsystem.h
#include tchar.h#define WIN_WIDTH 460 //窗口宽度
#define WIN_HEIGHT 700 //窗口高度
#define HERO_HP 6 //英雄飞机血量
#define ENEMY_HP 3 //敌机血量
using namespace std;class BK //背景方便实现背景滚动效果
{
public:BK(IMAGE img) :img(img),y(-WIN_HEIGHT){}void show() {if (!y) y -WIN_HEIGHT;y 2;putimage(0, y, img);}
private:IMAGE img;int y;
};class hero_plane //玩家飞机
{
public:hero_plane(IMAGE* hero_planey, IMAGE* hero_planeb, IMAGE* hp_down1y, IMAGE* hp_down1b, IMAGE* hp_down2y, IMAGE* hp_down2b):point(0),hpy(hero_planey), hpb(hero_planeb), hpd1y(hp_down1y), hpd1b(hp_down1b), hpd2y(hp_down2y), hpd2b(hp_down2b), HP(HERO_HP){rect.left WIN_WIDTH / 2 - (*hpy).getwidth() / 2;rect.top WIN_HEIGHT - (*hpy).getheight();rect.right rect.left (*hpy).getwidth();rect.bottom WIN_HEIGHT;}void MouseControl() {ExMessage mouse_mess;if (peekmessage(mouse_mess, EM_MOUSE)){if (mouse_mess.y 450) {mouse_mess.y 450;}rect.left mouse_mess.x - (*hpy).getwidth() / 2;rect.top mouse_mess.y - (*hpy).getheight() / 2;rect.right rect.right rect.left (*hpy).getwidth();rect.bottom rect.top (*hpy).getheight();}}void show() {//putimage(185, 550, imgy, SRCAND);//putimage(185, 550, imgb, SRCPAINT);if (HP 5) {putimage(rect.left, rect.top, hpy, SRCAND);putimage(rect.left, rect.top, hpb, SRCPAINT);}else if (HP 3 HP 4) {putimage(rect.left, rect.top, hpd1y, SRCAND);putimage(rect.left, rect.top, hpd1b, SRCPAINT);}else if (HP 1 HP 2) {putimage(rect.left, rect.top, hpd2y, SRCAND);putimage(rect.left, rect.top, hpd2b, SRCPAINT);}}RECT GetRect() { return rect; }long long point;int HP;
private:IMAGE* hpy;IMAGE* hpb;IMAGE* hpd1y;IMAGE* hpd1b;IMAGE* hpd2y;IMAGE* hpd2b;RECT rect;//int HP;
};class plane_bullet //玩家飞机发射的子弹
{
public:plane_bullet(IMAGE* img, hero_plane* hp): imgPtr(img), move(0){rect.left hp-GetRect().left (hp-GetRect().right - hp-GetRect().left - imgPtr-getwidth()) / 2;rect.right rect.left imgPtr-getwidth();rect.top hp-GetRect().top;rect.bottom rect.top imgPtr-getheight();}bool show() {if (rect.top 0) return false;rect.top - 3;rect.bottom - 3;move 3;putimage(rect.left, rect.top, imgPtr);return true;}LONG getleft() {return rect.left;}RECT GetRect() { return rect; }int move;
private:IMAGE* imgPtr;RECT rect;
};// 前向声明 enemy_plane 类
class enemy_plane;class enemy_bullet //敌方飞机发射的子弹
{
public:enemy_bullet(IMAGE* img, enemy_plane* ep);bool show();RECT GetRect();int move;
private:IMAGE* bullet_ptr;RECT bullet_rect;enemy_plane* plane_ref;
};class enemy_plane //敌方飞机
{
public:enemy_plane(IMAGE* enemy1y, IMAGE* enemy1b, IMAGE* enemy1_down1y, IMAGE* enemy1_down1b, IMAGE* enemy1_down2y, IMAGE* enemy1_down2b, int x);bool show();void CreateBullet(IMAGE* enemy_bullet);RECT GetRect();int enemy1_hp;int width() {return imgenemy1b-getwidth();}vectorenemy_bullet enemy_bul_vec;
private:IMAGE* imgenemy1y;IMAGE* imgenemy1b;IMAGE* imgenemy1_down1y;IMAGE* imgenemy1_down1b;IMAGE* imgenemy1_down2y;IMAGE* imgenemy1_down2b;IMAGE* imgenemy_bullet;RECT rect;
};void transparentimage(int x, int y, IMAGE img);//便于直接使用透明背景贴图的函数bool is_click(int x, int y, RECT r); //判断是否点击指定区域bool RectCrashRect(RECT r1, RECT r2); //判断两个贴图的碰撞void Welcome(); //初始化界面void Over(long long kill);//游戏结束结算void CreateEnemy(vectorenemy_plane ep_vec, IMAGE enemy1y, IMAGE enemy1b, IMAGE enemy1_down1y, IMAGE enemy1_down1b, IMAGE enemy1_down2y, IMAGE enemy1_down2b);void CreatePlaneBullet(vectorplane_bullet bul_vec, hero_plane hp, IMAGE img); //创建弹药void EnemyShow(vectorenemy_plane ep_vec);//敌机运动void BulletShow(vectorplane_bullet bul_vec);//弹药运动void DeleteEnemy(vectorenemy_plane ep_vec);//删除出界后的敌机void DeleteBullet(std::vectorplane_bullet bul_vec);//删除出界弹药void DeleteEnemyBullet(vectorenemy_bullet enemy_bul_vec);//删除敌方出界子弹使程序更高效void DestroyEnemy(vectorplane_bullet bul_vec, vectorenemy_plane ep_vec, hero_plane hp);//成功击中敌机后的操作void RunGame(); //游戏主体运行/*
技术要点/实现功能
1、地图的绘制移动
2、主角战机的移动和绘制
3、敌机的生成、绘制和移动
4、主角战机发射的弹药的移动和绘制
5、弹药和敌机的碰撞判定
6、敌机/主角战机坠毁
7、开始、结束界面以及得分栏等
*/Plane.cpp
#include plane.hvoid transparentimage(int x, int y, IMAGE img) //便于直接使用透明背景贴图的函数可用于功能拓展
{IMAGE img1;DWORD* d1;img1 img;d1 GetImageBuffer(img1);float h, s, l;for (int i 0; i img1.getheight() * img1.getwidth(); i) {RGBtoHSL(BGR(d1[i]), h, s, l);if (l 0.03) {d1[i] BGR(WHITE);}if (d1[i] ! BGR(WHITE)) {d1[i] 0;}}putimage(x, y, img1, SRCAND);putimage(x, y, img, SRCPAINT);
}bool is_click(int x, int y, RECT r) //判断是否点击指定区域
{return (r.left x r.right x r.top y r.bottom y);
}bool RectCrashRect(RECT r1, RECT r2) //判断两个贴图的碰撞
{RECT r;r.left r1.left - (r2.right - r2.left);r.right r1.right;r.top r1.top - (r2.bottom - r2.top);r.bottom r1.bottom;return (r.left r2.left r2.left r.right r.top r2.top r2.top r.bottom);
}void Welcome() //初始化界面
{LPCTSTR title _T(打飞机);LPCTSTR title_play _T(开始游戏);LPCTSTR title_exit _T(退出游戏);IMAGE background; RECT title_playr, title_exitr;BeginBatchDraw();loadimage(background, ./images/bk.png);putimage(0, 0, background);setbkcolor(WHITE);//cleardevice();settextstyle(40, 0, _T(黑体));settextcolor(BLACK);outtextxy(WIN_WIDTH / 2 - textwidth(title) / 2,WIN_HEIGHT/5,title);outtextxy(WIN_WIDTH / 2 - textwidth(title_play) / 2, WIN_HEIGHT *2 / 5, title_play);title_playr.left WIN_WIDTH / 2 - textwidth(title_play) / 2;title_playr.right title_playr.left textwidth(title_play);title_playr.top WIN_HEIGHT *2/ 5;title_playr.bottom title_playr.top textheight(title_play);outtextxy(WIN_WIDTH / 2 - textwidth(title_exit) / 2, WIN_HEIGHT *3 / 5, title_exit);title_exitr.left WIN_WIDTH / 2 - textwidth(title_exit) / 2;title_exitr.right title_exitr.left textwidth(title_exit);title_exitr.top WIN_HEIGHT *3/ 5;title_exitr.bottom title_exitr.top textheight(title_exit);EndBatchDraw();//判断鼠标是否点击了开始或结束while (true) {ExMessage mouse_mess;getmessage(mouse_mess, EM_MOUSE);if (mouse_mess.lbutton) //判断鼠标左键是否按下{if (is_click(mouse_mess.x, mouse_mess.y, title_playr)) {return;//如果在title_playr范围内点击则返回,return后接着主函数继续运行游戏。}else if (is_click(mouse_mess.x, mouse_mess.y, title_exitr)) {exit(0);//如果如果在title_exitr范围内点击直接退出程序}}}
}void Over(long long kill)//游戏结束结算
{printf_s(o);TCHAR* str new TCHAR[128];_stprintf_s(str, 128, _T(击杀数%llu), kill);settextcolor(RED);outtextxy(WIN_WIDTH / 2 - textwidth(str) / 2, WIN_HEIGHT / 5, str);// 键盘事件 按Enter返回LPCTSTR info _T(按Enter返回);settextstyle(20, 0, _T(黑体));outtextxy(WIN_WIDTH - textwidth(info), WIN_HEIGHT - textheight(info), info);while (true){ExMessage mess;getmessage(mess, EM_KEY);if (mess.vkcode 0x0D){return;}}
}void CreateEnemy(vectorenemy_plane ep_vec, IMAGE enemy1y, IMAGE enemy1b,IMAGE enemy1_down1y, IMAGE enemy1_down1b, IMAGE enemy1_down2y, IMAGE enemy1_down2b)
//创建敌机
{ep_vec.push_back(enemy_plane(enemy1y, enemy1b, enemy1_down1y, enemy1_down1b, enemy1_down2y, enemy1_down2b, abs(rand()) % (WIN_WIDTH - enemy1y.getwidth())));
}void EnemyShow(vectorenemy_planeep_vec) //敌机移动显示
{for (int i 0; i ep_vec.size(); i) {ep_vec[i].show();}
}void CreatePlaneBullet(vectorplane_bullet plane_bul_vec,hero_plane hp,IMAGE img) //创建玩家飞机子弹
{plane_bul_vec.push_back(plane_bullet(img, hp));
}void BulletShow(vectorplane_bullet bul_vec) //玩家飞机子弹移动显示
{for (int i 0; i bul_vec.size();i) {bul_vec[i].show();}
}void DeleteEnemy(vectorenemy_plane ep_vec) //删除敌方出界的飞机提高程序效率
{if (ep_vec.empty()) return;if (ep_vec[0].GetRect().top WIN_HEIGHT) {ep_vec.erase(ep_vec.begin());}
}void DeleteBullet(vectorplane_bullet bul_vec) //删除玩家飞机出界的子弹提高程序效率
{auto it bul_vec.begin();while (it ! bul_vec.end()) {if (it-move WIN_HEIGHT) {it bul_vec.erase(it);}else {it;}}
}void DeleteEnemyBullet(vectorenemy_bullet enemy_bul_vec) //删除敌方飞机出界的子弹提高程序效率
{auto it enemy_bul_vec.begin();while (it ! enemy_bul_vec.end()) {if (it-move WIN_HEIGHT) {it enemy_bul_vec.erase(it);}else {it;}}
}void DestroyEnemy(vectorplane_bullet bul_vec, vectorenemy_plane ep_vec, hero_plane hp) //成功击中敌机后的操作
{// 处理敌机与我方飞机的碰撞for (auto ep ep_vec.begin(); ep ! ep_vec.end();ep) {if (RectCrashRect((*ep).GetRect(), hp.GetRect())) {ep ep_vec.erase(ep);hp.HP - 3;break;}}// 处理子弹与敌机的碰撞auto bul bul_vec.begin();while (bul ! bul_vec.end()) {auto ep ep_vec.begin();while (ep ! ep_vec.end()) {if (RectCrashRect((*bul).GetRect(), (*ep).GetRect())) {bul bul_vec.erase(bul);(*ep).enemy1_hp--;if ((*ep).enemy1_hp 0) {ep ep_vec.erase(ep);hp.point;if (ep ep_vec.end()) {break;}//当删除一个敌机后ep迭代器可能已经失效后续的循环可能会出现未定义的行为。//解决方法可以是在删除敌机后正确地更新迭代器确保循环的正确性}break;}else {ep;}}if (bul ! bul_vec.end()) {bul;}}
}// 实现 enemy_bullet 的成员函数
enemy_bullet::enemy_bullet(IMAGE* img, enemy_plane* ep)//初始化: bullet_ptr(img), move(0), plane_ref(ep)
{bullet_rect.left (ep-width() - img-getwidth()) / 2 ep-GetRect().left;bullet_rect.right bullet_rect.left img-getwidth();bullet_rect.top ep-GetRect().bottom;bullet_rect.bottom bullet_rect.top img-getheight();
}bool enemy_bullet::show() //敌方飞机子弹的移动显示
{if (bullet_rect.top 0) return false;bullet_rect.top 2;bullet_rect.bottom 2;move 2;putimage(bullet_rect.left, bullet_rect.top, bullet_ptr);return true;
}RECT enemy_bullet::GetRect() //获取敌方飞机发射的子弹的贴图相关信息
{return bullet_rect;
}// 实现 enemy_plane 的成员函数
enemy_plane::enemy_plane(IMAGE* enemy1y, IMAGE* enemy1b, IMAGE* enemy1_down1y, IMAGE* enemy1_down1b, IMAGE* enemy1_down2y, IMAGE* enemy1_down2b, int x)//初始化: imgenemy1y(enemy1y), imgenemy1b(enemy1b), imgenemy1_down1y(enemy1_down1y), imgenemy1_down1b(enemy1_down1b),imgenemy1_down2y(enemy1_down2y), imgenemy1_down2b(enemy1_down2b), enemy1_hp(ENEMY_HP)
{rect.left x;rect.right x imgenemy1b-getwidth();rect.top -imgenemy1b-getheight();rect.bottom 0;
}bool enemy_plane::show() //敌方飞机的移动显示
{if (rect.top WIN_HEIGHT) return false;rect.top 1;rect.bottom 1;if (enemy1_hp 3) {putimage(rect.left, rect.top, imgenemy1y, SRCAND);putimage(rect.left, rect.top, imgenemy1b, SRCPAINT);}else if (enemy1_hp 2) {putimage(rect.left, rect.top, imgenemy1_down1y, SRCAND);putimage(rect.left, rect.top, imgenemy1_down1b, SRCPAINT);}else if (enemy1_hp 1) {putimage(rect.left, rect.top, imgenemy1_down2y, SRCAND);putimage(rect.left, rect.top, imgenemy1_down2b, SRCPAINT);}return true;
}RECT enemy_plane::GetRect() //获取敌方飞机贴图相关信息
{return rect;
}void RunGame() //主体运行
{setbkcolor(WHITE);cleardevice();IMAGE background;IMAGE my_plane[2];IMAGE hpd1[2];IMAGE hpd2[2];IMAGE enemy1[2];IMAGE enemy1_down1[2];IMAGE enemy1_down2[2];IMAGE enemy2[2];IMAGE bullet1;IMAGE bullet2;loadimage(background, _T(./images/bk2.png),WIN_WIDTH);putimage(0, 0, background);loadimage(my_plane[0], _T(./images/me1y.png),80,88);loadimage(my_plane[1], _T(./images/me1b.png), 80, 88);loadimage(hpd1[0], _T(./images/hpd1y.png), 80, 88);loadimage(hpd1[1], _T(./images/hpd1b.png), 80, 88);loadimage(hpd2[0], _T(./images/hpd2y.png), 80, 88);loadimage(hpd2[1], _T(./images/hpd2b.png),80,88);loadimage(bullet1, _T(./images/bullet1.png));loadimage(bullet2, _T(./images/bullet2.png));loadimage(enemy1[0], _T(./images/enemy1y.png));loadimage(enemy1[1], _T(./images/enemy1b.png));loadimage(enemy1_down1[0], _T(./images/enemy1_down1y.png));loadimage(enemy1_down1[1], _T(./images/enemy1_down1b.png));loadimage(enemy1_down2[0], _T(./images/enemy1_down2y.png));loadimage(enemy1_down2[1], _T(./images/enemy1_down2b.png));BK bk BK(background);hero_plane hp(my_plane[0], my_plane[1],hpd1[0],hpd1[1],hpd2[0],hpd2[1]);vectorenemy_planeep_vec;vectorplane_bulletplane_bul_vec; //我方飞机子弹vectorenemy_bulletenemy_bul_vec; //敌方飞机子弹ep_vec.push_back(enemy_plane(enemy1[0], enemy1[1], enemy1_down1[0], enemy1_down1[1], enemy1_down2[0], enemy1_down2[1], 50));int count 0;while (hp.HP0) {count;BeginBatchDraw();flushmessage();//开始准备bk.show();Sleep(8);hp.MouseControl();hp.show();//生成敌方飞机if (ep_vec.empty() || ep_vec[ep_vec.size() - 1].GetRect().top50) {CreateEnemy(ep_vec, enemy1[0], enemy1[1], enemy1_down1[0], enemy1_down1[1], enemy1_down2[0], enemy1_down2[1]);}EnemyShow(ep_vec);DeleteEnemy(ep_vec);//生成我方飞机子弹if (!plane_bul_vec.size()) {CreatePlaneBullet(plane_bul_vec, hp, bullet1);}else if (plane_bul_vec[plane_bul_vec.size() - 1].move 36) {CreatePlaneBullet(plane_bul_vec, hp, bullet1);}//生成敌机子弹auto ep ep_vec.begin();while (ep ! ep_vec.end()) {if ((*ep).GetRect().bottom % 150 20) {//enemy_plane epn *ep;enemy_bullet eb(bullet2, (*ep));enemy_bul_vec.push_back(eb);}ep;}//敌机子弹数组移动和击中判定auto eb enemy_bul_vec.begin();while (eb ! enemy_bul_vec.end()) {if (RectCrashRect((*eb).GetRect(), hp.GetRect())) {hp.HP - 1;eb enemy_bul_vec.erase(eb);}(*eb).show();eb;}BulletShow(plane_bul_vec);DeleteBullet(plane_bul_vec);DeleteEnemyBullet(enemy_bul_vec);DestroyEnemy(plane_bul_vec, ep_vec, hp);EndBatchDraw();}Over(hp.point);
}
main.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include Plane.h
int main()
{initgraph(WIN_WIDTH, WIN_HEIGHT);bool game_status true;while (game_status) {Welcome();RunGame();}return 0;
}