Godot 4技能与升级系统,支持JSON配置技能、解锁条件判断、升级触发逻辑、UI面板设计。
Scanned 9/12/2026
Install to Claude Code
npx -y skills add 925236118/AlphaAgent --skill godot-skill-upgrade --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Godot Skill Upgrade?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/925236118-godot-skill-upgrade)More formats (shields.io, HTML) on the badges page.
---
name: godot-skill-upgrade
description: Godot 4技能与升级系统,支持JSON配置技能、解锁条件判断、升级触发逻辑、UI面板设计。
---
# Godot 4 技能与升级系统
完整的技能系统和升级机制,支持JSON配置、技能解锁条件、升级树、经验值系统。
## 何时使用此技能
- 实现玩家技能树
- 角色升级系统
- 技能解锁与前置条件
- 天赋点/技能点系统
## 技能配置格式
### JSON技能配置
```json
{
"skills": [
{
"id": "fireball",
"name": "火球术",
"description": "发射一个火球攻击敌人",
"icon": "res://icons/fireball.png",
"type": "attack",
"cost": {
"mana": 30,
"cooldown": 2.0
},
"unlock": {
"level_required": 3,
"skill_required": null,
"item_required": null
},
"upgrade_levels": [
{
"level": 1,
"damage": 50,
"range": 10.0
},
{
"level": 2,
"damage": 75,
"range": 12.0
},
{
"level": 3,
"damage": 100,
"range": 15.0
}
]
},
{
"id": "fire_mastery",
"name": "火焰精通",
"description": "火焰技能伤害提升15%",
"icon": "res://icons/fire_mastery.png",
"type": "passive",
"cost": {
"skill_points": 1
},
"unlock": {
"level_required": 5,
"skill_required": "fireball",
"item_required": null
},
"upgrade_levels": [
{
"level": 1,
"damage_bonus": 0.15
},
{
"level": 2,
"damage_bonus": 0.25
}
]
}
]
}
```
### 技能数据结构
```gdscript
# skill_data.gd
class_name SkillData
extends Resource
## 技能唯一标识符
@export var id: StringName
## 技能显示名称
@export var skill_name: String
## 技能描述
@export_multiline var description: String
## 技能图标路径
@export var icon_path: String
## 技能类型
@export_enum("Attack", "Defense", "Support", "Passive", "Ultimate") var skill_type: int = 0
## 消耗
@export var mana_cost: float = 0.0
@export var cooldown: float = 0.0
@export var skill_points_cost: int = 0
## 解锁条件
@export var level_required: int = 1
@export var skill_required: StringName # 前置技能ID
@export var item_required: StringName # 所需物品ID
## 升级配置
@export var upgrade_levels: Array[Dictionary] = []
## 最大等级
var max_level: int:
get: return upgrade_levels.size()
func get_upgrade_data(level: int) -> Dictionary:
if level <= 0 or level > max_level:
return {}
return upgrade_levels[level - 1]
func can_unlock(player_level: int, unlocked_skills: Array, owned_items: Array) -> bool:
# 检查等级
if player_level < level_required:
return false
# 检查前置技能
if skill_required != null and not skill_required in unlocked_skills:
return false
return true
```
## 解锁条件判断
### 解锁条件系统
```gdscript
# unlock_condition.gd
class_name UnlockCondition
extends RefCounted
enum ConditionType { LEVEL, SKILL, ITEM, QUEST }
var condition_type: ConditionType
var required_value: Variant
func _init(type: ConditionType, value: Variant) -> void:
condition_type = type
required_value = value
static func level_required(level: int) -> UnlockCondition:
return UnlockCondition.new(ConditionType.LEVEL, level)
static func skill_required(skill_id: StringName) -> UnlockCondition:
return UnlockCondition.new(ConditionType.SKILL, skill_id)
static func item_required(item_id: StringName) -> UnlockCondition:
return UnlockCondition.new(ConditionType.ITEM, item_id)
func check(player: Player) -> bool:
match condition_type:
ConditionType.LEVEL:
return player.level >= required_value
ConditionType.SKILL:
return player.skills.has(required_value)
ConditionType.ITEM:
return player.inventory.has_item(required_value)
return false
```
### 技能解锁管理器
```gdscript
# skill_unlock_manager.gd
class_name SkillUnlockManager
extends Node
## 可用技能列表
var available_skills: Array[SkillData] = []
## 已解锁技能字典
var unlocked_skills: Dictionary = {}
## 信号:技能解锁
signal skill_unlocked(skill_id: StringName)
## 信号:解锁条件变化
signal unlock_conditions_changed
func _ready() -> void:
pass
func load_skills_from_json(json_path: String) -> void:
var file := FileAccess.open(json_path, FileAccess.READ)
if file == null:
push_error("无法加载技能配置: ", json_path)
return
var json_str := file.get_as_text()
file.close()
var json := JSON.parse_string(json_str)
if json == null or not json.has("skills"):
return
for skill_json in json.skills:
var skill := _create_skill_data(skill_json)
available_skills.append(skill)
func _create_skill_data(data: Dictionary) -> SkillData:
var skill := SkillData.new()
skill.id = data.get("id", "")
skill.skill_name = data.get("name", "")
skill.description = data.get("description", "")
skill.icon_path = data.get("icon", "")
skill.skill_type = data.get("type", "attack")
skill.mana_cost = data.get("cost", {}).get("mana", 0)
skill.cooldown = data.get("cost", {}).get("cooldown", 0)
skill.skill_points_cost = data.get("cost", {}).get("skill_points", 0)
skill.level_required = data.get("unlock", {}).get("level_required", 1)
skill.skill_required = data.get("unlock", {}).get("skill_required")
skill.item_required = data.get("unlock", {}).get("item_required")
skill.upgrade_levels = data.get("upgrade_levels", [])
return skill
func try_unlock_skill(skill_id: StringName, player: Player) -> bool:
var skill := _get_skill_by_id(skill_id)
if skill == null:
return false
# 检查是否已解锁
if unlocked_skills.has(skill_id):
return false
# 检查解锁条件
if not skill.can_unlock(player.level, unlocked_skills.keys(), []):
return false
# 解锁技能
_unlock_skill(skill_id)
return true
func _unlock_skill(skill_id: StringName) -> void:
unlocked_skills[skill_id] = 1 # 等级1
skill_unlocked.emit(skill_id)
func get_skill_level(skill_id: StringName) -> int:
return unlocked_skills.get(skill_id, 0)
func can_upgrade_skill(skill_id: StringName) -> bool:
if not unlocked_skills.has(skill_id):
return false
var skill := _get_skill_by_id(skill_id)
if skill == null:
return false
var current_level := get_skill_level(skill_id)
return current_level < skill.max_level
func upgrade_skill(skill_id: StringName, player: Player) -> bool:
if not can_upgrade_skill(skill_id):
return false
var skill := _get_skill_by_id(skill_id)
# 检查消耗
if player.skill_points < skill.skill_points_cost:
return false
# 消耗资源
player.skill_points -= skill.skill_points_cost
# 升级
unlocked_skills[skill_id] += 1
return true
func _get_skill_by_id(skill_id: StringName) -> SkillData:
for skill in available_skills:
if skill.id == skill_id:
return skill
return null
func get_unlocked_skills() -> Array[SkillData]:
var result: Array[SkillData] = []
for skill in available_skills:
if unlocked_skills.has(skill.id):
result.append(skill)
return result
func get_available_skills_for_player(player: Player) -> Array[SkillData]:
var result: Array[SkillData] = []
for skill in available_skills:
if not unlocked_skills.has(skill.id) and skill.can_unlock(player.level, unlocked_skills.keys(), []):
result.append(skill)
return result
```
## 升级触发逻辑
### 经验值系统
```gdscript
# experience_system.gd
class_name ExperienceSystem
extends Node
## 信号:升级
signal level_up(new_level: int)
## 信号:经验值变化
signal experience_changed(current: int, required: int)
@export var level_multiplier: float = 1.5
@export var base_exp: int = 100
var current_level: int = 1
var current_exp: int = 0
func _ready() -> void:
pass
func get_required_exp(level: int) -> int:
"""获取指定等级所需经验值"""
return int(base_exp * pow(level, level_multiplier))
func add_exp(amount: int) -> bool:
current_exp += amount
experience_changed.emit(current_exp, get_required_exp(current_level))
while current_exp >= get_required_exp(current_level):
current_exp -= get_required_exp(current_level)
current_level += 1
level_up.emit(current_level)
return true
func get_exp_progress() -> float:
var required := get_required_exp(current_level)
return float(current_exp) / float(required) if required > 0 else 0.0
```
### 技能点系统
```gdscript
# skill_point_system.gd
class_name SkillPointSystem
extends Node
## 信号:技能点变化
signal skill_points_changed(points: int)
@export var base_points_per_level: int = 1
@export var bonus_points_from_quests: int = 0
var total_points: int = 0
var spent_points: int = 0
func _ready() -> void:
pass
func grant_level_points() -> void:
total_points += base_points_per_level
skill_points_changed.emit(get_available_points())
func grant_bonus_points(amount: int) -> void:
total_points += amount
skill_points_changed.emit(get_available_points())
func spend_points(amount: int) -> bool:
if amount > get_available_points():
return false
spent_points += amount
skill_points_changed.emit(get_available_points())
return true
func refund_points(amount: int) -> void:
spent_points = maxi(spent_points - amount, 0)
skill_points_changed.emit(get_available_points())
func get_available_points() -> int:
return total_points - spent_points
```
### 升级触发器
```gdscript
# upgrade_trigger.gd
class_name UpgradeTrigger
extends Node
## 信号:触发升级
signal upgrade_triggered(upgrade_type: StringName, value: Variant)
func _ready() -> void:
# 连接玩家等级变化
var exp_system := ExperienceSystem
exp_system.level_up.connect(_on_level_up)
func _on_level_up(new_level: int) -> void:
upgrade_triggered.emit("level", new_level)
# 给玩家技能点
var point_system := SkillPointSystem
point_system.grant_level_points()
# 检查新技能解锁
_check_new_skill_unlocks(new_level)
func _check_new_skill_unlocks(player_level: int) -> void:
var unlock_manager := SkillUnlockManager
var available := unlock_manager.get_available_skills_for_player(get_parent())
for skill in available:
if skill.level_required == player_level:
unlock_manager.try_unlock_skill(skill.id, get_parent())
## 任务完成触发
func trigger_quest_complete(quest_id: StringName) -> void:
upgrade_triggered.emit("quest", quest_id)
## 物品获得触发
func trigger_item_acquired(item_id: StringName) -> void:
upgrade_triggered.emit("item", item_id)
```
## UI面板设计
### 技能树面板
```gdscript
# skill_tree_panel.gd
class_name SkillTreePanel
extends Control
@export var skill_unlock_manager: SkillUnlockManager
@export var skill_slot_scene: PackedScene
@onready var scroll_container: ScrollContainer = %ScrollContainer
@onready var skill_grid: GridContainer = %SkillGrid
@onready var skill_points_label: Label = %SkillPointsLabel
@onready var skill_info_panel: PanelContainer = %SkillInfoPanel
@onready var skill_name_label: Label = %SkillNameLabel
@onready var skill_desc_label: Label = %SkillDescLabel
@onready var skill_level_label: Label = %SkillLevelLabel
@onready var upgrade_button: Button = %UpgradeButton
var skill_slotuis: Array[SkillSlotUI] = []
var selected_skill: SkillData = null
func _ready() -> void:
skill_unlock_manager.skill_unlocked.connect(_on_skill_unlocked)
skill_unlock_manager.unlock_conditions_changed.connect(_refresh_all)
_create_skill_slots()
_refresh_all()
func _create_skill_slots() -> void:
for skill in skill_unlock_manager.available_skills:
var slot: SkillSlotUI = skill_slot_scene.instantiate()
slot.skill_data = skill
slot.clicked.connect(_on_skill_slot_clicked)
skill_grid.add_child(slot)
skill_slotuis.append(slot)
func _refresh_all() -> void:
for slot in skill_slotuis:
slot.update_display()
_update_skill_points()
func _on_skill_slot_clicked(skill: SkillData) -> void:
selected_skill = skill
_show_skill_info(skill)
_update_upgrade_button()
func _show_skill_info(skill: SkillData) -> void:
skill_name_label.text = skill.skill_name
skill_desc_label.text = skill.description
var current_level := skill_unlock_manager.get_skill_level(skill.id)
if current_level > 0:
skill_level_label.text = "等级: %d/%d" % [current_level, skill.max_level]
# 显示当前等级效果
if current_level > 0 and skill.upgrade_levels.size() >= current_level:
var upgrade := skill.upgrade_levels[current_level - 1]
# 显示具体效果...
else:
skill_level_label.text = "未解锁"
skill_info_panel.visible = true
func _update_upgrade_button() -> void:
if selected_skill == null:
upgrade_button.disabled = true
return
if skill_unlock_manager.unlocked_skills.has(selected_skill.id):
if skill_unlock_manager.can_upgrade_skill(selected_skill.id):
upgrade_button.text = "升级"
upgrade_button.disabled = false
else:
upgrade_button.text = "已满级"
upgrade_button.disabled = true
else:
upgrade_button.text = "解锁"
upgrade_button.disabled = false
func _on_upgrade_button_pressed() -> void:
if selected_skill == null:
return
var player := get_tree().get_first_node_in_group("player")
if player == null:
return
if skill_unlock_manager.unlocked_skills.has(selected_skill.id):
skill_unlock_manager.upgrade_skill(selected_skill.id, player)
else:
skill_unlock_manager.try_unlock_skill(selected_skill.id, player)
_refresh_all()
_show_skill_info(selected_skill)
_update_upgrade_button()
func _on_skill_unlocked(skill_id: StringName) -> void:
_refresh_all()
func _update_skill_points() -> void:
var point_system := SkillPointSystem
skill_points_label.text = "可用技能点: %d" % point_system.get_available_points()
```
### 技能槽UI
```gdscript
# skill_slot_ui.gd
class_name SkillSlotUI
extends PanelContainer
signal clicked(skill: SkillData)
@export var skill_data: SkillData
@onready var icon_texture: TextureRect = %IconTexture
@onready var lock_icon: TextureRect = %LockIcon
@onready var level_label: Label = %LevelLabel
@onready var name_label: Label = %NameLabel
var is_unlocked: bool = false
var current_level: int = 0
func _ready() -> void:
mouse_entered.connect(_on_mouse_entered)
mouse_exited.connect(_on_mouse_exited)
gui_input.connect(_on_gui_input)
func update_display() -> void:
if skill_data == null:
return
var unlock_manager := SkillUnlockManager
is_unlocked = unlock_manager.unlocked_skills.has(skill_data.id)
current_level = unlock_manager.get_skill_level(skill_data.id)
# 加载图标
if skill_data.icon_path != "":
icon_texture.texture = load(skill_data.icon_path)
# 锁定状态
lock_icon.visible = not is_unlocked
# 等级显示
if is_unlocked and current_level > 0:
level_label.text = "Lv.%d" % current_level
level_label.visible = true
else:
level_label.visible = false
name_label.text = skill_data.skill_name
func _on_gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
clicked.emit(skill_data)
func _on_mouse_entered() -> void:
modulate = Color(1.2, 1.2, 1.2)
func _on_mouse_exited() -> void:
modulate = Color(1.0, 1.0, 1.0)
```
### 技能信息面板
```gdscript
# skill_info_panel.gd
class_name SkillInfoPanel
extends PanelContainer
@export var name_label: Label
@export var type_label: Label
@export var desc_label: Label
@export var cost_label: Label
@export var effect_label: Label
@export var unlock_condition_label: Label
func show_skill(skill: SkillData, current_level: int) -> void:
name_label.text = skill.skill_name
var types := ["攻击", "防御", "支援", "被动", "终极"]
type_label.text = "类型: %s" % types[skill.skill_type]
desc_label.text = skill.description
# 消耗信息
var cost_text := ""
if skill.mana_cost > 0:
cost_text += "魔法消耗: %d\n" % skill.mana_cost
if skill.cooldown > 0:
cost_text += "冷却时间: %.1f秒\n" % skill.cooldown
if skill.skill_points_cost > 0:
cost_text += "技能点: %d" % skill.skill_points_cost
cost_label.text = cost_text if cost_text else "无消耗"
# 当前等级效果
if current_level > 0 and skill.upgrade_levels.size() >= current_level:
var upgrade := skill.upgrade_levels[current_level - 1]
var effect_text := ""
for key in upgrade.keys():
effect_text += "%s: %s\n" % [key, str(upgrade[key])]
effect_label.text = effect_text
else:
effect_label.text = "效果: 未解锁"
# 解锁条件
var cond_text := "解锁条件: "
if skill.level_required > 1:
cond_text += "等级%d" % skill.level_required
if skill.skill_required != null:
cond_text += " | 前置: %s" % skill.skill_required
unlock_condition_label.text = cond_text
```
## 使用示例
### 玩家角色集成
```gdscript
# player.gd
extends CharacterBody2D
@export var experience_system: ExperienceSystem
@export var skill_point_system: SkillPointSystem
@export var skill_unlock_manager: SkillUnlockManager
@export var buff_component: BuffComponent
var level: int:
get: return experience_system.current_level
func _ready() -> void:
skill_unlock_manager.load_skills_from_json("res://data/skills.json")
# 监听升级
experience_system.level_up.connect(_on_level_up)
func _on_level_up(new_level: int) -> void:
skill_point_system.grant_level_points()
# 检查自动解锁的技能
var available := skill_unlock_manager.get_available_skills_for_player(self)
for skill in available:
if skill.level_required == new_level:
skill_unlock_manager.try_unlock_skill(skill.id, self)
func use_skill(skill_id: StringName) -> bool:
var skill := skill_unlock_manager._get_skill_by_id(skill_id)
if skill == null:
return false
var level := skill_unlock_manager.get_skill_level(skill_id)
if level <= 0:
return false
# 检查冷却和消耗
if skill.mana_cost > 0 and _mana < skill.mana_cost:
return false
if skill.cooldown > 0 and _is_in_cooldown(skill_id):
return false
# 执行技能效果
_execute_skill(skill, level)
return true
func _execute_skill(skill: SkillData, level: int) -> void:
var upgrade_data := skill.get_upgrade_data(level)
match skill.skill_type:
0: # Attack
_cast_attack_skill(skill.id, upgrade_data)
1: # Defense
_cast_defense_skill(skill.id, upgrade_data)
2: # Support
_cast_support_skill(skill.id, upgrade_data)
3: # Passive
_apply_passive_skill(skill.id, upgrade_data)
4: # Ultimate
_cast_ultimate_skill(skill.id, upgrade_data)
func _cast_attack_skill(skill_id: StringName, data: Dictionary) -> void:
match skill_id:
"fireball":
_cast_fireball(data.get("damage", 50), data.get("range", 10.0))
"ice_blast":
_cast_ice_blast(data.get("damage", 30), data.get("slow_duration", 2.0))
```
### 保存与加载
```gdscript
# 保存技能数据
func get_save_data() -> Dictionary:
return {
"unlocked_skills": skill_unlock_manager.unlocked_skills,
"experience": experience_system.current_exp,
"skill_points_spent": skill_point_system.spent_points
}
# 加载技能数据
func load_save_data(data: Dictionary) -> void:
if data.has("unlocked_skills"):
skill_unlock_manager.unlocked_skills = data.unlocked_skills
if data.has("experience"):
experience_system.current_exp = data.experience
if data.has("skill_points_spent"):
skill_point_system.spent_points = data.skill_points_spent
```
## 最佳实践
1. **JSON配置分离** - 技能数据与代码分离,便于策划调整
2. **Resource管理** - SkillData使用Resource便于编辑器编辑
3. **条件检查统一** - 解锁条件集中管理
4. **信号解耦** - UI通过信号监听变化
5. **升级预留** - 支持多级升级配置
6. **点树可视化** - 技能树UI展示前置关系
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!