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