当前位置: 首页 > article >正文

《C++ 小游戏:简易飞机大战游戏的实现》

文章目录

  • 《C++ 游戏代码解析:简易飞机大战游戏的实现》
    • 一、游戏整体结构与功能概述
    • 二、各个类和函数的功能分析
      • (一)`BK`类 - 背景类
      • (二)`hero_plane`类 - 玩家飞机类
      • (三)`plane_bullet`类 - 玩家飞机发射的子弹类
      • (四)`enemy_plane`类 - 敌方飞机类
      • (五)`enemy_bullet`类 - 敌方飞机发射的子弹类
      • (六)其他重要函数
    • 三、完整代码:
      • `Plane.h`
      • `Plane.cpp`
      • `main.cpp`

《C++ 游戏代码解析:简易飞机大战游戏的实现》

在这篇博客中,将简单实现并深入解析一个C++ 飞机大战游戏的代码,该小游戏界面由EasyX图形库实现,相关内容可以参考EasyX图形库基础使用教程(快速上手),小游戏运行演示效果见飞机大战效果演示,源码和相关资源文件课件已上传gitee:PLANE · TTKun/Project - 码云

一、游戏整体结构与功能概述

这个飞机大战游戏主要由多个类和函数构成,整体功能包括游戏的初始化、玩家操作、敌机与子弹的生成和管理、碰撞检测以及游戏的开始和结束界面展示等。

二、各个类和函数的功能分析

(一)BK类 - 背景类

  1. 构造函数
    • 这个类用于实现背景滚动效果。构造函数接受一个IMAGE类型的引用,同时初始化背景图像的y坐标为-WIN_HEIGHT,这是为了让背景从屏幕上方开始滚动。
  2. 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类 - 玩家飞机类

  1. 构造函数
    • 构造函数接受多个IMAGE类型的指针,用于不同状态下的玩家飞机图像,同时初始化玩家飞机的初始位置、血量等属性。例如,通过计算将飞机初始位置设置在屏幕下方中央。
  2. MouseControl函数
    • 此函数用于实现玩家飞机的鼠标控制。通过获取鼠标消息,如果鼠标的y坐标小于等于450则限制为450,然后根据鼠标的x坐标重新计算并更新飞机的位置矩形。
  3. show函数
    • 根据玩家飞机的血量值,使用不同的飞机图像进行绘制。如果血量大于等于5使用一种图像,血量在3 - 4之间使用另一种,血量在1 - 2之间使用第三种图像进行绘制。
  4. 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类 - 玩家飞机发射的子弹类

  1. 构造函数
    • 接受一个IMAGE类型的指针和一个hero_plane类型的指针。根据玩家飞机的位置初始化子弹的位置矩形,确保子弹从飞机中间位置发射。
  2. show函数
    • 实现子弹的移动显示。如果子弹的顶部超出屏幕顶部(rect.top <= 0)则返回false,表示子弹已出界。每次将子弹的topbottom坐标减少3(实现向上移动),移动距离move增加3,然后使用putimage函数在新位置绘制子弹。
  3. getleft函数
    • 返回子弹位置矩形的左边坐标,这可能在某些碰撞检测场景中有用。
  4. 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类 - 敌方飞机类

  1. 构造函数
    • 接受多个IMAGE类型的指针,用于不同状态下的敌机图像,以及一个x坐标值。初始化敌机的位置、血量等属性,将敌机的初始位置设置在屏幕上方的指定x坐标处。
  2. show函数
    • 根据敌机的血量值,使用不同的敌机图像进行绘制。同时,每次将敌机的topbottom坐标增加1(实现向下移动),如果敌机的top坐标超出屏幕底部则返回false
  3. CreateBullet函数(代码中未实现具体函数体)
    • 从函数名推测应该是用于创建敌机发射的子弹。
  4. 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();
	}
	vector<enemy_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类 - 敌方飞机发射的子弹类

  1. 构造函数
    • 接受一个IMAGE类型的指针和一个enemy_plane类型的指针。根据敌机的宽度和位置初始化子弹的位置矩形,确保子弹从敌机中间位置发射。
  2. show函数
    • 实现子弹的移动显示。如果子弹的顶部超出屏幕底部(bullet_rect.top <= 0)则返回false,表示子弹已出界。每次将子弹的topbottom坐标增加2(实现向下移动),移动距离move增加2,然后使用putimage函数在新位置绘制子弹。
  3. 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;
}

(六)其他重要函数

  1. transparentimage透明背景贴图函数

    • 这个函数用于实现透明背景贴图的功能,相对复杂一些。它首先创建一个新的IMAGE对象,获取其图像缓冲区的指针。然后遍历图像的每个像素,将亮度低于0.03的像素设置为白色,其他非白色像素设置为黑色,最后通过SRCANDSRCPAINT模式将处理后的图像与原始图像合成,从而实现透明背景的效果。

    代码:

    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);
    }
    
  2. 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);
    }
    
  3. 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);
    }
    
  4. 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范围内点击,直接退出程序
    			}
    		}
    	}
    }
    
  5. 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;
    		}
    	}
    }
    
  6. CreateEnemy创建敌机函数

    • 功能比较简单,向敌机向量ep_vec中添加一个新的敌机对象,敌机的初始位置是在屏幕宽度范围内随机生成的。

    代码:

    void CreateEnemy(vector<enemy_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())));
    }
    
  7. EnemyShow敌机移动显示函数

    • 遍历敌机向量ep_vec,调用每个敌机的show函数来显示敌机。

    代码:

    void EnemyShow(vector<enemy_plane>&ep_vec) //敌机移动显示
    {
    	for (int i = 0; i < ep_vec.size(); i++) {
    		ep_vec[i].show();
    	}
    }
    
  8. CreatePlaneBullet创建玩家飞机弹药函数

    • 向玩家飞机子弹向量plane_bul_vec中添加一个新的子弹对象,子弹的初始位置和属性根据玩家飞机来确定。

    代码:

    void CreatePlaneBullet(vector<plane_bullet>& plane_bul_vec,hero_plane& hp,IMAGE& img) //创建玩家飞机子弹
    {
    	plane_bul_vec.push_back(plane_bullet(&img, &hp));
    }
    
  9. BulletShow玩家飞机弹药移动显示函数

    • 遍历玩家飞机子弹向量plane_bul_vec,调用每个子弹的show函数来显示子弹。

    代码:

   void BulletShow(vector<plane_bullet>& bul_vec) //玩家飞机子弹移动显示
   {
   	for (int i = 0; i < bul_vec.size();i++) {
   		bul_vec[i].show();
   	}
   }
  1. DeleteEnemy删除出界敌机函数
  • 如果敌机向量不为空并且第一个敌机的顶部超出屏幕底部,则删除这个敌机,这样可以提高程序效率,避免处理已经出界的敌机。

代码:

	void DeleteEnemy(vector<enemy_plane>& ep_vec) //删除敌方出界的飞机,提高程序效率
	{
		if (ep_vec.empty())	return;
		if (ep_vec[0].GetRect().top >= WIN_HEIGHT) {
			ep_vec.erase(ep_vec.begin());
		}
	}
  1. DeleteBullet删除玩家飞机出界子弹函数
  • 使用迭代器遍历玩家飞机子弹向量bul_vec,如果子弹的移动距离超过屏幕高度则删除这个子弹。

代码:

	void DeleteBullet(vector<plane_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;
			}
		}
	}
  1. DeleteEnemyBullet删除敌机出界子弹函数
  • 类似DeleteBullet函数,使用迭代器遍历敌方飞机子弹向量enemy_bul_vec,如果子弹的移动距离超过屏幕高度则删除这个子弹。

代码:

	void DeleteEnemyBullet(vector<enemy_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;
			}
		}
	}
  1. DestroyEnemy摧毁敌机函数
  • 重难点:这个函数实现了敌机与玩家飞机、子弹与敌机的碰撞处理。首先处理敌机与玩家飞机的碰撞,如果碰撞则删除敌机并减少玩家飞机的血量。然后处理子弹与敌机的碰撞,使用嵌套的迭代器遍历子弹向量和敌机向量,如果碰撞则删除子弹并减少敌机的血量,当敌机血量小于等于0时删除敌机并增加玩家飞机的得分。这里需要注意迭代器的有效性,在删除敌机后需要正确处理迭代器,以避免出现未定义行为。

代码:

	void DestroyEnemy(vector<plane_bullet>& bul_vec, vector<enemy_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;
			}
		}
	}
	
  1. 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]);
	
	
		vector<enemy_plane>ep_vec;
		vector<plane_bullet>plane_bul_vec;	//我方飞机子弹
		vector<enemy_bullet>enemy_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.HP>0) {
			count++;
			BeginBatchDraw();
			flushmessage();
	
			//开始准备
			bk.show();
			Sleep(8);
			hp.MouseControl();
			hp.show();
	
			//生成敌方飞机
			if (ep_vec.empty() || ep_vec[ep_vec.size() - 1].GetRect().top>=50) {
				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
#include<iostream>
#include<graphics.h>
#include<vector>
#include<queue>
#include<mmsystem.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();
	}
	vector<enemy_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(vector<enemy_plane>& ep_vec, IMAGE& enemy1y, IMAGE& enemy1b, IMAGE& enemy1_down1y, IMAGE& enemy1_down1b, IMAGE& enemy1_down2y, IMAGE& enemy1_down2b);

void CreatePlaneBullet(vector<plane_bullet>& bul_vec, hero_plane& hp, IMAGE& img); //创建弹药

void EnemyShow(vector<enemy_plane>& ep_vec);//敌机运动

void BulletShow(vector<plane_bullet>& bul_vec);//弹药运动

void DeleteEnemy(vector<enemy_plane>& ep_vec);//删除出界后的敌机

void DeleteBullet(std::vector<plane_bullet>& bul_vec);//删除出界弹药

void DeleteEnemyBullet(vector<enemy_bullet>& enemy_bul_vec);//删除敌方出界子弹,使程序更高效

void DestroyEnemy(vector<plane_bullet>& bul_vec, vector<enemy_plane>& ep_vec, hero_plane& hp);//成功击中敌机后的操作

void RunGame(); //游戏主体运行


/*
技术要点/实现功能:
1、地图的绘制(移动)
2、主角战机的移动和绘制
3、敌机的生成、绘制和移动
4、主角战机发射的弹药的移动和绘制
5、弹药和敌机的碰撞判定
6、敌机/主角战机坠毁
7、开始、结束界面以及得分栏等
*/

Plane.cpp

#include "plane.h"

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);
}

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(vector<enemy_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(vector<enemy_plane>&ep_vec) //敌机移动显示
{
	for (int i = 0; i < ep_vec.size(); i++) {
		ep_vec[i].show();
	}
}

void CreatePlaneBullet(vector<plane_bullet>& plane_bul_vec,hero_plane& hp,IMAGE& img) //创建玩家飞机子弹
{
	plane_bul_vec.push_back(plane_bullet(&img, &hp));
}

void BulletShow(vector<plane_bullet>& bul_vec) //玩家飞机子弹移动显示
{
	for (int i = 0; i < bul_vec.size();i++) {
		bul_vec[i].show();
	}
}

void DeleteEnemy(vector<enemy_plane>& ep_vec) //删除敌方出界的飞机,提高程序效率
{
	if (ep_vec.empty())	return;
	if (ep_vec[0].GetRect().top >= WIN_HEIGHT) {
		ep_vec.erase(ep_vec.begin());
	}
}

void DeleteBullet(vector<plane_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(vector<enemy_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(vector<plane_bullet>& bul_vec, vector<enemy_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]);


	vector<enemy_plane>ep_vec;
	vector<plane_bullet>plane_bul_vec;	//我方飞机子弹
	vector<enemy_bullet>enemy_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.HP>0) {
		count++;
		BeginBatchDraw();
		flushmessage();

		//开始准备
		bk.show();
		Sleep(8);
		hp.MouseControl();
		hp.show();

		//生成敌方飞机
		if (ep_vec.empty() || ep_vec[ep_vec.size() - 1].GetRect().top>=50) {
			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;
}

http://www.kler.cn/a/331141.html

相关文章:

  • html + css 淘宝网实战
  • 数据仓库和数据湖 数据仓库和数据库
  • 【我的世界】起床战争攻略
  • 深入理解 PyTorch 的 view() 函数:以多头注意力机制(Multi-Head Attention)为例 (中英双语)
  • 01.HTTPS的实现原理-HTTPS的概念
  • 前端网页开发学习(HTML+CSS+JS)有这一篇就够!
  • 第十二章--- fixed 和 setprecision 函数、round 函数、进制转换及底层逻辑
  • 【Unity单机游戏框架】K-Framework
  • Unity实战案例全解析:RTS游戏的框选和阵型功能(2) 生成选择框
  • Servlet的生命周期及用户提交表单页面的实现(实验报告)
  • ENV | WSL 拓展虚拟磁盘空间
  • 【mod分享】极品飞车12无间风云高清重置mod,车模重构和材质贴图高清化
  • 【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
  • Element UI教程:如何将Radio单选框的圆框改为方框
  • 通信工程学习:什么是DNS域名系统
  • 做数据抓取工作要如何选择ip池
  • Windows 11 24H2正式发布
  • Linux如何查看每个文件及文件夹的大小
  • C语言复习概要(一)
  • 链表面试编程题
  • 在中序线索树中找到数据域A,并在其左子树中插入数据域为x的结点
  • Java JUC(三) AQS与同步工具详解
  • 机器学习【教育领域及其平台搭建】
  • 用好AI告别灵感枯竭!如何用300个选题提示词打造病毒式内容?
  • Python笔记 - 函数、方法和类装饰器
  • react-问卷星项目(4)