基于easyx库的C/C++游戏编程实例-植物大战僵尸

植物大战僵尸游戏设计

1.设计物体结构

植物:

struct zhiwu
{
	int type;//0无,1第一种植物,...
	int x, y;
	int frameIndex;//当前植物序列帧的序号
	bool catched;//是否被僵尸捕获
	int deadTimer;//死亡计数器,被吃时+1
	int timer;//产生阳光时间
	int shootTimer;//子弹发射延迟一段时间,避免重叠
}map[ROW][COL];

僵尸:

struct zm
{
	int x, y;//僵尸移动过程中的位置
	int row;
	int frameindex;//当前僵尸序列帧的序号
	bool live;//是否被使用
	int speed;
	int hp;
	bool dead;//是否死亡,是死亡动画的判断条件
	bool eating;//是否正在吃食物
//	int attack;
}zm[ZM_NUM];

阳光:

struct sunshine
{
	int x, y;//阳光移动过程中的位置
	int frameindex;//当前阳光序列帧的序号
	int destY;//飘落的y坐标终点
	bool flutter;//向日葵生产后是否已经向上飘到一定位置
	bool flag=false;//是否被点击
	int timer;//存在时间
	float xoff,yoff;//阳光收回时的偏移量
	int status;
}suns[SUN_NUM];

子弹:

struct bullet
{
	int x, y;//子弹移动过程中的位置
	int row;
	bool live;//是否被使用
	int speed;
	bool blast;//是否爆炸,是爆炸动画的判断条件
	int harm;//伤害值
	int frameindex;

}bul[BULLET_NUM];
2.游戏初始化
void GameInit()
{
	//初始化为0,避免野指针
	memset(imgPlants, 0, sizeof(imgPlants));
	memset(map, 0, sizeof(map));
	memset(suns, 0, sizeof(suns));
	memset(zm, 0, sizeof(zm));
	memset(bul, 0, sizeof(bul));
	//游戏状态,胜利判定条件
	killCnt = 0;
	zmCnt = 0;
	gameStatus = GOING;
	//光标不选中
	curPlant = 0;
	//卡牌栏
	loadimage(&imgBar, "res/toolbar.png"); 
	loadimage(&imgBarWithoutShovel, "res/toolbarWithoutShovel.png");
	loadimage(&imgShovel, "res/shovel.png");
	//植物卡牌和卡牌上的植物
	char name[64] = { 0 };
	for (int i = 0; i < P_num; i++)
	{
		sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);
		loadimage(&imgCards[i], name);
		for (int j = 0; j < P_DYNAMIC_MAX; j++)
		{
			sprintf_s(name, sizeof(name), "res/zhiwu/%d/%d.png", i, j + 1);
			if (fileExist(name))
			{
				imgPlants[i][j] = new IMAGE;
				loadimage(imgPlants[i][j], name);
			}
			else
			{
				break;
			}
		}
	}
	//阳光
	for (int i = 0; i < S_DYNAMIC_MAX; i++)
	{
		sprintf_s(name, sizeof(name), "res/sunshine/%d.png", i + 1);
		loadimage(&imgSunshine[i], name);
	}
	//僵尸行走
	for (int i = 0; i < ZM_DYNAMIC_MAX; i++)
	{
		sprintf_s(name, sizeof(name), "res/zm/%d.png", i + 1);
		loadimage(&imgZM[i], name);
	}
	//僵尸死亡
	for (int i = 0; i < ZM_DEAD_DYNAMIC_MAX; i++)
	{
		sprintf_s(name, sizeof(name), "res/zm_dead/ZombieDust/%d.png", i + 1);
		loadimage(&imgZMDead[i], name);
	}
	//僵尸吃植物
	for (int i = 0; i < ZM_EAT_DYNAMIC_MAX; i++)
	{
		sprintf_s(name, sizeof(name), "res/zm_eat/%d.png", i + 1);
		loadimage(&imgZMEat[i], name);
	}
	//子弹
	loadimage(&imgBulNormal, "res/bullets/bullet_normal.png");
	loadimage(&imgBulBlast[BUL_BLAST_MAX-1], "res/bullets/bullet_blast.png");
	for (int i = 0; i < BUL_BLAST_MAX - 1; i++)
	{
		float k = (i + 1) * 0.2;
		loadimage(&imgBulBlast[i], "res/bullets/bullet_blast.png",
			imgBulBlast[BUL_BLAST_MAX - 1].getwidth() * k,
			imgBulBlast[BUL_BLAST_MAX - 1].getheight()*k,true);
	}

	//设置阳光值和字体
	sunScore = INIT_SUN_SCORE;
	LOGFONT f;
	gettextstyle(&f);
	f.lfHeight = 30;
	f.lfWeight = 15;
	strcpy(f.lfFaceName, "SEgoe UI Black");
	f.lfQuality = ANTIALIASED_QUALITY;//抗锯齿效果
	settextstyle(&f);
	setbkmode(TRANSPARENT);//设置透明背景
	settextcolor(BLACK);
}
3.刻画界面
void DrawGame()
{
	BeginBatchDraw();
	//游戏背景
	putimage(-120, 0, &imgBg);
	//卡牌栏
	if (curPlant == -1)
	{
		transparentimage2(NULL, 250 - 120, 0, &imgBarWithoutShovel);
	}
	else
	{
		transparentimage2(NULL, 250 - 120, 0, &imgBar);
	}
	//阳光值
	char scoreText[8];
	sprintf_s(scoreText, sizeof(scoreText), "%d", sunScore);
	outtextxy(276- 120, 67, scoreText);
	//卡牌
	for (int i = 0; i < P_num; i++)
	{
		transparentimage2(NULL, 338- 120 +i*64, 6, &imgCards[i]);
	}
	//渲染植物,放在移动中的植物光标前面,避免覆盖移动中的植物
	drawPlant();
	//渲染僵尸
	drawZM();
	//子弹
	drawBullet();
	//阳光
	drawSun();
	//移动中的植物
	if (curPlant>0)
	{
		IMAGE* img = imgPlants[curPlant - 1][0];
		int x = curX - img->getwidth() / 2;
		int y = curY - img->getheight() / 2;
		transparentimage2(NULL, x, y, img);
	}
	//移动中的铲子
	else if (curPlant == -1)
	{
		int x = curX - imgShovel.getwidth() / 2;
		int y = curY - imgShovel.getheight() / 2;
		transparentimage2(NULL, x, y, &imgShovel);
	}

	EndBatchDraw();
}

其中包括:

刻画植物:

void drawPlant()
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (map[i][j].type>0)
			{
				int P_type = map[i][j].type - 1;
				int index = map[i][j].frameIndex;
				transparentimage2(NULL, map[i][j].x, map[i][j].y, imgPlants[P_type][index]);
			}
		}
	}
}

刻画僵尸:

void drawZM()
{
	int zmMax = sizeof(zm) / sizeof(zm[0]);
	for (int i = 0; i < zmMax; i++)
	{
		if (zm[i].live)
		{
			//IMAGE* img =zm[i].dead ? &imgZMDead[zm[i].frameindex] : &imgZM[zm[i].frameindex];
			//IMAGE* img = zm[i].dead ? imgZMDead:imgZM;
			IMAGE* img = NULL;
			if (zm[i].dead)img = imgZMDead;
			else if (zm[i].eating)img = imgZMEat;
			else img = imgZM;

			img += zm[i].frameindex;
			transparentimage2(NULL, zm[i].x, zm[i].y - img->getheight(), img);
		}
	}
}

刻画阳光:

void drawSun()
{
	int sunMax = sizeof(suns) / sizeof(suns[0]);
	for (int i = 0; i < sunMax; i++)
	{
		if (suns[i].flag)
		{
			IMAGE* img = &imgSunshine[suns[i].frameindex];
			transparentimage2(NULL, suns[i].x, suns[i].y, img);
		}
	}
}

刻画子弹:

void drawBullet()
{
	int bulMax = sizeof(bul) / sizeof(bul[0]);
	for (int k = 0; k < bulMax; k++)
	{
		if (bul[k].live)
		{
			if (bul[k].blast)
			{
				IMAGE* img = &imgBulBlast[bul[k].frameindex];
				transparentimage2(NULL, bul[k].x, bul[k].y, img);
			}
			else
			{
				transparentimage2(NULL, bul[k].x, bul[k].y, &imgBulNormal);
			}
		}
	}
}
4.创造阳光机制
void CreateSunshine()
{
	//随机掉落阳光
	static int count = 0;
	count++;
	if (count > 200 + rand() % 200)
	{
		count = 0;
		int sunMax = sizeof(suns) / sizeof(suns[0]);
		int i;
		for (i = 0; i < sunMax && suns[i].flag; i++);
		if (i >= sunMax)	return;
		//对没用过的初始化为0
		memset(&suns[i], 0, sizeof(suns[i]));
		suns[i].status = SUN_DOWN;
		suns[i].flag = true;
		suns[i].x = 260-120 + rand() % (900 -100- 260+120);
		suns[i].y = 30;
		int t=rand() % 6;
		suns[i].destY = t==5?650:110 + t * 90;
	}
	//向日葵产生阳光
	int sunMax = sizeof(suns) / sizeof(suns[0]);
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (map[i][j].type - 1 == xiangrikui)
			{
				map[i][j].timer++;
				if (map[i][j].timer > XIANGRIKUI_TIMER)
				{
					map[i][j].timer=0;
					int k;
					for (k = 0; k < sunMax && suns[k].flag; k++);
					if (k >= sunMax)return;
					//对没用过的初始化为0
					memset(&suns[k], 0, sizeof(suns[k]));
					suns[k].status = SUN_PRODUCE;
					suns[k].flag = true;
					suns[k].x = map[i][j].x+rand()%31*(rand()%2?1:-1);
					suns[k].y = map[i][j].y-50;
					suns[k].destY = rand() % 21+20+ map[i][j].y + imgPlants[xiangrikui][0]->getheight() -imgSunshine[0].getheight();//xiangrikui或map[i][j].type-1
				}
			}
		}
	}
}
5.阳光收集机制
void CollectSunshine(ExMessage* msg)
{
	int sunMax = sizeof(suns) / sizeof(suns[0]);
	int w = imgSunshine[0].getwidth();
	int h = imgSunshine[0].getheight();
	for (int i = 0; i < sunMax; i++)
	{
		if (suns[i].flag&&suns[i].status!= SUN_COLLECT)
		{
			int x = suns[i].x;
			int y = suns[i].y;
			if (msg->x > x && msg->x<x + w
				&& msg->y >y && msg->y < y + h)
			{
				//mciSendString("play res/collectSunshine.wav", 0, 0, 0);//连续点多次要等播放完了才能再播放,不能从头播放,有延迟
				PlaySound("res/collectSunshine.wav", NULL, SND_FILENAME | SND_ASYNC);//只支持wav格式
				float destY = 0;
				float destX = 262- 120;
				float angle = atan((y - destY) / (x - destX));
				suns[i].xoff = COLLECT_SUN_SPEED * cos(angle);
				suns[i].yoff = COLLECT_SUN_SPEED * sin(angle);
				suns[i].status = SUN_COLLECT;
			}
		}

	}
}
6.使用鼠标控制种植、铲除植物以及收集阳光的设计
void CreatePlant()
{
	ExMessage msg;
	static int status =0;
	if (peekmessage(&msg))
	{
		if (msg.message == WM_LBUTTONDOWN)
		{
			//选择植物
			if (msg.x > 338-120 && msg.x < 338-120 + 65 * P_num && msg.y < 96)
			{
				//卡牌
				int index = (msg.x - 338+120) / 65;
				//判断选择的植物种类
				switch (index)
				{
					case xiangrikui:
					{
						//判断能否种植
						if (sunScore >= xiangrikui_SunScore)
						{
							//状态标志
							status = 1;
							curPlant = index + 1;//1-向日葵
							curX = msg.x;
							curY = msg.y;
						}
						break;
					}
					case wandou:
					{
						if (sunScore >= wandou_SunScore)
						{
							//状态标志
							status = 1;
							curPlant = index + 1;//2-豌豆
							curX = msg.x;
							curY = msg.y;
						}
						break;
					}
					default:break;
				}
			}
			//选择铲子
			else if (msg.x > 338 - 120 + 65 * 8 + 20 && msg.x < 338 - 120 + 65 * 9 + 20 && msg.y < 85 && msg.y >17)
			{
				status = 1;
				curPlant = -1;
				curX = msg.x;
				curY = msg.y;
			}
			else//选择收集阳光
			{
				CollectSunshine(&msg);
			}
		}
		//移动选中的植物或铲子
		else if (msg.message == WM_MOUSEMOVE&& status == 1)
		{
			curX = msg.x;
			curY = msg.y;
		}
		//放置选中的植物或铲子
		else if (msg.message == WM_LBUTTONUP && status == 1)
		{
			//判断放置范围
			if (msg.x > 256-120&& msg.y> 90 && msg.y < 100 + 100 * ROW)
			{
				int r = (msg.y - 90) / 100;
				int c = (msg.x - 256+120) / 80;
				//判断位置是否为空,空可种植物
				if (map[r][c].type == 0&& curPlant>0)
				{
					//判断选择的植物种类来扣除相应的阳光值
					switch (curPlant-1)
					{
					case xiangrikui:
					{
						map[r][c].type = curPlant;
						map[r][c].x = 256 - 120 + c * 80;
						map[r][c].y = 90 + r * 100;
						map[r][c].frameIndex = 0;
						map[r][c].shootTimer = 0;
						sunScore -= xiangrikui_SunScore;
						break;
					}
					case wandou:
					{
						map[r][c].type = curPlant;
						map[r][c].x = 256 - 120 + c * 80;
						map[r][c].y = 90 + r * 100;
						map[r][c].frameIndex = 0;
						map[r][c].shootTimer = 0;
						sunScore -= wandou_SunScore;
						break;
					}
					default:break;
					}
				}
				//判断位置是否为空,不空可铲除植物
				else if (curPlant == -1&& map[r][c].type > 0)
				{
					//铲除植物
					map[r][c].type = 0;
					//判断僵尸是否在吃,吃则还原成行走状态
					int zmMax = sizeof(zm) / sizeof(zm[0]);
					for (int i = 0; i < zmMax; i++)
					{
						if (zm[i].live && !zm[i].dead)
						{
							if (r == zm[i].row && map[r][c].x + 10 < zm[i].x + 120 && map[r][c].x + 60 > zm[i].x + 80)
							{
								zm[i].eating = false;
								zm[i].speed = ZM_SPEED;
								zm[i].frameindex = 0;
							}
						}
					}					
				}
			}
			curPlant = 0;
			status = 0;
		}
	}

}
7.创造僵尸
void CreateZM()
{
	if (zmCnt >= ZM_MAX)return;
	static int zmFre = 100;
	static int count = 0;
	count++;
	if (count > zmFre)
	{
		count = 0;
		zmFre = 300 + rand() % 200;

		int zmMax = sizeof(zm) / sizeof(zm[0]);
		int i;
		for (i = 0; i < zmMax && zm[i].live; i++);
		if (i < zmMax)
		{
			//对没用过的初始化为0
			memset(&zm[i], 0, sizeof(zm[i]));
			zm[i].live = true;
			zm[i].x = WIN_WIDTH;
			zm[i].row =rand() % 5;
			zm[i].y = 72 + (1 + zm[i].row )* 100;
			zm[i].speed = ZM_SPEED;
			zm[i].hp = ZM_HP;
			zmCnt++;
		}
		
	}
}
8.发射子弹设计
void Shoot()
{
	//static int cnt = 0;
	//if (++cnt < 3)return;//让子弹产生更慢一点
	//cnt = 0;

	int zmMax = sizeof(zm) / sizeof(zm[0]);
	int bulMax = sizeof(bul) / sizeof(bul[0]);
	int dangerX = WIN_WIDTH - imgZM[0].getwidth()+120-40;
	//标记出现在范围内的僵尸
	/*int lines[ROW] = { 0 };
	for (int k = 0; k < zmMax; k++)
	{
		if (zm[k].live && zm[k].x < dangerX)
		{
			lines[zm[k].row] = 1;
		}
	}*/
	//对每只植物发射条件
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			for (int z = 0; z < zmMax; z++)
			{
				//判断发射条件
				if (map[i][j].type - 1 == wandou && zm[z].live && zm[z].row == i
					&& zm[z].x < dangerX && zm[z].x + 85 > map[i][j].x)//&& lines[i])
				{
					map[i][j].shootTimer++;//延迟产生子弹
					if (map[i][j].shootTimer > BUL_SHOOTTIMER)
					{
						map[i][j].shootTimer = 0;
						int k;//产生子弹
						for (k = 0; k < bulMax && bul[k].live; k++);
						if (k < bulMax)
						{
							bul[k].row = i;
							bul[k].speed = BULLET_SPEED;
							bul[k].harm = BUL_HARM;
							bul[k].blast = false;
							bul[k].frameindex = 0;
							bul[k].live = true;

							bul[k].x = map[i][j].x + imgPlants[map[i][j].type - 1][0]->getwidth() - 5;
							bul[k].y = map[i][j].y + 5;
						}
					}
					break;//一旦出现一只僵尸,就发射子弹,不再判断改行的其他僵尸
				}
			}
		}
	}
}
9.游戏画面更新
void UpdateGame()
{
	//植物
	updatePlant();
	//阳光
	CreateSunshine();
	updateSunshine();
	//僵尸
	CreateZM();
	updateZM();
	//子弹
	Shoot();
	updateBullet();
	CheckCollision();
}

植物更新:

void updatePlant()
{
	static int count = 0;
	if (++count > P_TIMER)
	{
		count = 0;
		for (int i = 0; i < ROW; i++)
		{
			for (int j = 0; j < COL; j++)
			{
				if (map[i][j].type>0)
				{
					map[i][j].frameIndex++;
					int P_type = map[i][j].type - 1;
					int index = map[i][j].frameIndex;
					if (imgPlants[P_type][index] == NULL)
					{
						map[i][j].frameIndex = 0;
					}
				}
			}
		}
	}
}

阳光更新:

void updateSunshine()
{
	int sunMax = sizeof(suns) / sizeof(suns[0]);
	for (int i = 0; i < sunMax ; i++)
	{
		if (suns[i].flag)
		{
			suns[i].frameindex = (suns[i].frameindex + 1)% S_DYNAMIC_MAX;
			switch (suns[i].status)
			{
			case SUN_DOWN: 
			{
				suns[i].y += SUN_SPEED;
				if (suns[i].y >= suns[i].destY)
				{
					suns[i].status = SUN_GOUND;
					suns[i].timer = 0;
				}
				break;
			}
			case SUN_GOUND:
			{
				suns[i].timer++;
				if (suns[i].timer > SUN_TIMER)
				{
					suns[i].flag = false;
				}
				break;
			}
			case SUN_COLLECT:
			{
				suns[i].x -= suns[i].xoff;
				suns[i].y -= suns[i].yoff;
				if (suns[i].x < 262-120 || suns[i].y < 0)
				{
					suns[i].flag = false;
					sunScore += FALL_SUN_SCORE;
				}
				break;
			}
			case SUN_PRODUCE:
			{
				struct sunshine* s = &suns[i];
				if (!s->flutter)
				{
					s->y -= 2;
					if (s->y < s->destY-100)
					{
						s->flutter = true;
					}
				}
				else
				{					
					s->y += 2;
					if (s->y >= s->destY)
					{
						s->status = SUN_GOUND;
						s->timer = 0;
					}
				}
				break;
			}
			default:break;
			}
		}
	}
}

僵尸更新:

void updateZM()
{
	int zmMax = sizeof(zm) / sizeof(zm[0]);

	//static int count = 0;
	//count++;
	//if (count > 2)
	//{
	//	count = 0;
	//	for (int i = 0; i < zmMax; i++)
	//	{
	//		if (zm[i].live)
	//		{
	//			zm[i].x -= zm[i].speed;
	//			if (zm[i].x < 120)
	//			{
	//				MessageBox(NULL, "over", "over", 0);
	//				exit(0);
	//			}
	//		}
	//	}
	//}
	//static int count2 = 0;
	//count2++;
	//if (count2 > 4)
	//{
	//	count2 = 0;
	//	for (int i = 0; i < zmMax; i++)
	//	{
	//		if (zm[i].live)
	//		{
	//			zm[i].frameindex = (zm[i].frameindex + 1) % ZM_DYNAMIC_MAX;
	//		}
	//	}
	//}

	static int count = 0;
	count++;
	if (count > ZM_TIMER)//让僵尸移动慢一点
	{
		count = 0;
		for (int i = 0; i < zmMax; i++)
		{
			if (zm[i].live)
			{
				if (zm[i].dead)
				{
					zm[i].frameindex++;
					if (zm[i].frameindex >= ZM_DEAD_DYNAMIC_MAX)
					{
						zm[i].frameindex = 0;
						zm[i].live = false;
						killCnt++;
						if(killCnt==ZM_MAX)gameStatus = WIN;
					}
				}
				else if (zm[i].eating)
				{					
					//zm[i].speed = 0;
					zm[i].frameindex = (zm[i].frameindex + 1) % ZM_EAT_DYNAMIC_MAX;					
				}
				else
				{
					zm[i].frameindex = (zm[i].frameindex + 1) % ZM_DYNAMIC_MAX;
				}
				zm[i].x -= zm[i].speed;
				if (zm[i].x < 0)
				{
					gameStatus = FAIL;
				}
			}
		}
	}
}

子弹更新:

void updateBullet()
{
	int bulMax = sizeof(bul) / sizeof(bul[0]);
	for (int k = 0; k < bulMax; k++)
	{
		if (bul[k].live)
		{
			bul[k].x+= bul[k].speed;
			if (bul[k].x > WIN_WIDTH)
			{
				bul[k].live = false;
			}
			if (bul[k].blast)
			{
				bul[k].frameindex++;
				if (bul[k].frameindex >= 4)
				{
					bul[k].live = false;
				}
			}
		}
	}
}
10.碰撞检测机制
void CheckCollision()
{
	checkBul2ZM();
	checkP2Z();
					
}

子弹vs僵尸:

void checkBul2ZM()
{
	int bulMax = sizeof(bul) / sizeof(bul[0]);
	int zmMax = sizeof(zm) / sizeof(zm[0]);

	for (int k = 0; k < bulMax; k++)
	{
		if (bul[k].live && !bul[k].blast)
		{
			for (int i = 0; i < zmMax; i++)
			{
				if (zm[i].live && !zm[i].dead
					&& bul[k].row == zm[i].row
					&& bul[k].x > zm[i].x + 80 && bul[k].x < zm[i].x + 160)
				{
					bul[k].blast = true;
					bul[k].speed = 0;
					zm[i].hp -= bul[k].harm;
					if (zm[i].hp <= 0)
					{
						zm[i].dead = true;
						zm[i].speed = 0;
						zm[i].frameindex = 0;
					}
					break;//单体伤害
				}
			}
		}
	}
}

植物vs僵尸:

void checkP2Z()
{
	int zmMax = sizeof(zm) / sizeof(zm[0]);
	for (int i = 0; i < zmMax; i++)
	{
		//找可以为吃状态的僵尸
		if (zm[i].live && !zm[i].dead)
		{
			int r = zm[i].row;
			int k;
			for (k = 0; k < COL; k++)
			{
				//找有植物的位置
				if (map[r][k].type==0)continue;
				int px1 = map[r][k].x + 10;
				int px2 = map[r][k].x + 60;
				int zx1 = zm[i].x + 80;
				int zx2 = zm[i].x + 120;
				//判断该植物位置周围是否有僵尸
				if (zx1<px2 && zx2>px1)
				{
					if (!zm[i].eating)//非吃状态变为吃状态
					{
						map[r][k].deadTimer = 0;
						zm[i].eating = true;
						zm[i].speed = 0;
						zm[i].frameindex = 0;
					}
					else//吃状态
					{
						map[r][k].deadTimer++;
						//植物是否死亡
						if (map[r][k].deadTimer > P_HP)
						{
							//植物死亡
							map[r][k].deadTimer = 0;
							map[r][k].type = 0;
							zm[i].eating = false;
							zm[i].speed = ZM_SPEED;
							zm[i].frameindex = 0;
							//对周围在吃的僵尸转化为行走状态
							for (int j = 0; j < zmMax; j++)
							{
								if (j != i&& zm[i].row== zm[j].row&& abs(zm[i].x- zm[j].x)<=80)
								{
									zm[j].eating = false;
									zm[j].speed = ZM_SPEED;
									zm[j].frameindex = 0;
								}
							}
						}
					}
				}
			}
		}
	}
}
11.开始界面设计
void StartUI()
{
	//真随机数
	srand((unsigned int)time(NULL));
	//窗口
	initgraph(WIN_WIDTH, WIN_HEIGHT);

	IMAGE imgBg, imgMenu1, imgMenu2;
	loadimage(&imgBg, "res/menu.png");
	loadimage(&imgMenu1, "res/menu1.png");
	loadimage(&imgMenu2, "res/menu2.png");
	int flag = 0;
	
	while (1)
	{
		BeginBatchDraw();
		putimage(0, 0, &imgBg);
		transparentimage2(NULL,474, 75, flag ? &imgMenu2 : &imgMenu1);

		ExMessage msg;
		if (peekmessage(&msg))
		{
			if (msg.message == WM_LBUTTONDOWN
				&& msg.x > 474 && msg.x < 474 + 300
				&& msg.y>75 && msg.y < 75 + 140)
			{
				flag = 1;
			}
			else if (msg.message == WM_LBUTTONUP&&flag==1)
			{
				EndBatchDraw();
				break;
			}
		}
		EndBatchDraw();
	}
}
12.场景布置和移动
void ViewScence()
{
	//游戏背景
	loadimage(&imgBg, "res/bg.jpg");
	//僵尸站立
	char name[64];
	for (int i = 0; i < ZM_STAND_DYNAMIC_MAX; i++)
	{
		sprintf_s(name, sizeof(name), "res/zm_stand/%d.png", i + 1);
		loadimage(&imgZMStand[i], name);
	}
	//随机生成僵尸位置
	for (int k = 0; k < ZM_STAND_NUM; k++)
	{
		zm[k].x = imgBg.getwidth() - 400 + rand()%(400 - imgZMStand[0].getwidth());
		zm[k].y = 50 + rand() % (520- imgZMStand[0].getheight());
	}
	//让僵尸几乎不重叠
	while (1)
	{
		int i = 0;
		for (; i < ZM_STAND_NUM; i++)
		{
			bool flag = false;
			for (int k = i+1; k < ZM_STAND_NUM; k++)
			{
				if (zm[i].x + 50 > zm[k].x && zm[i].x < zm[k].x + 50
					&& zm[i].y + 80 > zm[k].y && zm[i].y < zm[k].y + 80)
				{
					zm[k].x = imgBg.getwidth() - 400 + rand() % (400 - imgZMStand[0].getwidth());
					zm[k].y = 50 + rand() % (520 - imgZMStand[0].getheight());
					flag = true;
				}
			}
			if (flag)break;
		}
		if (i == ZM_STAND_NUM)break;
	}

	//让僵尸站立变化(帧序列)不同
	int index[ZM_STAND_NUM];
	for (int i = 0; i < ZM_STAND_NUM; i++)
	{
		index[i] = rand() % ZM_STAND_DYNAMIC_MAX;
	}

	int cnt=0;//切换帧序列
	//场景右移
	int xMin = WIN_WIDTH - imgBg.getwidth();//900-1400
	for (int x = 0; x >= xMin; x-=2)
	{
		BeginBatchDraw();

		putimage(x, 0, &imgBg);
		cnt++;
		for (int k = 0; k < ZM_STAND_NUM; k++)
		{
			transparentimage2(NULL, zm[k].x+x, zm[k].y,&imgZMStand[index[k]]);
			if (cnt >= 10)
			{
				index[k] = (index[k]+1)% ZM_STAND_DYNAMIC_MAX;	//让每只僵尸站立变化(帧序列)不同
			}
		}
		if (cnt >= 10)cnt = 0;

		EndBatchDraw();
		Sleep(5);
	}
	//停留几秒
	for (int i = 0; i < 100; i++)
	{
		BeginBatchDraw();

		putimage(xMin, 0, &imgBg);
		cnt++;
		for (int k = 0; k < ZM_STAND_NUM; k++)
		{
			transparentimage2(NULL, zm[k].x + xMin, zm[k].y, &imgZMStand[index[k]]);
			if (cnt >= 10)
			{
				index[k] = (index[k] + 1) % ZM_STAND_DYNAMIC_MAX;	//让每只僵尸站立变化(帧序列)不同
			}
		}
		if (cnt >= 10)cnt = 0;
		EndBatchDraw();
		Sleep(5);
	}
	//场景左移
	for (int x = xMin; x < -120; x+=2)
	{
		BeginBatchDraw();

		putimage(x, 0, &imgBg);
		cnt++;
		for (int k = 0; k < ZM_STAND_NUM; k++)
		{
			transparentimage2(NULL, zm[k].x + x, zm[k].y, &imgZMStand[index[k]]);
			if (cnt >= 10)
			{
				index[k] = (index[k] + 1) % ZM_STAND_DYNAMIC_MAX;	//让每只僵尸站立变化(帧序列)不同
			}
		}
		if (cnt >= 10)cnt = 0;


		EndBatchDraw();
		Sleep(5);
	}
}
13.卡牌栏开始下拉+开始安放植物
void BarDown()
{
	//卡牌栏开始下拉
	int h = imgBar.getheight();
	for (int y = -h; y <= 0; y+=2)
	{
		BeginBatchDraw();

		putimage(-120, 0, &imgBg);
		transparentimage2(NULL, 250 - 120, y, &imgBar);

		for (int i = 0; i < P_num; i++)
		{
			transparentimage2(NULL, 338 - 120 + i * 64, 6+y, &imgCards[i]);
		}

		EndBatchDraw();
		Sleep(5);
	}

	//开始安放植物
	IMAGE imgReadySetPlant1, imgReadySetPlant2, imgReadySetPlant3;
	loadimage(&imgReadySetPlant1, "res/readySetPlant1.png");
	loadimage(&imgReadySetPlant2, "res/readySetPlant2.png");
	loadimage(&imgReadySetPlant3, "res/readySetPlant3.png");
	int t = 0;
	BeginBatchDraw();
	while (++t < 2500)
	{
		transparentimage2(NULL, (WIN_WIDTH - imgReadySetPlant1.getwidth()) / 2, (WIN_HEIGHT - imgReadySetPlant1.getheight()) / 2, &imgReadySetPlant1);
		FlushBatchDraw();
	}
	DrawGame();//刷新背景,去除刚刚加的贴图
	t = 0;
	while (++t < 2000)
	{
		transparentimage2(NULL, (WIN_WIDTH - imgReadySetPlant2.getwidth()) / 2, (WIN_HEIGHT - imgReadySetPlant2.getheight()) / 2, &imgReadySetPlant2);
		FlushBatchDraw();
	}
	DrawGame();//刷新背景,去除刚刚加的贴图
	t = 0;
	while (++t < 2200)
	{
		transparentimage2(NULL, (WIN_WIDTH - imgReadySetPlant3.getwidth()) / 2, (WIN_HEIGHT - imgReadySetPlant3.getheight()) / 2, &imgReadySetPlant3);
		FlushBatchDraw();
	}
	EndBatchDraw();
}
14.判断游戏结束条件
bool CheckOver()
{
	int ret = false;
	if (gameStatus == WIN)
	{
		Sleep(2000);
		loadimage(0, "res/gameWin.png");
		ret = true;
	}
	else if (gameStatus == FAIL)
	{

		Sleep(2000);
		loadimage(0, "res/gameFail.png");
		ret = true;
	}
	return ret;
}
15.执行
int main(void)
{
	//开始界面
	StartUI();
	//场景布置和移动
	ViewScence();
	//游戏初始化
	GameInit();
	//卡牌栏开始下拉+开始安放植物
	BarDown();
	int timer = 0;
	bool flag = true;
	while (1)
	{
		CreatePlant();
		timer += getDelay();
		if (timer > 10)
		{
			timer = 0;
			flag = true;
		}
		if (flag)
		{
			flag = false;
			DrawGame();
			UpdateGame();
			if (CheckOver())break;
		}
		
	}
	system("pause");
	return 0;
}

至此,一个植物大战僵尸的游戏设计已经完成,它是最基本的设计,接下来你可以继续添加植物和僵尸,设计关卡等,也可以增加玩法,提高趣味性。我在此基础上继续设计,添加了植物和僵尸,优化了画面等,最终的一个版本演示视频如下:

基于easyx库的C/C++游戏编程实例-植物大战僵尸

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/498167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm的bbs论坛系统

开发环境&#xff1a;idea 前端&#xff1a;JQueryBootstraplayui后端&#xff1a;SpringSpringMVCMybatis数据库&#xff1a;mysqlredis 基于ssm的bbs论坛系统&#xff0c;功能有论坛、导读、动态、排行榜以及后台管理系统等等 话不多说&#xff0c;看图&#xff01;&#x…

VTK 9.2.6 加 QT6 编译

上一篇的example编译VTK 9.2.6 源码和VTK Examples 编译 Visual Studio 2022 增加 VTK_GROUP_ENABLE_Qt 为yes 指定QT6-DIR的路径为 C:\Qt\6.6.3\mingw_64\lib\cmake\Qt6

Android room 在dao中不能使用挂起suspend 否则会报错

错误&#xff1a; Type of the parameter must be a class annotated with Entity or a collection/array of it. kotlin.coroutines.Continuation<? super kotlin.Unit> $completion); 首先大家检查一下几个点 一、kotlin-kapt 二、 是否引入了 room-ktx 我是2024年…

康耐视visionpro-CogCaliperTool工具详细说明

CogCaliperTool功能说明: 卡尺工具,用于测量距离 CogCaliperTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogCaliperTool ②.添加输入图像,右键“链接到”或以连线拖拽的方式选择相应输入源 ③.拖动屏幕上的矩形框到需要测量的位置。卡尺的搜索框角度与边缘不平…

C/C++ ③ —— C++11新特性

1. 类型推导 1.1 auto auto可以让编译器在编译期就推导出变量的类型 auto的使⽤必须⻢上初始化&#xff0c;否则⽆法推导出类型auto在⼀⾏定义多个变量时&#xff0c;各个变量的推导不能产⽣⼆义性&#xff0c;否则编译失败auto不能⽤作函数参数在类中auto不能⽤作⾮静态成员…

MYSQL数字函数:不可不知的数据处理利器

&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自&#xff1a;MYSQL数字函数&#xff1a;不可不知的数据处理利器 ✨​​​​​…

快速上手Spring Cloud 十五:与人工智能的智慧交融

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

使用pytorch构建一个无监督的深度卷积GAN网络模型

本文为此系列的第二篇DCGAN&#xff0c;上一篇为初级的GAN。普通GAN有训练不稳定、容易陷入局部最优等问题&#xff0c;DCGAN相对于普通GAN的优点是能够生成更加逼真、清晰的图像。 因为DCGAN是在GAN的基础上的改造&#xff0c;所以本篇只针对GAN的改造点进行讲解&#xff0c;其…

Pytorch的hook函数

hook函数是勾子函数&#xff0c;用于在不改变原始模型结构的情况下&#xff0c;注入一些新的代码用于调试和检验模型&#xff0c;常见的用法有保留非叶子结点的梯度数据&#xff08;Pytorch的非叶子节点的梯度数据在计算完毕之后就会被删除&#xff0c;访问的时候会显示为None&…

RegSeg 学习笔记(待完善)

论文阅读 解决的问题 引用别的论文的内容 可以用 controlf 寻找想要的内容 PPM 空间金字塔池化改进 SPP / SPPF / SimSPPF / ASPP / RFB / SPPCSPC / SPPFCSPC / SPPELAN &#xfffc; ASPP STDC&#xff1a;short-term dense concatenate module 和 DDRNet SE-ResNeXt …

快速入门Axure RP:解答4个关键问题!

软件Axure RP 是一种功能强大的设计工具&#xff0c;用于使用 Web、移动和桌面应用程序项目创建交互原型。Axure RP软件中的 RP代表快速原型制作&#xff0c;这是软件Axure RP的核心特征。用户使用Axurere RP软件可以快速地将简单的想法创建成线框图和原型。Axure 因此&#xf…

实时数仓之实时数仓架构(Hudi)

目前比较流行的实时数仓架构有两类&#xff0c;其中一类是以FlinkDoris为核心的实时数仓架构方案&#xff1b;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍&#xff0c;这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…

【二叉树】Leetcode 98. 验证二叉搜索树【中等】

验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例1&a…

【Python函数和类2/6】函数的参数

目录 目标 为函数设置参数 传递实参 关键字实参 关键字实参的顺序 位置实参 常见错误 缺少实参 位置实参的顺序 默认值形参 参数的优先级 默认值形参的位置 总结 目标 上篇博客中&#xff0c;我们在定义函数时&#xff0c;使用了空的括号。这表示它不需要任何信息就…

浅谈C语言编译与链接

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 翻译环境和运行环境 在ANSI C&#xff08;标准 C&#xff09;的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个…

ssh 公私钥(github)

一、生成ssh公私钥 生成自定义名称的SSH公钥和私钥对&#xff0c;需要使用ssh-keygen命令&#xff0c;这是大多数Linux和Unix系统自带的标准工具。下面&#xff0c;简单展示如何使用ssh-keygen命令来生成具有自定义名称的SSH密钥对。 步骤 1: 打开终端 首先&#xff0c;打开我…

增强现实(AR)和虚拟现实(VR)营销的未来:沉浸式体验和品牌参与

--- 如何将AR和VR技术应用于营销&#xff0c;以提高品牌知名度、客户参与度 增强现实&#xff08;AR&#xff09;和虚拟现实&#xff08;VR&#xff09;不再只是游戏。这些技术为品牌与受众互动提供了创新的方式。营销人员可以创造更好的客户体验&#xff0c;并为身临其境的故…

hadoop-3.1.1分布式搭建与常用命令

一、准备工作 1.首先需要三台虚拟机&#xff1a; master 、 node1 、 node2 2.时间同步 ntpdate ntp.aliyun.com 3.调整时区 cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 4.jdk1.8 java -version 5.修改主机名 三台分别执行 vim /etc/hostname 并将内容指定为…

电脑突然死机怎么办?

死机是电脑常见的故障问题&#xff0c;尤其是对于老式电脑来说&#xff0c;一言不合电脑画面就静止了&#xff0c;最后只能强制关机重启。那么你一定想知道是什么原因造成的吧&#xff0c;一般散热不良最容易让电脑死机&#xff0c;还有系统故障&#xff0c;比如不小心误删了系…

【实现报告】学生信息管理系统(顺序表)

目录 实验一 线性表的基本操作 一、实验目的 二、实验内容 三、实验提示 四、实验要求 五、实验代码如下&#xff1a; &#xff08;一&#xff09;顺序表的构建及初始化 &#xff08;二&#xff09;检查顺序表是否需要扩容 &#xff08;三&#xff09;根据指定学生个…
最新文章