• C++实现坦克大战(超详细)(文末附源码!!!)


    一、成果展示

    在这里插入图片描述在这里插入图片描述

    二、开发环境及工具

    C++开发,使用工具为vs2019的community版本,坦克大战需要借助EasyX库来完成坦克大战的图形绘制。

    三、游戏规则设定

    (1)玩家移动及发射炮弹:
    单人版:玩家通过W,S,A,D分别控制坦克进行上、下、左、右的移动,J键表示发射炮弹。
    双人版:玩家一通过W,S,A,D分别控制坦克进行上、下、左、右的移动,J键表示发射炮弹;
    玩家二通过↑↓←→分别控制坦克进行上、下、左、右的移动,1键表示发射炮弹。
    敌方坦克移动及发射炮弹:自动移动,自动发射。
    (2)敌方坦克设置有四种不同分值的坦克(从100到400分四种,以下分别称为一、二、三、四级敌方坦克),从一级到四级敌方坦克攻击速度逐渐增长。
    (3)玩家从开始游戏时会有三条生命(即三次游戏机会),进入下一关生命值不会重新刷新。
    (4)对于第一敌方坦克,玩家炮弹击中一次则死亡。对于四级敌方坦克,玩家炮弹需要击中四次才死亡。同理可得二、三级敌方坦克死亡规则。(但由于游戏目前尚未能解决的bug,有时四级敌方坦克击中一次可会死亡。一、二、三级敌方坦克暂未出现此bug)
    若玩家被敌方坦克击中,则玩家死亡,失去一条生命,重新在出生地出生。当玩家生命值全部失去,则玩家失败,游戏结束。
    同时,玩家需要守家(即守卫自己的出生地),一旦玩家出生地被攻击也意味着游戏结束。
    (5)草地地图可隐藏坦克踪迹;
    河流地图坦克无法经过,但炮弹可以经过。
    (6)玩家每攻击掉敌方坦克可以进行升级,当玩家升至4级,炮弹可击穿铁块。而当玩家重新出生时,等级会重置。敌方坦克不能打掉铁块。
    (7)一旦游戏开始,玩家不能暂停游戏。
    (8)玩家出生地固定在出生点(即家),敌方炮弹随机智能地出现在地图上任一出生点。
    (9)每个关卡对应不同的地图、背景音乐及难度。

    四、游戏界面美工要求

    (1)整体布局清新简洁。
    (2)色调、背景、图标均仿照或选取于经典坦克大战游戏中,为老玩家找回童年的快乐。
    (3)游戏中界面主要显示玩家坦克、敌方坦克、障碍物、玩家出生地等。
    (4)游戏界面右侧上方实时显示当前剩余敌方坦克数量。
    (5)游戏界面右侧下方实时显示当前玩家所剩生命值和游戏关卡数。
    (6)游戏每当击中死亡一个敌方坦克时,在死亡地会短暂出现该敌方坦克所价值分值后消失。
    (6)游戏结束后跳出分数面板页面,显示本次玩家所获得分数。包括玩家一、玩家二个人所获得总分数和分别击中一、二、三、四级敌方坦克的个数和获得的单项分数。

    五、类的设计

    创建一个Tankunit类:用来存储所有的坦克都有的属性。共有属性:方向,前进方向和射击方向;装甲等级,不同装甲类型的坦克有不同的特点,区分不同类型坦克等级。
    创建一个PlayerUnit类:用来存档玩家的坦克。新增变量lives,用来存放玩家剩余坦克数亮。这个变量将会继承于Tank类。
    创建一个ComputerPlayerunit:用来存档电脑玩家,新增静态变量,用来保存敌军剩余坦克数量,Left Ops=20:每一关卡20个敌军坦克。
    创建一个map-res类:对地图信息的重新处理,用以存储每关的地图信息。
    创建一个Music类:用以存储并处理音乐相关的函数及内容。
    创建一个Setting类:用以存储所有相关的宏定义及基础类的初始值。
    创建一个Timer类:用以分离时间相关的所有函数。
    创建一个GameWindow类:用以窗口类的相关函数以及窗口动画。

    //参数及类的设定(部分)
    enum BulletShootKind {None, Player_1 = PLAYER_SIGN, Player_2 = PLAYER_SIGN + 1, Camp, Other};   //障碍物标记
    enum BlastState { Blasting, BlastEnd, NotBlast };//坦克状态
    enum TANK_KIND { PROP, COMMON };    //坦克类型
    enum Star_State {Star_Timing,Star_Failed,Star_Out,Star_Showing,Star_Stop,Tank_Out};
    
    struct BoxMarkStruct
    {
    	int box_8[26][26];			
    	int box_4[52][52];			// 墙击中标记
    	int bullet_4[52][52];		// 子弹标记
    };
    
    struct BulletUnit
    {
    	int x, y;					// 子弹坐标
    	int dir;					// 子弹方向
    	int speed[4];				// 子弹速度
    	int mKillId;				// 玩家或者电脑坦克的数字标记
    	static IMAGE mBulletImage[4];		// 图片
    	static int mBulletSize[4][2];		
    	static int devto_tank[4][2];		
    	static int devto_head[4][2];		
    	static int bomb_center_dev[4][2];	
    };
    
    struct BombStruct
    {
    	static IMAGE mBombImage[3];				// 子弹爆炸图
    	int mBombX, mBombY;						// 爆炸点坐标
    	bool canBomb;							
    	int counter;					     	// 计数器
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    类的继承与关系流程图(逻辑概念)
    在这里插入图片描述

    六 、部分重要代码及功能分析

    1.绘制坦克

    (1)加载图片
    采用嵌套循环加载玩家坦克图片并提前对图片资源进行处理,一开始加载图片的尺寸比原始尺寸大了几倍,所以每次对图片处理需要更多的CPU资源,所以缩小图片加载到内存中的尺寸,并减小占用的空间,这样处理之后可使CPU占用率从24%降到7%左右。

    在这里插入图片描述
    在这里插入图片描述

    TankUnit::TankUnit(byte player, byte level)
    {
    	switch(player)
    	{
    	case 1:
    		{
    			for ( int i = 0; i < 4; i++ )
    			{
    				_stprintf_s(c, L"./res/%dPlayer/m%d-%d-1.gif", player, level, i);
    				loadimage(&CPTankPic[i][0], c);
    				_stprintf_s(c, L"./res/%dPlayer/m%d-%d-2.gif", player, level, i );
    				loadimage(&CPTankPic[i][1], c);
    			}
    		}
    		break;
    	default:
    		throw _T("数值越界, TankUnit.cpp-> TankUnit construct function");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (2)绘制坦克
    采用EaxyX系统函数,绘制图像,需要在绘图界面进行两次绘图。第一次,用遮罩图和背景进行按位与的操作,第二次,用真正坦克图和背景图进行按位或的操作。在移动之前应该把原来的坦克图像去除掉。背景颜色的更换,在EasyX资料中提到需要用cleardevice()函数和setbkcolor()函数进行配合。可通过这两函数对背景颜色进行更换。

    void putimage(
    	int dstX,	// 绘制位置的 x 坐标
    	int dstY,	// 绘制位置的 y 坐标
    	int dstWidth,	// 绘制的宽度
    	int dstHeight,	// 绘制的高度
    	IMAGE *pSrcImg,	// 要绘制的 IMAGE 对象指针
    	int srcX,	// 绘制内容在 IMAGE 对象中的左上角 x 坐标
    	int srcY,	// 绘制内容在 IMAGE 对象中的左上角 y 坐标
    	DWORD dwRop = SRCCOPY	// 三元光栅操作码);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.绘制地图

    地图数据利用map-res.cpp提前存储

    //这里仅展示了第1关与第35关的地图信息
    //一共设计了35关卡,所以一共有35张地图
    class MAP
    {
    public:
    	MAP() {}
    	~MAP() {}
    	
    	void map_1();
    	void map_2();
    	void map_3();
    	......
    }
    void MAP::map_1()
    {
    
    	char s[26][27] = {
    		"00000000000000000000000000",
    		"00000000000000000000000000",
    		"00330033003300330033003300",
    		"00330033003300330033003300",
    		"00330033003300330033003300",
    		"00330033003300330033003300",
    		"00330033003355330033003300",
    		"00330033003355330033003300",
    		"00330033003300330033003300",
    		"00330033000000000033003300",
    		"00330033000000000033003300",
    		"00000000003300330000000000",
    		"00000000003300330000000000",
    		"33003333000000000033330033",
    		"55003333000000000033330055",
    		"00000000003300330000000000",
    		"00000000003333330000000000",
    		"00330033003333330033003300",
    		"00330033003300330033003300",
    		"00330033003300330033003300",
    		"00330033003300330033003300",
    		"00330033000000000033003300",
    		"00330033000000000033003300",
    		"00330033000333300033003300",
    		"00000000000300300000000000",
    		"00000000000300300000000000" };
    	for (int i = 0; i < 26; i++)
    	{
    		strcpy_s(buf[i], s[i]);
    	}
    }
    
    void MAP::map_35()
    {
    
    	char s[26][27] = { "00000000000000000000000000",
    	"00000000000000000000000000",
    	"00000000330033000000000000",
    	"00000000330033000000000000",
    	"11000011331133110000110000",
    	"11000011331133110000110000",
    	"33111133333333331111331100",
    	"33111133333333331111331100",
    	"33333333553355333333331100",
    	"33333333553355333333331100",
    	"44444433333333334444441100",
    	"44444433333333334444441100",
    	"44333333333333333333444411",
    	"44333333333333333333444411",
    	"33333344333333443333331111",
    	"33333344333333443333331111",
    	"33334444443344444433334444",
    	"33334444443344444433334444",
    	"11444411111111114444114411",
    	"11444411111111114444114411",
    	"00111100000000001111001100",
    	"00111100000000001111001100",
    	"00000000000000000000000000",
    	"00000000000333300000000000",
    	"00000000000300300000000000",
    	"00000000000300300000000000"};
    	for (int i = 0; i < 26; i++)
    	{
    		strcpy_s(buf[i], s[i]);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    初次绘制之后,地图显示的墙、草、水和铁并无区别,在初次绘制的基础上,进行加工,采用多次绘图的方式,使得坦克在穿过草丛时显示在草丛下面,而在经过冰面时显示在冰面上面。

    // 森林地图展示
    		for (int i = 0; i < 26; i++)
    		{
    			for (int j = 0; j < 26; j++)
    			{
    				x = j * BOX_SIZE;
    				y = i * BOX_SIZE;
    				if (CPBoxMarkUnit->box_8[i][j] == _FOREST)
    					TransparentBlt(CPCenter_HDC, x, y, BOX_SIZE, BOX_SIZE, GetImageHDC(&ForestPic), 0, 0, BOX_SIZE, BOX_SIZE, 0x000000);
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.扩充坦克类

    (1)移动
    a.怎样将他的坐标设置为持续变化的状态?
    先用一个变量保存坦克的坐标,然后对这个变量进行修改,让他的x或者y坐标进行变化,再传输到P1这个对象当中。此时还得防止坦克运动过快的问题,只需加上一个Sleep函数,让每次执行循环一次之后休眠一段时间sleep的休眠时间可以根据想要效果进行更改。

    b.初次玩家坦克移动实现了玩家通过WSAD控制玩家坦克的移动,但此时出现了一个问题,在玩家同时按下WA或者WD等时玩家坦克会出现斜着走的问题,为了解决这一问题,增加了键盘上的按键监听,使得每次只能按下一个方向的按键。

    if (GetAsyncKeyState('A') & 0x8000)
    		{
    			if (PLOn && CPTankDir == DIR_LEFT )
    			{
    				PLAiMove = true;
    				PLAiCount = 0;
    				PLRandCount = rand() % 8 + 7;
    			}
    			PLMoving = true;
    			Move(DIR_LEFT);
    		}
    		else if (GetAsyncKeyState('W') & 0x8000)
    		{
    			if (PLOn && CPTankDir == DIR_UP)
    			{
    				PLAiMove = true;
    				PLAiCount = 0;
    				PLRandCount = rand() % 8 + 7;
    			}
    			PLMoving = true;
    			Move(DIR_UP);
    		}
    		else if (GetAsyncKeyState('D') & 0x8000)
    		{
    			if (PLOn && CPTankDir == DIR_RIGHT)
    			{
    				PLAiMove = true;
    				PLAiCount = 0;
    				PLRandCount = rand() % 8 + 7;
    			}
    			PLMoving = true;
    			Move(DIR_RIGHT);
    		}
    		else if (GetAsyncKeyState('S') & 0x8000)
    		{
    			if (PLOn && CPTankDir == DIR_DOWN)
    			{
    				PLAiMove = true;
    				PLAiCount = 0;
    				PLRandCount = rand() % 8 + 7;
    			}
    			PLMoving = true;
    			Move(DIR_DOWN);
    		}
    		else if (PLMoving)
    		{
    			PLMoving = false;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    c.电脑坦克的AI移动,在电脑坦克移动过程中,需要重新对电脑坦克的方向做出设定。

    if (unit1->renewXYPos())
    {
    		ctrl(*unit1, map,*unit2);
    				
    }
    if (unit2->renewXYPos())
    {
    static int i= rand() % 4 ;
    	if (!(rand() % 40)) {
    	i= rand() % 4 ;
    	ctrlpc(*unit2, map, i,*unit1);
    	}
    else {
    	ctrlpc(*unit2, map, i,*unit1);
    		}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    d.电脑坦克的AI移动采用随机数控制,但是采用简单的随机数控制时,有时坦克会一直撞墙,而不会产生方向上的改变,在随机数的基础上新增一些函数,使得坦克在碰撞时会增大改变方向的几率,与此同时增加了回头开炮函数,增大了电脑坦克回头开炮的几率。

    // 原左右变上下方向
    		if (CPTankDir == DIR_LEFT || CPTankDir == DIR_RIGHT)
    		{
    			if (PLTankX > (PLTankX / BOX_SIZE) * BOX_SIZE + BOX_SIZE / 2 - 1)	
    				PLTankX = (PLTankX / BOX_SIZE + 1) * BOX_SIZE;
    			else
    				PLTankX = (PLTankX / BOX_SIZE) * BOX_SIZE;	
    		}
    		// 上下变左右
    		else
    		{
    			if (PLTankY > (PLTankY / BOX_SIZE) * BOX_SIZE + BOX_SIZE / 2 - 1)	
    				PLTankY = (PLTankY / BOX_SIZE + 1) * BOX_SIZE;
    			else
    				PLTankY = (PLTankY / BOX_SIZE) * BOX_SIZE;					
    		}
    
    		// 更改方向
    CPTankDir = new_dir;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (2)碰撞
    a.增加碰撞检测函数,用以检测玩家与地图之间,玩家和电脑坦克之间,电脑坦克和电脑坦克之间的碰撞。

    bool TankUnit::ifTouch(const Map& map)const
    {
    	Pos_RC curPos[2] = { GetMapPos(),GetMapPos() };//获取当前行列坐标,有两个碰撞判定点
    	switch (GetDirection())
    	{
    	case DIR_UP:
    		curPos[0].row--;
    		curPos[1].row--;
    		curPos[1].col++;
    		break;
    	case DIR_LEFT:
    		curPos[0].col--;
    		curPos[1].col--;
    		curPos[1].row++;
    		break;
    	case DIR_DOWN:
    		curPos[0].row += 2;
    		curPos[1].row += 2;
    		curPos[1].col++;
    		break;
    	case DIR_RIGHT:
    		curPos[0].col += 2;
    		curPos[1].col += 2;
    		curPos[1].row++;
    		break;
    	default:
    		break;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    b.在测试中发现,坦克与地图之间的碰撞是正确的,但是如果有两个坦克,他们都是运动的,两个坦克的碰撞会发生偏差,其中一个坦克会嵌入另一个坦克中一部分,这是因为坦克碰撞的坐标计算错误,不能运用中心坐标,应该增加运用多个边缘坐标。

    // 四个方向坦克中心点
    int dev[4][2][2] = { {{-1,-1},{0,-1}},  {{-1,-1},{-1,0}},  {{-1,1},{0,1}},{ {1,-1},{1,0}} };
    //障碍物格子检测
    int temp1 = bms->box_8[index_i + dev[CPTankDir][0][0]][index_j + dev[CPTankDir][0][1]];
    int temp2 = bms->box_8[index_i + dev[CPTankDir][1][0]][index_j + dev[CPTankDir][1][1]];
    //下标偏移量
    int  dev_4[4][4][2] = { {{-2,-2},{1,-2},{-1,-2},{0,-2}}, {{-2,-2},{-2,1},{-2,-1},{-2,0}},{{-2,2},{1,2},{-1,2},{0,2}}, {{2,-2},{2,1},{2,-1},{2,0}} };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    在这里插入图片描述
    (3)子弹
    设置玩家和电脑的子弹等级都分为四种情况,不同等级的坦克子弹的打击效果不同,子弹打击墙块,四级玩家坦克打击铁块,其他等级玩家坦克打击铁块,玩家打击电脑,电脑打击玩家,测试结果均正确。

    //子弹类
    struct BulletUnit
    {
    	int x, y;				// 子弹坐标
    	int dir;					// 子弹方向
    	int speed[4];				// 子弹速度
    	int mKillId;				// 玩家或者电脑坦克的数字标记
    	static IMAGE mBulletImage[4];		// 图片
    	static int mBulletSize[4][2];		
    	static int devto_tank[4][2];		
    	static int devto_head[4][2];			
    };
    //子弹移动与碰撞检测
    BulletShootKind ComputerUnit::MoveBullet()
    {
    	if (CPBulletUnit.x == SHOOTABLE_X || !CPBulletTimer.TimeOver() )
    		return BulletShootKind::None;
    	BulletShootKind result = CheckBomb();
    	switch (result)
    	{
    	case BulletShootKind::Camp:
    	case BulletShootKind::Other:
    	case BulletShootKind::Player_1:
    	case BulletShootKind::Player_2:
    		return result;
    	}
    	int dir = CPBulletUnit.dir;
    	SignBullet(CPBulletUnit.x, CPBulletUnit.y, dir, _EMPTY );
    	CPBulletUnit.x = CPBulletUnit.x + CPBulletUnit.speed[CPLevel] * CPXY[dir][0];
    	CPBulletUnit.y = CPBulletUnit.y + CPBulletUnit.speed[CPLevel] * CPXY[dir][1];
    	SignBullet(CPBulletUnit.x, CPBulletUnit.y, dir, CP_BUTTLE_SIGN );
    	return BulletShootKind::None;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    4.电脑坦克小图标

    在窗口的右侧部分显示剩余敌军坦克数量,并且每当新出现一个电脑坦克,该小图标就减少一个。

    int x[2] = {233, 241};
    int n, index;
    for ( int i = 0; i < RemainCPTankNum; i++ )
    {
    	n = i / 2;
    	index = i % 2;
    	TransparentBlt( CPPic_HDC, x[index], 19 + n * 8, ENEMY_TANK_ICO_SIZE, ENEMY_TANK_ICO_SIZE, 
    			GetImageHDC(&CPTankIcoPic), 0, 0, ENEMY_TANK_ICO_SIZE, ENEMY_TANK_ICO_SIZE, 0xffffff );	
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    5.出生星星

    在电脑玩家新生成一个的时候,会先显示1s的出生星星然后再显示电脑坦克。

    // 电脑坦克出生并伴有星星
    Star_State ComputerUnit::Opening(int& renumber,const HDC& center_hdc)
    {
    
    
    	Star_State result = CPStar.EnemyShowStar(center_hdc, CPX, CPY, bms);
    	switch (result)
    	{
    		
    		case Star_State::Star_Out:
    			Grid_4(CPX, CPY, STAR_SIGN);     // 出生星星星开始出现
    			break;
    		case Star_State::Star_Showing:       // 出生星星正在出现
    			break;
    		case Star_State::Star_Stop:           // 出生星星消失
    			CPNumber = TOTAL_ENEMY_NUMBER - renumber;
    			renumber -= 1;
    			Grid_4(CPX, CPY, COMPUTER_SIGN + 1000 * CPLevel + 100 * CPKind + CPNumber);
    			break;
    	}
    	return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    6.分数面板

    要区分每种电脑坦克,记录不同的分数值,且要在每个电脑坦克被玩家击中后实时记录分数请况。

    // 如果胜利跳转到分数面板
    void Settings::IFWin()
    {
    	if (WWin && WWinCount++ > 210 && !GameOverFlag && ScorePanel == false)
    	{
    		//Music::PauseBk(true);
    		ScorePanel = true;
    		for (list<PlayerUnit*>::iterator itor = PlayerList.begin(); itor != PlayerList.end(); itor++)
    		{
    			(*itor)->ResetScorePanelData(PlayerList.size(), GameNowStage);
    		}
    	}
    }
    // 每行分数依次显示
    	for (int i = 0; i < 4; i++)
    	{
    		// 当前显示的行
    		if (cur_line == i)
    		{
    			int temp = kill_num2[i] + 1;
    
    			if (temp <= kill_num[i])
    			{
    				kill_num2[i]++;
    				Music::MicPlay(Mic_SCOREPANEL_DI);
    				break;
    			}
    			else
    			{
    				line_done_flag[player_id] = true;
    				bool temp = true;
    				for (int m = 0; m < player_num; m++)
    				{
    					if (line_done_flag[m] == false)
    					{
    						temp = false;
    						break;
    					}
    				}
    				if (temp)
    				{
    					cur_line++; 
    					for (int m = 0; m < player_num; m++)
    						line_done_flag[m] = false;
    				}
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述

    七、源码

    项目整体采用C++编写,编译器为VS2019,需要下载easyx库:源码(全部代码)

  • 相关阅读:
    Leetcode刷题详解——使用最小花费爬楼梯
    【板栗糖GIS】global mapper 如何通过dsm批量制作贴地等高线
    2024“钉耙编程”中国大学生算法设计超级联赛(7)
    ffmpeg常用命令
    XLSX.utils.sheet_to_json()解析excel,给空的单元格赋值为空字符串
    【原创】虚拟化技术及实时虚拟化概述
    2022年最新山东交安安全员模拟真题及答案
    Python网络爬虫4-实战爬取pdf
    派派派森03
    SQL语言---视图操作
  • 原文地址:https://blog.csdn.net/qq_44757221/article/details/126039705