4. 塔类型系统
2026/4/14大约 13 分钟
4. 保卫萝卜——塔类型系统
防御塔的"家族"
在保卫萝卜中,防御塔有很多种类型。你可以把不同类型的塔想象成不同"职业"的守卫——有的像狙击手(远程高伤害),有的像交警(减速控制),有的像炮兵(范围攻击)。
每种塔都有自己独特的"性格":
| 塔类型 | 比喻 | 核心特点 | 价格 |
|---|---|---|---|
| 瓶子塔 | 狙击手 | 攻击力高,攻击单个目标 | 100 金 |
| 风扇塔 | 交警 | 攻击范围大,减速敌人 | 120 金 |
| 火箭塔 | 炮兵 | 超高伤害,攻击缓慢 | 200 金 |
| 雷达塔 | 指挥官 | 不攻击,减速周围所有敌人 | 80 金 |
塔基类设计
既然所有塔都有一些共同的"行为"(比如寻找目标、攻击、显示攻击范围),我们先创建一个"基类"——就像所有汽车的"通用设计图纸",具体的车型(瓶子塔、风扇塔等)都在这个基础上增加自己的特色。
塔数据资源
C
using Godot;
/// <summary>
/// 防御塔数据资源 —— 每种塔的"设计图纸"
/// </summary>
[GlobalClass]
public partial class TowerData : Resource
{
/// <summary>塔名称</summary>
[Export] public string TowerName { get; set; } = "基础塔";
/// <summary>塔类型标识</summary>
[Export] public string TowerType { get; set; } = "bottle";
/// <summary>塔描述</summary>
[Export] public string Description { get; set; } = "普通防御塔";
/// <summary>购买价格</summary>
[Export] public int Cost { get; set; } = 100;
/// <summary>基础攻击力</summary>
[Export] public int BaseDamage { get; set; } = 10;
/// <summary>攻击范围(像素)</summary>
[Export] public float Range { get; set; } = 150.0f;
/// <summary>攻击间隔(秒)</summary>
[Export] public float FireRate { get; set; } = 1.0f;
/// <summary>最大升级等级</summary>
[Export] public int MaxLevel { get; set; } = 3;
/// <summary>每级升级费用(索引0 = 升到2级,索引1 = 升到3级)</summary>
[Export] public int[] UpgradeCosts { get; set; } = { 80, 150 };
/// <summary>每级攻击力加成</summary>
[Export] public int[] DamagePerLevel { get; set; } = { 5, 10 };
/// <summary>每级攻击范围加成</summary>
[Export] public float[] RangePerLevel { get; set; } = { 20.0f, 30.0f };
/// <summary>塔场景(预制体)</summary>
[Export] public PackedScene TowerScene { get; set; }
/// <summary>塔图标</summary>
[Export] public Texture2D Icon { get; set; }
}GDScript
class_name TowerData
extends Resource
## 防御塔数据资源 —— 每种塔的"设计图纸"
## 塔名称
@export var tower_name: String = "基础塔"
## 塔类型标识
@export var tower_type: String = "bottle"
## 塔描述
@export var description: String = "普通防御塔"
## 购买价格
@export var cost: int = 100
## 基础攻击力
@export var base_damage: int = 10
## 攻击范围(像素)
@export var range: float = 150.0
## 攻击间隔(秒)
@export var fire_rate: float = 1.0
## 最大升级等级
@export var max_level: int = 3
## 每级升级费用
@export var upgrade_costs: Array[int] = [80, 150]
## 每级攻击力加成
@export var damage_per_level: Array[int] = [5, 10]
## 每级攻击范围加成
@export var range_per_level: Array[float] = [20.0, 30.0]
## 塔场景(预制体)
@export var tower_scene: PackedScene
## 塔图标
@export var icon: Texture2D塔基类脚本
C
using Godot;
using System.Collections.Generic;
/// <summary>
/// 防御塔基类 —— 所有塔的"通用模板"
/// 提供寻找目标、攻击、范围显示等通用功能
/// </summary>
public partial class Tower : Node2D
{
// ===== 信号 =====
/// <summary>塔被卖出时发送</summary>
[Signal]
public delegate void TowerSoldEventHandler(int refundGold);
/// <summary>塔升级时发送</summary>
[Signal]
public delegate void TowerUpgradedEventHandler(int newLevel);
// ===== 导出属性 =====
[ExportGroup("Tower Stats")]
/// <summary>基础攻击力</summary>
[Export] public int BaseDamage { get; set; } = 10;
/// <summary>攻击范围</summary>
[Export] public float AttackRange { get; set; } = 150.0f;
/// <summary>攻击间隔(秒)</summary>
[Export] public float FireRate { get; set; } = 1.0f;
/// <summary>塔的购买价格(用于计算出售返还)</summary>
[Export] public int PurchaseCost { get; set; } = 100;
/// <summary>最大等级</summary>
[Export] public int MaxLevel { get; set; } = 3;
/// <summary>子弹场景</summary>
[Export] public PackedScene ProjectileScene { get; set; }
/// <summary>攻击范围检测的碰撞层</summary>
[Export] public uint TargetCollisionLayer { get; set; } = 1;
// ===== 运行时状态 =====
private int _currentLevel = 1;
private int _totalInvested; // 总投入金币(含升级费用)
private float _fireCooldown;
private Monster _currentTarget;
private bool _isSelected = false;
// 子节点引用
private Sprite2D _sprite;
private Area2D _rangeArea;
private CollisionShape2D _rangeShape;
/// <summary>当前等级</summary>
public int CurrentLevel => _currentLevel;
/// <summary>当前攻击力(含升级加成)</summary>
public virtual int CurrentDamage => BaseDamage + (_currentLevel - 1) * 5;
/// <summary>当前攻击范围(含升级加成)</summary>
public virtual float CurrentRange => AttackRange + (_currentLevel - 1) * 20.0f;
/// <summary>总投入金币</summary>
public int TotalInvested => _totalInvested;
public override void _Ready()
{
_sprite = GetNode<Sprite2D>("Sprite2D");
_rangeArea = GetNode<Area2D>("RangeArea");
_rangeShape = GetNode<CollisionShape2D>("RangeArea/RangeShape");
_totalInvested = PurchaseCost;
// 设置攻击范围圆形区域
UpdateRangeIndicator();
}
public override void _Process(double delta)
{
if (!IsInsideTree()) return;
// 更新攻击冷却
_fireCooldown -= (float)delta;
// 寻找目标并攻击
FindAndAttackTarget();
}
/// <summary>
/// 寻找目标并攻击
/// 这是塔的核心逻辑:找到最近的/最靠前的敌人,然后攻击
/// </summary>
protected virtual void FindAndAttackTarget()
{
// 冷却中,不能攻击
if (_fireCooldown > 0) return;
// 寻找范围内的怪物
_currentTarget = FindBestTarget();
if (_currentTarget != null && _currentTarget.IsAlive)
{
// 攻击目标
Fire(_currentTarget);
// 重置冷却
_fireCooldown = FireRate;
}
}
/// <summary>
/// 寻找最佳攻击目标
/// 策略:选择走得最远的怪物(最接近萝卜的)
/// </summary>
protected Monster FindBestTarget()
{
var monsters = GetTree()
.GetNodesInGroup("monsters");
Monster bestTarget = null;
float bestProgress = -1f;
foreach (var node in monsters)
{
if (node is not Monster monster) continue;
if (!monster.IsAlive) continue;
// 检查是否在攻击范围内
float distance = GlobalPosition.DistanceTo(monster.GlobalPosition);
if (distance > CurrentRange) continue;
// 选择 progress 最大的(走得最远的)
if (monster.Progress > bestProgress)
{
bestProgress = monster.Progress;
bestTarget = monster;
}
}
return bestTarget;
}
/// <summary>
/// 开火 —— 创建子弹飞向目标
/// 子类可以重写这个方法来实现不同的攻击方式
/// </summary>
protected virtual void Fire(Monster target)
{
if (ProjectileScene == null)
{
// 没有子弹场景,直接造成伤害(即时攻击)
target.TakeDamage(CurrentDamage);
return;
}
// 创建子弹
var projectile = ProjectileScene.Instantiate<Node2D>();
GetTree().Root.AddChild(projectile);
// 设置子弹初始位置
projectile.GlobalPosition = GlobalPosition;
// 让子弹飞向目标
if (projectile is Projectile proj)
{
proj.Initialize(target, CurrentDamage);
}
}
/// <summary>
/// 升级塔
/// </summary>
/// <returns>升级是否成功</returns>
public virtual bool Upgrade(int cost)
{
if (_currentLevel >= MaxLevel) return false;
if (!GameManager.Instance.SpendGold(cost)) return false;
_currentLevel++;
_totalInvested += cost;
// 更新范围显示
UpdateRangeIndicator();
// 播放升级特效
PlayUpgradeEffect();
EmitSignal(SignalName.TowerUpgraded, _currentLevel);
return true;
}
/// <summary>
/// 出售塔 —— 返还部分金币
/// </summary>
public void Sell()
{
int refund = Mathf.FloorToInt(
_totalInvested * GameConstants.SellRefundRate);
GameManager.Instance.AddGold(refund);
EmitSignal(SignalName.TowerSold, refund);
// 播放出售特效后移除
PlaySellEffect();
QueueFree();
}
/// <summary>
/// 更新攻击范围指示器
/// </summary>
protected void UpdateRangeIndicator()
{
if (_rangeShape == null) return;
var circle = _rangeShape.Shape as CircleShape2D;
if (circle != null)
{
circle.Radius = CurrentRange;
}
}
/// <summary>
/// 选中/取消选中塔
/// </summary>
public void SetSelected(bool selected)
{
_isSelected = selected;
if (_rangeArea != null)
{
_rangeArea.Visible = selected;
}
}
/// <summary>
/// 升级特效
/// </summary>
protected virtual void PlayUpgradeEffect()
{
// 闪烁效果
if (_sprite == null) return;
var tween = CreateTween();
tween.TweenProperty(_sprite, "scale",
_sprite.Scale * 1.3f, 0.15);
tween.TweenProperty(_sprite, "scale",
_sprite.Scale / 1.3f, 0.15);
}
/// <summary>
/// 出售特效
/// </summary>
protected virtual void PlaySellEffect()
{
if (_sprite == null) return;
var tween = CreateTween();
tween.TweenProperty(_sprite, "modulate",
new Color(1, 1, 1, 0), 0.3);
}
}GDScript
extends Node2D
class_name Tower
## 防御塔基类 —— 所有塔的"通用模板"
## 提供寻找目标、攻击、范围显示等通用功能
# ===== 信号 =====
## 塔被卖出时发送
signal tower_sold(refund_gold: int)
## 塔升级时发送
signal tower_upgraded(new_level: int)
# ===== 导出属性 =====
## 基础攻击力
@export var base_damage: int = 10
## 攻击范围
@export var attack_range: float = 150.0
## 攻击间隔(秒)
@export var fire_rate: float = 1.0
## 塔的购买价格
@export var purchase_cost: int = 100
## 最大等级
@export var max_level: int = 3
## 子弹场景
@export var projectile_scene: PackedScene
## 攻击范围检测的碰撞层
@export var target_collision_layer: int = 1
# ===== 运行时状态 =====
var _current_level: int = 1
var _total_invested: int = 0
var _fire_cooldown: float = 0.0
var _current_target: Monster = null
var _is_selected: bool = false
# 子节点引用
@onready var _sprite: Sprite2D = $Sprite2D
@onready var _range_area: Area2D = $RangeArea
@onready var _range_shape: CollisionShape2D = $RangeArea/RangeShape
## 当前等级
var current_level: int:
get: return _current_level
## 当前攻击力
var current_damage: int:
get: return base_damage + (_current_level - 1) * 5
## 当前攻击范围
var current_range: float:
get: return attack_range + (_current_level - 1) * 20.0
## 总投入金币
var total_invested: int:
get: return _total_invested
func _ready() -> void:
_total_invested = purchase_cost
_update_range_indicator()
func _process(delta: float) -> void:
if not is_inside_tree():
return
# 更新攻击冷却
_fire_cooldown -= delta
# 寻找目标并攻击
_find_and_attack_target()
## 寻找目标并攻击
func _find_and_attack_target() -> void:
if _fire_cooldown > 0:
return
_current_target = _find_best_target()
if _current_target != null and _current_target.is_alive:
_fire(_current_target)
_fire_cooldown = fire_rate
## 寻找最佳攻击目标
## 策略:选择走得最远的怪物(最接近萝卜的)
func _find_best_target() -> Monster:
var monsters = get_tree().get_nodes_in_group("monsters")
var best_target: Monster = null
var best_progress: float = -1.0
for node in monsters:
if not node is Monster monster:
continue
if not monster.is_alive:
continue
# 检查是否在攻击范围内
var distance: float = global_position.distance_to(monster.global_position)
if distance > current_range:
continue
# 选择 progress 最大的
if monster.progress > best_progress:
best_progress = monster.progress
best_target = monster
return best_target
## 开火 —— 创建子弹飞向目标
func _fire(target: Monster) -> void:
if not projectile_scene:
# 没有子弹场景,直接造成伤害
target.take_damage(current_damage)
return
# 创建子弹
var projectile = projectile_scene.instantiate()
get_tree().root.add_child(projectile)
projectile.global_position = global_position
# 让子弹飞向目标
if projectile is Projectile proj:
proj.initialize(target, current_damage)
## 升级塔
func upgrade(cost: int) -> bool:
if _current_level >= max_level:
return false
if not GameManager.spend_gold(cost):
return false
_current_level += 1
_total_invested += cost
_update_range_indicator()
_play_upgrade_effect()
tower_upgraded.emit(_current_level)
return true
## 出售塔
func sell() -> void:
var refund: int = floori(_total_invested * GameConstants.SELL_REFUND_RATE)
GameManager.add_gold(refund)
tower_sold.emit(refund)
_play_sell_effect()
queue_free()
## 更新攻击范围指示器
func _update_range_indicator() -> void:
if not _range_shape:
return
var circle = _range_shape.shape as CircleShape2D
if circle:
circle.radius = current_range
## 选中/取消选中
func set_selected(selected: bool) -> void:
_is_selected = selected
if _range_area:
_range_area.visible = selected
## 升级特效
func _play_upgrade_effect() -> void:
if not _sprite:
return
var tween = create_tween()
tween.tween_property(_sprite, "scale", _sprite.scale * 1.3, 0.15)
tween.tween_property(_sprite, "scale", _sprite.scale / 1.3, 0.15)
## 出售特效
func _play_sell_effect() -> void:
if not _sprite:
return
var tween = create_tween()
tween.tween_property(_sprite, "modulate", Color(1, 1, 1, 0), 0.3)四种塔的具体实现
瓶子塔——单体高伤害
瓶子塔是最基础的攻击塔,就像一个精准的"水枪",一次打一个目标,伤害稳定。
C
/// <summary>
/// 瓶子塔 —— 单体攻击塔
/// 像狙击手一样精准攻击单个目标
/// </summary>
public partial class BottleTower : Tower
{
public override void _Ready()
{
BaseDamage = 15;
AttackRange = 150.0f;
FireRate = 0.8f;
PurchaseCost = 100;
base._Ready();
}
/// <summary>
/// 瓶子塔的攻击:射出一颗子弹
/// </summary>
protected override void Fire(Monster target)
{
// 旋转朝向目标
LookAt(target.GlobalPosition);
// 调用基类的开火逻辑(创建子弹)
base.Fire(target);
}
}GDScript
extends Tower
class_name BottleTower
## 瓶子塔 —— 单体攻击塔
## 像狙击手一样精准攻击单个目标
func _ready() -> void:
base_damage = 15
attack_range = 150.0
fire_rate = 0.8
purchase_cost = 100
super._ready()
## 瓶子塔的攻击:射出一颗子弹
func _fire(target: Monster) -> void:
# 旋转朝向目标
look_at(target.global_position)
# 调用基类的开火逻辑
super._fire(target)风扇塔——范围减速
风扇塔不直接造成大量伤害,但能让范围内所有怪物"慢下来"。就像交警在路口设了减速带。
C
/// <summary>
/// 风扇塔 —— 范围减速塔
/// 像减速带一样让范围内所有怪物慢下来
/// </summary>
public partial class FanTower : Tower
{
/// <summary>减速因子(0.5 = 速度减半)</summary>
[Export] public float SlowFactor { get; set; } = 0.5f;
/// <summary>减速持续时间</summary>
[Export] public float SlowDuration { get; set; } = 2.0f;
/// <summary>减速范围(比攻击范围稍大)</summary>
[Export] public float SlowRange { get; set; } = 180.0f;
public override void _Ready()
{
BaseDamage = 5;
AttackRange = 150.0f;
FireRate = 1.2f;
PurchaseCost = 120;
base._Ready();
}
/// <summary>
/// 风扇塔的特殊逻辑:持续对范围内所有怪物减速
/// </summary>
protected override void FindAndAttackTarget()
{
// 除了正常攻击,还要持续减速
ApplySlowToAllInRange();
// 调用基类逻辑进行攻击
base.FindAndAttackTarget();
}
/// <summary>
/// 对范围内所有怪物施加减速
/// </summary>
private void ApplySlowToAllInRange()
{
var monsters = GetTree()
.GetNodesInGroup("monsters");
foreach (var node in monsters)
{
if (node is not Monster monster) continue;
if (!monster.IsAlive) continue;
float distance = GlobalPosition.DistanceTo(
monster.GlobalPosition);
if (distance <= SlowRange)
{
monster.ApplySlow(SlowFactor, 0.5f);
}
}
}
/// <summary>
/// 等级提升后加强减速效果
/// </summary>
public override int CurrentDamage =>
BaseDamage + (_currentLevel - 1) * 3;
public float CurrentSlowFactor =>
Mathf.Clamp(SlowFactor - (_currentLevel - 1) * 0.1f, 0.2f, 1.0f);
}GDScript
extends Tower
class_name FanTower
## 风扇塔 —— 范围减速塔
## 像减速带一样让范围内所有怪物慢下来
## 减速因子(0.5 = 速度减半)
@export var slow_factor: float = 0.5
## 减速持续时间
@export var slow_duration: float = 2.0
## 减速范围
@export var slow_range: float = 180.0
func _ready() -> void:
base_damage = 5
attack_range = 150.0
fire_rate = 1.2
purchase_cost = 120
super._ready()
## 风扇塔的特殊逻辑:持续减速
func _find_and_attack_target() -> void:
# 持续对范围内所有怪物减速
_apply_slow_to_all_in_range()
# 正常攻击
super._find_and_attack_target()
## 对范围内所有怪物施加减速
func _apply_slow_to_all_in_range() -> void:
var monsters = get_tree().get_nodes_in_group("monsters")
for node in monsters:
if not node is Monster monster:
continue
if not monster.is_alive:
continue
var distance: float = global_position.distance_to(monster.global_position)
if distance <= slow_range:
monster.apply_slow(slow_factor, 0.5)
## 等级提升后加强减速效果
var current_damage: int:
get: return base_damage + (_current_level - 1) * 3
var current_slow_factor: float:
get: return clampf(slow_factor - (_current_level - 1) * 0.1, 0.2, 1.0)火箭塔——高伤害慢攻
火箭塔就像真正的火箭炮——威力巨大但装填很慢。适合对付血厚的 Boss。
C
/// <summary>
/// 火箭塔 —— 高伤害慢速攻击塔
/// 像火箭炮一样威力巨大但装填缓慢
/// </summary>
public partial class RocketTower : Tower
{
/// <summary>爆炸范围(范围伤害)</summary>
[Export] public float ExplosionRadius { get; set; } = 80.0f;
public override void _Ready()
{
BaseDamage = 50;
AttackRange = 200.0f;
FireRate = 2.5f; // 攻击很慢!
PurchaseCost = 200;
base._Ready();
}
protected override void Fire(Monster target)
{
LookAt(target.GlobalPosition);
// 火箭弹命中后造成范围伤害
// 这里通过创建特殊子弹来实现
if (ProjectileScene != null)
{
var projectile = ProjectileScene.Instantiate<RocketProjectile>();
GetTree().Root.AddChild(projectile);
projectile.GlobalPosition = GlobalPosition;
projectile.Initialize(target, CurrentDamage, ExplosionRadius);
}
}
public override int CurrentDamage =>
BaseDamage + (_currentLevel - 1) * 20;
}GDScript
extends Tower
class_name RocketTower
## 火箭塔 —— 高伤害慢速攻击塔
## 像火箭炮一样威力巨大但装填缓慢
## 爆炸范围
@export var explosion_radius: float = 80.0
func _ready() -> void:
base_damage = 50
attack_range = 200.0
fire_rate = 2.5 # 攻击很慢!
purchase_cost = 200
super._ready()
func _fire(target: Monster) -> void:
look_at(target.global_position)
if projectile_scene:
var projectile = projectile_scene.instantiate()
get_tree().root.add_child(projectile)
projectile.global_position = global_position
if projectile is RocketProjectile proj:
proj.initialize(target, current_damage, explosion_radius)
var current_damage: int:
get: return base_damage + (_current_level - 1) * 20雷达塔——纯辅助减速
雷达塔不攻击,但能对大范围内的所有怪物持续减速。就像一个"信号干扰器",让所有敌人的"导航系统"变慢。
C
/// <summary>
/// 雷达塔 —— 纯辅助减速塔
/// 不造成伤害,但对大范围内所有怪物持续减速
/// </summary>
public partial class RadarTower : Tower
{
/// <summary>减速范围(非常大)</summary>
[Export] public float RadarRange { get; set; } = 250.0f;
/// <summary>减速因子</summary>
[Export] public float SlowFactor { get; set; } = 0.7f;
public override void _Ready()
{
BaseDamage = 0; // 不造成伤害!
AttackRange = RadarRange;
FireRate = 0.0f; // 不需要攻击冷却
PurchaseCost = 80;
base._Ready();
}
/// <summary>
/// 雷达塔的核心逻辑:持续减速范围内所有怪物
/// </summary>
protected override void FindAndAttackTarget()
{
// 不攻击,只减速
ApplyRadarSlow();
}
private void ApplyRadarSlow()
{
var monsters = GetTree()
.GetNodesInGroup("monsters");
foreach (var node in monsters)
{
if (node is not Monster monster) continue;
if (!monster.IsAlive) continue;
float distance = GlobalPosition.DistanceTo(
monster.GlobalPosition);
if (distance <= CurrentRange)
{
monster.ApplySlow(CurrentSlowFactor, 0.5f);
}
}
}
public float CurrentSlowFactor =>
Mathf.Clamp(
SlowFactor - (_currentLevel - 1) * 0.1f,
0.3f, 1.0f);
public override float CurrentRange =>
RadarRange + (_currentLevel - 1) * 30.0f;
}GDScript
extends Tower
class_name RadarTower
## 雷达塔 —— 纯辅助减速塔
## 不造成伤害,但对大范围内所有怪物持续减速
## 减速范围
@export var radar_range: float = 250.0
## 减速因子
@export var slow_factor: float = 0.7
func _ready() -> void:
base_damage = 0 # 不造成伤害!
attack_range = radar_range
fire_rate = 0.0 # 不需要攻击冷却
purchase_cost = 80
super._ready()
## 雷达塔核心逻辑:持续减速
func _find_and_attack_target() -> void:
_apply_radar_slow()
func _apply_radar_slow() -> void:
var monsters = get_tree().get_nodes_in_group("monsters")
for node in monsters:
if not node is Monster monster:
continue
if not monster.is_alive:
continue
var distance: float = global_position.distance_to(monster.global_position)
if distance <= current_range:
monster.apply_slow(current_slow_factor, 0.5)
var current_slow_factor: float:
get: return clampf(slow_factor - (_current_level - 1) * 0.1, 0.3, 1.0)
var current_range: float:
get: return radar_range + (_current_level - 1) * 30.0塔的场景结构
所有塔的场景结构都类似,以瓶子塔为例:
BottleTower (Node2D) ← 挂载 bottle_tower.gd
├── Sprite2D ← 塔的外观贴图
├── RangeArea (Area2D) ← 攻击范围显示
│ └── RangeShape (CollisionShape2D) ← 圆形范围
└── Pivot (Marker2D) ← 子弹发射点(旋转用)塔的属性对比表
| 属性 | 瓶子塔 | 风扇塔 | 火箭塔 | 雷达塔 |
|---|---|---|---|---|
| 价格 | 100 | 120 | 200 | 80 |
| 伤害 | 15 | 5 | 50 | 0 |
| 攻速 | 0.8秒 | 1.2秒 | 2.5秒 | 无 |
| 范围 | 150 | 150 | 200 | 250 |
| 特殊 | 无 | 范围减速 | 范围爆炸 | 大范围减速 |
| 适合打 | 普通怪 | 快速怪 | 坦克/Boss | 辅助控制 |
本节小结
| 概念 | 说明 |
|---|---|
| TowerData | 塔的数据资源,定义价格、伤害等属性 |
| Tower (基类) | 提供寻找目标、攻击、升级、出售等通用功能 |
| BottleTower | 瓶子塔,单体攻击,性价比最高 |
| FanTower | 风扇塔,攻击+范围减速 |
| RocketTower | 火箭塔,高伤害+范围爆炸,适合打 Boss |
| RadarTower | 雷达塔,纯辅助,大范围持续减速 |
四种塔各有特色,合理搭配使用才是塔防游戏的精髓!下一节我们将实现升级与出售系统,让玩家能够强化自己的防御力量。
