不少同学都玩过《植物大战僵尸》,最近PopCap公司又带来了新版的消息,这次高兴的轮到Xbox的用户了,日前PopCap公司公布了《植物大战僵尸》XBLA版的截图,这个版本的《植物大战僵尸》引入了多人合作与对抗模式,看图就知道好玩多了又刺激多了。
详见游戏说明,
游戏视频
于是,我在非常强烈的好奇心和求知欲下,自己动手写了一个简易的双人对战版。开发环境是VC6.0,开发语言是C语言。
游戏最终完成情况C语言植物大战僵尸
第一章 需求分析
设计一个双人对战的植物大战僵尸,两人在一台电脑上玩。
植物方使用鼠标控制,基本上与原版的控制的方法一样。植物方获胜条件是打掉僵尸最后面的五个墓碑中的三个。
僵尸方使用键盘控制,W控制僵尸选择光标的上移,S控制僵尸选择光标的下移,ENTER是僵尸选择的确认,方向键控制僵尸安放的位置,空格键是安放僵尸。僵尸方的获胜条件是与原版一样走到戴夫家里。
第二章 概要设计
首先,根据我的设计,我把游戏分为几个元素:地图格子元素,卡片元素,僵尸元素,推车元素,僵尸方的墓碑靶子元素,植物的子弹元素,动画效果播放元素。
其次,把游戏分为几个处理:子弹碰到僵尸处理,僵尸碰到植物处理,推车碰到僵尸处理,子弹碰到墓碑靶子处理等。每个处理完后立马接上动画播放效果。
第三章 详细设计
3.1加载GIF图像
植物元素和僵尸元素都是动态的,于是我想到了加载GIF。经过查找资料,找到了在VC6.0中用GDI+加载GIF的方法。
详见VC6.0使用GIF资料
资料和示例程序地址VC6.0加载GIF方法,示例程序
程序的结果如图所示
这是因为当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图。
双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。双缓冲实现过程如下:
1、在内存中创建与画布一致的缓冲区
2、在缓冲区画图
3、将缓冲区位图拷贝到当前画布上
4、释放内存缓冲区
增加双缓冲后的示例程序地址双缓冲加载GIF
程序结果如图所示
3.2游戏元素
3.2.1地图格子元素
植物大战僵尸的地图中的草地是有一格一格的,于是可以建立一个二维数组的结构体,来表示地图上当前格子的状态。
typedef struct { char cName; //格子中植物的名字,没植物时是0 int iLife; //格子中植物的生命值 char cSun; //格子中是否有阳光 char cChomper; //是否是食人花 char cSquash; //是否是窝瓜 char cBomb; //是否是炸弹 char cCherryBomb;//是否是炸弹 POINT ptSite; //格子的坐标位置 char cBeat; //格子中的植物是否正在被攻击 }__MAPNATURE; __MAPNATURE _MAP[5][9]; //植物的格子 __MAPNATURE _MAPZOM[5][3]; //僵尸的格子3.2.2卡片元素
植物与僵尸都有选择的卡片,如图所示
植物有植物的卡片,僵尸有僵尸的卡片。每张卡片它们具有不同的属性,建立一个结构体。
typedef struct { char cCanFlg; //是否能被选择 int iCount; //冷却的时间 POINT ptSite; //卡片的位置 char pTime[8];//冷却的时间 int iColor; //要隐藏的颜色 int iMoney; //卡片所花费的金钱 }__CARDNATURE; __CARDNATURE _CARD[8];//植物的卡片 __CARDNATURE _CARDZOM[7];//僵尸的卡片3.2.3僵尸元素
每个僵尸都是一个独立的单元,包含着它的存活,位置等。我用的是一个结构体数组,其实可以用循环队列的。
typedef struct { char cAlive; //是否活着 char cName; //僵尸的名字 int iLife; //僵尸的生命值 char cPole; //撑杆僵尸的杆子是否存在 int iSpeed; //僵尸的行走的速度 POINT ptSite; //僵尸的位置 char cBeat; //僵尸是否被子弹打击 char cAttack; //僵尸是否正在吃植物 char cPass; //僵尸能否行走,碰到植物不通过 char cProtect;//僵尸的保护是否在,针对报纸,铁桶僵尸 char cPoleVaulting;//撑杆僵尸是否正在跳跃 }__ZOMBIENATURE; __ZOMBIENATURE _ZOMBIE[ZOMMAX];//僵尸的数组3.2.4其他元素
这里其他元素是推车元素,僵尸方的墓碑靶子元素,植物的子弹元素。同理,它们有自己特性。
//推车 typedef struct { char cActive; //推车激活标志。有僵尸碰到激活 char cAlive; //推车跑出最右端,不在存活 POINT ptSite; //推车位置 }__LAWNMOVERNATURE; //僵尸靶子 typedef struct { int iLife; //僵尸靶子的生命值 POINT ptSite; //靶子的位置 char cBeat; //靶子是否被攻击 }__TARGETNATURE; //豌豆子弹 typedef struct { char cAlive; //子弹激活标志,每隔多少时间射一次 char cOpen; //子弹能否激活 POINT ptSite; //子弹老的位置 POINT ptNew; //子弹新的位置 }__BULLETNATURE;3.3游戏处理
3.3.1子弹碰到僵尸处理
子弹是一个结构体数组,僵尸也是结构体数组,用两个for循环扫描这两个数组,当子弹的坐标与僵尸的坐标满足相碰关系时,子弹激活标志清零,即子弹消失,同时僵尸的生命值减一。直到僵尸清零,僵尸死亡。
3.3.2僵尸碰到植物处理
For循环扫描每个僵尸,通过僵尸的位置判断出它此时所对应地图上的前一个格子。通过当前格子的状态,判断接下来发生的事情。例如:格子里没植物,僵尸通过;僵尸碰到豌豆射手,坚果墙,向日葵就停下来开吃,同时,格子的生命值不停地减,直到吃掉植物,向前通行,当是撑杆僵尸有一个杆子,碰到它们后要跳过它们;僵尸碰到炸弹,窝瓜,食人花就被吃掉或炸死,同时播放动画效果。
3.3.3推车碰到僵尸处理
For循环扫描每个僵尸,当僵尸走到最左端的时候,激活推车的标志,并且推车向右行驶。推车在这条路上碰到僵尸,僵尸就死亡,当推车到达最右端时,推车死亡。
工程全部源码
https://github.com/luoyikun/PvZInC