写在前面

本文参考了课程先辈留下来的报告,在前人的基础上进行了代码框架和逻辑的整合,希望对大家有所帮助!

会实现PVZ的哪些功能?

PVZ逻辑示意图

  1. 阳光逐渐下落、触地逐渐消失
  2. 僵尸随机出现在某一行并向前移动
  3. 僵尸判断当前格子有无植物,并吃掉
  4. 点击豌豆卡片、选择豌豆、将豌豆种下
  5. 豌豆判断当前行、右边是否有僵尸,并射击

准备素材

开始前,先将所需的素材拖入An的库中,并为每个素材创建链接。

这是我们这个demo所需的所有素材,现在来详细讲讲:

  1. plantMc:这是我们最终种下的植物的样子(前后摇摆的豌豆)
  2. selectorMc:直译意思是选择器,就是我们决定把植物种在某个格子内时会出现的影子
  3. sunMc:太阳的样子
  4. bulletMc:子弹的样子
  5. zombieMc:僵尸的样子
  6. plantMovingMc:选择卡片后我们拖动的植物的样子
  7. peaShooter:豌豆的卡片

功能模块拆分实现

这个demo不是很难!大家冲鸭!!
下面将用朴实的语言来描述各个功能以及其代码实现。建议大家在看下面内容之前概览一下
PVZ逻辑示意图
以清晰的知道每一步处于框架中的哪个位置。

看看主函数

首先我们来看看Main函数中包含什么。

  1. steupField():游戏区域设置,并初始化,即创建一个5×9的数组
  2. drawField():将各个容器添加到舞台,创建金钱显示区域
  3. addZombies():每隔一段时间创建一个僵尸,放置在屏幕右边外侧
  4. addPlants():创建一个植物卡片,在卡片上添加点击侦听事件
  5. fallingSuns():每隔一段时间创建一个太阳
  6. stage.addEventListener(“tick”,onEnterFrm):遍历植物->存储子弹->遍历子弹,遍历僵尸,遍历太阳

初始化一些变量

不妨先把背景图在An中放入舞台,试一试图片大小,量一下相关尺寸。

var plantsArray = new Array()//种植下的植物数组
var zombiesArray = new Array();//出现的僵尸数组
//
//行高和列宽
//
var gridHeight = 115;
var gridWidth = 95;
var borderTop = 110;//bordertop
var borderLeft = 320;//borderleft
//
// 容器
//
var sunContainer = new cjs.Container();// 阳光容器
var plantContainer = new cjs.Container();// 植物容器
var bulletContainer = new cjs.Container();// 子弹容器
var zombieContainer = new cjs.Container();// 僵尸容器
var overlayContainer = new cjs.Container();// 覆盖物容器
//
// 种植物会用到的
//
var movingPlant;// 拖动中的植物
var selector;// 选择器(影子)
//
// 其他
//
var money = 100;// 金钱总量
var moneyText = new cjs.Text("Money:"+money,"50px Times","#000000")  ;// 动态文本框,用来显示玩家的金钱
var playerMoving = false;// 标志玩家是否在移动一个植物
var totalZombies = 0;

接下来,我们就要用这些变量做一些事情了!

初始化区域 setupField()和drawField()

//
// 游戏区域设置,创建用来存储植物和僵尸信息的数组
//
function setupField(){for (var i = 0; i < 5; i++) {plantsArray[i] = new Array();zombiesArray[i] = new Array();for (var j=0; j<9; j++) {plantsArray[i][j] = 0;}}
}//
// 画出游戏区域
//
function drawField(){stage.addChild(sunContainer);stage.addChild(plantContainer);stage.addChild(bulletContainer);stage.addChild(zombieContainer);stage.addChild(overlayContainer);//Container前不能加stage.overlayContainer.addChild(moneyText);updateMoney();moneyText.textColor = 0x000000;moneyText.height=20;
}
//
// 更新显示阳光值
// 每次变量money发生变化时都要记得更新
//
function updateMoney(){moneyText.text = money.toString();
}

此时,我们就拥有了土地和钱,虽然它们现在看不到摸不着。

造僵尸!


创建一个计时器,每隔3000ms会让newZombie()这个函数执行一次。

//
// 初始化僵尸
//
function addZombies(){var zombieTimer = setInterval(newZombie, 3000);
}//
// 增加一个新的僵尸
//
function newZombie(TimerEvent){var zombie = new lib.zombieMc();// 构造僵尸totalZombies++;zombieContainer.addChild(zombie);// 添加僵尸zombie.zombieRow=Math.floor(Math.random()*5);// 随机行数zombie.name="zombie_"+totalZombies;//僵尸取名zombiesArray[zombie.zombieRow].push(zombie.name);// 增加第row行的僵尸zombie.x=1000;// 把僵尸放在屏幕的右边zombie.y=zombie.zombieRow*gridHeight+borderTop;//把僵尸放到对应的行上
}

这样,每隔一会儿我们就会得到一只停在原地的僵尸。
接下来,我们会在每一帧遍历僵尸,让他们走动起来或者吃东西。

种太阳!

//
// 初始化太阳
//
function fallingSuns(){var fallingSunsTimer = setInterval(newSun, 3000);
}
//
// 增加一个新的阳光
//
function newSun(){var sunRow=Math.floor(Math.random()*5);// 随机行var sunCol=Math.floor(Math.random()*9);// 随机列var sun = new lib.sunMc();// 构造太阳sun.mouseChildren = false;//让整个太阳影片剪辑成为一个整体sun.buttonMode = true;// 当鼠标滑过阳光时,改变鼠标的形状sunContainer.addChild(sun);// 添加sun.x=borderLeft + sunCol*gridWidth;// 把太阳放在对应的列上sun.destinationY = borderTop+sunRow*gridHeight;// definines the sun y destination pointsun.y=20;// 把阳光放在舞台顶部的上方sun.addEventListener("click",sunClicked);// 给阳光注册点击事件
}

注意sun.mouseChildren = false这句话,他很重要。
种太阳的逻辑和生成僵尸一样。
不同的是,我们要给太阳添加鼠标点击事件。

//
// 点击阳光
//
function sunClicked(event){var sunToRemove = event.target;//获得被点击的太阳sunToRemove.removeEventListener("click",sunClicked);// removes the CLICK listenermoney += 50;// makes the player earn money (5)updateMoney();// updates money text//console.log("sunClicked! ");sunContainer.removeChild(sunToRemove);// 移除
}

好的,现在我们每隔一段时间就会有一个不会动的太阳生成。
后面要在每帧中遍历太阳,让他们下落,或者逐渐消失。

种豌豆

种下植物的逻辑有些不一样,我们来捋一下。
首先在舞台上要有一个卡片让我们选择,并且这个卡片具有一个点击侦听。
点击后会执行onPlantClicked()。

//
// 创建一个植物栏,现在只有一种植物
//
function addPlants(){var card_peaShooter = new lib.peaShooter();// 构造一株新的植物overlayContainer.addChild(card_peaShooter);// 增加植物card_peaShooter.buttonMode=true;// 鼠标滑过改变形状card_peaShooter.x = 60;card_peaShooter.y = 60;card_peaShooter.addEventListener("click",onPlantClicked);// 植物选择区域注册点击事件
}

点击卡片后会发生什么事情呢?

//
// 选择植物卡片
//
function onPlantClicked(){// 检查玩家是否有足够的钱(当前是10)来购买植物,并且是否正在拖动一个植物if (money >= 100&&! playerMoving) {money -= 100;// pays the plantupdateMoney();// updates money textselector = new lib.selectorMc();// 创建一个新的选择器selector.visible = false;// 使选择器不可见overlayContainer.addChild(selector);// 把选择器加入到显示列表movingPlant = new lib.plantMovingMc();// 构建一个供玩家拖动的植物movingPlant.addEventListener("click",placePlant);// 给拖动的植物注册点击事件overlayContainer.addChild(movingPlant);//把该植物加入到显示列表playerMoving = true;//告诉脚本正在移动一株植物}
}

好的,现在我们拥有一个卡片,点击后会创建一个可以拖动的植物,我们会在每帧执行的函数中让它的坐标等于我们鼠标的坐标。
我们给拖动中的植物也添加了点击侦听,点击后会发生什么?

//
// 放置植物
//
function placePlant(){var plantRow=Math.floor((stage.mouseY-borderTop)/gridHeight);var plantCol=Math.floor((stage.mouseX-borderLeft)/gridWidth);// 判断鼠标是否在放置区域内部并且上面没有植物if (plantRow>=0&&plantCol>=0&&plantRow<5&&plantCol<9&&plantsArray[plantRow][plantCol]==0) {var placedPlant=new lib.plantMc();// 构建一株植物,用来种植placedPlant.name="plant_"+plantRow+"_"+plantCol;// 给植物一个名字placedPlant.fireRate=48;//  植物的开火速率,单位帧placedPlant.recharge=48;// 当recharge 等于 fireRate时,植物已经准备好开火了placedPlant.plantRow=plantRow;// plant rowplantContainer.addChild(placedPlant);// adds the plant//console.log("plantRow:"+plantRow);//console.log("plantCol:"+plantCol);placedPlant.x=plantCol*gridWidth+borderLeft+30;placedPlant.y=plantRow*gridHeight+borderTop+30;//console.log("plant's x:"+placedPlant.x);//console.log("plant's y:"+placedPlant.y);playerMoving=false;// tells the script the player is no longer movingmovingPlant.removeEventListener("click",placePlant);// removes the CLICK listener from the draggable plantoverlayContainer.removeChild(selector);// removes the selectoroverlayContainer.removeChild(movingPlant);// removes the plant itselfplantsArray[plantRow][plantCol]=1;// 更新游戏区块信息}
}

仔细看,这段有一个bool变量叫做playerMoving,这个变量会告诉selector(植物影子)是否要判断自己出不出现。

下面的内容每帧都要执行!!!

下面的每一个代码片段,都被包含在onEnterFrm()函数里面。

selector(植物影子)和拖动中的植物


每一帧中,我们都判断玩家是否在移动植物。

    //// 植物放置//if (playerMoving) {movingPlant.x=stage.mouseX;movingPlant.y=stage.mouseY;//console.log("mouseY:"+stage.mouseY);var plantRow=Math.floor((stage.mouseY-borderTop)/gridHeight);var plantCol=Math.floor((stage.mouseX-borderLeft)/gridWidth);//console.log("plantRow:"+plantRow);//console.log("plantCol:"+plantCol);// 鼠标在区域内if (plantRow>=0&&plantCol>=0&&plantRow<5&&plantCol<9) {selector.visible = true;// shows the selectorselector.x=borderLeft+plantCol*gridWidth+30;selector.y=borderTop+plantRow*gridHeight+30;} else {selector.visible=false;// hide the selector}}
让太阳动起来

先前我们创建了不会动的太阳,现在每一帧,我们遍历它,让它不做懒狗。

 //// 阳光管理//for (i=0; i<sunContainer.numChildren; i++) {var fallingSun = sunContainer.getChildAt(i);// 阳光下落if (fallingSun.y<fallingSun.destinationY) {fallingSun.y++;// 接着下落} else {fallingSun.alpha-=0.01;// 落完逐渐消失// 阳光消失了if (fallingSun.alpha<0) {fallingSun.removeEventListener("click",sunClicked);sunContainer.removeChild(fallingSun);// removes the sun//console.log("下面展现的是消失的太阳");//console.log(fallingSun);}}}
现在要让植物吐子弹了

 //// 植物管理//for (i = 0; i < plantContainer.numChildren; i++) {var currentPlant = plantContainer.getChildAt(i);//遍历每个植物// 准备好开火if (currentPlant.recharge == currentPlant.fireRate ) {// 检查是否有僵尸与植物处于同一行if (zombiesArray[currentPlant.plantRow].length > 0) {// 同行有僵尸for (j=0; j < zombiesArray[currentPlant.plantRow].length; j++) {//遍历当前行的僵尸var targetZombie = zombieContainer.getChildByName(zombiesArray[currentPlant.plantRow][j]);// 获取僵尸// 僵尸在右if (targetZombie.x > currentPlant.x) {var bullet = new lib.bulletMc();// 构造新子弹bulletContainer.addChild(bullet);// 添加子弹bullet.x = currentPlant.x+20;bullet.y = currentPlant.y;bullet.sonOf = currentPlant;//子弹当儿子,存储该子弹是由哪一株植物射出的currentPlant.recharge = 0;// 重新装填break;// exits the j for loop}}}}// 子弹装填中if (currentPlant.recharge < currentPlant.fireRate) {currentPlant.recharge ++;// recharges the plant}}

非常棒,现在我们的子弹容器储存了全部的子弹,并且我们可以通过bullet.sonOf属性得到子弹的主人。
接下来,我们就要遍历子弹,控制子弹移动和打人。

子弹打人


不难发现,我们需要内外两重循环,内重循环中我们还需要做判断。

 //// 子弹管理//for (i=0; i<bulletContainer.numChildren; i++) {var movingBullet = bulletContainer.getChildAt(i);movingBullet.x+=3;//子弹移动var firingPlant = movingBullet.sonOf;//获得这个子弹是哪个植物射击的// 子弹飞走了for (j=0; j<zombieContainer.numChildren; j++) {var movingZombie=zombieContainer.getChildAt(j);if (movingZombie.zombieRow != firingPlant.plantRow) continue;// 碰撞检测if (movingZombie.x < movingBullet.x && movingZombie.x > movingBullet.x-5 ){movingZombie.alpha-=0.3;// 僵尸逐渐死亡bulletContainer.removeChild(movingBullet);// 移除子弹// 僵尸死了if (movingZombie.alpha<0) {zombiesArray[movingZombie.zombieRow].splice(zombiesArray[movingZombie.zombieRow].indexOf(movingZombie.name),1);// 减少该行的僵尸 zombieContainer.removeChild(movingZombie);// 移除显示列表}break;}}if (movingBullet.x>1200) {bulletContainer.removeChild(movingBullet);// 移除这颗子弹}}

到目前为止,植物会发射子弹,并且子弹会打僵尸了。

僵尸吃菜

这个逻辑没有植物打人那么绕。

 //// 僵尸吃菜//var zombieColumn;for (i=0; i<zombieContainer.numChildren; i++) {//遍历僵尸movingZombie=zombieContainer.getChildAt(i);zombieColumn = Math.floor((movingZombie.x-borderLeft)/gridWidth);// 得到僵尸所在的列//console.log("zombieColumn"+zombieColumn);// 判断是否有植物在同一块中if (zombieColumn<0||zombieColumn>8||plantsArray[movingZombie.zombieRow][zombieColumn]==0) {movingZombie.x-=0.5;// moves each zombie left by 1/2 pixels} else {// 僵尸打人//console.log("僵尸吃菜中");var attackedPlant = plantContainer.getChildByName("plant_"+movingZombie.zombieRow+"_"+zombieColumn);attackedPlant.alpha-=0.01;// 植物逐渐死亡// 植物死了if (attackedPlant.alpha < 0) {plantsArray[movingZombie.zombieRow][zombieColumn] = 0;//removes the plant from the arrayplantContainer.removeChild(attackedPlant);//removes the plant Display Object from Display List}}}

好了!现在僵尸也学会了反抗!

代码总结

再贴一次思维导图,大家一定要对着这个来!
PVZ逻辑示意图
下面是全部代码:


//
// 二维数组游戏区块
//
var plantsArray = new Array()//种植下的植物数组
var zombiesArray = new Array();//出现的僵尸数组
//
//行高和列宽
//
var gridHeight = 115;
var gridWidth = 95;
var borderTop = 110;//bordertop
var borderLeft = 320;//borderleft
//
// 容器
//
var sunContainer = new cjs.Container();// 阳光容器
var plantContainer = new cjs.Container();// 植物容器
var bulletContainer = new cjs.Container();// 子弹容器
var zombieContainer = new cjs.Container();// 僵尸容器
var overlayContainer = new cjs.Container();// 覆盖物容器
//
// actors
//
var movingPlant;// 拖动中的植物
var selector;// 选择器
//
// other variables
//
var money = 100;// 金钱总量
var moneyText = new cjs.Text("Money:"+money,"50px Times","#000000")  ;// 动态文本框,用来显示玩家的金钱
var playerMoving = false;// 标志玩家是否在移动一个植物
var totalZombies = 0;//
// 游戏区域设置,创建用来存储植物和僵尸信息的数组
//
function setupField(){for (var i = 0; i < 5; i++) {plantsArray[i]=new Array();zombiesArray[i]=new Array();for (var j=0; j<9; j++) {plantsArray[i][j]=0;}}
}//
// 画出游戏区域
//
function drawField(){stage.addChild(sunContainer);stage.addChild(plantContainer);stage.addChild(bulletContainer);stage.addChild(zombieContainer);stage.addChild(overlayContainer);//Container前不能加stage.overlayContainer.addChild(moneyText);updateMoney();moneyText.textColor=0x000000;moneyText.height=20;
}//
// 更新显示阳光值
//
function updateMoney(){moneyText.text = money.toString();
}
//
// 初始化僵尸
//
function addZombies(){var zombieTimer = setInterval(newZombie, 3000);
}//
// 增加一个新的僵尸
//
function newZombie(TimerEvent){var zombie = new lib.zombieMc();// 构造僵尸totalZombies++;zombieContainer.addChild(zombie);// 添加僵尸zombie.zombieRow=Math.floor(Math.random()*5);// 随机行数zombie.name="zombie_"+totalZombies;//僵尸取名zombiesArray[zombie.zombieRow].push(zombie.name);// 增加第row行的僵尸zombie.x=1000;// 把僵尸放在屏幕的右边zombie.y=zombie.zombieRow*gridHeight+borderTop;//把僵尸放到对应的行上
}
//
// 初始化太阳
//
function fallingSuns(){var fallingSunsTimer = setInterval(newSun, 3000);
}
//
// 增加一个新的阳光
//
function newSun(){var sunRow=Math.floor(Math.random()*5);// 随机行var sunCol=Math.floor(Math.random()*9);// 随机列var sun = new lib.sunMc();// 构造太阳sun.mouseChildren = false;//让整个太阳影片剪辑成为一个整体sun.buttonMode = true;// 当鼠标滑过阳光时,改变鼠标的形状sunContainer.addChild(sun);// 添加sun.x=borderLeft + sunCol*gridWidth;// 把太阳放在对应的列上sun.destinationY = borderTop+sunRow*gridHeight;// definines the sun y destination pointsun.y=20;// 把阳光放在舞台顶部的上方sun.addEventListener("click",sunClicked);// 给阳光注册点击事件
}
//
// 点击阳光
//
function sunClicked(event){var sunToRemove = event.target;//获得被点击的太阳sunToRemove.removeEventListener("click",sunClicked);// removes the CLICK listenermoney += 50;// makes the player earn money (5)updateMoney();// updates money text//console.log("sunClicked! ");sunContainer.removeChild(sunToRemove);// 移除
}
//
// 创建一个植物栏,现在只有一种植物
//
function addPlants(){var card_peaShooter = new lib.peaShooter();// 构造一株新的植物overlayContainer.addChild(card_peaShooter);// 增加植物card_peaShooter.buttonMode=true;// 鼠标滑过改变形状card_peaShooter.x=60;card_peaShooter.y=60;card_peaShooter.addEventListener("click",onPlantClicked);// 植物选择区域注册点击事件
}//
// 选择植物卡片
//
function onPlantClicked(){// 检查玩家是否有足够的钱(当前是10)来购买植物,并且是否正在拖动一个植物if (money>=100&&! playerMoving) {money-=100;// pays the plantupdateMoney();// updates money textselector = new lib.selectorMc();// 创建一个新的选择器selector.visible=false;// 使选择器不可见console.log("创建了一个selector!"+selector);overlayContainer.addChild(selector);// 把选择器加入到显示列表movingPlant=new lib.plantMovingMc();// 构建一个供玩家拖动的植物movingPlant.addEventListener("click",placePlant);// 给拖动的植物注册点击事件overlayContainer.addChild(movingPlant);//把该植物加入到显示列表playerMoving=true;//告诉脚本正在移动一株植物}
}
//
// 放置植物
//
function placePlant(){var plantRow=Math.floor((stage.mouseY-borderTop)/gridHeight);var plantCol=Math.floor((stage.mouseX-borderLeft)/gridWidth);// 判断鼠标是否在放置区域内部并且上面没有植物if (plantRow>=0&&plantCol>=0&&plantRow<5&&plantCol<9&&plantsArray[plantRow][plantCol]==0) {var placedPlant=new lib.plantMc();// 构建一株植物,用来种植placedPlant.name="plant_"+plantRow+"_"+plantCol;// 给植物一个名字placedPlant.fireRate=48;//  植物的开火速率,单位帧placedPlant.recharge=48;// 当recharge 等于 fireRate时,植物已经准备好开火了placedPlant.plantRow=plantRow;// plant rowplantContainer.addChild(placedPlant);// adds the plant//console.log("plantRow:"+plantRow);//console.log("plantCol:"+plantCol);placedPlant.x=plantCol*gridWidth+borderLeft+30;placedPlant.y=plantRow*gridHeight+borderTop+30;//console.log("plant's x:"+placedPlant.x);//console.log("plant's y:"+placedPlant.y);playerMoving=false;// tells the script the player is no longer movingmovingPlant.removeEventListener("click",placePlant);// removes the CLICK listener from the draggable plantoverlayContainer.removeChild(selector);// removes the selectoroverlayContainer.removeChild(movingPlant);// removes the plant itselfplantsArray[plantRow][plantCol]=1;// 更新游戏区块信息}
}
//
// core function to be executed at every frame. The whole game is managed here
//
function onEnterFrm(){var i;var j;var k;//这些变量都是用来遍历的//// 植物管理//for (i = 0; i < plantContainer.numChildren; i++) {var currentPlant = plantContainer.getChildAt(i);//遍历每个植物// 准备好开火if (currentPlant.recharge == currentPlant.fireRate ) {// 检查是否有僵尸与植物处于同一行if (zombiesArray[currentPlant.plantRow].length > 0) {// 同行有僵尸for (j=0; j < zombiesArray[currentPlant.plantRow].length; j++) {//遍历当前行的僵尸var targetZombie = zombieContainer.getChildByName(zombiesArray[currentPlant.plantRow][j]);// 获取第j个僵尸// 僵尸在右if (targetZombie.x > currentPlant.x) {var bullet = new lib.bulletMc();// 构造新子弹bulletContainer.addChild(bullet);// 添加子弹bullet.x = currentPlant.x+20;bullet.y = currentPlant.y-10;bullet.sonOf = currentPlant;//子弹当儿子,存储该子弹是由哪一株植物射出的currentPlant.recharge = 0;// 重新装填break;// exits the j for loop}}}}// 子弹装填中if (currentPlant.recharge < currentPlant.fireRate) {currentPlant.recharge ++;// recharges the plant}}//// 子弹管理//for (i=0; i<bulletContainer.numChildren; i++) {var movingBullet = bulletContainer.getChildAt(i);movingBullet.x+=3;//子弹移动var firingPlant = movingBullet.sonOf;//获得这个子弹是哪个植物射击的// 子弹飞走了for (j=0; j<zombieContainer.numChildren; j++) {var movingZombie=zombieContainer.getChildAt(j);//对于当前子弹,遍历的这只僵尸如果不在同一行,就继续找下一只僵尸if (movingZombie.zombieRow != firingPlant.plantRow) continue;// 碰撞检测if (movingZombie.x < movingBullet.x && movingZombie.x > movingBullet.x-5 ){movingZombie.alpha-=0.3;// 僵尸逐渐死亡bulletContainer.removeChild(movingBullet);// 移除子弹// 僵尸死了if (movingZombie.alpha<0) {zombiesArray[movingZombie.zombieRow].splice(zombiesArray[movingZombie.zombieRow].indexOf(movingZombie.name),1);// 减少该行的僵尸 zombieContainer.removeChild(movingZombie);// 移除显示列表}break;}}if (movingBullet.x>1200) {bulletContainer.removeChild(movingBullet);// 移除这颗子弹}}//// 僵尸吃菜//var zombieColumn;for (i=0; i<zombieContainer.numChildren; i++) {//遍历僵尸movingZombie=zombieContainer.getChildAt(i);zombieColumn = Math.floor((movingZombie.x-borderLeft)/gridWidth);// 得到僵尸所在的列//console.log("zombieColumn"+zombieColumn);// 判断是否有植物在同一块中if (zombieColumn<0||zombieColumn>8||plantsArray[movingZombie.zombieRow][zombieColumn]==0) {movingZombie.x-=0.5;// moves each zombie left by 1/2 pixels} else {// 僵尸打人//console.log("僵尸吃菜中");var attackedPlant = plantContainer.getChildByName("plant_"+movingZombie.zombieRow+"_"+zombieColumn);attackedPlant.alpha-=0.01;// 植物逐渐死亡// 植物死了if (attackedPlant.alpha < 0) {plantsArray[movingZombie.zombieRow][zombieColumn] = 0;//removes the plant from the arrayplantContainer.removeChild(attackedPlant);//removes the plant Display Object from Display List}}}//// 阳光管理//for (i=0; i<sunContainer.numChildren; i++) {var fallingSun = sunContainer.getChildAt(i);// 阳光下落if (fallingSun.y<fallingSun.destinationY) {fallingSun.y++;// 接着下落} else {fallingSun.alpha-=0.01;// 落完逐渐消失// 阳光消失了if (fallingSun.alpha<0) {fallingSun.removeEventListener("click",sunClicked);sunContainer.removeChild(fallingSun);// removes the sun//console.log("下面展现的是消失的太阳");//console.log(fallingSun);}}}//// 植物放置//if (playerMoving) {movingPlant.x=stage.mouseX;movingPlant.y=stage.mouseY;//console.log("mouseY:"+stage.mouseY);var plantRow=Math.floor((stage.mouseY-borderTop)/gridHeight);var plantCol=Math.floor((stage.mouseX-borderLeft)/gridWidth);//console.log("plantRow:"+plantRow);//console.log("plantCol:"+plantCol);// 鼠标在区域内if (plantRow>=0&&plantCol>=0&&plantRow<5&&plantCol<9) {selector.visible = true;// shows the selectorselector.x=borderLeft+plantCol*gridWidth+30;selector.y=borderTop+plantRow*gridHeight+30;} else {selector.visible=false;// hide the selector}}
}main();
function main(){setupField();drawField();addZombies();addPlants();fallingSuns();stage.addEventListener("tick",onEnterFrm);}/
//有一些地方请大家自行探索,如有不会,请在群里讨论
//
//--图层显示遮挡的问题,请结合课本第三章拖动彩色方块的那个游戏,设置不同对象处于不同图层。
//--音乐。请结合实验报告,总结音乐的产生与消失。
//--对象的mouseChildren属性,这个属性究竟控制了什么内容。
//--还有关于分辨率的问题。在网页不同的缩放状态下,鼠标的位置在脚本中会有一定倍率的映射,简言之就是对不准。。此问题详见更新

好了!大致就是这样了!

最后BB两句

希望大家搞懂整个逻辑,在这个教程的基础上进行一定的创新拓展!
只需要稍稍动手,就可以完成很多有趣的东西!
不妨自己写一种植物的攻击逻辑、或者是一种僵尸的反抗逻辑
或者按照学长的实验报告写出铲子和太阳的功能
对教程中不清楚的地方请联系我。
有其他问题请找仕爷,他的咖啡味道不错的!
最后贴一下成品。
植物大战僵尸_演示demo
每个对象的小红点表示这个对象自身坐标系的原点。

更新日志

2019.11.13

关于网页缩放后鼠标位移和movingPlant对不准的问题:
问题描述:


以上两张图为网页在100%缩放下,鼠标在不同位置时,movingPlant和鼠标的相对位置。并没有出现异常。



后面两张是网页缩放120%时的样子,可以看见movingPlant和鼠标位置产生了偏移。
产生偏移时,我们的鼠标始终点击不到移动的植物,所以我们也就种不下豌豆。

产生原因:
这是因为在网页缩放时,只放大了我们所看到的内容。在网页的html文件代码中,我们会发现一个canvasResize()函数,它是用来缩放canvas的大小的。而在An中构建文件时,我选用的时1280*720,它并不会随着网页缩放而缩放。

  • 脚本读取的是我们的鼠标在stage上的位置,即stage.mouseX最大值为1280。
  • 试想一下,当我们的stage.mouseX最大时,我们的movingPlant应该在何处?在canvas的最右边。
  • 然而事实上,在网页放大时,我们的鼠标仅仅是在canvas的中间位置。
  • 所以,这就是为什么越往右下角走,两者的偏移量越大。

解决办法

在整个脚本中,** 所有 **用到stage.mouseX和stage.mouseY的地方,都让他们除以舞台的缩放数值。
如图。
再次尝试我们的demo,完美!无论我怎么缩放,movingPlant都粘着我的鼠标~

妈妈!我也会做植物大战僵尸啦!相关推荐

  1. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(二)—— 序列帧动画

    开始制作游戏,首先,我们要把游戏素材导入到项目中,我这里整理出来了一些项目中用到的图片音乐等素材,大家可以下载下来使用,或者自己从网上找其他好看的素材也可以. 植物大战僵尸素材 链接:https:// ...

  2. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(四)—— 自动生成一大波僵尸

    上一篇我们实现了僵尸行走,但是最终的游戏中可不止一个僵尸,具体有多少僵尸呢?不一定哦,所以我们不能说把僵尸多拷贝几个就好了,而是要让代码自动生成僵尸,并且僵尸要自动生成在屏幕右侧,我们就暂不考虑僵尸从 ...

  3. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(十四)—— 游戏胜利和失败界面

    1.游戏胜利界面 条件:所有僵尸都被打死了 2.游戏失败界面 条件:如果有僵尸走到了房子前

  4. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(十二)—— 向日葵生产太阳

    生产太阳 1.给向日葵模板添加子物体,子物体是太阳 2.太阳初始大小是0,给太阳添加Tween Scale组件 3.在SunFlowerAI脚本中每过三秒钟播放太阳的Tween动画,即让太阳从小变大 ...

  5. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(十七)—— 工程源码下载

    链接:https://pan.baidu.com/s/1OCopjaGyR7fpiOXlJRK3FA 提取码在下方 + + + + + + + + + + + + + + + + + + + + + ...

  6. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(六)—— 豌豆射手发射豌豆攻击僵尸

    上一篇我们讲了种植植物,但是植物还没有任何行为,豌豆射手不能攻击僵尸,向日葵也不能生产太阳,这一篇,我们就来讲解让豌豆射手发射豌豆来攻击僵尸. 一.制作子弹(豌豆) 前面我们搭建UI的时候已经搭建了豌 ...

  7. 如何用python简单做一个植物大战僵尸 源码

    简单实现的植物大战僵尸我自己网上扣了王校长的图做了个热狗射手hhhhhhhh最后面给大家分享一下我扣的热狗png图片求点赞!!!""" v1.81.完善僵尸类2.加载僵尸 ...

  8. 做一个像植物大战僵尸的Flash游戏1

    http://bbs.9ria.com/thread-80527-1-6.html 这个教程是我翻译的..不是我原创的..原来是发表在新闻资讯版块,后来被移到了游戏编程版块,所以关于原作者的一些信息等 ...

  9. 做一个植物大战僵尸有多难?python教你几步搞定

    一个简单的植物大战僵尸游戏. 仅供个人学习和非商业用途.如果这个游戏侵犯了版权,请告诉我. 已有的植物: 向日葵, 豌豆射手, 坚果墙, 寒冰豌豆射手, 樱桃炸弹, 三向豌豆射手, 食人花, 喷射蘑菇 ...

  10. unity 植物大战僵尸怎么做

    Unity 是一款著名的游戏引擎,在其中可以制作各种不同类型的游戏.要制作<植物大战僵尸>这样的游戏,你需要学习 Unity 的使用方法,并了解游戏开发的基本知识. 首先,你需要学习如何在 ...

最新文章

  1. iOS开发-Protocol协议及委托代理(Delegate)传值
  2. Delphi 的消息机制浅探三
  3. linux+gpfs配置文件,GPFS for linux实施
  4. 05-Exception Handling Framework
  5. CF-241 E.Flights(差分约束)
  6. Java反射技术概述
  7. 面试进阶之字符串常量池
  8. Qt5:渐变效果的实现
  9. 【Java】链表求解一元多项式加法
  10. BZOJ2801/洛谷P3544 [POI2012]BEZ-Minimalist Security(题目性质发掘+图的遍历+解不等式组)...
  11. 无人车创业正驶入分水岭
  12. 数据库原理及应用教程(第4版|微课版)陈志泊-第一章习题
  13. 飞信2009_AfterShip CTO 洪小军—我的移动互联网十年造梦记:飞信时代
  14. Layered Architecture 分层架构(完整翻译)
  15. 深入理解oracle的context,读者对于《深入解析Oracle》的评价
  16. 【基于Simulink+UG NX MCD 一级倒立摆控制系统仿真】建模和分析(一)
  17. 懒人玩Arm-2D究竟有几种姿势?
  18. PMM使用Grafana告警
  19. maven父子关系时,发生异常: Could not find artifact org.hlx.itrip:itrip-dao:jar:1.0-SNAPSHOT - [Help 1]
  20. ES版cpu如何购买和判断?

热门文章

  1. CodeBook 可以自定义字符集的密码本
  2. 15款免费的Wi-Fi安全测试工具
  3. Oracle添加语句
  4. opencv实现摄像头的实时人脸识别
  5. 戴尔服务器系统还原备份系统,戴尔电脑自带的正版系统恢复你会用吗?
  6. 2018最新圣思园JavaSE实地培训系列视频教程
  7. 【笔记】【机器学习基础】非负矩阵分解
  8. 分省老年抚养比和老年人口比重(1995-2018年)
  9. P4 用verilog描述单周期CPU的学习笔记和总结(基于P3)
  10. 在Linux中打印常用书信与信封(转)