分享做植物大战僵尸经验以及部分改进和新增


Rock老师的视频在这里

此博客用来记录博主在跟rock老师学习时的收获,具体写请看roke老师的视频,如果想在自己添加一些功能可以看博主的来了解一些,同时因为博主是初学者,希望大佬指正。

1.改进    

改进2个僵尸不能吃同一个植物同时保证2个僵尸吃植物是伤害全部算到,这个我使用的是在结构体zhiwu上添加血量而不是用roke老师的时间判断植物是否被吃死,把设置植物的血量与设置僵尸血量相似代码如下,其他结构体的变量可忽略。

struct zhiwu                                
{
	int type;                             
	int frameIndex;                       

	bool catched;          //判断植物是否被吃
	int blood;             //植物的血量
    int timer;                           
	int x, y;
    int shootTime;
    int planttime;
    bool wait;
};
struct zm                               
{
	int x,y;                            
	int frameIndex;                            
	bool used;
	int speed;
	int row;
	int blood;
	bool dead;
	bool eating;
	int sh;                      //僵尸的伤害
    int t;                               
	vector2 p1,p4;       
	vector2 pCur;                  
};

具体运行代码如下

关键是bool iseating的使用,让多个僵尸一起吃植物

void checkZm2Zhiwu()
{
	    int zCount = sizeof(zms) / sizeof(zms[0]);
		int count = 0;
		for (int i = 0; i < zCount; i++)
		{
			if (zms[i].used)
			{
				if (zms[i].dead) continue;
				int row = zms[i].row;

				bool isEating = false;
                    
//定义一个判断植物是否被一个僵尸吃的变量
//这个iseating放在循环里面,说明每一个僵尸都会进行iseating的判断
//进而保证每一个僵尸的伤害都算到

				for (int k = 0; k < 9; k++)
				{
					if (map[row][k].type == 0) continue;  //如果有植物就向下运行
					int zhiWuX = 256 - 112 + k * 81;
					int x1 = zhiWuX;                      //判断僵尸是否到达植物旁边
					int x2 = zhiWuX + 60;       
					int x3 = zms[i].x + 80;
					if (x3 > x1 && x3 < x2)
					{
						if (map[row][k].catched)
						{
					        if (!isEating)    
// 如果没有其他僵尸正在吃植物,则当前僵尸可以继续吃植物
							{
								isEating = true;      
//表明已经有一个僵尸吃了
								map[row][k].blood = map[row][k].blood - zms[i].sh;
								if (map[row][k].blood <= 0)
								{
									map[row][k].type = 0;
									map[row][k].catched = false;
                                }
							}
							static int count = 0;
							count++;
							if (++count < 20)return;   //计时器让植物死慢一点              
                            if (count > 20)
							{
								count = 0;
				            }
                       }
						else                
               //如果僵尸已经到可以吃植物的地方,植物却没有被抓,进行以下代码               
						{
							map[row][k].catched = true;
							
            if (map[row][k].type == 1 || map[row][k].type == 2 || map[row][k].type == 3)    
							{
								map[row][k].blood = 100;   
                   //这个设置植物血量,可以放在创建植物初始化植物血量那边,我放这边是因为顺手
							} 
							if (map[row][k].type == 4)
							{
								map[row][k].blood = 1000;
							}
							zms[i].eating = true;
							zms[i].speed = 0;
							zms[i].frameIndex = 0;
						}
					}
				}
				if (!isEating)   //如果僵尸没有正在吃植物,则当前僵尸恢复移动
				{
					zms[i].eating = false;
					zms[i].speed = 1;
				}
				else             //如果有僵尸正在吃植物,则当前僵尸停止移动,并且进行进食状态
				{
					zms[i].eating = true;
					zms[i].speed = 0;
				}
			}
		}
}

2.添加

1.添加新植物

因为rock老师在原来的代码中已经设置好了添加方法,所以只要在枚举的里面写新的植物就可以了,博主在发的素材里面新加了大嘴话咀嚼和吃的图片,以及高坚果的图片

枚举如下

enum { WAN_DOU, XIANG_RI_KUI,DA_ZUI_HUA,GAO_JIAN_GUO,YING_TAO,ZHI_WU_COUNT };  

在这里分享一下大嘴花的代码,大嘴花和土豆雷差不多的操作

其中初始化大嘴花正常状态,攻击,吞噬的图片,以及打印出来大嘴花正常状态,攻击,吞噬的图片我以及省略了,下面其他的初始化以及打印的图片我都会省略只说思路

首先在结构体中定义一个bool wait,方便大嘴花在咀嚼和正常状态的切换

struct zhiwu                               
{
	int type;                             
//0:表示没有植物,1:表示第一种植物 2:表示第2种,,为了和枚举对应后面记得减去一
	int frameIndex;                       

	bool catched;
	int blood;   

	int timer;            
	int x, y;

	int shootTime;

	int planttime;

	bool wait;      //为了判断大嘴话是否在咀嚼专门设置的变量,同时可以用在土豆雷中
};

   然后是判断僵尸是否进入大嘴花的攻击范围,因为原版大嘴花的攻击范围较长可以参考roke老师豌豆发射子弹的代码来写,我偷懒就用了一个不优雅的方法,就是通过上面植物结构体定义的bool  catched,就是僵尸吃到大嘴花的时候,大嘴花进行攻击,你吃我我吃你嘛就是,这段代码放在checkzm2zhiwu检验植物僵尸里面。

  值得注意的是当大嘴花吃掉僵尸时,为了跟原版一样我没有设置僵尸的状态zms[i].dead=true,因为这样会打印僵尸变成灰的图片,不美观,我就设置的zms[i].used=false,这样就可以使得僵尸直接在屏幕上消失,哎这时候问题就出来了,你会发现一旦你用大嘴花杀死了10个僵尸之后,僵尸不再出现,但是屏幕上并没有打印你获得胜利的图片,这是因为你zms[].dead的数量没有达到10个,你让大嘴花杀死的僵尸直接消失了而不是让zms[].dead增加,如何改进呢?因为判断游戏结束用的killcount是否等于zm-max,killcount在zms[].dead的时候加一,因此只需要在这里zms[i].used的下面加上killcount++,即让大嘴花杀死的僵尸也算数就可以了,最后加上判断游戏是否结束的代码。

  代码缺点特别明显,就是大嘴花吞咽的图片切换的太快了,我加了计时器却没有改进,只让大嘴花吃僵尸的速度变慢了并没有让大嘴花图片变慢,在这里也问大佬们改进方法。

if (map[row][k].type == DA_ZUI_HUA + 1 && map[row][k].wait == false)
{
				zms[i].speed = 0;
				zms[i].used = false;
				killCount++;               //小细节
				if (killCount == ZM_MAX)
				{
								gameStatus = WIN;
				}
				map[row][k].wait = true;     //进入吞噬状态
				static int count1 = 0;
                if (++count1 < 2)return;      //加上计时器
				count1 = 0;

}

当大嘴花攻击状态结束之后,大嘴花进入吞噬状态,此时只需要使用一个计时器即可,我调的时间如下

void tunshi()
{
	static int count = 0;
	if (++count < 20)return;
	count = 0;

	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			if (map[i][j].type == DA_ZUI_HUA + 1  && map[i][j].wait == true)
			{
				static int count = 0;
				if (++count < 125)return;
				count = 0;
            }
		}
	}
}

 打印植物状态的状态切换,当僵尸没有进入攻击范围时打印大嘴花正常状态的图片;当僵尸进入攻击范围并且大嘴花并没有处于吞噬状态时,打印大嘴花攻击的图片;当大嘴花处于吞噬状态时打印大嘴花咀嚼的图片

if (map[i][j].type == 3)                                          
	if (map[i][j].catched == false)          
//正常状态
	{
		int zhiWuType = map[i][j].type - 1;
		int index = map[i][j].frameIndex;
		putimagePNG(map[i][j].x, map[i][j].y - 25, imgZhiWu[zhiWuType][index]);
	}
	if (map[i][j].catched == true&&map[i][j].wait==false)    
//进攻状态
	{
		    IMAGE* img = NULL;
			img = imgDaZuiHuaEating;
			img += map[i][j].frameIndex;
			
putimagePNG(map[i][j].x, map[i][j].y - 25, &imgDaZuiHuaEating[map[i][j].frameIndex]);
	}
	if (map[i][j].wait == true)                
//吞噬状态
	{
		IMAGE* img = NULL;
		img = imgDaZuiHuaEating;
		
		map[i][j].frameIndex = map[i][j].frameIndex % 6;
		img += map[i][j].frameIndex;
		
putimagePNG(map[i][j].x, map[i][j].y - 25, &imgDaZuiHuaEated[map[i][j].frameIndex]);
	}
}

2.添加小推车

推车

小推车的运用我觉得类似僵尸,因此我给小推车创建了结构体

struct car
{
	int x, y;
	int used;
};

小推车的位置的存放我使用的roke老师设置的函数vector2来存放,存放方式类似阳光,这样方便挨个存放小车的位置,位置是实验过的,如下

vector2 Cars[5]{ {180 - 112, 75  + 14},{180 - 112, 75 + 1* 102 + 14 },{ 180 - 112 , 75 + 2 * 102 + 14}, {180 - 112 , 75 + 3 * 102 + 14},{180 - 112, 75 + 4 * 102 + 14} };

好了在初始化完小车图片以及打印小车图片之后,开始的部分已经准备完毕,只剩下具体如何实现小车与僵尸的碰撞,以及小车的移动并且使小车离僵尸近时使得僵尸死亡。

因为是检查碰撞,在collisioncheck中再创建一个checkCar2Zm,我使用的下面代码(含解释)。。


void collisionCheck()
{
	checkBullet2Zm();
	checkZm2Zhiwu();
	checkCar2Zm();
}
void checkCar2Zm()
{
	int zCount = sizeof(zms) / sizeof(zms[0]);                  //僵尸池子中僵尸的数量
	for (int i = 0; i < 5; i++)              
	{
		for (int k = 0; k < zCount; k++)
		{
			if (zms[k].used == false)continue;    //如果僵尸没有使用就不往下进行,循环重新开始
			int x1 = zms[k].x + 80;
			int x2 = zms[k].x + 110;
if (( Cars[i].y-14-15 == zms[k].y-102&& zms[k].x<=180-112&&Cars[i].x<1400)
||Cars[i].x>180-112)

//   关键部分,当这个车的y坐标和僵尸y坐标相同,并且到达攻击范围内时小车进行移动
//   ||Cars[i].x>180-112必须有,因为如果只有前面的判断,当小车杀死僵尸时判断不成立会使小车停在原
地,因此加上后面的代码,使得小车一旦发动就无法停止

			{
				Cars[i].x++;
				if (zms[k].row==i&&zms[k].x<=Cars[i].x)
				{
					zms[k].dead = true;
					zms[k].speed = 0;
				}
			}
		}
   
    }
}

3.添加选关界面

铲子和选关

在main中添加int pd=pdgamegk(),然后制作一个switch case来判断选的哪一关。

int main(void)
{
	int timer = 0;                                      
	bool flag = true;                                  
    gamestart();                                      
	
	int pd=pdgamegk();        //新建一个在游戏开始前的选关函数
    int a = pd;

	viewScence();
	startUI2();
	barsDown();
	switch (pd)
	{
     case 1:
		while (1)
		{
			userclick();                             
			timer += getDelay();                     
			if (timer > 10)                           
			{
				flag = true;                          
				timer = 0;
			}
			if (flag)                                 
			{
				flag = false;
				updateWindow(a);                      
				updateGame(a);                        
				if (checkOver())break;
			}
		}
	case 2:
		break;
	case 3:
		break;
	system("pause");           
	return 0;
}

pdgamegk函数和选择植物卡牌的实现差不多,当你设置的关卡数过多时你就可以多设置一行,我只设计了3关因此一行就够了,这个实现并不是特别难,具体如下。

int pdgamegk()
{
	int a = 0;
	float k = 0.2;
	enum{GK1,GK2,GK3,OVER};                     //枚举的应用是关键,类似植物卡牌中枚举的使用
	IMAGE imgBg, img1, img2, imgMenu3, img10;
	loadimage(&imgBg, "res/aa/pdgamegk.png");
	loadimage(&img1, "res/aa/dy1.png");
	loadimage(&img2, "res/aa/dy2.png");
	
	int flag = 0;
	int f = 0;
	ExMessage msg;
	while (1)
	{
		char menu[50] = "选择你的关卡,点击图标,忽略人";
		char game1[20] = "第一关";
		char game2[20] = "第二关";
		//设置字体
		LOGFONT f;
		gettextstyle(&f);                           //取出一个字体,方便设置字体类型
		f.lfHeight = 30;                            //设置字体的高度
		f.lfWeight = 15;                            //设置字体的宽度
		strcpy(f.lfFaceName, "Segoe UI Black");     //将字体设置成微软雅黑
		f.lfQuality = ANTIALIASED_QUALITY;          //抗锯齿
		settextstyle(&f);                           //将游戏中的字体变为刚才设置的字体
		setbkmode(TRANSPARENT);                     //设置字体背景透明
		setcolor(BLACK);                            //设置字体文本颜色

		char scoreText1[20];                        //存入文字的数组
		char scoreText2[20];
		char scoreText3[50]; 
		sprintf_s(scoreText1, sizeof(scoreText1), "%s", game1);                 
		sprintf_s(scoreText2, sizeof(scoreText2), "%s", game2);
		sprintf_s(scoreText3, sizeof(scoreText3), "%s", menu);



		BeginBatchDraw();                            //没有缓冲会很卡
		putimage(55, 0, &imgBg);
		putimagePNG(10 + 55, 20 + 55, &img1);
		putimagePNG(10 + 55+55, 20 + 55, &img2);
		outtextxy(19+55, 12+55, scoreText1);                //显示文字
		outtextxy(19 + 55+55, 12 + 55, scoreText2);
		

		setcolor(WHITE);                      //设置字体文本颜色
		outtextxy(200+50+50,20,scoreText3);
		setcolor(BLACK);                      //防止阳光数字打印颜色为白色
		           

		if (peekmessage(&msg))
		{
			if (msg.message == WM_LBUTTONDOWN && msg.x >= 25+55 && msg.x <=74+55*OVER && msg.y>=94 && msg.y <=159)              //枚举的使用,限制了鼠标的有效点击位置
			{
				flag = 1;
			}
			else if (msg.message == WM_LBUTTONUP && flag == 1)
			{
				FlushMouseMsgBuffer();
				EndBatchDraw();
				a = (msg.x - 25 - 55) / 55;     //算出点击的关卡是第几关
				return a+1;
			}
		}
		EndBatchDraw();                   //双缓冲结束
	}
}

4.添加铲子

这个实现在userclick函数上,实现过程及其类似植物的种植,只需要当铲子到达指定位置时让map[row][col].type=0即可,这个实现很简单没什么说的,代码在下面。

void userclick()                                       
{
	ExMessage msg;                                          
	static int status = 0;                                  
    static int Cz = 0;                                 



//改变处 再次定义了一个静态变量cz,就类似种植植物时定义静态变量的思路,方便拿铲子以及铲子回到原位置
	 
	
	
	
         while(peekmessage(&msg))                                 
		{
		int row = (msg.y - 75) / 102;
	     int col = (msg.x - 256 + 112) / 81;
		if (msg.message == WM_LBUTTONDOWN&&status==0&&Cz==0)                          
            //翻译过来就是如果接受的信息为鼠标左键,并且没有进行其他点击,那么下面开始实行
			{
				if (msg.x > 840 && msg.x < 1000 && msg.y>0 && msg.y < 100)
				{
					Suspend();
				}

				if (msg.x + 112 > 338 && msg.x + 112 < 338 + 65 * ZHI_WU_COUNT && msg.y > 6 && msg.y < 96)   
				{
					int index = (msg.x - 338 + 112) / 65;                                      
                    status = 1;
					curX = msg.x + 112;
					curY = msg.y;
					curZhiWu = index + 1;           
				}


				if (msg.x > 750 && msg.x < 750 + 68 && msg.y>6 && msg.y < 72)
                    //判断你是不是选择了铲子
				{
					Cz = 1;
					curXCz = msg.x + 112;      //表示铲子现在的坐标,方便打印铲子
					curYCz = msg.y;
					curCz = 1;                 //将静态变量表示为正在使用
				}
				else
				{
					collectSunshine(&msg);                     
				}
			}
			else if ((msg.message == WM_MOUSEMOVE && status == 1)|| (msg.message == WM_MOUSEMOVE &&Cz==1))                         
 //翻译过来为如果接受的信息为鼠标左键单击之后拖动,并且鼠标左键点进有效范围,那么开始以下操作
			{
				curX = msg.x + 112;                                                       
				curY = msg.y;
				if (Cz == 1)
				{
					curXCz = msg.x + 112;                                                       
					curYCz = msg.y;           //铲子位置跟随鼠标变化
				}

			
				
			}

			else if ((msg.message == WM_LBUTTONUP && status == 1)|| (msg.message == WM_LBUTTONUP &&Cz==1))                                   
            //翻译过来为如果接受的信息为鼠标左键弹起,那么开始以下操作
			{
				
				
				if (msg.x > 256 - 112)
				{
					int row = (msg.y -75)/ 102;
					int col = (msg.x - 256 + 112) / 81;
					if (map[row][col].type == 0)                          
//map数组里面刚开始都是零元素,所以第一次一定执行,如果不是0表面该块地皮已经有植物了,以后想魔改植物大战僵尸可以改这个地方,使多个植物能种在同一个地方
					{
						map[row][col].type = curZhiWu;                   
                      	map[row][col].wait = false;
						map[row][col].frameIndex = 0;                    
                        map[row][col].shootTime = 0;
                        map[row][col].x = 256 - 112 + col * 81;
						map[row][col].y = 75 + row * 102 + 14;
						map[row][col].planttime = 0;
					}
				}

				if (Cz == 1)
				{
					map[row][col].type = 0;
					//curXCz = 1500;                     
          //刚开始不会弄,人才操作,把铲子移动走
					//curYCz = 900;
					Cz = 0;
				}
                status = 0;
				curZhiWu = 0;                   
                curCz = 0;
                //送开鼠标后将curCz清空,让玩家从新选择	,此时铲子就会自动消失
            }
			
		}
		
}

5.添加植物种植消耗阳光,植物卡牌的冷却

原版植物大战僵尸当植物使用一次后,植物卡牌会变灰色然后慢慢变成原来的颜色,因为能力有限,而且我只会计时器,所以我就制作了一个计时器来代表冷却时间,当卡牌在冷却时间点击时你的点击无效。

 好,思路说完之后,因为我的思路是使点击无效因此要还要在userclick中做改变,首先是使点击无效的代码如下。


void userclick()                                          
{
	ExMessage msg;                                       
	static int status = 0;                                 
	static int Cz = 0;                                                      
	while(peekmessage(&msg))                                  
		{
	    int row = (msg.y - 75) / 102;
		int col = (msg.x - 256 + 112) / 81;
		for (int i = 0; i < 5; i++)
		{


           //下面计时器用于给植物进行冷却,如果点击的卡牌还在冷却那么重新点击    
            for (int j = 0; j < 9; j++)
			{
				int a = map[i][j].type;
				map[i][j].planttime++;
				if (map[i][j].planttime < 1 && a == 1)
				{
					if (msg.x + 112 > 338 && msg.x + 112 < 338 + 65 && msg.y > 6 && msg.y < 96)	
					{
						FlushMouseMsgBuffer();
						return;
					}
				}
				if (map[i][j].planttime<=2 && a == 2)
				{
					if (msg.x + 112 > 338 + 65 && msg.x + 112 < 338 + 65 *2 && msg.y > 6 && msg.y < 96)
					{
						FlushMouseMsgBuffer();
						return;
					}
				}
				if (map[i][j].planttime < 1 && a == 3)//5
				{
					if (msg.x + 112 > 338 + 65 * 2 && msg.x + 112 < 338 + 65 *3 && msg.y > 6 && msg.y < 96)
					{
						FlushMouseMsgBuffer();
						return;
					}
				}
				if (map[i][j].planttime < 10 && a == 4)
				{
					if (msg.x + 112 > 338 + 65 * 3 && msg.x + 112 < 338 + 65*4 && msg.y > 6 && msg.y < 96)
					{
						FlushMouseMsgBuffer();
						return;
					}
				}
				if (map[i][j].planttime < 10 && a == 5)
				{
					if (msg.x + 112 > 338 + 65 * 4 && msg.x + 112 < 338 + 65*5 && msg.y > 6 && msg.y < 96)
					{
						FlushMouseMsgBuffer();
						return;
					}
				}
				else
				{
					map[i][j].planttime = 0;
				}
			}
		}




			if (msg.message == WM_LBUTTONDOWN&&status==0&&Cz==0)                          
			{
				if (msg.x > 840 && msg.x < 1000 && msg.y>0 && msg.y < 100)
				{
					Suspend();
				}

				if (msg.x + 112 > 338 && msg.x + 112 < 338 + 65 * ZHI_WU_COUNT && msg.y > 6 && msg.y < 96)      
				{
					int index = (msg.x - 338 + 112) / 65;                                      
					status = 1;
					curX = msg.x + 112;
					curY = msg.y;
					curZhiWu = index + 1;                           
				}
				if (msg.x > 750 && msg.x < 750 + 68 && msg.y>6 && msg.y < 72)
				{
					Cz = 1;
					curXCz = msg.x + 112;
					curYCz = msg.y;
					curCz = 1;
				}
				else
				{
					collectSunshine(&msg);                 
				}
			}
			else if ((msg.message == WM_MOUSEMOVE && status == 1)|| (msg.message == WM_MOUSEMOVE &&Cz==1))                         
			{
				curX = msg.x + 112;                                                       
				curY = msg.y;
				if (Cz == 1)
				{
					curXCz = msg.x + 112;                                                       
					curYCz = msg.y;
				}
}
			else if ((msg.message == WM_LBUTTONUP && status == 1)|| (msg.message == WM_LBUTTONUP &&Cz==1))                                  
			{							
				if (msg.x > 256 - 112)
				{
					int row = (msg.y -75)/ 102;
					int col = (msg.x - 256 + 112) / 81;
					if (map[row][col].type == 0)                          
					{
						map[row][col].type = curZhiWu;                   
         //下面用于种植植物之后扣除阳光,如果阳光不足那么就种植失败
				        if (curZhiWu == 1 && sunshine < 100)
						{
							map[row][col].type = 0;
						}
						if (curZhiWu == 1 && sunshine >= 100)
						{
							sunshine = sunshine - 100;
						}
						if (curZhiWu == 2 && sunshine < 50)
						{
							map[row][col].type = 0;
						}
						if (curZhiWu == 2 && sunshine >= 50)
						{
							sunshine = sunshine - 50;
						}
						if (curZhiWu == 3 && sunshine < 50)
						{
							map[row][col].type = 0;
						}
						if (curZhiWu == 3 && sunshine >= 0)
						{
							sunshine = sunshine - 0;                                                 
						}
						if (curZhiWu == 4 && sunshine < 50)
						{
							map[row][col].type = 0;
						}
						if (curZhiWu == 4 && sunshine >= 50)
						{
							sunshine = sunshine - 50;
						}

						map[row][col].wait = false;
						map[row][col].frameIndex = 0;                 
						map[row][col].shootTime = 0;
                        map[row][col].x = 256 - 112 + col * 81;
						map[row][col].y = 75 + row * 102 + 14;
						map[row][col].planttime = 0;
					}
				}

				if (Cz == 1)
				{
					map[row][col].type = 0;
					Cz = 0;
				}

				status = 0;
				curZhiWu = 0;                                             
				curCz = 0;
            }
			
		}
		
}

还有其他的改进如暂停游戏,就不再一一列举,谢谢大家观看,也希望和更多的人交朋友。

游戏还有更多的玩法休闲时候可以瞎改

光速

  • 31
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值