这是跟着b站rock老师写的代码,一个简易版的植物大战僵尸。
然后可以实现阳光生产 自动回收 登入界面还有过场动画 。
有三种植物向日葵,豌豆和食人花。
可以随机生成普通僵尸,豌豆会进行射击,向日葵也会生成阳光。
缺点是只有三种植物,目前还没有实现食人花吃僵尸的动作,bug是僵尸碰见食人花就消失了 然后食人花过一会也消失了,属于同归于尽了。而且阳光可以无限贷款,就是阳光不够会变成负数,这个我没有调整,而且没有设置关卡,只能一直打打打。也没有小推车,但是不管怎么说,也是自己一个一个敲出来的第一个正经实战项目,话不多看看效果。
植物大战僵尸
下面展示一下代码
main.cpp
#include"tools.h"
bool fileExist(const char* name)
{
FILE* fp = fopen(name, "r");
if(fp==NULL)
{
return false;
}
else
{
fclose(fp);
return true;
}
}
void gameInit()
{
//加载有背景图片
loadimage(&imgBg, "res/bg.jpg");
loadimage(&imgBar, "res/bar5.png");
memset(imgZhiWu, 0, sizeof(imgZhiWu));
memset(map, 0, sizeof(map));
//初始化植物卡牌
char name[64];
for ( int i = 0; i < ZHI_WU_COUNT; i++)
{
sprintf_s(name, sizeof(name), "res/Cards/card_%d.png", i + 1);//生成植物卡牌的文件名
loadimage(&imageCards[i], name);
for (int j = 0; j < 20; j++)
{
sprintf_s(name, sizeof(name), "res/zhiwu/%d/%d.png", i,j+1);
//先判断这个文件是否存在
if (fileExist(name))
{
imgZhiWu[i][j] = new IMAGE;
loadimage(imgZhiWu[i][j], name);
}
else
{
break;
}
}
}
curZhiWu = 0;
sunshine = 50;
memset(balls, 0, sizeof(balls));
for (int i = 0; i < 29; i++)
{
sprintf_s(name, sizeof(name), "res/sunshine/%d.png", i + 1);
loadimage(&imgSunshineBall[i], name);
}
srand(time(NULL));//配置随机种子
//创建游戏的图形窗口
initgraph(WIN_WIDTH, WIN_HEIGHT);//1的意思是运行保存一个控制台
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);//文本颜色
memset(zms, 0, sizeof(zms));//初始化僵尸数据
for (int i = 0; i < 22; i++)
{
sprintf_s(name, sizeof(name), "res/zm/%d.png", i + 1);
loadimage(&imgZM[i], name);
}
loadimage(&imgBulletNormal, "res/bullets/bullet_normal.png");
memset(bullets, 0, sizeof(bullets));
//初始化豌豆子弹的帧图片数组
loadimage(&imgBullBlast[3], "res/bullets/bullet_blast.png");
for(int i=0;i<3;i++)
{
float k = (i + 1) * 0.2;
loadimage(&imgBullBlast[i], "res/bullets/bullet_blast.png", imgBullBlast[3].getwidth() * k, imgBullBlast[3].getheight() * k,true);
}
for (int i = 0; i < 20; i++)//初始化僵尸死亡
{
sprintf_s(name, sizeof(name), "res/zm_dead/%d.png", i+1);
loadimage(&imgZMDead[i], name);
}
for (int i = 0; i < 21; i++)//初始化僵尸吃植物
{
sprintf_s(name,"res/zm_eat/%d.png", i + 1);
loadimage(&imgZMEat[i], name);
}
for (int i = 0; i < 11; i++)//初始化僵尸站立
{
sprintf_s(name, "res/zm_stand/%d.png", i + 1);
loadimage(&imgZmStand[i], name);
}
for (int i = 0; i < 4; i++)
{
sprintf_s(name, sizeof(name), "res/zhiwu_eat/%d.png", i + 1);
loadimage(&imgeating[i], name);
}
}
void userClick()
{
ExMessage msg;
static int status = 0;
if (peekmessage(&msg))
{
if (msg.message == WM_LBUTTONDOWN)//鼠标点击
{
if (msg.x > 338 && msg.x < 338 + 65 * ZHI_WU_COUNT && msg.y < 96)
{
int index = (msg.x - 338) / 65;
status = 1;
curZhiWu = index + 1;
}
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-112&&msg.y>179&&msg.y<489)
{
int row = (msg.y - 179) / 102;
int col = (msg.x - (256 - 112)) / 81;
if(map[row][col].type==0)
{
map[row][col].type = curZhiWu;
map[row][col].frameIndex = 0;
if (map[row][col].type == SHI_REN_HUA+1)
{
map[row][col].x = 256 - 122 + col * 81;
map[row][col].y = 179 + row * 102 -15;
}
else
{
map[row][col].x = 256 - 112 + col * 81;
map[row][col].y = 179 + row * 102 + 14;
}
}
if (map[row][col].type == WAN_DOU+1)//种植的是豌豆
{
sunshine -= 100;
}
else if(map[row][col].type==XIANG_RI_KUI+1)//种植的是向日葵
{
sunshine -= 50;
}
else if (map[row][col].type == SHI_REN_HUA + 1)
{
sunshine -= 150;
}
}
curZhiWu = 0;
status = 0;
}
}
}
void updateWindow()//更新窗口
{
BeginBatchDraw();//开始缓冲
putimage(-112, 0, &imgBg);
//putimage(250, 0, &imgBar);
putimagePNG(250, 0, &imgBar);
drawcard();
drawZhiWu();
drawSunshine();
drawZm();//绘制僵尸
int bulletMax = sizeof(bullets) / sizeof(bullets[0]);//渲染子弹
for (int i = 0; i < bulletMax; i++)
{
if (bullets[i].used)
{
if (bullets[i].blast)
{
IMAGE* img = &imgBullBlast[bullets[i].frameIndex];
putimagePNG(bullets[i].x, bullets[i].y, img);
}
else
{
putimagePNG(bullets[i].x, bullets[i].y, &imgBulletNormal);
}
}
}
EndBatchDraw();//结束缓冲
}
void updateGame()
{
updateZhiWu();//更新植物
createSunshine();//创建阳光
updateSunshine();//更新阳光的状态
createZM();//创建僵尸
updateZM();//更新僵尸
shoot();//发射子弹
updateBullets();//更新子弹
collisionCheck();//实现碰撞检测
}
void startUI()
{
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);
putimagePNG(475, 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();
}
}
void viewScence()
{
int xMin = WIN_WIDTH - imgBg.getwidth();
vector2 point[9] = {
{550,80},{530,160},{630,170},{530,200},{515,270},{565,370},{605,340},{705,280},{690,340}
};
int index[9];
mciSendString("play res/bg.mp3", 0, 0, 0);
for (int i= 0; i <9; i++)
{
index[i] = rand() % 11;
}
for (int x = 0; x >= xMin; x-=2)
{
BeginBatchDraw();
putimage(x, 0, &imgBg);
for (int k = 0; k < 9; k++)
{
putimagePNG(point[k].x - xMin + x,point[k].y,&imgZmStand[index[k]]);
index[k] = (index[k] + 1) % 11;
}
EndBatchDraw();
Sleep(10);
}
for(int i=0;i<100;i++)
{
BeginBatchDraw();
putimage(xMin, 0, &imgBg);
for (int k = 0; k < 9; k++)
{
putimagePNG(point[k].x, point[k].y, &imgZmStand[index[k]]);
index[k] = (index[k] + 1) % 11;
}
EndBatchDraw();
Sleep(10);
}
for (int x = xMin; x < -112; x += 2)
{
BeginBatchDraw();
putimage(x, 0, &imgBg);
for (int k = 0; k < 9; k++)
{
putimagePNG(point[k].x - xMin + x, point[k].y, &imgZmStand[index[k]]);
index[k] = (index[k] + 1) % 11;
}
EndBatchDraw();
Sleep(10);
}
}
int main(void)
{
gameInit();
startUI();
viewScence();//转场
int timer = 0;
bool flag = true;
while(1)
{
userClick();
timer += getDelay();
if(timer>10)
{
flag = true;
timer = 0;
}
if(flag)
{
flag = false;
updateWindow();
updateGame();
}
}
while (1);
return 0;
}
yang_guang.cpp
#include"tools.h"
struct sunshineBall balls[10];
IMAGE imgSunshineBall[29];
int sunshine;
void createSunshine()//创建阳光
{
static int count = 0;
static int fre = 100;
count++;
if (count >= fre)
{
fre = 200 + rand() % 200;
count = 0;
int ballMax = sizeof(balls) / sizeof(balls[0]);//从阳光池里取一个可以用的
int i;
for (i = 0; i < ballMax && balls[i].used; i++);
if (i >= ballMax) return;
balls[i].used = true;
balls[i].frameIndex = 0;
balls[i].timer = 0;
balls[i].status = SUNSHINE_DOWN;
balls[i].t = 0;
balls[i].p1 = vector2(260 + rand() % (900 - 260), 60);
balls[i].p4 = vector2(balls[i].p1.x, 200 + (rand() % 4) * 90);
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 < 3; i++)
{
for (int j = 0; j < 9; j++)
{
if (map[i][j].type == XIANG_RI_KUI + 1)
{
map[i][j].timer++;
if (map[i][j].timer > 200)
{
map[i][j].timer = 0;
int k;
for (k = 0; k < ballMax && balls[k].used; k++);
if (k >= ballMax) return;
balls[k].used = true;
balls[k].p1 = vector2(map[i][j].x, map[i][j].y);
int w = (100 + rand() % 50) * (rand() % 2 ? 1 : -1);
balls[k].p4 = vector2(map[i][j].x + w, map[i][j].y + imgZhiWu[XIANG_RI_KUI][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 updateSunshine()//更新阳光
{
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (int i = 0; i < ballMax; i++)
{
if (balls[i].used)
{
balls[i].frameIndex = (balls[i].frameIndex + 1) % 29;
if (balls[i].status == SUNSHINE_DOWN)
{
struct sunshineBall* sun = &balls[i];
sun->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].p1 = balls[i].pCur;//起点
balls[i].p4 = vector2(262, 0);//终点
float distance = dis(balls[i].p1 - balls[i].p4);
float off = 8;
balls[i].speed = 1.0 / (distance / off);
balls[i].t = 0;
balls[i].status = SUNSHINE_COLLECT;
break;;
}
}
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;
}
}
}
}
}
void drawSunshine()//绘制阳光,更新分数
{
int ballMax = sizeof(balls) / sizeof(balls[0]);
for (int i = 0; i < ballMax; i++)
{
if (balls[i].used)
{
IMAGE* img = &imgSunshineBall[balls[i].frameIndex];
putimagePNG(balls[i].pCur.x, balls[i].pCur.y, img);
}
}
char scoreText[8];
sprintf_s(scoreText, sizeof(scoreText), "%d", sunshine);
outtextxy(276, 67, scoreText);//输出分数
}
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 < count; i++)
{
if (balls[i].used)
{
int x = balls[i].pCur.x;
int y = balls[i].pCur.y;
if (msg->x > x && msg->x < x + w && msg->y > y && msg->y < y + h)
{
balls[i].status = SUNSHINE_COLLECT;
PlaySound("res/sunshine.wav", NULL, SND_FILENAME | SND_ASYNC);
balls[i].p1 = balls[i].pCur;//起点
balls[i].p4 = vector2(262, 0);//终点
float distance = dis(balls[i].p1 - balls[i].p4);
float off = 8;
balls[i].speed = 1.0 / (distance / off);
balls[i].t = 0;
break;
}
}
}
}
我展示的分别是主函数和阳光部分代码,然后其它的后续我会上传到资源里面,如果需要图片音乐文件可以私信我,或者和我一起讨论代码 完成整个游戏过程。