行业知识
C语言游戏实战(12):植物大战僵尸(坤版)
植物大战僵尸
前言:
本游戏使用C语言和easyx图形库编写,通过这个项目我们可以深度的掌握C语言的各种语言特性和高级开发技巧,以及锻炼我们独立的项目开发能力,
在开始编写代码之前,我们需要先了解一下游戏的基本规则和功能:
游戏界面:游戏界面是一个矩形区域,玩家可以在区域内进行植物的放置和铲除等操作。
僵尸:僵尸会从游戏界面的右侧向左测移动,靠近植物后会停下来吃植物。
植物:不同的植物有不同的功能,在这里我们可以将植物分为三大类:
1. 生产型植物(如太阳花):这种植物的特点是在一定的时间生产出太阳,以增加太阳的产量。
2.发射型植物(如豌豆射手):这种植物的特点是发射相应类型的子弹,对僵尸产生伤害和特定的效果。
3. 爆炸性植物(火爆辣椒):这种植物的特点是对一定区域的所有僵尸产生高额伤害。(一次性植物)
接下来,我们将通过以下几个步骤来实现这个游戏:
初始化游戏界面。
实现僵尸的创建、僵尸的更新、僵尸吃植物的检测。
实现的植物的放置。
实现植物的功能:
对于生产型植物我们需要写出阳光的生产,以及阳光的收集操作。
对于发射型植物我们需要判断植物是否发射子弹,发射子弹后还需更新子弹、检测子弹与僵尸的碰撞。
对于爆炸型植物,我们需要判断僵尸是否在爆炸范围内,然后杀***在爆炸范围内的僵尸。
1. 初始化游戏界面
我们需要先将游戏地图、卡牌和卡牌槽绘制出来。可以利用windows自带的画图工具测出游戏地图的位置,从而将这些卡牌和卡牌槽放置到合适的位置。
//地图putimage(-150, 0, &img);//卡牌槽putimagePNG(80, -10, &imgBar);//植物卡牌for (int i = 0; i2. 放置植物
因为需要在这个9*5的草地区域内放置植物,所以我们得计算出每个草方块的位置。我利用画图软件测出每个草方块大约是长81、宽100个像素点。 第一个草方块距离游戏窗口大约101个像素点。
这样就可以得出每个草方块的位置:256 - 150 + col * 81;100 + row * 100 - 15;
每个植物都有相似的特性,所以我们需要写一个放置植物信息的结构体。然后创建一个5*9的结构体数组,用来表示每个草方块上的植物。
enum {WAN_DOU, TAI_YANG, LA_JIAO, KUN_KUN, JIAN_GUO,HAN_BING_WAN_DOU, YING_TAO, SAN_TOU_WAN_DOU, PLANT_COUNT};struct plant{int type;//植物类型int frameIndex;//植物动作帧//bool catched;//是否被吃int blood;//血量int shootTime;//植物子弹的射速int timer;//阳光生产的时间int x, y;//植物坐标bool shoot;//判断植物是否处于发射状态};struct plant map[5][9];在 Windows 编程中,我们可以利用下面的变量获取鼠标信息。
ExMessage msg;msg.messageExMessage msg;static int status = 0;if (peekmessage(&msg))//判断有没有消息{if (msg.message == WM_LBUTTONDOWN)//左键按下{ //鼠标是否在卡牌的位置按下if (msg.x > 163 && msg.x = 100){curPlant = index + 1;status = 1;curX = msg.x;curY = msg.y;sunshine -= 100;}*/curPlant = index + 1;status = 1;curX = msg.x;curY = msg.y;}}else if (msg.message == WM_MOUSEMOVE && status == 1)//鼠标移动{curX = msg.x;curY = msg.y;}else if (msg.message == WM_LBUTTONUP)//鼠标放下{if (msg.x > 256 - 150 && msg.x 100 && msg.y3. 僵尸
3.1 创建僵尸
僵尸也是和植物一样需要创建一个结构体存放信息。然后创建一个结构体数组,作为一个僵尸池,当需要创建一个僵尸时,我们就从这个池里取一个未被使用的僵尸。
struct zm{int x, y;//僵尸的坐标int frameIndex;//僵尸动作帧bool used;//僵尸是否被使用int speed;//僵尸每一次移动的位移int row;//僵尸所在行int blood;//僵尸血量bool dead;//僵尸是否***亡bool eating;//僵尸是否在吃植物bool boom;//僵尸是否被炸***int zmSpeed;//僵尸的移动快慢};struct zm zms[ZM_MAX];//找一个可用的僵尸int i;for (i = 0; i到一定的时间就创建一个僵尸。
void createZM(){if (zmCount >= zmCount_max)return;static int zmFre = 500;static int count = 0; //控制僵尸的生成快慢count++;if (count > zmFre){count = 0;zmFre = 200;int i = 0;int zmMax = sizeof(zms) / sizeof(zms[0]);for (i = 0; i3. 2 检测僵尸对植物的伤害检测
僵尸靠近植物就将僵尸置为吃东西的状态,减植物的血量,当植物的血量小于等于0时,就去除植物。
void checkZM2Zhiwu(){char name[64];int zCount = sizeof(zms) / sizeof(zms[0]);for (int i = 0; i = x1 && x3 100){lines[zms[i].row] = 1;}}for (int i = 0; i 20){map[i][j].shootTime = 0; //子弹池int k;for (k = 0; k getwidth() - 10;bullets[k].y = zwY + 5;}}}}}}4. 2 更新子弹
不断的改变加子弹的横坐标,当子弹出游戏界面时,子弹就被置为未被使用。
以坤坤为例:
void updateBullets_kunkun(){int countMax = sizeof(bullets) / sizeof(bullets[0]);for (int i = 0; i Wide){bullets[i].used = false;}}}}4.3 检测子弹对僵尸的伤害
当子弹靠近僵尸时减去僵尸的一定量血量,然后对僵尸造成一定的效果(例如,坤坤子弹的效果是击退僵尸,那么我们只需要更改僵尸的横坐标即可。),最后将子弹置为未被使用。
以坤坤为例:
void checkBullet2Zm_kunkun(){int bCount = sizeof(bullets) / sizeof(bullets[0]);int zCount = sizeof(zms) / sizeof(zms[0]);for (int i = 0; i x1 && x 14){map[i][j].type = 0;map[i][j].frameIndex = 0;}}}}}}6. 小推车
创建存放小车信息的结构体数组,小推车一条路一辆共5辆,所以我们只需写一个大小为5的结构体数组即可。
//小推车struct car{bool move;//是否处于移动状态int x, y;//位置bool used;//是否被使用};struct car cars[5];放置小推车
for (int i = 0; i检测小推车对小车的伤害
当僵尸的横坐标小于等于小推车最左端的坐标时,小车置为移动状态,处于小推车左边的僵尸***亡。
void checkcars2Zm(){for (int i = 0; i zmX && cars[i].used){if (cars[i].move == false)cars[i].move = true;else{killCount++;zms[j].dead = true;zms[j].speed = 0;zms[j].frameIndex = 0;}}}}}}更新小推车的位置
当小车被置为移动时,小推车开始移动。
void updatecar(){for (int i = 0; i Wide){cars[i].move = false;cars[i].used = false;}}}源码:
test.cpp文件:
#include"game.h"//判断文件是否存在bool fileExist(const char* name){FILE* pf = fopen(name, "r");if (pf == NULL){return false;}else{fclose(pf);return true;}}//初始化豌豆子弹的帧图片数组void bulletsInit(){//坤坤篮球loadimage(&imgBulletNormal, "res/bullets/basketball.png", 40, 40);memset(bullets, 0, sizeof(bullets));//初始化豌豆子弹的帧图片数组loadimage(&imgBallBlast[3], "res/bullets/PeaNormalExplode/PeaNormalExplode_0.png");for (int i = 0; i getheight(), img);}}}void drawSunshine(){int ballMax = sizeof(balls) / sizeof(balls[0]);for (int i = 0; i frameIndex];//putimagePNG(balls[i].x, balls[i].y, img);putimagePNG(balls[i].pCur.x, balls[i].pCur.y, img);}}}void drawBullets_kunkun(){int bulletsMax = sizeof(bullets) / sizeof(bullets[0]);for (int i = 0; i 0){/*int x = 256 - 150 + j * 81;int y = 100 + i * 100 - 15;*/int PlantType = map[i][j].type - 1;int index = map[i][j].frameIndex;if (map[i][j].type != 4 && map[i][j].type != YING_TAO + 1 && map[i][j].type != LA_JIAO + 1){putimagePNG(map[i][j].x, map[i][j].y, imgPlant[PlantType][index]);}else if (map[i][j].type == YING_TAO + 1){if (index == 8)putimagePNG(map[i][j].x - 75, map[i][j].y-35, imgPlant[PlantType][index]);elseputimagePNG(map[i][j].x - 22, map[i][j].y, imgPlant[PlantType][index]);}else if (map[i][j].type == LA_JIAO + 1){if (index > 7)putimagePNG(100, map[i][j].y - 35, imgPlant[PlantType][index]);elseputimagePNG(map[i][j].x, map[i][j].y, imgPlant[PlantType][index]);}else{putimagePNG(map[i][j].x + 5, map[i][j].y - 20, imgPlant[PlantType][index]);}}}}//渲染子弹drawBullets();//铲子if (curPlant != PLANT_COUNT + 1){IMAGE* img = imgPlant[8][0];putimagePNG(163 + 8 * 65 + 8, 0, img);}//僵尸drawZm();//渲染拖动中的植物if (curPlant > 0){IMAGE* img = imgPlant[curPlant - 1][0];putimagePNG(curX - img->getwidth() / 2, curY - img->getheight() / 2, img);}//渲染阳光drawSunshine();EndBatchDraw();}void collectSunshine(ExMessage* msg){int count = sizeof(balls) / sizeof(balls[0]);int w = imgSunshineBall[0].getwidth();int h = imgSunshineBall[0].getheight();for (int i = 0; i x > x && msg->xy>y && msg->y 163 && msg.x = 100){curPlant = index + 1;status = 1;curX = msg.x;curY = msg.y;sunshine -= 100;}*/curPlant = index + 1;status = 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)//鼠标放下{if (msg.x > 256 - 150 && msg.x 100 && msg.y = ZM_MAX)return;int i = 0;int zmMax = sizeof(zms) / sizeof(zms[0]);for (i = 0; i = fre){fre = 200 + rand() % 200;count = 0;//从阳光池取一个可以使用的int ballMax = sizeof(balls) / sizeof(balls[0]);int i;for (i = 0; i = ballMax)return;balls[i].used = true;balls[i].frameIndex = 0;/*balls[i].x = 160 + rand() % 600;balls[i].y = 60;balls[i].destY = (rand() % 4) * 90 + 160;*/balls[i].timer = 0;/*balls[i].xoff = 0;balls[i].yoff = 0;*/balls[i].status = SUNSHINE_DOWN;balls[i].t = 0;balls[i].p1 = vector2(160 + rand() % 600, 60);balls[i].p4 = vector2(balls[i].p1.x, (rand() % 4) * 90 + 160);int off = 2;float distance = balls[i].p4.y - balls[i].p1.y;balls[i].speed = 1.0 / (distance / off);}int ballMax = sizeof(balls) / sizeof(balls[0]);//向日葵生产阳光for (int i = 0; i 100){map[i][j].timer = 0;int k;for (k = 0; k = ballMax)return;balls[k].used = true;balls[k].p1 = vector2(map[i][j].x, map[i][j].y);int w = (40 + rand() % 50) * (rand() % 2 ? 1 : -1);balls[k].p4 = vector2(map[i][j].x + w,map[i][j].y + imgPlant[TAI_YANG][0]->getheight() - imgSunshineBall[0].getheight());balls[k].p2 = vector2(balls[k].p1.x + w * 0.3, balls[k].p1.y - 100);balls[k].p3 = vector2(balls[k].p1.x + w * 0.7, balls[k].p1.y - 100);balls[k].status = SUNSHINE_RPODUCT;balls[k].speed = 0.05;balls[k].t = 0;}}}}}void updatSunshine(){int ballMax = sizeof(balls) / sizeof(balls[0]);for (int i = 0; i t += sun->speed;sun->pCur = sun->p1 + sun->t * (sun->p4 - sun->p1);if (sun->t >= 1){sun->status = SUNSHINE_GROUND;sun->timer = 0;}}else if (balls[i].status == SUNSHINE_GROUND){balls[i].timer++;if (balls[i].timer > 100){balls[i].used = false;balls[i].timer = 0;}}else if (balls[i].status == SUNSHINE_COLLECT){struct sunshineBall* sun = &balls[i];sun->t += sun->speed;sun->pCur = sun->p1 + sun->t * (sun->p4 - sun->p1);if (sun->t > 1){sun->used = false;sunshine += 25;}}else if (balls[i].status == SUNSHINE_RPODUCT){struct sunshineBall* sun = &balls[i];sun->t += sun->speed;sun->pCur = calcBezierPoint(sun->t,sun->p1, sun->p2, sun->p3, sun->p4);if (sun->t > 1){sun->status = SUNSHINE_GROUND;sun->timer = 0;}}}}}int mciZmTime = 0;void createZM(){if (zmCount >= zmCount_max)return;static int zmFre = 5000000;static int count = 0;count++;if (count > zmFre){if (mciZmTime == 0){mciSendString("play res/audio/awooga.mp3", 0, 0, 0);mciZmTime++;}count = 0;zmFre = 202 - 20 * wava_count;int i = 0;int zmMax = sizeof(zms) / sizeof(zms[0]);for (i = 0; i = (gameStatus == GOING ? zms[i].zmSpeed : zmSpeed)){//printf("%d ", (gameStatus == GOING ? zms[i].zmSpeed : zmSpeed));count[i] = 0;if (zms[i].used){if (zms[i].dead){zms[i].frameIndex++;if (zms[i].boom == true){if (zms[i].frameIndex >= 20){//printf("%d ", killCount);zms[i].used = false;zms[i].row = 0;//killCount++;if (killCount >= ZM_MAX)gameStatus = WIN;else if (killCount >= zmCount_max){wava_count++;zmCount_max *= 2;killCount = 0;zmCount = 0;}}}else{if (zms[i].frameIndex >= 38){//printf("%d ", killCount);zms[i].used = false;zms[i].row = 0;if (killCount >= ZM_MAX)gameStatus = WIN;else if (killCount >= zmCount_max){wava_count++;zmCount_max *= 2;killCount = 0;zmCount = 0;}}}}else if (zms[i].eating){//mciSendString("play res/audio/chomp.mp3", 0, 0, 0);//mciSendString("play res/audio/chompsoft.mp3", 0, 0, 0);zms[i].frameIndex = (zms[i].frameIndex + 1) % 21;}else{zms[i].frameIndex++;if (zms[i].frameIndex >= 22){zms[i].frameIndex = 0;}}zms[i].x -= zms[i].speed;if (zms[i].x 100){lines[zms[i].row] = 1;}}for (int i = 0; i 20){map[i][j].shootTime = 0;int k;for (k = 0; k getwidth() - 10;bullets[k].y = zwY + 5;}}}//豌豆else if (map[i][j].type == WAN_DOU + 1 && lines[i]){map[i][j].shootTime++;if (map[i][j].shootTime > 35){map[i][j].shootTime = 0;int k;for (k = 0; k 1)map[i][j].frameIndex = 2;bullets_wandou[k].used = true;bullets_wandou[k].row = i;bullets_wandou[k].speed = 8;bullets_wandou[k].blast = false;bullets_wandou[k].frameIndex = 0;int zwX = 256 - 150 + j * 81;int zwY = 100 + i * 100 - 15;bullets_wandou[k].x = zwX + imgPlant[map[i][j].type - 1][0]->getwidth() - 10;bullets_wandou[k].y = zwY + 5;}}}//寒冰豌豆else if (map[i][j].type == HAN_BING_WAN_DOU + 1 && lines[i]){map[i][j].shootTime++;if (map[i][j].shootTime > 35){map[i][j].shootTime = 0;int k;for (k = 0; k 1)map[i][j].frameIndex = 4;bullets_hanbing[k].used = true;bullets_hanbing[k].row = i;bullets_hanbing[k].speed = 10;bullets_hanbing[k].blast = false;bullets_hanbing[k].frameIndex = 0;int zwX = 256 - 150 + j * 81;int zwY = 100 + i * 100 - 15;bullets_hanbing[k].x = zwX + imgPlant[map[i][j].type - 1][0]->getwidth() - 10;bullets_hanbing[k].y = zwY + 5;}}}//三头豌豆else if (map[i][j].type == SAN_TOU_WAN_DOU + 1 && lines[i]){map[i][j].shootTime++;if (map[i][j].shootTime > 35){map[i][j].shootTime = 0;int k;for (int b = 0; b 1)//map[i][j].frameIndex = 2;bullets_santou[k].used = true;bullets_santou[k].row = i;bullets_santou[k].speed = 8;bullets_santou[k].blast = false;bullets_santou[k].frameIndex = 0;int zwX = 256 - 150 + j * 81;int zwY = 100 + i * 100 - 15;bullets_santou[k].x = zwX + imgPlant[map[i][j].type - 1][0]->getwidth() - 30;bullets_santou[k].y = zwY + 16;direction_santou[k] = b;row_santou[k] = i;}}}}}}}void updateBullets_kunkun(){int countMax = sizeof(bullets) / sizeof(bullets[0]);for (int i = 0; i Wide){bullets[i].used = false;}//待完善if (bullets[i].blast){bullets[i].frameIndex++;if (bullets[i].frameIndex >= 4){bullets[i].used = false;}}}}}void updateBullets_wandou(){int countMax_wandou = sizeof(bullets_wandou) / sizeof(bullets_wandou[0]);for (int i = 0; i Wide){bullets_wandou[i].used = false;}//待完善if (bullets_wandou[i].blast){bullets_wandou[i].frameIndex++;if (bullets_wandou[i].frameIndex >= 4){bullets_wandou[i].used = false;}}}}}void updateBullets_hanbing(){int countMax_hanbing = sizeof(bullets_hanbing) / sizeof(bullets_hanbing[0]);for (int i = 0; i Wide){bullets_hanbing[i].used = false;}//待完善if (bullets_hanbing[i].blast){bullets_hanbing[i].frameIndex++;if (bullets_hanbing[i].frameIndex >= 4){bullets_hanbing[i].used = false;}}}}}void updateBullets_santou(){int countMax_santou = sizeof(bullets_santou) / sizeof(bullets_santou[0]);for (int i = 0; i Wide){bullets_santou[i].used = false;}}else if (direction_santou[i] == UP){int destY = 85 + (row_santou[i] - 1) * 100;//int zwX = 256 - 150 + j * 81;float angle = atan(0.6);bullets_santou[i].x += bullets_santou[i].speed;bullets_santou[i].y -= bullets_santou[i].speed * tan(angle);//printf("%d\n", row_santou[i]);//printf("destY = %d\n", destY);//printf("bullets[i].y = %lf\n", bullets[i].y);if (bullets_santou[i].y = destY + 16){direction_santou[i] = MIDDLE;}}//待完善if (bullets_santou[i].blast){bullets_santou[i].frameIndex++;if (bullets_santou[i].frameIndex >= 4){bullets_santou[i].used = false;}}}}}void updateBullets(){//坤坤updateBullets_kunkun();//豌豆射手updateBullets_wandou();//寒冰豌豆updateBullets_hanbing();//三头豌豆updateBullets_santou();}void checkBullet2Zm_kunkun(){int bCount = sizeof(bullets) / sizeof(bullets[0]);int zCount = sizeof(zms) / sizeof(zms[0]);for (int i = 0; i x1 && x = y1 && zmY 7){if (map[i][j].frameIndex == 8)PlaySound("res/audio/firepea.wav", NULL, SND_FILENAME | SND_ASYNC);for (int k = 0; k 14){map[i][j].type = 0;map[i][j].frameIndex = 0;}}}}}}void checkZM2Zhiwu(){int chomp = 0;//mciSendString("play ZM_BGM repeat", NULL, NULL, NULL);char name[64];int bCount = sizeof(bullets) / sizeof(bullets[0]);int zCount = sizeof(zms) / sizeof(zms[0]);for (int i = 0; i = x1 && x3 20){chomp_time = 0;PlaySound("res/audio/chomp.wav", NULL, SND_FILENAME | SND_ASYNC);//mciSendString("play ZM_BGM", NULL, NULL, NULL);//printf("1 ");}}void checkcars2Zm(){for (int i = 0; i zmX && cars[i].used){if (cars[i].move == false)cars[i].move = true;else{killCount++;zms[j].dead = true;zms[j].speed = 0;zms[j].frameIndex = 0;}}}}}}void collistionCheck(){//子弹对僵尸的检测checkBullet2Zm();//僵尸对植物的检测checkZM2Zhiwu();//爆炸对植物的检测checkBoom2Zm();//小推车对僵尸的检测checkcars2Zm();}void updatecar(){for (int i = 0; i Wide){cars[i].move = false;cars[i].used = false;}}}void updateGame(){srand((unsigned)time(NULL));static int count = 0;count++;if (count > 4){count = 0;for (int i = 0; i 0){map[i][j].frameIndex++;int PlantType = map[i][j].type - 1;int index = map[i][j].frameIndex;if (map[i][j].shoot){if (map[i][j].frameIndex > 1){map[i][j].shoot = false;}}else{if (imgPlant[PlantType][index] == NULL){map[i][j].frameIndex = 0;}}}}}}createSunshine();//创建阳光updatSunshine();//更新阳光状态createZM();//创建僵尸updateZM();//更新僵尸状态shoot();//发射豌豆子弹updateBullets();//更新子弹collistionCheck();//实现豌豆子弹的碰撞检测updatecar();//更新小推车}void menu(){mciSendString("play BGM", NULL, NULL, NULL);IMAGE imgBg, imgMenu1, imgMenu2;loadimage(&imgBg, "res/menu.png");loadimage(&imgMenu1, "res/menu2.png");loadimage(&imgMenu2, "res/menu1.png");int flag = 0;while (1){BeginBatchDraw();putimage(0, 0, &imgBg);putimagePNG(474, 75, flag ? &imgMenu1 : &imgMenu2);ExMessage msg;if (peekmessage(&msg)){if (msg.message == WM_LBUTTONDOWN &&msg.x > 474 && msg.x 75 && msg.y = -500; i-=3){BeginBatchDraw();putimage(i, 0, &img);count++;for (int k = 0; k = 10){index[k] = (index[k] + 1) % 11;} }if (count >= 10)count = 0;EndBatchDraw();Sleep(10);}for (int i = 0; i = 10) count = 0;}EndBatchDraw();Sleep(10);}}void barsDown(){int height = imgBar.getheight();for (int y = -height; y getwidth(); //获取picture的宽度,EASYX自带int picture_height = picture->getheight(); //获取picture的高度,EASYX自带int graphWidth = getwidth(); //获取绘图区的宽度,EASYX自带int graphHeight = getheight(); //获取绘图区的高度,EASYX自带int dstX = 0; //在显存里像素的角标// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算for (int iy = 0; iy > 24); //0xAArrggbb;AA是透明度int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的Rint sg = ((src[srcX] & 0xff00) >> 8); //Gint sb = src[srcX] & 0xff; //Bif (ix >= 0 && ix = 0 && iy 16);int dg = ((dst[dstX] & 0xff00) >> 8);int db = dst[dstX] & 0xff;draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) = getwidth()) {return;}else if (y + picture->getheight() > winHeight) {SetWorkingImage(picture);getimage(&imgTmp, x, y, picture->getwidth(), winHeight - y);SetWorkingImage();picture = &imgTmp;}if (x getwidth() + x, picture->getheight());SetWorkingImage();x = 0;picture = &imgTmp2;}if (x > winWidth - picture->getwidth()) {SetWorkingImage(picture);getimage(&imgTmp3, 0, 0, winWidth - x, picture->getheight());SetWorkingImage();picture = &imgTmp3;}_putimagePNG(x, y, picture);}int getDelay() {static unsigned long long lastTime = 0;unsigned long long currentTime = GetTickCount();if (lastTime == 0) {lastTime = currentTime;return 0;}else {int ret = currentTime - lastTime;lastTime = currentTime;return ret;}}tools.h文件
#pragma once#include void putimagePNG(int picture_x, int picture_y, IMAGE* picture);int getDelay();
上一篇:校友啊,哪届的,可否认识一下
下一篇:网站备案怎么换?