在这个阶段,我们会修改一个漏洞,这个漏洞允许植物开火只要有僵尸与植物处于同一行,不论僵尸是在植物的左边还是右边。同时我们也会增加僵尸攻击植物的功能。
首先,让我来解释一下一些改变:
在前面的步骤里,zombiesArray数组只存储每一行上的僵尸的数量。这个信息对我们想知道僵尸是在植物的左边还是右边是不够的,所以从现在开始zombiesArray改为一个二维数组,用来存储每一行上僵尸的名字。
你将会明白这一特性当我们讨论源代码的时候。
要使僵尸攻击植物,我们必须使他们停下来一旦他们与植物处于同一区块。
让我们来看源代码:
package {
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.text.TextField;
public class Main extends Sprite {
//
// 一个2维数组用来存储游戏区块
//
private var plantsArray:Array;// 种植在游戏区域里的植物
private var zombiesArray:Array;//在游戏区域里的僵尸
//
// 计时器
//
private var flowersTimer:Timer=new Timer(5000);//计时器,使得阳光落下
private var zombieTimer:Timer=new Timer(5000);// 计时器,让僵尸出场
//
// 容器
//
private var sunContainer:Sprite=new Sprite();// 所有阳光的容器
private var plantContainer:Sprite=new Sprite();// 所有植物的容器
public var bulletContainer:Sprite=new Sprite();// 所有子弹的容器
private var zombieContainer:Sprite=new Sprite();// 所有僵尸的容器
private var overlayContainer:Sprite=new Sprite();// 所有覆盖物的容器
//
// 我们的演员
//
private var movingPlant:plantMc;// 玩家在游戏区域能够拖动的植物
private var selector:selectorMc;// 选择器(一个高亮的区块),告诉玩家他将把植物种在哪
//
// 其它变量
//
private var money:uint=0;// 玩家所拥有的金钱数量
private var moneyText:TextField=new TextField ;// 动态文本框,用来显示玩家的金钱
private var playerMoving:Boolean=false;// 布尔型变量,标志玩家是否在移动一个植物
private var totalZombies:uint=0;//僵尸的总数
public function Main():void {
setupField();//初始化游戏区块
drawField();//画出游戏区块
fallingSuns();// 初始化下落的阳光
addPlants();// 初始化植物
addZombies();//初始化僵尸
addEventListener(Event.ENTER_FRAME,onEnterFrm);
}
//
// 游戏区域设置,创建用来存储植物和僵尸信息的数组
//
private function setupField():void {
plantsArray=new Array();
zombiesArray=new Array();
for (var i:uint=0; i<5; i++) {
plantsArray[i]=new Array();
zombiesArray[i]=new Array();
for (var j:uint=0; j<9; j++) {
plantsArray[i][j]=0;
}
}
}
//
// 显示玩家的金钱
//
private function updateMoney():void {
moneyText.text="Money: "+money.toString();
}
//
// 画出游戏区域
//
private function drawField():void {
var fieldSprite:Sprite=new Sprite();
var randomGreen:Number;
addChild(fieldSprite);
fieldSprite.graphics.lineStyle(1,0xFFFFFF);
for (var i:uint=0; i<5; i++) {
for (var j:uint=0; j<9; j++) {
randomGreen=(125+Math.floor(Math.random()*50))*256;
fieldSprite.graphics.beginFill(randomGreen);
fieldSprite.graphics.drawRect(25+65*j,80+75*i,65,75);
}
}
addChild(sunContainer);
addChild(plantContainer);
addChild(bulletContainer);
addChild(zombieContainer);
addChild(overlayContainer);
overlayContainer.addChild(moneyText);
updateMoney();
moneyText.textColor=0xFFFFFF;
moneyText.height=20;
}
//
// 初始化僵尸
//
private function addZombies():void {
zombieTimer.start();
zombieTimer.addEventListener(TimerEvent.TIMER,newZombie);
}
//
// 增加一个新的僵尸
//
private function newZombie(e:TimerEvent):void {
var zombie:zombieMc=new 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=660;// 把僵尸放在屏幕的右边
zombie.y=zombie.zombieRow*75+115;
}
//
// 初始化阳光
//
private function fallingSuns():void {
flowersTimer.start();
flowersTimer.addEventListener(TimerEvent.TIMER, newSun);
}
//
// 增加一束新的阳光
//
private function newSun(e:TimerEvent):void {
var sunRow:uint=Math.floor(Math.random()*5);// 随机行
var sunCol:uint=Math.floor(Math.random()*9);// 随机列
var sun:sunMc = new sunMc();// 构造阳光
sun.buttonMode=true;// 当鼠标滑过阳光时,改变鼠标的形状
sunContainer.addChild(sun);// 加入显示列表
sun.x=52+sunCol*65;// 把阳光放在合适的位置
sun.destinationY=130+sunRow*75;// 定义阳光destinationY属性
sun.y=-20;// 把阳光放在舞台顶部的上方
sun.addEventListener(MouseEvent.CLICK,sunClicked);// 给阳光注册鼠标点击事件
}
//
// 阳光的鼠标点击事件句柄
//
private function sunClicked(e:MouseEvent):void {
e.currentTarget.removeEventListener(MouseEvent.CLICK,sunClicked);// 移除鼠标事件侦听
money+=5;//让玩家赚到5个金币
updateMoney();// 更新动态文本
var sunToRemove:sunMc=e.currentTarget as sunMc;// 获得我们必须移除的阳光
sunContainer.removeChild(sunToRemove);// 移除该阳光
}
// 创建一个植物栏,现在只有一种植物
//
private function addPlants():void {
var plant:plantMc=new plantMc();// 构造一株新的植物
overlayContainer.addChild(plant);// 增加植物
plant.buttonMode=true;// 使鼠标改变形状,当它滑过新植物时
plant.x=90;
plant.y=40;
plant.addEventListener(MouseEvent.CLICK,onPlantClicked);// 给新植物注册鼠标点击事件
}
//
// 植物的鼠标点击事件句柄
//
private function onPlantClicked(e:MouseEvent):void {
// 检查玩家是否有足够的钱(当前是10)来购买植物,并且是否正在拖动一个植物
if (money>=10&&! playerMoving) {
money-=10;// 付款
updateMoney();// 更新动态文本
selector=new selectorMc();// 创建一个新的选择器
selector.visible=false;// 使选择器不可见
overlayContainer.addChild(selector);// 把选择器加入到显示列表
movingPlant=new plantMc();// 构建一个新的供玩家拖动的植物
movingPlant.addEventListener(MouseEvent.CLICK,placePlant);// 给该植物注册一个鼠标点击事件
overlayContainer.addChild(movingPlant);// 把该植物加入到显示列表
playerMoving=true;// 告诉脚本正在移动一株植物
}
}
//
// 把植物放置在游戏区域中
//
private function placePlant(e:MouseEvent):void {
var plantRow:int=Math.floor((mouseY-80)/75);
var plantCol:int=Math.floor((mouseX-25)/65);
// let's see if the tile is inside the game field and it's free
if (plantRow>=0&&plantCol>=0&&plantRow<5&&plantCol<9&&plantsArray[plantRow][plantCol]==0) {
var placedPlant:plantMc=new plantMc();// 构建一株植物,用来种植
placedPlant.name="plant_"+plantRow+"_"+plantCol;// 给植物一个名字
placedPlant.fireRate=75;// 植物的开火速率,单位帧
placedPlant.recharge=0;// 当recharge 等于 fireRate时,植物已经准备好开火了
placedPlant.isFiring=false;// 一个布尔变量来存储植物是否正在开火
placedPlant.plantRow=plantRow;// 植物所在的行
plantContainer.addChild(placedPlant);// 把该植物加入到显示列表
placedPlant.x=plantCol*65+57;
placedPlant.y=plantRow*75+115;
playerMoving=false;// 告诉脚本玩家不在移动植物了
movingPlant.removeEventListener(MouseEvent.CLICK,placePlant);// 移除事件侦听
overlayContainer.removeChild(selector);// 移除选择器
overlayContainer.removeChild(movingPlant);// 移除供拖动的植物
plantsArray[plantRow][plantCol]=1;// 更新游戏区块信息
}
}
//
// 游戏循环,游戏的核心函数
//
private function onEnterFrm(e:Event):void {
var i:int;
var j:int;
//
// 植物管理
//
for (i=0; i<plantContainer.numChildren; i++) {
var currentPlant:plantMc=plantContainer.getChildAt(i) as plantMc;
// 让我们看看植物是否能开火
if (currentPlant.recharge==currentPlant.fireRate&&! currentPlant.isFiring) {
// 检查是否有僵尸与植物处于同一行
if (zombiesArray[currentPlant.plantRow].length>0) {
// 遍历僵尸
for (j=0; j<zombiesArray[currentPlant.plantRow].length>0; j++) {
var targetZombie:zombieMc=zombieContainer.getChildByName(zombiesArray[currentPlant.plantRow]
[j]) as zombieMc;// 获得第j个僵尸
// 如果僵尸在植物的右边
if (targetZombie.x>currentPlant.x) {
var bullet:bulletMc=new bulletMc();// 创建一个新子弹
bulletContainer.addChild(bullet);// 加入到显示列表
bullet.x=currentPlant.x;
bullet.y=currentPlant.y;
bullet.sonOf=currentPlant;// 存储该子弹是由哪一株植物射出的
currentPlant.recharge=0;// 重新准备开火
currentPlant.isFiring=true;// 植物正在开火
break;// 终止for循环
}
}
}
}
if (currentPlant.recharge<currentPlant.fireRate) {
currentPlant.recharge++;
}
}
//
// 子弹管理
//
for (i=0; i<bulletContainer.numChildren; i++) {
var movingBullet:bulletMc=bulletContainer.getChildAt(i) as bulletMc;
movingBullet.x+=3;//把每个子弹向右移动3个像素
var firingPlant:plantMc=movingBullet.sonOf as plantMc;// 获得这个子弹是哪个植物射击的
// 让我们看看子弹是否飞出了舞台
if (movingBullet.x>650) {
firingPlant.isFiring=false;// 植物不再处于正在开火的状态
bulletContainer.removeChild(movingBullet);// 移除子弹
} else {
for (j=0; j<zombieContainer.numChildren; j++) {
var movingZombie:zombieMc=zombieContainer.getChildAt(j) as zombieMc;
// 让我们看看植物是否被子弹击中
if (movingZombie.hitTestPoint(movingBullet.x,movingBullet.y,true)) {
movingZombie.alpha-=0.3;//减少僵尸的能量(透明度)
firingPlant.isFiring=false;// 植物不再处于正在开火的状态
bulletContainer.removeChild(movingBullet);// 移除子弹
// 让我们看看僵尸的能量(透明度)是否降至为0了
if (movingZombie.alpha<0) {
zombiesArray[movingZombie.zombieRow].splice(zombiesArray
[movingZombie.zombieRow].indexOf(movingZombie.name),1);// 减少该行的僵尸
zombieContainer.removeChild(movingZombie);// 移除显示列表
}
break;
}
}
}
}
//
// 僵尸管理
//
var zombieColumn:int;
for (i=0; i<zombieContainer.numChildren; i++) {
movingZombie=zombieContainer.getChildAt(i) as zombieMc;
zombieColumn = Math.floor((movingZombie.x-25)/65);// 得到僵尸所在的列
// 检查是否有植物与之处于同一个区块
if (zombieColumn<0||zombieColumn>8||plantsArray[movingZombie.zombieRow][zombieColumn]==0) {
movingZombie.x-=0.5;// 每一个僵尸往左移动0.5个像素
} else {
// 僵尸开始攻击!!
var attackedPlant:plantMc=plantContainer.getChildByName("plant_"+movingZombie.zombieRow+"_"+zombieColumn) as
plantMc;
attackedPlant.alpha-=0.01;// drains plant energy
//检查植物是否死了
if (attackedPlant.alpha<0) {
plantsArray[movingZombie.zombieRow][zombieColumn]=0;//把植物移出数组
plantContainer.removeChild(attackedPlant);//移出显示列表
}
}
}
//
// 阳光管理
//
for (i=0; i<sunContainer.numChildren; i++) {
var fallingSun:sunMc=sunContainer.getChildAt(i) as sunMc;
// 让我们看看阳光是否还在下落
if (fallingSun.y<fallingSun.destinationY) {
fallingSun.y++;// 把阳光往下移动一个像素
} else {
fallingSun.alpha-=0.01;// 使阳光淡出
// 检查阳光是否消失了
if (fallingSun.alpha<0) {
fallingSun.removeEventListener(MouseEvent.CLICK,sunClicked);// 移除事件侦听
from the
sun
sunContainer.removeChild(fallingSun);// 移出显示列表
}
}
}
//
// 安置植物
//
if (playerMoving) {
movingPlant.x=mouseX;
movingPlant.y=mouseY;
var plantRow:int=Math.floor((mouseY-80)/75);
var plantCol:int=Math.floor((mouseX-25)/65);
// 检查是否在游戏区域内
if (plantRow>=0&&plantCol>=0&&plantRow<5&&plantCol<9) {
selector.visible=true;// 显示选择器
selector.x=25+plantCol*65;
selector.y=80+plantRow*75;
} else {
selector.visible=false;// 隐藏选择器
}
}
}
}
}
在setupField函数中,zombiesArray变成了一个二维数组,一个元素代表一行:
private function setupField():void {
plantsArray=new Array();
zombiesArray=new Array();
for (var i:uint=0; i<5; i++) {
plantsArray=new Array();
zombiesArray=new Array();
for (var j:uint=0; j<9; j++) {
plantsArray[j]=0;
}
}
}
在newZombie函数中,我们根据僵尸所在的行,把僵尸的名字加入到zombiesArray的合适的位置:
private function newZombie(e:TimerEvent):void {
var zombie:zombieMc=new 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=660;// 把僵尸放在舞台右边
zombie.y=zombie.zombieRow*75+115;
}
现在,整个判断一株植物是否能开火就被改成了这样:
if (currentPlant.recharge==currentPlant.fireRate&&! currentPlant.isFiring) {
if (zombiesArray[currentPlant.plantRow].length>0) {
for (j=0; j<zombiesArray[currentPlant.plantRow].length>0; j++) {
var targetZombie:zombieMc=zombieContainer.getChildByName(zombiesArray[currentPlant.plantRow][j]) as zombieMc;
zombie
if (targetZombie.x>currentPlant.x) {
var bullet:bulletMc=new bulletMc();
bulletContainer.addChild(bullet);
bullet.x=currentPlant.x;
bullet.y=currentPlant.y;
bullet.sonOf=currentPlant;
currentPlant.recharge=0;
currentPlant.isFiring=true;
break;
}
}
}
}
我们遍历了所有与植物处于同一行上的僵尸,然后检查僵尸是在植物的左边还是在植物的右边(第214行)。
下面几行简单的代码使得僵尸攻击植物:
var zombieColumn:int;
for (i=0; i<zombieContainer.numChildren; i++) {
movingZombie=zombieContainer.getChildAt(i) as zombieMc;
zombieColumn = Math.floor((movingZombie.x-25)/65);
if (zombieColumn<0||zombieColumn>8||plantsArray[movingZombie.zombieRow][zombieColumn]==0) {
movingZombie.x-=0.5;
} else {
var attackedPlant:plantMc=plantContainer.getChildByName("plant_"+movingZombie.zombieRow+"_"+zombieColumn) as plantMc;
attackedPlant.alpha-=0.01;
if (attackedPlant.alpha<0) {
plantsArray[movingZombie.zombieRow][zombieColumn]=0;
plantContainer.removeChild(attackedPlant);
}
}
}
我们先检查了是否有植物被安置在僵尸当前所在的区块上,如果有,僵尸就停下脚步然后开始攻击植物,减少植物的生命值(当前是透明度)。
下面是结果:
http://www.emanueleferonato.com/wp-content/uploads/2011/03/pvz.swf