引言:
前两天看到小朋友在玩植物大战僵尸,想起来多年以前自己也经常玩这个游戏,是比较经典的一款休闲游戏,然后就心血来潮就周末写了一个,花了不少时间去找素材和编写代码,感觉上基本的功能是做好了(要上班,没那么多时间搞),写出来大家看看,确实有点爆肝!
效果图:
实现思路
- 用两张画布来实现,第一个画布绘制不用更新的东西,比如背景图、按钮、积分图,卡牌图等;
- 第二个画布,绘制经常更新的东西,比如僵尸的走动、僵尸吃植物、僵尸死亡、植物的摇摆、豌豆苗发射豌豆、子弹的运动、阳光的产生、阳光的收集等等;
- 动画的实现,通过图片的不停切换来实现的,开启一个总定时任务100毫秒重新绘制画布2,当然其他的每个动画都会重新开启定时任务(我称他们为子任务),它们不负责绘制,只负责改变对应的参数,绘制都是由总任务来完成的, 比如僵尸走动动画:开启子任务100毫秒执行一次图片切换,切换到最后一张的时候,返回到第一张,如果要走动的话同时改变图片的位置就好,子任务修改完成后,总任务自然会绘制出来;
- 卡牌的实现,目前就写了2张卡牌(向日葵、豌豆苗),给卡牌绘制了相同大小的方形来控制鼠标点击事件,当点击卡牌的时候,会创建对应的植物并且跟随鼠标移动,移动鼠标到合适的位置后点击(田 里面对应的方块),会在对应的位置种植;
- 田位置的控制,以方形来划分,每一块可以种植物的区域都用一个小方块来控制,植物就种在对应的方块内,当选择一个卡牌后,鼠标移动到田里面就会标示出来一个方形的区域,标示植物种植在这块区域里面。
- 豌豆苗被种植后,会定时的发射子弹,当子弹的位置和僵尸的位置交汇的时候,就判断为击中(处理子弹击中动画、子弹消失、僵尸扣除相应血量、击中的音效等),僵尸血量归零后会停止走动的动画,开启新的倒地动画,倒地完成后删除僵尸,同时累计得到的分数;
- 当僵尸的位置和植物的位置交汇的时候,僵尸会停止行走的动画,开启吃的动画(植物被扣除血量、僵尸吃的音效),植物血量归零后,植物对象会被清理;
- 阳光有两种产生方式,定时产生和向日葵产生,产生后会开启往下飘的动画,飘到一定范围后停止动画、开启计数器(目前设定为10秒),计数归零没有此阳光依然未被点击收集的话就会消失,在指定时间内点击了该阳光(音效),则会开启往左上角飞行的动画,到终点后阳光消失,阳光分增加(音效);
- 结束条件:1)僵尸触及田的最左边--判定为失败。2)得分300--判定为胜利!
实现
绘制背景
-
//绘制背景
-
Plants.prototype.drawBG=function(){
-
var image,img,sx=
150,sy=
0,sWidth=
900,sHeight=
600,dx=
0,dy=
0,dWidth=
900,dHeight=
600;
-
//背景
-
image =
this.imgObj[
1];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.renderArr.push(img);
-
}
绘制上方的卡牌区域、积分区域,相关按钮
-
//绘制游戏上方的相关图片(卡片等)
-
Plants.prototype.drawCard=function(){
-
var image,img,sx=
0,sy=
0,sWidth=
446,sHeight=
87,dx=
0,dy=
0,dWidth=
446,dHeight=
80;
-
//方形卡片盘
-
image =
this.imgObj[
2];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.renderArr.push(img);
-
-
sWidth=
128,sHeight=
31,dx=
450,dy=
0,dWidth=
128,dHeight=
40;
-
//积分
-
image =
this.imgObj[
12];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.renderArr.push(img);
-
-
sWidth=
50,sHeight=
70,dx=
76,dy=
5,dWidth=
50,dHeight=
68;
-
//方形卡片 太阳花
-
image =
this.imgObj[
3];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
img.sunCost=
50;
//生产一个苗需要50阳光
-
img.type=
'sun';
//植物类型
-
this.renderArr.push(img);
-
this.cardArr.push(img);
-
-
sWidth=
50,sHeight=
70,dx=
130,dy=
4,dWidth=
50,dHeight=
70;
-
//方形卡片 豌豆
-
image =
this.imgObj[
4];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
img.sunCost=
100;
//生产一个苗需要100阳光
-
img.type=
'wandou';
//植物类型
-
this.renderArr.push(img);
-
this.cardArr.push(img);
-
-
-
sWidth=
97,sHeight=
33,dx=
780,dy=
8,dWidth=
97,dHeight=
33;
-
//开始按钮图片
-
image =
this.imgObj[
5];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.startImage=img;
-
this.renderArr.push(img);
-
-
sWidth=
97,sHeight=
33,dx=
650,dy=
8,dWidth=
97,dHeight=
33;
-
//创建僵尸图片
-
image =
this.imgObj[
8];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.createZombiesImage=img;
-
this.renderArr.push(img);
-
}
点击开始的逻辑
点击开始就是游戏的入口,游戏的大部分功能都是在这个逻辑里面实现,包含:
展示开始图片、开启背景音乐、阳光计分显示、积分显示、创建田的背景方形、创建卡牌的背景方形、开启总任务、定时创建太阳光、定时创建僵尸。
展示开始图片
-
//展示开始图片
-
Plants.prototype.startShow=function(){
-
var image,img,sx=
0,sy=
0,sWidth=
225,sHeight=
108,dx=
this.w
/2-110,dy=this.h/
2
-100,dWidth=
225,dHeight=
108;
-
image =
this.imgObj[
10];
-
img =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.renderArr2.push(img);
-
-
var that=
this;
-
setTimeout(function(){
-
that.clear(img);
-
},
2000);
-
}
这里设置一个延时2秒后自动清除掉这个图片
阳光计分显示
-
//创建阳光分
-
Plants.prototype.createSunText=
function(){
-
x=
40,y=
74,content=
this.sunTotalCount;
-
var text =
new _.Text({
-
x:x,
-
y:y,
-
text:content,
-
font:
'20px ans-serif',
-
textAlign:
'center',
-
fill:
true,
-
fillStyle:
'green'
-
});
-
this.renderArr2.push(text);
-
this.sunCountObj=text;
-
}
积分显示
-
//创建积分
-
Plants.prototype.createCountText=
function(){
-
x=
530,y=
34,content=
this.curCount;
-
var text =
new _.Text({
-
x:x,
-
y:y,
-
text:content,
-
font:
'30px ans-serif',
-
textAlign:
'center',
-
fill:
true,
-
fillStyle:
'pink'
-
});
-
this.renderArr2.push(text);
-
this.countObj=text;
-
}
创建卡牌的背景方形(用于监听鼠标点击卡牌)
根据卡牌数组来创建,循环这个数组,方形的坐标和宽高与卡牌数组元素相对应,并且方形的fillStyle采用rgba来处理,如:rgba(192,192,192,0) rgba(192,192,192,0.6),当最后一个数字是0的时候卡牌可用,当为0.6的时候,卡牌会被遮罩起来不可用(不可用是在鼠标点击的时候控制,这里只是一个遮罩的效果),当然这里会设置一个参数alive,当它为true表示可用,false则点击无效,鼠标点击的时候就是根据这个参数来控制的。
-
//创建卡片背景方形
-
Plants.prototype.createCardBGRect=function(){
-
var x=
0,y=
0,rect,fillStyle,alive;
-
for(
var i=
0;i<
this.cardArr.length;i++){
-
var item=
this.cardArr[i];
-
fillStyle =
this.sunTotalCount>=item.sunCost?
'rgba(192,192,192,0)':
'rgba(192,192,192,0.5)';
-
alive =
this.sunTotalCount>=item.sunCost?
true:
false;
-
rect = new _.Rect({
-
x:item.dx,
-
y:item.dy,
-
width:item.dWidth,
-
height:item.dHeight,
-
fill:
true,
-
fillStyle:fillStyle
-
})
-
rect.sunCost=item.sunCost;
//设定需要花费的阳光数值
-
rect.alive=alive;
-
rect.type=item.type;
-
this.renderArr2.push(rect);
-
this.cardRectArr.push(rect);
-
}
-
-
}
创建田的背景方形(用于监听植物的种植)
根据背景上田的规格,来设置好X、Y坐标以及宽高,这样创建的方形就会和背景相对应,种植物的时候就比较好控制了,解释如下:
上图是我自己随便画的,也没有画好、没画全,实际上每个里面都有,并且比较整齐,我把代码稍微修改一下截图,最终的代码肯定不是这样的哦
这样,就把田的区域一块块的覆盖起来,但我们这里也是要用rgba的方式来,种植物的时候才会突出显示
-
//创建植物田背景方形
-
Plants.prototype.createBGRect=
function(){
-
var x=
0,y=
0,rect;
-
for(
var i=
1;i<=
5;i++){
//5行
-
y =
75+(i
-1)*
100;
-
for(
var j=
1;j<=
9;j++){
//9列
-
x =
105+(j
-1)*
80;
-
rect =
new _.Rect({
-
x:x,
-
y:y,
-
width:
80,
-
height:
100,
-
fill:
true,
-
//fillStyle:_.getRandomColor()
-
fillStyle:
'rgba(0,250,154, 0)'
-
})
-
rect.index=i;
//标记行数
-
this.renderArr2.push(rect);
-
this.bgRectArr.push(rect);
-
}
-
}
-
}
创建阳光
1、向日葵植物创建阳光和定时创建阳光都放到这里了,他们的区别是:定时创建的X坐标随机产生,而向日葵创建的阳光X、Y坐标是根据向日葵的位置来的。
2、设定阳光的分值、阳光的血量、阳光默认运动的终点位置(这个位置可以自己定,我定义Y坐标的是400),阳光为什么有血量呢?这个血量是用来控制消失时间的,比如我设定血量为100,当阳光运动到底部停止运动后,就会开启计算血量的任务,每100毫秒执行一次,让血量 -1,因100毫秒执行10次是1秒,1秒后血量就变成90了,当血量归零后如若依然没有去收集这个阳光,需要让阳光消失,同时关闭此定时器。
3、当点击阳光后,就把计算血量的定时器关闭,开启向左上角运动的动画,当然这里要用到 Math.atan2 根据阳光点击的位置和做上角的位置,计算出角度,然后根据角度利用Math.cos、Math.sin 计算出运动的X、Y的数值,定时器将根据这个数值来运动。
4、运动指定的位置后,要做以下动作:清除运动定时器、阳光积分累加与显示、收集音效开启、阳光消失、卡牌可用状态的更新。
-
//创建阳光
-
Plants.prototype.createSun=
function(plant){
-
var image,sun,sx=
0,sy=
0,sWidth=
77,sHeight=
74,dx=
0,dy=
70,dWidth=
45,dHeight=
44;
-
-
if(plant){
//这种是植物创建的太阳
-
dx = plant.dx;
-
dy = plant.dy;
-
}
else{
-
dx = _.getRandom(
200,
800);
//x方形随机200-800
-
}
-
-
//绘制时的图片下标
-
var startKey=
this.count+
this.zombiesRunCount+
this.wandousRunCount+
this.zombiesDeadCount+
1;
-
//方形卡片盘
-
image =
this.imgObj[startKey];
-
sun =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
sun.imageKey=startKey;
//执行动画更新的下标
-
sun.key=startKey;
//原始下标
-
sun.value=
20;
//收集一个20分
-
sun.blood=
100;
//默认10点血量(10秒消失,因为我给太阳设置的是100毫秒执行一次,所以这个blood设置为100,每次-1 ,10秒就刚好100)
-
sun.floor=
400;
//到底的位置
-
this.renderArr2.push(sun);
-
-
this.suns.push(sun);
-
-
sun.timmer =
setInterval(animate.bind(
this,sun),
100);
-
function animate(z){
-
var that=
this;
-
z.imageKey ++;
-
//一个循环了,重新回到初始位置
-
if(z.imageKey>=(that.sunRunCount+z.key)){
-
z.imageKey=z.key;
-
}
-
z.image = that.imgObj[z.imageKey];
-
z.dy+=
2;
-
if(z.dy>=z.floor){
-
//console.log('太阳到位置了');
-
clearInterval(z.timmer);
-
//开启定时任务,多少秒以后消失
-
sun.timmer =
setInterval(time.bind(
this,sun),
100);
-
}
-
}
-
-
//太阳计时
-
function time(z){
-
var that=
this;
-
z.imageKey ++;
-
//一个循环了,重新回到初始位置
-
if(z.imageKey>=(that.sunRunCount+z.key)){
-
z.imageKey=z.key;
-
}
-
z.image = that.imgObj[z.imageKey];
-
//计算消失时间
-
z.blood--;
-
if(z.blood<=
0){
-
clearInterval(z.timmer);
-
//console.log('太阳到时间了');
-
fade.call(
this,z);
//执行消失
-
}
-
}
-
-
//太阳消失
-
function fade(z){
-
this.clear(z);
-
//console.log('太阳消失了');
-
this.clearAssign(
this.suns,z);
//清楚指定对象
-
z=
null;
-
}
-
//太阳被点击
-
function sunClick(z){
-
//console.log('太阳被点击了')
-
clearInterval(z.timmer);
//清楚之前的定时器
-
-
this.pointsMusic.play();
-
-
var cx=cy=
20;
//收集点的X\Y坐标
-
var angle =
Math.atan2((z.dy-cy), (z.dx-cx))
//弧度
-
-
//计算出X\Y每一帧移动的距离
-
var mx = my=
0;
-
mx =
Math.cos(angle)*
20;
-
my =
Math.sin(angle)*
20;
-
z.mx=mx,z.my=my;
-
//开启移动定时器
-
z.timmer =
setInterval(sunCollect.bind(
this,z),
100);
-
}
-
//收集太阳动画
-
function sunCollect(z){
-
var that=
this;
-
z.imageKey ++;
-
//一个循环了,重新回到初始位置
-
if(z.imageKey>=(that.sunRunCount+z.key)){
-
z.imageKey=z.key;
-
}
-
z.image = that.imgObj[z.imageKey];
-
z.dx-=z.mx;
-
z.dy-=z.my;
-
if(z.dy<=
20||z.dx<=
20){
-
//console.log('太阳收集完成');
-
clearInterval(z.timmer);
-
-
this.moneyfallsMusic.play();
-
-
fade.call(
this,z);
//执行消失
-
//计数累加
-
that.sunTotalCount+=z.value;
-
-
//更新卡片是否可用情况
-
that.updateCardUse();
-
//更新阳光数值
-
that.sunCountObj.text=that.sunTotalCount;
-
}
-
}
-
-
sun.click=sunClick.bind(
this,sun);
//给这个sun对象绑定点击函数
-
}
创建僵尸
1、创建僵尸:定时(10秒)、随机数量(1-5只)、随机行(1-5行,这里表示出现在地图的哪一行)。
2、设定僵尸的血量、僵尸所处的行数、僵尸的状态(run、eat、dead),状态是用来控制切换图片的下标的,不然动画会出错。
3、行走动画依赖于图片的切换和X坐标的改变(每一帧x坐标 减少2即可)。
4、每一帧都要判断x坐标与植物的坐标是否交汇,如果是先关闭行走的动画,更新状态为 eat ,开启吃的动画,切换图片的下标。
5、每一次吃的时候递减植物的血量,判断植物的血量,如果血量归零则表示吃完了,此时要清理掉植物,僵尸回归行走的动画,状态改为run;若没吃完则继续吃。
6、每一帧也要判断僵尸的x坐标是否到了最左边,如果是游戏结束。
创建
-
//创建僵尸
-
Plants.prototype.createZombie=
function(){
-
var image,zomble,sx=
0,sy=
0,sWidth=
75,sHeight=
119,dx=
900
-75,dy=
270,dWidth=
75,dHeight=
119;
-
-
var index = _.getRandom(
1,
6);
//随机获取1\2\3\4\5 行数
-
if(index==
1){
-
dy=
60;
-
}
else
if(index==
2){
-
dy=
160;
-
}
else
if(index==
3){
-
dy=
260;
-
}
else
if(index==
4){
-
dy=
355;
-
}
else
if(index==
5){
-
dy=
460;
-
}
-
-
//绘制时的图片下标
-
var startKey=
this.count+
1;
-
//方形卡片盘
-
image =
this.imgObj[startKey];
-
zomble =
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
zomble.imageKey=startKey;
//执行动画更新的下标
-
zomble.key=startKey;
//原始下标
-
zomble.blood=
10;
//默认10点血量
-
zomble.index=index;
//设定僵尸的行数
-
zomble.state=
'run';
-
this.renderArr2.push(zomble);
-
this.zombies.push(zomble);
-
-
zomble.run=run.bind(
this);
-
-
zomble.run();
-
-
function run(){
-
zomble.timmer =
setInterval(animate.bind(
this,zomble),
100);
-
}
-
function animate(z){
-
var that=
this;
-
z.imageKey ++;
-
//一个循环了,重新回到初始位置
-
if(z.imageKey>=(that.zombiesRunCount+z.key)){
-
z.imageKey=z.key;
-
}
-
z.image = that.imgObj[z.imageKey];
-
z.dx-=
2;
-
//判断有没有接触到植物,如果有开始吃植物
-
that.eat(zomble)
-
-
if(z.dx<=
100){
-
console.log(
'结束了');
-
that.end();
-
}
-
}
-
}
僵尸吃
-
//僵尸吃
-
Plants.prototype.eat=function(zomble){
-
//先判断当前僵尸有没有到达吃的位置,有的话就开始吃,关闭掉之前的僵尸动画,开始吃的动画
-
var plants=
this.plants;
-
var plant;
//被捕获的植物
-
for(
var i=
0;i<plants.length;i++){
-
var item=plants[i];
-
if(item.index==zomble.index){
-
if(item.dx+item.dWidth-
20>=zomble.dx){
//判断为吃
-
plant=item;
-
break;
-
}
-
}
-
-
}
-
if(plant){
-
clearInterval(zomble.timmer);
//清除移动动画
-
zomble.imageKey=zomble.key=
this.count+
this.zombiesRunCount+
this.wandousRunCount+
this.zombiesDeadCount+
this.sunRunCount+
1;
//设定key
-
zomble.state=
'eat';
-
zomble.timmer = setInterval(animate.bind(
this,zomble,plant),
100);
-
}
-
-
function animate(z,p){
-
this.eatMusic.play();
-
var that=
this;
-
z.imageKey ++;
-
//一个循环了,重新回到初始位置
-
if(z.imageKey>=(that.zombiesEatCount+z.key)){
-
z.imageKey=z.key;
-
}
-
z.image = that.imgObj[z.imageKey];
-
-
p.blood--;
//植物血量的处理
-
if(p.blood<=
0){
-
//console.log('植物被吃了');
-
clearInterval(z.timmer);
-
//清除植物
-
this.delPlant(p);
-
zomble.state=
'run';
-
zomble.imageKey=zomble.key=
this.count+
1;
//设定key
-
//继续移动
-
z.run();
-
}
-
}
-
}
删除植物
-
//删除掉植物
-
Plants.prototype.delPlant=
function(plant,type){
-
if(!
type){
//还没有创建的植物不需要清除这两个任务
-
//停止植物自身的动画
-
clearInterval(plant.timmerSelf);
-
//停止植物发射子弹的动画
-
clearInterval(plant.timmer);
-
}
-
//渲染中删除
-
this.clear(plant);
-
//plants数组中删除
-
this.clearAssign(
this.plants,plant);
-
//植物对应的背景处理
-
if(plant.bgRect){
-
plant.bgRect.alive=
false;
-
plant.bgRect.plant=
false;
-
}
-
plant=
null;
-
}
创建向日葵
1、点击卡牌后的创建,所以肯定是要传入鼠标的位置,创建的跟随鼠标移动(此时向日葵已经创建)
2、设定 alive 函数,当在田里面选中位置后,执行此函数,进行种植操作。
3、扣除阳光花费、更新卡牌是否可用、开启定时产生阳光的任务。
-
//创建太阳植物
-
Plants.prototype.createSunPlant=function(pos,item){
-
var image,plant,sx=
0,sy=
0,sWidth=
63,sHeight=
73,dx=
110,dy=
300,dWidth=
63,dHeight=
73;
-
dx = pos.x,dy=pos.y;
//设定初始位置为鼠标的位置
-
//绘制时的图片下标
-
var startKey=
this.count+
this.zombiesRunCount+
this.wandousRunCount+
this.zombiesDeadCount+
this.sunRunCount+
this.zombiesEatCount+
1;
-
//方形卡片盘
-
image =
this.imgObj[startKey];
-
plant = new _.ImageDraw({image:image,sx:sx,sy:sy,sWidth:sWidth,sHeight:sHeight, dx:dx, dy:dy ,dWidth:dWidth,dHeight:dHeight});
-
plant.imageKey=startKey;
//执行动画更新的下标
-
plant.key=startKey;
//原始下标
-
plant.sunCost=item.sunCost;
//阳光花费值
-
plant.blood=
50;
//设定为50血量,实际是5秒吃完,因为100毫秒计算一次吃
-
plant.id=
'SunPlant';
-
this.renderArr2.push(plant);
-
-
this.plants.push(plant);
-
this.currPlant=plant;
//标记正在创建的植物
-
-
plant.alive=alive.bind(
this);
-
-
function alive(bgRect){
-
//设置背景对象
-
plant.bgRect=bgRect;
-
//扣除阳光花费
-
this.sunTotalCount-=plant.sunCost;
-
//更新卡片是否可用情况
-
this.updateCardUse();
-
//更新阳光数值
-
this.sunCountObj.text=
this.sunTotalCount;
-
//每6秒发射一个阳光
-
plant.timmer = setInterval(shoot.bind(
this),
6000);
-
this.plantMusic.play();
//音乐
-
}
-
-
function shoot(){
-
this.createSun(plant);
-
}
-
//植物本身的动画
-
plant.timmerSelf = setInterval(animate.bind(
this,plant),
100);
-
function animate(p){
-
var that=
this;
-
p.imageKey ++;
-
//一个循环了,重新回到初始位置
-
if(p.imageKey>=(that.sunPlantRunCount+p.key)){
-
p.imageKey=p.key;
-
}
-
p.image = that.imgObj[p.imageKey];
-
}
-
}
点击卡牌后移动鼠标,向日葵会跟随鼠标移动,移动到田的方形位置,则此块方形的颜色会突出,点击它则会种植下去。
创建豌豆植物
1、与向日葵的创建很相似,就不同的时候向日葵创建的是阳光,豌豆植物创建的是小豌豆,可以攻击僵尸的。
2、子弹在运动的时候、判断是否与僵尸接触,如果接触则执行击中的动画、删除这个子弹、减去僵尸的血量、如果僵尸死亡,则开启僵尸死亡动画,增加积分。
创建
-
//创建豌豆
-
Plants.prototype.createWandou=
function(plant){
-
var image,img,sx=
0,sy=
0,sWidth=
28,sHeight=
28,dx=plant.dx+
50,dy=plant.dy,dWidth=
28,dHeight=
28;
-
//绘制时的图片下标
-
var startKey=
6;
-
//方形卡片盘
-
image =
this.imgObj[startKey];
-
var wandou=
new _.ImageDraw({
image:image,
sx:sx,
sy:sy,
sWidth:sWidth,
sHeight:sHeight,
dx:dx,
dy:dy ,
dWidth:dWidth,
dHeight:dHeight});
-
this.renderArr2.push(wandou);
-
-
wandou.index=plant.index;
//给子弹设定行数标示
-
-
this.shootMusic.play();
//射击音乐
-
wandou.timmer =
setInterval(wandouMove.bind(
this,wandou),
100);
-
-
function wandouMove(wandou){
-
wandou.dx+=
10;
-
//判断击中僵尸
-
var flag =
this.hit(wandou);
-
if(flag || wandou.dx>
850){
//击中目标 或者 超出边界
-
clearInterval(wandou.timmer);
-
if(flag){
//击中目标
-
//创建子弹击中的图片
-
this.hitAnimate(wandou);
-
this.hitMusic.play();
//击中音乐
-
}
-
//清除这个子弹
-
this.clear(wandou);
-
wandou=
null;
-
}
-
}
-
}
击中目标
-
//击中
-
Plants.prototype.hit=function(obj){
-
var arr =
this.zombies;
//僵尸对象
-
for(
var i=
0;i<arr.length;i++){
-
var item = arr[i];
-
if(item.dx<=obj.dx+obj.dWidth && obj.index==item.index ){
//子弹的函数标示与僵尸的行数标示也要相等
-
item.blood--;
-
if(item.blood==
0){
//消灭这个僵尸
-
clearInterval(item.timmer);
-
if(item.state==
'run'){
-
item.imageKey=item.key=item.key+
this.zombiesRunCount+
this.wandousRunCount;
//设置死亡图片下标
-
}
else
if(item.state==
'eat'){
-
item.imageKey=item.key=item.key-
this.sunRunCount-
this.zombiesDeadCount;
//设置死亡图片下标
-
}
-
-
item.state=
'dead';
-
this.addCount();
//增加积分
-
this.dead(item);
//死亡动画
-
arr.splice(i,
1);
-
}
-
return
true;
-
}
-
}
-
}
加入事件控制
-
//给canvas2画布添加鼠标移动事件(因为画布2在上面)
-
canvas2.addEventListener(
'mousemove',
this.mouseMove.bind(
this));
-
-
//给canvas2画布添加鼠标点击事件(因为画布2在上面)
-
canvas2.addEventListener(
'click',
this.mouseClick.bind(
this));
-
-
//给canvas2画布添加鼠标右键事件(因为画布2在上面)
-
canvas2.addEventListener(
'contextmenu',
this.contextMenu.bind(
this));
鼠标移动事件
-
//鼠标移动事件
-
Plants.prototype.mouseMove=function(e){
-
var that=
this;
-
if(that.gameOver)
return ;
//目前设定的是结束后,要刷新页面才可以开始
-
if(!
this.startImage)
return;
//防止没加载完成报错
-
var pos = _.getOffset(e);
//获取鼠标位置
-
var isCatch =
this.startImage.isPoint(pos);
//鼠标捕捉
-
-
if(!isCatch &&
this.gameAlive){
-
isCatch =
this.createZombiesImage.isPoint(pos);
//鼠标捕捉
-
}
-
-
if(!isCatch){
-
if(
this.gameAlive && !
this.currPlant) {
//游戏开始,并且没有正在创建的植物的时候可以执行
-
//循环卡片背景数组
-
for(
var i=
0;i<
this.cardRectArr.length;i++){
-
var item=
this.cardRectArr[i];
-
if(item.isPoint(pos) && item.alive){
//鼠标捕捉
-
isCatch=
true;
-
break;
-
}
-
}
-
}
-
}
-
if(!isCatch){
-
//循环太阳数组
-
for(
var i=
0;i<
this.suns.length;i++){
-
var item=
this.suns[i];
-
if(item.isPoint(pos)){
//鼠标捕捉
-
isCatch=
true;
-
break;
-
}
-
}
-
}
-
-
var plant =
this.currPlant;
-
if(!isCatch){
-
if(plant){
//创建植物的时候,才出现填入背景框
-
//循环田背景数组
-
for(
var i=
0;i<
this.bgRectArr.length;i++){
-
var item=
this.bgRectArr[i];
-
if(item.isPoint(pos) && !item.plant){
//鼠标捕捉,并且当前没有植物
-
isCatch=
true;
-
item.fillStyle=
"rgba(0,250,154, 0.5)";
-
}
else{
-
item.fillStyle=
"rgba(0,250,154, 0)";
-
}
-
}
-
}
-
}
-
-
if(isCatch){
-
this.el.style.cursor =
'pointer';
//改为手状形态
-
}
else{
-
this.el.style.cursor =
'';
-
}
-
//表示当前正在创建植物
-
if(plant){
-
plant.dx=pos.x;
-
plant.dy=pos.y;
-
}
-
-
this.render2();
-
}
鼠标点击事件
-
//鼠标点击事件
-
Plants.prototype.mouseClick=function(e){
-
if(
this.gameOver)
return ;
//目前设定的是结束后,要刷新页面才可以开始
-
var that=
this;
-
var pos = _.getOffset(e);
//获取鼠标位置
-
var isCatch = that.startImage.isPoint(pos);
//鼠标捕捉
-
if(isCatch){
-
that.start();
-
}
-
if(!isCatch &&
this.gameAlive){
-
isCatch =
this.createZombiesImage.isPoint(pos);
//鼠标捕捉
-
if(isCatch){
-
that.createZombie();
-
}
-
}
-
if(!isCatch){
-
if(
this.gameAlive && !
this.currPlant) {
//游戏开始,并且没有正在创建的植物的时候可以执行
-
//循环卡片数组
-
for(
var i=
0;i<
this.cardRectArr.length;i++){
-
var item=
this.cardRectArr[i];
-
if(item.isPoint(pos) && item.alive){
//鼠标捕捉
-
isCatch=
true;
-
//生成一个新的豌豆苗,跟随鼠标移动
-
this.createPlant(pos,item);
//创建植物
-
break;
-
}
-
}
-
}
-
}
-
-
if(!isCatch){
-
//循环太阳数组
-
for(
var i=
0;i<
this.suns.length;i++){
-
var item=
this.suns[i];
-
if(item.isPoint(pos)){
//鼠标捕捉
-
item.click && item.click();
-
break;
-
}
-
}
-
}
-
if(!isCatch){
-
var plant =
this.currPlant;
-
if(plant){
//正在创建植物
-
//循环田背景数组
-
for(
var i=
0;i<
this.bgRectArr.length;i++){
-
var item=
this.bgRectArr[i];
-
if(item.plant)
continue;
//如果当前有植物,则跳过
-
if(item.isPoint(pos)){
//鼠标捕捉
-
isCatch=
true;
-
plant.dx=item.x+
10;
-
plant.dy=item.y+
20;
-
plant.index=item.index;
//给plant设置行数
-
plant.alive(item);
//植物生效
-
-
item.plant=
true;
//表示有植物
-
item.fillStyle=
"rgba(0,250,154, 0)";
-
-
this.currPlant=
null;
-
break;
-
}
-
}
-
}
-
-
}
-
}
鼠标右键事件
-
//右键事件
-
Plants.prototype.contextMenu=function(e){
-
var e = e||window.event;
-
//取消右键默认事件
-
e.preventDefault && e.preventDefault();
-
if(
this.gameOver)
return ;
//目前设定的是结束后,要刷新页面才可以开始
-
if(!
this.startImage)
return;
//防止没加载完成报错
-
if(!
this.gameAlive)
return;
-
console.log(
'oncontextmenu');
-
//正在创建的植物删除
-
this.delPlant(
this.currPlant,
1);
-
this.currPlant=
null;
-
//循环田背景数组
-
for(
var i=
0;i<
this.bgRectArr.length;i++){
-
var item=
this.bgRectArr[i];
-
item.fillStyle=
"rgba(0,250,154, 0)";
-
}
-
}
总结
基本的功能实现了,但是有很多没有实现的功能这里交代一下,确实没有时间去搞了,要上班。。。。。,不能像在座赚了几个亿的大佬一样逍遥自在:
1、结束后必须刷新页面,才能重新开始(”点击开始“按钮)。
2、卡牌支持的作物也比较少,作物也不能用铲子替换。
3、没有过关的感觉、没有车子压僵尸的场景等等(不说了还有好多...)。
如果作为完整的游戏来说,肯定还有很多功能要做、很多地方要完善、其中相当的代码也可以重构;但是如果作为一个练手、学习、思路的分享,我觉得是足够了。
昨天做好的东西,今天晚上写出来也花了不少时间,能看到这里的都是大佬。
欢迎各位大佬 点赞+评论+关注,谢谢!
源码下载
方式1:少量积分,下载代码
方式2:关注下方公众号,回复 129 下载代码
★ 更多源码
♥ 老父亲给女儿做的下雪特效,满足女儿看雪的愿望(附源码)♥
♥ 雷达扫描特效(附源码)♥
♥ 香港黄金配角吴孟达去世,80后程序员以轮播图来悼念达叔,达叔一路走好!(附源码)♥
♥ 原生js写的左侧飞入拼图特效,你是喜欢美女单飞还是双飞(附源码)♥
♥ 用js写的旋转木马,在新年献给各位刚登基的皇帝,让你的后宫转起来!程序员就是可以为所欲为!(附源码)♥
♥ 用js写的轮播图,八位女明星,你翻谁的牌,程序员就是可以为所欲为!(附源码)♥
♥ 原生js实现美女拼图,把美女老婆抱回家,5个美女够不够!程序员就是可以为所欲为!(附源码)♥
♥ 用js仿探探拖拽卡片的效果、飞卡片的效果,感觉挺酷,最后有美女看哦!(附源码)♥
♥ 老婆说程序员不懂浪漫,程序员默默拿起了键盘,这就亲手带你去看流星雨,女人真的会影响男人拔刀的速度!(附源码)♥
♥ 学生成绩管理系统(jsp+jquery+java+mysql+tomcat)有源码,你的毕设我的心(附源码)♥
转载:https://blog.csdn.net/dkm123456/article/details/115871302