6. 升级与技能选择
2026/4/13大约 10 分钟
升级与技能选择
升级:割草游戏的心脏
如果割草游戏是一辆车,那么"升级选技能"就是这辆车的发动机。没有升级系统,割草游戏就变成了一个"30分钟后必死"的无聊跑酷。有了升级系统,每一局都充满了"如果我当时选了另一个就好了"的悬念和期待。
升级系统的核心体验是:暂停游戏 -> 展示3~4个随机选项 -> 玩家选择一个 -> 游戏继续。这个短暂的"决策时刻",是割草游戏中唯一的策略深度所在。
经验系统设计
经验从哪里来?
敌人被消灭后会掉落经验宝石(蓝色的小菱形)。玩家需要走过去"捡"起来。这和自动攻击一样,都是割草游戏的标志性设计。
| 设计选择 | 效果 | 我们的选择 |
|---|---|---|
| 击杀直接获得经验 | 简单直接,但缺少互动 | 不采用 |
| 掉落拾取 | 增加移动决策,要去捡宝石 | 采用 |
| 自动吸收经验 | 太简单,不需要策略 | 不采用 |
为什么要"捡"经验?
如果经验自动到账,玩家就没有理由移动了——站在原地等死就行。要求玩家走过去捡经验,迫使玩家在"安全区域"和"经验宝石位置"之间做出选择,增加了策略性。
经验值等级表
每一级需要的经验值会越来越多,就像爬山一样——越到山顶越难爬。
| 等级 | 累计经验 | 说明 |
|---|---|---|
| 1 | 0 | 起始等级 |
| 2 | 10 | 很快就能升 |
| 5 | 100 | 开始有选择压力 |
| 10 | 500 | 需要击杀大量敌人 |
| 20 | 2000 | 后期升级很慢 |
| 30 | 6000 | 大多数对局的最终等级 |
经验值计算公式
C
// ExperienceManager.cs
// 管理经验值和升级逻辑
using Godot;
public partial class ExperienceManager : Node
{
// === 经验参数 ===
// 升级所需基础经验值(1级升2级)
[Export] public float BaseExperience = 10f;
// 每级增长系数(经验需求递增)
[Export] public float GrowthFactor = 1.3f;
// === 运行时状态 ===
public int CurrentLevel { get; private set; } = 1;
public float CurrentExperience { get; private set; } = 0f;
public float ExperienceToNextLevel { get; private set; }
// 信号:升级时触发
[Signal] public delegate void LevelUpEventHandler(int newLevel);
// 信号:获得经验时触发(更新UI)
[Signal] public delegate void ExperienceChangedEventHandler(
float current, float required);
public override void _Ready()
{
UpdateExperienceRequirement();
}
/// <summary>
/// 增加经验值
/// </summary>
public void AddExperience(float amount)
{
CurrentExperience += amount;
// 检查是否升级
while (CurrentExperience >= ExperienceToNextLevel)
{
CurrentExperience -= ExperienceToNextLevel;
CurrentLevel++;
UpdateExperienceRequirement();
// 触发升级信号
EmitSignal(SignalName.LevelUp, CurrentLevel);
GD.Print($"升级!当前等级:{CurrentLevel}");
}
// 触发经验变化信号(更新UI)
EmitSignal(SignalName.ExperienceChanged,
CurrentExperience, ExperienceToNextLevel);
}
/// <summary>
/// 计算下一级需要的经验值
/// 使用公式:baseExp * growthFactor^(level-1)
/// </summary>
private void UpdateExperienceRequirement()
{
ExperienceToNextLevel = BaseExperience
* Mathf.Pow(GrowthFactor, CurrentLevel - 1);
}
/// <summary>
/// 获取升级进度百分比(0~1)
/// </summary>
public float GetProgress()
{
return CurrentExperience / ExperienceToNextLevel;
}
}GDScript
# experience_manager.gd
# 管理经验值和升级逻辑
extends Node
# === 经验参数 ===
# 升级所需基础经验值(1级升2级)
@export var base_experience: float = 10.0
# 每级增长系数(经验需求递增)
@export var growth_factor: float = 1.3
# === 运行时状态 ===
var current_level: int = 1
var current_experience: float = 0.0
var experience_to_next_level: float
# 信号:升级时触发
signal level_up(new_level: int)
# 信号:获得经验时触发(更新UI)
signal experience_changed(current: float, required: float)
func _ready():
update_experience_requirement()
## 增加经验值
func add_experience(amount: float):
current_experience += amount
# 检查是否升级
while current_experience >= experience_to_next_level:
current_experience -= experience_to_next_level
current_level += 1
update_experience_requirement()
# 触发升级信号
level_up.emit(current_level)
print("升级!当前等级:%d" % current_level)
# 触发经验变化信号(更新UI)
experience_changed.emit(current_experience, experience_to_next_level)
## 计算下一级需要的经验值
## 使用公式:baseExp * growthFactor^(level-1)
func update_experience_requirement():
experience_to_next_level = base_experience \
* pow(growth_factor, current_level - 1)
## 获取升级进度百分比(0~1)
func get_progress() -> float:
return current_experience / experience_to_next_level技能选择系统
技能的类型
升级时弹出的选项分为三大类:
| 类型 | 说明 | 举例 |
|---|---|---|
| 新武器 | 获得一把全新的武器 | 旋转刀片、弹幕、火焰AOE |
| 武器升级 | 已有武器升一级 | 旋转刀片 Lv.2(伤害+20%) |
| 被动增益 | 永久增加一项属性 | 移速+10%、拾取范围+20% |
技能稀有度
不是所有技能都一样珍贵。稀有度决定了出现的概率和效果强度:
| 稀有度 | 出现概率 | 边框颜色 | 效果强度 |
|---|---|---|---|
| 普通 | 70% | 灰色/白色 | 基础效果 |
| 稀有 | 25% | 蓝色 | 1.5倍效果 |
| 传说 | 5% | 金色/紫色 | 2倍效果,特殊效果 |
稀有度的心理作用
稀有度让升级选择更有"开盲盒"的感觉。当你看到金色边框的选项时,心跳会加速——"这是传说级!选它!"这种期待感是游戏成瘾的重要因素之一。
技能选择管理器
C
// SkillSelectionManager.cs
// 管理升级时的技能选择
using Godot;
using System.Collections.Generic;
public partial class SkillSelectionManager : Node
{
// === 所有可用的技能定义 ===
private List<SkillDefinition> _allSkills = new();
// === 引用 ===
private ExperienceManager _expManager;
private CanvasLayer _selectionUI;
// === 状态 ===
public bool IsSelecting { get; private set; } = false;
public override void _Ready()
{
_expManager = GetParent()
.GetNode<ExperienceManager>("ExperienceManager");
// 连接升级信号
_expManager.LevelUp += OnLevelUp;
// 注册所有技能
RegisterSkills();
}
/// <summary>
/// 注册所有可用技能
/// </summary>
private void RegisterSkills()
{
// 武器类技能
_allSkills.Add(new SkillDefinition
{
Name = "旋转刀片",
Description = "围绕你旋转的刀片",
Type = SkillType.NewWeapon,
Rarity = SkillRarity.Common,
WeaponScene = GD.Load<PackedScene>(
"res://scenes/weapons/spinning_blade.tscn")
});
_allSkills.Add(new SkillDefinition
{
Name = "弹幕",
Description = "自动发射子弹",
Type = SkillType.NewWeapon,
Rarity = SkillRarity.Common,
WeaponScene = GD.Load<PackedScene>(
"res://scenes/weapons/bullet_weapon.tscn")
});
// 被动增益类技能
_allSkills.Add(new SkillDefinition
{
Name = "移速提升",
Description = "移动速度 +10%",
Type = SkillType.Passive,
Rarity = SkillRarity.Common,
PassiveEffect = PassiveEffect.MoveSpeed,
PassiveValue = 0.1f
});
_allSkills.Add(new SkillDefinition
{
Name = "拾取范围",
Description = "经验拾取范围 +25%",
Type = SkillType.Passive,
Rarity = SkillRarity.Rare,
PassiveEffect = PassiveEffect.PickupRange,
PassiveValue = 0.25f
});
_allSkills.Add(new SkillDefinition
{
Name = "最大生命",
Description = "最大生命值 +20%",
Type = SkillType.Passive,
Rarity = SkillRarity.Common,
PassiveEffect = PassiveEffect.MaxHealth,
PassiveValue = 0.2f
});
_allSkills.Add(new SkillDefinition
{
Name = "伤害提升",
Description = "所有武器伤害 +15%",
Type = SkillType.Passive,
Rarity = SkillRarity.Rare,
PassiveEffect = PassiveEffect.DamageBonus,
PassiveValue = 0.15f
});
_allSkills.Add(new SkillDefinition
{
Name = "护甲",
Description = "减少受到伤害 10%",
Type = SkillType.Passive,
Rarity = SkillRarity.Common,
PassiveEffect = PassiveEffect.Armor,
PassiveValue = 0.1f
});
}
/// <summary>
/// 当玩家升级时调用
/// </summary>
private void OnLevelUp(int newLevel)
{
// 暂停游戏
GetTree().Paused = true;
IsSelecting = true;
// 随机选择3~4个技能
var options = GenerateRandomOptions(3);
// 显示选择UI
ShowSelectionUI(options);
}
/// <summary>
/// 生成随机技能选项
/// </summary>
private List<SkillDefinition> GenerateRandomOptions(int count)
{
var options = new List<SkillDefinition>();
var available = new List<SkillDefinition>(_allSkills);
// 过滤掉不可用的技能(如武器栏已满)
available = FilterAvailableSkills(available);
// 随机抽取
for (int i = 0; i < count && available.Count > 0; i++)
{
int index = GD.RandRange(0, available.Count - 1);
options.Add(available[index]);
available.RemoveAt(index);
}
return options;
}
/// <summary>
/// 过滤可用的技能
/// </summary>
private List<SkillDefinition> FilterAvailableSkills(
List<SkillDefinition> skills)
{
var filtered = new List<SkillDefinition>();
var weaponManager = GetParent()
.GetNodeOrNull<Node>("WeaponManager");
foreach (var skill in skills)
{
if (skill.Type == SkillType.NewWeapon)
{
// 检查武器栏是否已满
int weaponCount = weaponManager?.GetChildCount() ?? 0;
if (weaponCount < 6) // 最多6把武器
{
filtered.Add(skill);
}
}
else
{
filtered.Add(skill);
}
}
return filtered;
}
/// <summary>
/// 显示选择UI
/// </summary>
private void ShowSelectionUI(List<SkillDefinition> options)
{
// 这里需要和UI场景配合
// 弹出一个面板,显示每个技能的名称、描述、稀有度
GD.Print("=== 升级选择 ===");
for (int i = 0; i < options.Count; i++)
{
GD.Print($"选项{i + 1}: [{options[i].Rarity}] " +
$"{options[i].Name} - {options[i].Description}");
}
}
/// <summary>
/// 玩家选择了一个技能
/// </summary>
public void SelectSkill(int optionIndex)
{
// 应用技能效果
// (这里需要根据选择的技能类型做不同处理)
// 关闭UI
// 恢复游戏
GetTree().Paused = false;
IsSelecting = false;
}
}
// === 辅助数据结构 ===
public enum SkillType { NewWeapon, WeaponUpgrade, Passive }
public enum SkillRarity { Common, Rare, Legendary }
public enum PassiveEffect
{
MoveSpeed, PickupRange, MaxHealth,
DamageBonus, Armor, CooldownReduction
}
public class SkillDefinition
{
public string Name { get; set; }
public string Description { get; set; }
public SkillType Type { get; set; }
public SkillRarity Rarity { get; set; }
public PackedScene WeaponScene { get; set; }
public PassiveEffect PassiveEffect { get; set; }
public float PassiveValue { get; set; }
}GDScript
# skill_selection_manager.gd
# 管理升级时的技能选择
extends Node
# === 技能类型枚举 ===
enum SkillType { NEW_WEAPON, WEAPON_UPGRADE, PASSIVE }
enum SkillRarity { COMMON, RARE, LEGENDARY }
enum PassiveEffect {
MOVE_SPEED, PICKUP_RANGE, MAX_HEALTH,
DAMAGE_BONUS, ARMOR, COOLDOWN_REDUCTION
}
# === 所有可用的技能定义 ===
var _all_skills = []
# === 引用 ===
var _exp_manager: Node
var _selection_ui: CanvasLayer
# === 状态 ===
var is_selecting: bool = false
func _ready():
_exp_manager = get_parent().get_node("ExperienceManager")
# 连接升级信号
_exp_manager.level_up.connect(_on_level_up)
# 注册所有技能
register_skills()
## 注册所有可用技能
func register_skills():
# 武器类技能
_all_skills.append({
"name": "旋转刀片",
"description": "围绕你旋转的刀片",
"type": SkillType.NEW_WEAPON,
"rarity": SkillRarity.COMMON,
"weapon_scene": load(
"res://scenes/weapons/spinning_blade.tscn")
})
_all_skills.append({
"name": "弹幕",
"description": "自动发射子弹",
"type": SkillType.NEW_WEAPON,
"rarity": SkillRarity.COMMON,
"weapon_scene": load(
"res://scenes/weapons/bullet_weapon.tscn")
})
# 被动增益类技能
_all_skills.append({
"name": "移速提升",
"description": "移动速度 +10%",
"type": SkillType.PASSIVE,
"rarity": SkillRarity.COMMON,
"passive_effect": PassiveEffect.MOVE_SPEED,
"passive_value": 0.1
})
_all_skills.append({
"name": "拾取范围",
"description": "经验拾取范围 +25%",
"type": SkillType.PASSIVE,
"rarity": SkillRarity.RARE,
"passive_effect": PassiveEffect.PICKUP_RANGE,
"passive_value": 0.25
})
_all_skills.append({
"name": "最大生命",
"description": "最大生命值 +20%",
"type": SkillType.PASSIVE,
"rarity": SkillRarity.COMMON,
"passive_effect": PassiveEffect.MAX_HEALTH,
"passive_value": 0.2
})
_all_skills.append({
"name": "伤害提升",
"description": "所有武器伤害 +15%",
"type": SkillType.PASSIVE,
"rarity": SkillRarity.RARE,
"passive_effect": PassiveEffect.DAMAGE_BONUS,
"passive_value": 0.15
})
_all_skills.append({
"name": "护甲",
"description": "减少受到伤害 10%",
"type": SkillType.PASSIVE,
"rarity": SkillRarity.COMMON,
"passive_effect": PassiveEffect.ARMOR,
"passive_value": 0.1
})
## 当玩家升级时调用
func _on_level_up(new_level: int):
# 暂停游戏
get_tree().paused = true
is_selecting = true
# 随机选择3个技能
var options = generate_random_options(3)
# 显示选择UI
show_selection_ui(options)
## 生成随机技能选项
func generate_random_options(count: int) -> Array:
var options = []
var available = _all_skills.duplicate()
# 过滤掉不可用的技能
available = filter_available_skills(available)
# 随机抽取
for i in range(min(count, available.size())):
var idx = randi() % available.size()
options.append(available[idx])
available.remove_at(idx)
return options
## 过滤可用的技能
func filter_available_skills(skills: Array) -> Array:
var filtered = []
var weapon_manager = get_parent().get_node_or_null("WeaponManager")
for skill in skills:
if skill.type == SkillType.NEW_WEAPON:
# 检查武器栏是否已满
var weapon_count = weapon_manager.get_child_count() \
if weapon_manager else 0
if weapon_count < 6: # 最多6把武器
filtered.append(skill)
else:
filtered.append(skill)
return filtered
## 显示选择UI
func show_selection_ui(options: Array):
print("=== 升级选择 ===")
for i in range(options.size()):
var skill = options[i]
print("选项%d: [%s] %s - %s" % [
i + 1, skill.rarity, skill.name, skill.description])
## 玩家选择了一个技能
func select_skill(option_index: int):
# 应用技能效果
# 关闭UI,恢复游戏
get_tree().paused = false
is_selecting = false被动增益详解
被动增益不像武器那样有视觉表现,但它们默默地让角色变得更强。
常见被动增益
| 增益名称 | 效果 | 叠加方式 | 推荐搭配 |
|---|---|---|---|
| 移速提升 | 移动速度+10% | 可叠加5次 | 弹幕流(需要走位) |
| 拾取范围 | 经验拾取距离+25% | 可叠加5次 | 所有流派 |
| 最大生命 | 生命上限+20% | 可叠加5次 | 近战流(容易被围) |
| 伤害提升 | 所有伤害+15% | 可叠加5次 | 所有流派 |
| 护甲 | 受伤减少10% | 可叠加5次 | 新手推荐 |
| 冷却缩减 | 攻击间隔-8% | 可叠加5次 | 多武器流 |
拾取范围的重要性
在游戏前期,"拾取范围"可能是最实用的被动增益。更大的拾取范围意味着你不需要走到每个经验宝石旁边就能自动收集,节省了大量跑路时间,也降低了被敌人包围的风险。
经验宝石的拾取机制
经验宝石需要一个简单的拾取系统:
- 玩家靠近宝石到一定距离内
- 宝石自动飞向玩家(磁吸效果)
- 到达玩家位置时,增加经验值
C
// ExperienceGem.cs
// 经验宝石:敌人掉落,玩家拾取后获得经验
using Godot;
public partial class ExperienceGem : Area3D
{
[Export] public float BasePickupRange = 1.5f;
[Export] public float MagnetSpeed = 8f;
[Export] public float BaseExperienceValue = 1f;
private float _experienceValue;
private bool _isBeingAttracted = false;
private Node3D _player;
public void SetValue(float value)
{
_experienceValue = value;
}
public override void _Ready()
{
_player = GetTree().CurrentScene
.GetNode<Node3D>("Player");
// 设置碰撞检测
var shape = new SphereShape3D();
shape.Radius = BasePickupRange;
var collision = new CollisionShape3D();
collision.Shape = shape;
AddChild(collision);
}
public override void _PhysicsProcess(double delta)
{
if (_player == null) return;
float distance = GlobalPosition.DistanceTo(
_player.GlobalPosition);
// 在拾取范围内,磁吸飞向玩家
if (distance < BasePickupRange)
{
_isBeingAttracted = true;
}
if (_isBeingAttracted)
{
Vector3 direction = (_player.GlobalPosition
- GlobalPosition).Normalized();
GlobalPosition += direction * MagnetSpeed * (float)delta;
// 到达玩家位置,收集经验
if (distance < 0.3f)
{
Collect();
}
}
}
private void Collect()
{
// 通知经验管理器
var expManager = GetTree().CurrentScene
.GetNode<ExperienceManager>("ExperienceManager");
expManager?.AddExperience(_experienceValue);
// 消失
QueueFree();
}
}GDScript
# experience_gem.gd
# 经验宝石:敌人掉落,玩家拾取后获得经验
extends Area3D
@export var base_pickup_range: float = 1.5
@export var magnet_speed: float = 8.0
@export var base_experience_value: float = 1.0
var _experience_value: float
var _is_being_attracted: bool = false
var _player: Node3D
func set_value(value: float):
_experience_value = value
func _ready():
_player = get_tree().current_scene.get_node("Player")
# 设置碰撞检测
var shape = SphereShape3D.new()
shape.radius = base_pickup_range
var collision = CollisionShape3D.new()
collision.shape = shape
add_child(collision)
func _physics_process(delta):
if _player == null:
return
var distance = global_position.distance_to(
_player.global_position)
# 在拾取范围内,磁吸飞向玩家
if distance < base_pickup_range:
_is_being_attracted = true
if _is_being_attracted:
var direction = (_player.global_position \
- global_position).normalized()
global_position += direction * magnet_speed * delta
# 到达玩家位置,收集经验
if distance < 0.3:
collect()
func collect():
# 通知经验管理器
var exp_manager = get_tree().current_scene \
.get_node("ExperienceManager")
if exp_manager:
exp_manager.add_experience(_experience_value)
# 消失
queue_free()总结
本章我们实现了:
- 经验系统 — 收集经验宝石升级,经验需求逐级递增
- 技能选择 — 升级时暂停游戏,展示3个随机技能
- 技能分类 — 新武器、武器升级、被动增益三种类型
- 技能稀有度 — 普通/稀有/传说,不同的出现概率和效果
- 被动增益 — 移速、拾取范围、生命、伤害、护甲等
- 经验宝石 — 磁吸拾取机制
升级系统让每一局游戏都有不同的体验。下一章,我们进一步完善道具和装备系统。
