在经历了5篇大段代码的"重"教程后,我们终于到了塔防游戏最核心的组件:塔楼!塔楼在你的塔防游戏里至关重要.现在,我使用塔楼这个词并不一定意味这是一些一柱擎天的鸡巴,石头结构那样的东西并且给附近的敌人浇一阵死亡之雨( Now, by “towers” I don’t necessarily mean some phallic, stone structure jutting into the sky that rains death upon enemies nearby.).这里的"塔"可以是任何装置,对象,动物等等玩家可以放置在地图上的阻碍敌人到达他们目的地的.一些游戏可能会有实际的塔楼结构,一些游戏可能会有带有火箭炮的军队,一些游戏可能会有一个你可以放置在附近的房子或者产生装置,并且玩家的装置从其中产出以阻挡敌人.塔是一个主要的技工给玩家提供了一种方式来体验策略,手段,成就以及乐趣(更详尽的咱就不说了).
即便如此,不幸的是我在这个demo中对塔不太公平.如果你之前玩过demo,你就会看见我的两个塔只是简单地蓝色和红色框.敌人是动态的并且比塔漂亮得多.该死,即使子弹也有些跟随他们的粒子也比塔楼漂亮得多.不管怎么说,这就是关键.我上次像你展示了我是如何创建敌人动画的.你可以为塔楼做同样的事情自己做些有漂亮动画的塔楼他们有着不同的状态"装载弹药","射击"等等.
更新8/1/13-抽象出Tower的敌人寻找策略为ITowerStrategy.as以及TowerStrategyNearest.as.另外也加了些注释在设计模式上.
Ok,像我们之前有关敌人的文章一样,我们从我们如何定义塔楼的JSON数据开始
JSON数据结构
{
"towers": [
{
"id": "tower1",
"index": 0,
"name": "Blue Tower",
"imageName": "towers/tower1_001",
"bulletImageName": "bullets/sparkle_blue_001",
"bulletSpeed": 12,
"bulletWidth": 32,
"bulletHeight": 32,
"towerWidth": 20,
"towerHeight": 20,
"maxLevel": 3,
"sounds": [
{
"state": "onFire",
"soundId": "shot1"
}
],
"levelData": [
{
"level": 1,
"range": 75,
"damage": 5,
"speed": 1500,
"dps": 3.33,
"cost": 10
},
{
"level": 2,
"range": 150,
"damage": 10,
"speed": 1250,
"dps": 8,
"cost": 30
},
{
"level": 3,
"range": 200,
"damage": 20,
"speed": 900,
"dps": 22.22,
"cost": 50
}
]
}
]
}
这个是在src/assets/json/towers/towerData.json文件中列出的第一个塔,这个文件定义了两种类型的塔.我们逐行看下.
- 2行-这个是稍后将要使用的"塔"对象的一个数组.
- 4行-这是第一个塔的id
- 5行-从这个JSON文件中,索引(index)允许我重新安排塔.塔的创建和安放在舞台上基于索引(index)属性.
- 6行-这个是玩家在信息框等等里面所面对的塔的名字
- 7行-imageName由HUDManager使用来将代表塔的图像放置在UI上以便玩家可以点击.不幸的是它也成了地图中实际使用的塔的图像.理想情况下像Enemy数据,应该在某处有一个材质前缀(texturePrefix)来说明,"这个塔使用'tower/tower1_的瓦片(tiles)'"
- 8行-塔楼射击的子弹所使用的图像.如果你想要你的塔根据不同的等级发射不同的子弹,也许你能够将这个属性移动到下面的levelData?
- 9行-bulletSpeed是在每个时间间隔中子弹在地图上穿行的有多快.显然你想要这个要比敌人移动的快速,但也应该慢到你能够实际的看到子弹在飞.
- 10-11行-子弹图像的宽度和高度
- 12-13行-塔楼图像的宽度和高度
- 14行-一个塔楼最大能升到多少级.所以这个塔从第一级开始能够能升级到第二级最后升到第三级.所以本质上它能升级两次.
- 15行-这是一个像我们之前在Enemy数据中看到的声音状态数组.我们稍后将会看到,我定义了当塔楼射击子弹时播放塔楼声音状态"onFire".如果你在那里定义了"onFire",它将播放那个声音.
- 21行-这是一个塔在不同等级所拥有的不同参数的数组.
- 23行-这个data对应哪个等级,在这个对象中对应的第一级.
- 24行-在这个等级,塔的(攻击)范围是75
- 25行-在这个等级,当塔的子弹撞击(敌人)时,伤害为5
- 26行-在这个等级,塔的装填弹药延迟为1500毫秒,也就是1.5秒
- 27行-在这个等级,塔的每秒钟伤害值(Damage Per Second,简写为DPS)为3.33.(5dmg / 1.5s = 3.33dps)
- 28行-在这个等级,塔花费10个金币.
TowerManager.as
package com.zf.managers
{
import com.zf.core.Config;
import com.zf.objects.enemy.Enemy;
import com.zf.objects.tower.Tower;
import com.zf.states.Play;
import flash.geom.Point;
import org.osflash.signals.Signal;
import starling.display.Sprite;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
public class TowerManager implements IZFManager
{
public var play:Play;
public var onTowerRemoved:Signal;
public var onTowerAdded:Signal;
public var onTowerManagerRemovingEnemy:Signal;
private var _towers:Array;
private var _towerData:Object;
private var _currentTower:Tower;
private var _p:Point = new Point();
private var _isDragging:Boolean = false;
private var _canvas:Sprite;
public function TowerManager(playState:Play, towerData:Object) {
play = playState;
_canvas = play.towerLayer;
_towers = [];
_setTowerData(towerData);
onTowerAdded = new Signal(Tower);
onTowerRemoved = new Signal(Tower);
onTowerManagerRemovingEnemy = new Signal(Enemy);
}
public function update():void {
if(_towers.length > 0) {
var t:Tower,
len:int = _towers.length;
for(var i:int = len - 1; i >= 0; i--) {
t = _towers[i];
t.update();
}
}
}
public function destroy():void {}
public function destroyTower(t:Tower):void {
var len:int = _towers.length;
for(var i:int = 0; i < len; i++) {
if(t == _towers[i]) {
_towers.splice(i, 1);
t.destroy();
t.removeFromParent(true);
}
}
onTowerRemoved.dispatch(t);
}
public function createNewTower(towerID:String, pos:Point):void {
if(_currentTower) {
_currentTower.deactivate();
}
var tower:Tower = new Tower(_towerData[towerID], this);
tower.setSoundData(_towerData[towerID].sounds);
tower.x = pos.x - tower.halfWidth;
tower.y = pos.y - tower.halfHeight;
tower.activated = true;
_currentTower = tower;
play.addChild(tower);
play.addEventListener(TouchEvent.TOUCH, towerFollowMouse);
}
public function towerFollowMouse(evt:TouchEvent):void {
var touch:Touch = evt.getTouch(play);
if(touch)
{
switch(touch.phase) {
case TouchPhase.BEGAN:
var checkObject:Object = play.map.checkCanPlaceTower(_p);
if(checkObject.canPlace) {
play.removeEventListener(TouchEvent.TOUCH, towerFollowMouse);
evt.stopImmediatePropagation();
placeTower(checkObject.point);
}
break;
case TouchPhase.ENDED:
break;
case TouchPhase.MOVED:
break;
case TouchPhase.HOVER:
var tPos:Point = touch.getLocation(play);
_currentTower.x = _p.x = tPos.x - _currentTower.halfWidth;
_currentTower.y = _p.y = tPos.y - _currentTower.halfHeight;
// Check if we can place, then update the tower image accordingly
_currentTower.enterFrameTowerPlaceCheck(play.map.checkCanPlaceTower(_p).canPlace);
break;
}
}
}
todo