此博客用来记录博主在跟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;
}
}
}
还有其他的改进如暂停游戏,就不再一一列举,谢谢大家观看,也希望和更多的人交朋友。
游戏还有更多的玩法休闲时候可以瞎改
光速