6. 升级与技能
2026/4/14大约 7 分钟
塔防——升级与技能
想象你的箭塔一开始只能射普通箭。花点金币升级后,箭变锋利了、射程变远了。升到最高级后,解锁一个"暴风箭雨"技能——每隔一段时间可以手动释放一次,一次性射出大量箭矢覆盖整个区域。
升级和技能让游戏有了"成长感",玩家会觉得自己在变强。
升级系统设计
升级带来的变化
每次升级应该让塔在某些方面变得更强:
| 升级维度 | 说明 | 示例 |
|---|---|---|
| 伤害提升 | 每次攻击造成更多伤害 | 10 → 13 → 17 |
| 攻速提升 | 攻击间隔缩短 | 1.0秒 → 0.85秒 → 0.7秒 |
| 射程增加 | 可以打到更远的敌人 | 150像素 → 165像素 → 180像素 |
| 特殊效果 | 解锁新的能力 | 10%概率暴击、附带灼烧 |
升级费用递增
每次升级的花费应该比上一次多。比如第一次升级花 30 金币,第二次 50,第三次 80。这样玩家需要权衡"是升级现有的塔,还是放一座新塔"。
升级数据配置
用 Resource 文件来定义每个等级的属性,方便调整平衡。
C
using Godot;
/// <summary>
/// 塔的等级数据——定义每个等级的属性。
/// 每种塔有三组等级数据,对应 Lv1/Lv2/Lv3。
/// </summary>
[GlobalClass]
public partial class TowerLevelData : Resource
{
[ExportGroup("属性")]
[Export] public int Damage { get; set; } = 10;
[Export] public float AttackRange { get; set; } = 150.0f;
[Export] public float AttackInterval { get; set; } = 1.0f;
[Export] public int UpgradeCost { get; set; } = 30;
[ExportGroup("特殊效果")]
[Export] public float CriticalChance { get; set; } = 0f; // 暴击概率(0~1)
[Export] public float CriticalMultiplier { get; set; } = 2.0f; // 暴击倍率
[Export] public float BurnDamage { get; set; } = 0f; // 每秒灼烧伤害
[Export] public float BurnDuration { get; set; } = 0f; // 灼烧持续时间
}
/// <summary>
/// 塔的完整升级数据——包含所有等级的配置。
/// </summary>
[GlobalClass]
public partial class TowerUpgradeData : Resource
{
[Export] public PackedScene TowerScene { get; set; }
[Export] public string TowerName { get; set; } = "箭塔";
[Export] public Godot.Collections.Array<TowerLevelData> Levels { get; set; } = new();
/// <summary>获取指定等级的数据</summary>
public TowerLevelData GetLevelData(int level)
{
int index = Mathf.Clamp(level - 1, 0, Levels.Count - 1);
return Levels[index];
}
}GDScript
class_name TowerLevelData
extends Resource
## 塔的等级数据——定义每个等级的属性。
## 每种塔有三组等级数据,对应 Lv1/Lv2/Lv3。
@export_group("属性")
@export var damage: int = 10
@export var attack_range: float = 150.0
@export var attack_interval: float = 1.0
@export var upgrade_cost: int = 30
@export_group("特殊效果")
@export var critical_chance: float = 0.0 # 暴击概率(0~1)
@export var critical_multiplier: float = 2.0 # 暴击倍率
@export var burn_damage: float = 0.0 # 每秒灼烧伤害
@export var burn_duration: float = 0.0 # 灼烧持续时间
class_name TowerUpgradeData
extends Resource
## 塔的完整升级数据——包含所有等级的配置。
@export var tower_scene: PackedScene
@export var tower_name: String = "箭塔"
@export var levels: Array[TowerLevelData] = []
## 获取指定等级的数据
func get_level_data(level: int) -> TowerLevelData:
var index = clamp(level - 1, 0, levels.size() - 1)
return levels[index]升级逻辑实现
升级时,塔从配置数据中读取新等级的属性并应用。
C
using Godot;
/// <summary>
/// 支持升级的塔基类——在 TowerBase 基础上增加升级功能。
/// </summary>
public partial class UpgradeableTower : TowerBase
{
[Export] public TowerUpgradeData UpgradeData { get; set; }
private Sprite2D _levelIndicator; // 等级标识
public override void _Ready()
{
base._Ready();
_levelIndicator = GetNodeOrNull<Sprite2D>("LevelIndicator");
}
/// <summary>升级塔到下一级</summary>
public new void Upgrade()
{
if (_level >= MaxLevel) return;
if (UpgradeData == null) return;
TowerLevelData nextLevel = UpgradeData.GetLevelData(_level + 1);
// 检查金币
if (GameManager.Instance.Gold < nextLevel.UpgradeCost)
{
GD.Print("金币不足,无法升级!");
return;
}
// 扣除金币
GameManager.Instance.Gold -= nextLevel.UpgradeCost;
// 应用新等级属性
_level++;
Damage = nextLevel.Damage;
AttackRange = nextLevel.AttackRange;
AttackInterval = nextLevel.AttackInterval;
_attackTimer.WaitTime = AttackInterval;
// 更新射程显示
float diameter = AttackRange * 2;
_rangeVisual.Scale = new Vector2(diameter / 64, diameter / 64);
// 更新碰撞检测范围
var shape = _detectionArea.GetChild<CollisionShape2D>(0);
if (shape.Shape is CircleShape2D circle)
{
circle.Radius = AttackRange;
}
// 显示等级标识
UpdateLevelVisual();
// 播放升级特效
PlayUpgradeEffect();
}
/// <summary>更新等级标识显示</summary>
private void UpdateLevelVisual()
{
if (_levelIndicator == null) return;
// 根据等级显示不同数量的星星
// Lv1 = 无星,Lv2 = 1星,Lv3 = 2星
// 这里简单用颜色区分
switch (_level)
{
case 1:
_levelIndicator.Modulate = Colors.White;
break;
case 2:
_levelIndicator.Modulate = Colors.Green;
break;
case 3:
_levelIndicator.Modulate = Colors.Gold;
break;
}
}
/// <summary>播放升级特效</summary>
private void PlayUpgradeEffect()
{
// 创建一个简单的闪光效果
var tween = CreateTween();
_turret.Modulate = Colors.Yellow;
tween.TweenProperty(_turret, "modulate", Colors.White, 0.5);
}
}GDScript
extends "res://scripts/towers/tower_base.gd"
## 支持升级的塔基类——在 TowerBase 基础上增加升级功能。
@export var upgrade_data: TowerUpgradeData = null
@onready var level_indicator: Sprite2D = $LevelIndicator
func _ready():
super._ready()
## 升级塔到下一级
func upgrade():
if level >= max_level:
return
if upgrade_data == null:
return
var next_level = upgrade_data.get_level_data(level + 1)
# 检查金币
if GameManager.instance.gold < next_level.upgrade_cost:
push_warning("金币不足,无法升级!")
return
# 扣除金币
GameManager.instance.gold -= next_level.upgrade_cost
# 应用新等级属性
level += 1
damage = next_level.damage
attack_range = next_level.attack_range
attack_interval = next_level.attack_interval
attack_timer.wait_time = attack_interval
# 更新射程显示
var diameter = attack_range * 2
range_visual.scale = Vector2(diameter / 64.0, diameter / 64.0)
# 更新碰撞检测范围
var shape = detection_area.get_child(0)
if shape.shape is CircleShape2D:
shape.shape.radius = attack_range
# 显示等级标识
_update_level_visual()
# 播放升级特效
_play_upgrade_effect()
## 更新等级标识显示
func _update_level_visual():
if level_indicator == null:
return
# 根据等级显示不同颜色
match level:
1:
level_indicator.modulate = Color.WHITE
2:
level_indicator.modulate = Color.GREEN
3:
level_indicator.modulate = Color.GOLD
## 播放升级特效
func _play_upgrade_effect():
# 创建一个简单的闪光效果
var tween = create_tween()
turret.modulate = Color.YELLOW
tween.tween_property(turret, "modulate", Color.WHITE, 0.5)主动技能系统
除了被动升级,高级塔可以拥有一个主动技能——玩家手动点击释放。
技能设计
| 塔类型 | 技能名称 | 效果 | 冷却时间 |
|---|---|---|---|
| 箭塔 | 暴风箭雨 | 向范围内所有敌人射箭 | 30秒 |
| 炮塔 | 集中轰炸 | 在指定位置连续投弹3次 | 45秒 |
| 冰塔 | 暴风雪 | 冻结范围内所有敌人5秒 | 40秒 |
技能实现
C
using Godot;
/// <summary>
/// 塔的主动技能——玩家可以手动释放的强力技能。
/// 每个技能有冷却时间,用完后需要等待才能再次使用。
/// </summary>
public partial class TowerSkill : Node
{
[Export] public string SkillName { get; set; } = "暴风箭雨";
[Export] public float Cooldown { get; set; } = 30.0f; // 冷却时间(秒)
[Export] public Texture2D SkillIcon { get; set; } // 技能图标
private Timer _cooldownTimer;
private bool _isReady = true;
[Signal]
public delegate void SkillReadyEventHandler();
[Signal]
public delegate void CooldownUpdatedEventHandler(float remaining);
public override void _Ready()
{
_cooldownTimer = new Timer { OneShot = true };
_cooldownTimer.Timeout += OnCooldownFinished;
AddChild(_cooldownTimer);
}
/// <summary>使用技能</summary>
public virtual bool Activate()
{
if (!_isReady) return false;
_isReady = false;
_cooldownTimer.Start(Cooldown);
return true;
}
/// <summary>冷却完成</summary>
private void OnCooldownFinished()
{
_isReady = true;
EmitSignal(SignalName.SkillReady);
}
/// <summary>技能是否就绪</summary>
public bool IsReady => _isReady;
/// <summary>冷却剩余时间</summary>
public float RemainingCooldown => _cooldownTimer.TimeLeft;
}GDScript
extends Node
## 塔的主动技能——玩家可以手动释放的强力技能。
## 每个技能有冷却时间,用完后需要等待才能再次使用。
@export var skill_name: String = "暴风箭雨"
@export var cooldown: float = 30.0 # 冷却时间(秒)
@export var skill_icon: Texture2D # 技能图标
var cooldown_timer: Timer
var is_ready: bool = true
signal skill_ready()
signal cooldown_updated(remaining: float)
func _ready():
cooldown_timer = Timer.new()
cooldown_timer.one_shot = true
cooldown_timer.timeout.connect(_on_cooldown_finished)
add_child(cooldown_timer)
## 使用技能
func activate() -> bool:
if not is_ready:
return false
is_ready = false
cooldown_timer.start(cooldown)
return true
## 冷却完成
func _on_cooldown_finished():
is_ready = true
skill_ready.emit()
## 技能是否就绪
func get_is_ready() -> bool:
return is_ready
## 冷却剩余时间
func get_remaining_cooldown() -> float:
return cooldown_timer.time_left暴风箭雨技能实现
C
using Godot;
/// <summary>
/// 箭塔技能——暴风箭雨。
/// 对攻击范围内的所有敌人各射一箭。
/// </summary>
public partial class ArrowRainSkill : TowerSkill
{
private ArrowTower _tower;
private PackedScene _arrowScene;
public ArrowRainSkill(ArrowTower tower, PackedScene arrowScene)
{
_tower = tower;
_arrowScene = arrowScene;
SkillName = "暴风箭雨";
Cooldown = 30.0f;
}
public override bool Activate()
{
if (!base.Activate()) return false;
// 获取范围内所有敌人
var enemies = _tower.GetEnemiesInRange();
// 对每个敌人射一箭
foreach (var enemy in enemies)
{
var arrow = _arrowScene.Instantiate<ProjectileBase>();
arrow.GlobalPosition = _tower.GetTurretPosition();
arrow.Damage = _tower.Damage * 2; // 技能伤害翻倍
arrow.Target = enemy;
var container = GetNode<Node2D>("/root/Game/ProjectileContainer");
container.AddChild(arrow);
}
// 播放技能特效(简单闪烁)
var tween = _tower.CreateTween();
_tower.Modulate = Colors.Yellow;
tween.TweenProperty(_tower, "modulate", Colors.White, 0.3);
return true;
}
}GDScript
extends Node
## 箭塔技能——暴风箭雨。
## 对攻击范围内的所有敌人各射一箭。
var tower: Node
var arrow_scene: PackedScene
var skill_name: String = "暴风箭雨"
var cooldown: float = 30.0
var cooldown_timer: Timer
var is_ready: bool = true
signal skill_ready()
func _init(tower_node: Node, scene: PackedScene):
tower = tower_node
arrow_scene = scene
func _ready():
cooldown_timer = Timer.new()
cooldown_timer.one_shot = true
cooldown_timer.timeout.connect(_on_cooldown_finished)
add_child(cooldown_timer)
## 使用技能
func activate() -> bool:
if not is_ready:
return false
is_ready = false
cooldown_timer.start(cooldown)
# 获取范围内所有敌人
var enemies = tower.get_enemies_in_range()
# 对每个敌人射一箭
for enemy in enemies:
var arrow = arrow_scene.instantiate()
arrow.global_position = tower.get_turret_position()
arrow.damage = tower.damage * 2 # 技能伤害翻倍
arrow.target = enemy
var container = get_node("/root/Game/ProjectileContainer")
container.add_child(arrow)
# 播放技能特效
var tween = tower.create_tween()
tower.modulate = Color.YELLOW
tween.tween_property(tower, "modulate", Color.WHITE, 0.3)
return true
func _on_cooldown_finished():
is_ready = true
skill_ready.emit()出售塔
玩家可以卖掉已放置的塔,回收一部分金币(通常是花费的 60%)。
C
/// <summary>出售塔,回收部分金币</summary>
public void SellTower(UpgradeableTower tower, Vector2 gridPosition)
{
// 计算总投入(购买 + 所有升级花费)
int totalInvested = tower.UpgradeData.GetLevelData(1).UpgradeCost; // 购买花费
for (int i = 2; i <= tower.Level; i++)
{
totalInvested += tower.UpgradeData.GetLevelData(i).UpgradeCost;
}
// 回收 60%
int refund = (int)(totalInvested * 0.6f);
GameManager.Instance.Gold += refund;
// 释放网格位置
var gridManager = GetNode<GridManager>("/root/Game/Map/GridManager");
gridManager.FreeCell(gridPosition);
// 移除塔
tower.QueueFree();
}GDScript
## 出售塔,回收部分金币
func sell_tower(tower: Node, grid_position: Vector2):
# 计算总投入(购买 + 所有升级花费)
var total_invested = tower.upgrade_data.get_level_data(1).upgrade_cost
for i in range(2, tower.level + 1):
total_invested += tower.upgrade_data.get_level_data(i).upgrade_cost
# 回收 60%
var refund = int(total_invested * 0.6)
GameManager.instance.gold += refund
# 释放网格位置
var grid_manager = get_node("/root/Game/Map/GridManager")
grid_manager.free_cell(grid_position)
# 移除塔
tower.queue_free()下一章
升级和技能系统搞定了,接下来实现资源与经济系统。
