为解决BattleManager和Character越来越笨重的问题 ,将这两个“总导演”拆分为不同的具体的角色
解决battlemanager——设计模式:
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。
因为技能和效果的处理是一项通用的服务
核心目的:
- 控制某个类的实例数量(只允许一个)。
- 全局访问,避免频繁创建和销毁对象。
主要特点:
- 私有化构造函数:防止外部直接 new 创建实例。
- 静态私有实例变量:保存类的唯一实例。
- 静态公有方法:提供全局访问点(如
getInstance()),在该方法中创建或返回已有实例。
给Character瘦身——组件化设计,将一个庞大对象的不同功能分成一个个可以插拔的组件脚本中,character本身只作为一个组件的容器/视图层,负责管理和协调组件
项目设置—全局——自动加载可以将某个脚本作为全局的可以访问的单例
我来帮你详细分析 skill_system.gd 脚本文件。首先让我读取这个文件的内容。
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\autoload\skill_system.gd
让我先了解一下项目的整体结构,以便更好地分析技能系统与其他系统的交互。
toolName: view_folder
status: success
d:\Desktop\Godot\course\28技能系统组件化重构
toolName: file_search
status: success
file_pattern: */.gd
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\core\battle_manager.gd
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\resources\skill_data.gd
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\resources\skill_effect_data.gd
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scenes\characters\character.gd
现在让我查看一些具体的效果实现类,以便更全面地理解技能系统的工作机制。
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\resources\skill_effect_data\damage_effect_data.gd
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\resources\skill_effect_data\heal_effect_data.gd
toolName: view_files
status: success
filePath: d:\Desktop\Godot\course\28技能系统组件化重构\scripts\core\character\character_skill_component.gd
SkillSystem 技能系统详细分析
一、系统架构定位
1.1 在项目中的角色
skill_system.gd 作为 自动加载的单例(Autoload),是整个技能系统的核心协调器。它不直接依赖战斗系统组件,而是通过上下文(SkillExecutionContext)获取必要信息,实现了良好的解耦设计。
1.2 与其他系统的关系
┌─────────────────────────────────────────────────────────────┐
│ BattleManager │
│ (战斗流程控制、回合管理) │
└──────────────────────┬──────────────────────────────────────┘
│ 调用
↓
┌─────────────────────────────────────────────────────────────┐
│ SkillSystem (单例) │
│ (技能执行核心逻辑、效果协调) │
└──────┬──────────────────────────────────────────┬───────────┘
│ │
│ 使用 │ 使用
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ Character │ │ SkillEffectData │
│ (角色实体) │ │ (效果数据资源) │
│ - 属性管理 │ │ - 伤害效果 │
│ - 状态管理 │ │ - 治疗效果 │
│ - 技能列表 │ │ - 状态效果 │
└──────────────────┘ └──────────────────┘
二、核心功能模块
2.1 技能执行上下文(SkillExecutionContext)
class SkillExecutionContext:
var battle_manager : BattleManager
func _init(local_battle_manager : BattleManager = null) -> void:
battle_manager = local_battle_manager
设计目的:将战斗管理器引用封装在上下文中,避免技能系统直接依赖战斗系统,实现松耦合。
2.2 信号系统
| 信号名称 | 参数 | 触发时机 |
|---|---|---|
skill_execution_started | caster, skill, targets | 技能开始执行 |
skill_execution_completed | caster, skill, targets, results | 技能执行完成 |
skill_failed | caster, skill, reason | 技能执行失败 |
effect_applied | effect, source, target, result | 单个效果应用完成 |
这些信号允许其他系统(如UI、音效、动画系统)监听并响应技能事件。
三、核心函数详解
3.1 技能执行入口:attempt_execute_skill()
函数签名:
func attempt_execute_skill(
skill_data: SkillData,
caster: Character,
selected_targets: Array[Character],
context: SkillExecutionContext
) -> Dictionary
执行流程:
1. 参数验证
├─ 检查 caster 和 skill_data 有效性
└─ 无效则发出 skill_failed 信号并返回空字典
2. 技能可用性验证 (_validate_skill_usability)
├─ 检查 MP 是否足够
├─ 检查目标选择是否有效
└─ 验证失败则显示错误信息并返回
3. 发出 skill_execution_started 信号
4. 消耗资源 (_consume_skill_resources)
└─ 扣除施法者的 MP
5. 异步处理技能效果 (_process_skill_effects_async)
└─ 使用 await 等待效果处理完成
6. 返回执行结果
关键代码分析:
# 验证施法条件
var validation_result = _validate_skill_usability(skill_data, caster, selected_targets, context)
if not validation_result.is_usable:
skill_failed.emit(caster, skill_data, validation_result.reason)
if context.battle_manager and context.battle_manager.has_method("show_status_text"):
context.battle_manager.show_status_text(caster, validation_result.reason, true)
return {"error": validation_result.reason}
# 消耗资源
_consume_skill_resources(caster, skill_data)
# 异步处理效果
var result = await _process_skill_effects_async(skill_data, caster, selected_targets, context)
3.2 技能验证:_validate_skill_usability()
验证内容:
- MP 检查:
if caster.current_mp < skill.mp_cost:
result.is_usable = false
result.reason = "Not enough MP"
return result
- 目标类型验证:
根据技能的target_type枚举值,验证目标选择是否合法:
| 目标类型 | 验证逻辑 |
|---|---|
NONE | 无需目标 |
SELF | 目标为施法者自己 |
ENEMY_SINGLE | 单个敌方目标 |
ALLY_SINGLE | 单个友方目标(不含自己) |
ALLY_SINGLE_INC_SELF | 单个友方目标(含自己) |
ENEMY_ALL | 所有敌方目标 |
ALLY_ALL | 所有友方目标(不含自己) |
ALLY_ALL_INC_SELF | 所有友方目标(含自己) |
- 目标有效性验证:
if not _validate_skill_targets(skill, actual_targets_for_validation):
result.is_usable = false
result.reason = "Invalid target(s) for skill scope"
return result
3.3 效果处理核心:_process_skill_effects_async()
执行流程:
1. 确定实际执行目标 (_determine_execution_targets)
└─ 根据 skill.target_type 和 selected_targets 计算最终目标列表
2. 播放施法动画
├─ 如果 battle_manager 有 play_casting_animation 方法,调用它
└─ 否则等待 0.5 秒模拟施法时间
3. 遍历每个目标
└─ 对每个目标应用技能的所有效果
4. 对每个效果
├─ 确定效果的实际目标 (_determine_targets_for_effect)
├─ 应用单个效果 (_apply_single_effect)
└─ 等待 0.1 秒使效果更自然
5. 发出 skill_execution_completed 信号
6. 返回整体结果
关键代码:
# 确定实际目标
var actual_execution_targets = _determine_execution_targets(
caster, skill_data, initial_selected_targets, context
)
# 播放施法动画
if context.battle_manager and context.battle_manager.has_method("play_casting_animation"):
await context.battle_manager.play_casting_animation(caster, skill_data)
else:
await get_tree().create_timer(0.5).timeout
# 处理每个效果
for target in actual_execution_targets:
for effect in skill_data.effects:
var effect_targets = _determine_targets_for_effect(caster, effect, [target], context)
for effect_target in effect_targets:
var effect_result = await _apply_single_effect(caster, effect_target, effect, context)
overall_results[target][key] = effect_result[key]
await get_tree().create_timer(0.1).timeout
3.4 单个效果应用:_apply_single_effect()
实现原理:
func _apply_single_effect(
caster: Character,
target: Character,
effect: SkillEffectData,
_context: SkillExecutionContext
) -> Dictionary:
# 1. 参数验证
if !is_instance_valid(caster) or !is_instance_valid(target):
push_error("SkillSystem: 无效的角色引用")
return {}
if not effect:
push_error("SkillSystem: 无效的效果引用")
return {}
# 2. 调用效果的处理方法(多态)
var result = await effect.process_effect(caster, target)
# 3. 发出效果应用信号
effect_applied.emit(effect, caster, target, result)
return result
多态设计:SkillEffectData 是基类,具体效果(如 DamageEffectData、HealEffectData)继承并重写 process_effect() 方法。
3.5 目标确定逻辑
_determine_execution_targets():
match skill.target_type:
SkillData.TargetType.SELF:
final_targets.append(caster)
SkillData.TargetType.ALLY_ALL:
final_targets = context.battle_manager.get_valid_ally_targets(false, caster)
SkillData.TargetType.ENEMY_ALL:
final_targets = context.battle_manager.get_valid_enemy_targets(caster)
# ... 其他目标类型
_determine_targets_for_effect():
支持效果级别的目标覆盖,例如:
self_only:效果只影响施法者all_allies:效果影响所有友方all_enemies:效果影响所有敌方
四、与其他系统的交互
4.1 与 BattleManager 的交互
调用方式:
# 获取有效目标
context.battle_manager.get_valid_ally_targets(include_self, caster)
context.battle_manager.get_valid_enemy_targets(caster)
# 播放动画
context.battle_manager.play_casting_animation(caster, skill_data)
# 显示状态文本
context.battle_manager.show_status_text(caster, reason, is_error)
依赖关系:通过 SkillExecutionContext 间接访问,避免直接依赖。
4.2 与 Character 系统的交互
属性访问:
caster.current_mp # 当前 MP
caster.attack_power # 攻击力
caster.defense_power # 防御力
caster.magic_attack # 魔法攻击
caster.is_alive # 存活状态
方法调用:
caster.use_mp(amount) # 消耗 MP
caster.take_damage(damage) # 受到伤害
caster.heal(amount) # 恢复生命
4.3 与 SkillEffectData 的交互
效果处理流程:
SkillSystem._apply_single_effect()
↓
SkillEffectData.process_effect() [虚方法]
↓
具体效果类重写:
├─ DamageEffectData.process_effect()
├─ HealEffectData.process_effect()
└─ ApplyStatusEffectData.process_effect()
视觉效果请求:
# SkillEffectData 基类提供
_request_visual_effect("cast", caster, {})
_request_visual_effect("hit", target, {})
_request_visual_effect("heal", target, {})
4.4 与 CharacterSkillComponent 的交互
状态效果处理:
# 在 CharacterSkillComponent 中
func apply_status(status_template, source_character, effect_data):
# 应用状态效果
# 触发初始效果
SkillSystem.attempt_process_status_effects(
runtime_status_instance.initial_effects,
runtime_status_instance.source_character,
get_parent(),
SkillSystem.SkillExecutionContext.new()
)
五、游戏运行时的调用机制
5.1 典型调用链
玩家选择技能
↓
BattleScene -> BattleManager.player_select_action()
↓
Character.execute_action()
↓
CharacterCombatComponent.execute_action()
↓
SkillSystem.attempt_execute_skill()
↓
[技能执行流程]
↓
返回结果
↓
BattleManager 检查战斗结束条件
↓
切换到下一个回合
5.2 异步处理机制
为什么使用异步:
- 技能效果需要按顺序播放动画
- 需要等待视觉效果完成
- 需要在效果之间添加延迟以增强视觉体验
异步实现:
# 使用 await 等待异步操作
var result = await _process_skill_effects_async(skill_data, caster, selected_targets, context)
# 在效果处理中等待动画
await context.battle_manager.play_casting_animation(caster, skill_data)
# 在效果之间添加延迟
await get_tree().create_timer(0.1).timeout
六、设计模式和最佳实践
6.1 使用的设计模式
- 单例模式:作为自动加载的全局访问点
- 策略模式:不同的
SkillEffectData子类实现不同的效果策略 - 上下文模式:
SkillExecutionContext封装执行环境 - 观察者模式:通过信号系统实现事件通知
- 模板方法模式:
process_effect()定义效果处理框架
6.2 代码质量特点
- 解耦设计:通过上下文避免直接依赖战斗系统
- 异步友好:使用
await支持动画和延迟 - 错误处理:完善的参数验证和错误信号
- 可扩展性:通过继承
SkillEffectData轻松添加新效果 - 可测试性:通过上下文注入依赖,便于单元测试
七、总结
skill_system.gd 是整个技能系统的核心协调器,它:
- 不直接依赖战斗系统,通过上下文获取必要信息
- 统一管理技能的执行流程,包括验证、资源消耗、效果处理
- 提供信号让其他系统监听和响应技能事件
- 支持异步处理,确保动画和效果的正确时序
- 易于扩展,通过继承
SkillEffectData可以添加新的效果类型
这种设计使得技能系统既强大又灵活,能够支持复杂的技能效果组合,同时保持代码的可维护性和可扩展性。
