本文最后更新于17 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
设计思路——组合优于继承
开闭原则——对拓展开放,意味着有新的需求或者变化时,可以对现有代码进行拓展以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对已有代码进行任何修改
比如技能:吸血🗡——一个伤害效果加一个治疗效果(对象是自身)
效果处理器(effect—processor)
对每一个skilleffec都设置一个专门的处理器去处理这个效果
战斗效果处理系统分析
1. 效果处理器类的核心功能、数据结构及关键方法
1.1 基础架构
效果处理器系统采用了基于 EffectProcessor 基类的继承体系,每个具体处理器负责处理特定类型的效果。
核心数据结构:
EffectProcessor:基类,定义了所有效果处理器的通用接口- 具体处理器:
ApplyStatusProcessor、DamageEffectProcessor、DispelStatusProcessor、HealingEffectProcessor
关键方法:
process_effect():处理效果的核心方法,由子类实现get_processor_id():获取处理器唯一标识can_process_effect():检查是否可以处理指定效果类型_request_visual_effect():请求播放视觉效果的辅助方法
1.2 具体处理器分析
1.2.1 伤害效果处理器 (DamageEffectProcessor)
核心功能:处理伤害效果,计算并应用伤害。
关键方法:
process_effect():处理伤害效果,包括播放动画、计算伤害、应用伤害_calculate_damage():计算伤害值,考虑攻击力、防御力和随机因素
代码示例:
func process_effect(effect: SkillEffectData, caster: Character, target: Character) -> Dictionary:
# 播放施法动画
_request_visual_effect("cast", caster, {})
# 计算伤害
var damage_result = _calculate_damage(caster, target, effect)
var damage = damage_result["damage"]
# 播放命中动画
_request_visual_effect("hit", target, {})
_request_visual_effect("damage_number", target, {"damage": damage, "color": Color.RED})
# 应用伤害
var actual_damage = target.take_damage(damage)
# 记录结果
results["damage"] = actual_damage
return results
1.2.2 治疗效果处理器 (HealingEffectProcessor)
核心功能:处理治疗效果,计算并应用治疗量。
关键方法:
process_effect():处理治疗效果,包括播放动画、计算治疗量、应用治疗_calculate_healing():计算治疗量,考虑魔法攻击力和随机因素
1.2.3 状态效果处理器 (ApplyStatusProcessor)
核心功能:处理状态效果的应用,包括几率判定和状态管理。
关键方法:
process_effect():处理状态效果应用,包括几率判定、状态应用、触发初始效果_check_if_can_apply_status_by_chance():检查是否通过几率判定
代码示例:
func process_effect(effect_data: SkillEffectData, source: Character, target: Character) -> Dictionary:
# 检查状态模板有效性
var status_template_to_apply: SkillStatusData = effect_data.status_to_apply
if not is_instance_valid(status_template_to_apply):
results["error"] = "Invalid status_template in effect_data."
return results
# 几率判定
var chance = effect_data.status_application_chance
var applied_by_chance = _check_if_can_apply_status_by_chance(chance)
if not applied_by_chance:
results["reason"] = "chance_roll_failed"
return results
# 应用状态
var application_result: Dictionary = target.apply_skill_status(status_template_to_apply, source, effect_data)
results["success"] = application_result.get("applied_successfully", false)
# 触发初始效果
if results.success and is_instance_valid(applied_status_instance):
if not applied_status_instance.initial_effects.is_empty():
await _battle_manager._apply_skill_effects_to_targets(
applied_status_instance.get_initial_effects(),
applied_status_instance.source_char,
[target]
) return results
1.2.4 驱散效果处理器 (DispelStatusProcessor)
核心功能:处理状态效果的驱散,根据类型和数量移除目标身上的状态。
关键方法:
process_effect():处理驱散效果,包括筛选状态、执行驱散、播放视觉效果
2. 战斗管理器如何初始化、注册和调用效果处理器
2.1 初始化和注册
初始化流程:
BattleManager在_ready()方法中调用_init_effect_processors()_init_effect_processors()方法创建并注册各种效果处理器
代码示例:
func _init_effect_processors():
# 注册处理器
register_effect_processor(DamageEffectProcessor.new(self))
register_effect_processor(HealingEffectProcessor.new(self))
register_effect_processor(ApplyStatusProcessor.new(self))
注册机制:
register_effect_processor()方法将处理器实例存储在effect_processors字典中- 字典键为处理器的 ID,值为处理器实例
2.2 调用效果处理器
调用流程:
- 当技能被执行时,
_execute_skill()方法调用apply_effects() apply_effects()方法对每个目标应用每个效果- 对于每个效果,
_apply_effect()方法根据效果类型获取对应的处理器 - 调用处理器的
process_effect()方法处理效果
代码示例:
func _apply_effect(effect: SkillEffectData, source: Character, target: Character) -> Dictionary:
# 获取对应的处理器
var processor_id = _get_processor_id_for_effect(effect)
var processor = effect_processors.get(processor_id)
if processor and processor.can_process_effect(effect):
# 使用处理器处理效果
var result = await processor.process_effect(effect, source, target)
# 发出信号
effect_applied.emit(effect.effect_type, source, target, result)
return result
else:
push_error("无效的效果处理器: ", processor_id)
return {}
处理器选择机制:
_get_processor_id_for_effect()方法根据效果类型返回对应的处理器 ID- 系统使用这个 ID 从
effect_processors字典中获取处理器实例
3. 两者在战斗流程中的交互方式
3.1 事件触发机制
事件流程:
- 战斗管理器处理技能执行请求
- 战斗管理器调用效果处理器处理技能效果
- 效果处理器执行具体逻辑并返回结果
- 战斗管理器发出效果应用信号
- 战斗管理器继续处理战斗流程
3.2 数据传递机制
数据流向:
- 战斗管理器 → 效果处理器:传递
SkillEffectData、源角色、目标角色 - 效果处理器 → 战斗管理器:返回处理结果字典
- 效果处理器 → 战斗管理器:通过
_request_visual_effect()请求播放视觉效果
3.3 状态同步机制
状态管理:
- 效果处理器不直接管理战斗状态,而是通过调用角色方法修改角色状态
- 战斗管理器负责整体战斗状态的管理和同步
- 状态效果的持续时间、叠加等由角色对象自身管理
4. 关键协作点示例
4.1 技能执行与效果处理
协作流程:
- 玩家选择技能并指定目标
- 战斗管理器调用
_execute_skill() - 战斗管理器检查 MP 并扣除
- 战斗管理器获取技能目标
- 战斗管理器调用
apply_effects()处理技能效果 - 战斗管理器发出
skill_executed信号
代码示例:
func _execute_skill(caster: Character, custom_targets: Array[Character], skill_data: SkillData) -> Dictionary:
# 检查MP消耗
if not skill_data.can_cast(caster.current_mp):
return {"error": "mp_not_enough"}
# 扣除MP
if skill_data.mp_cost > 0:
caster.use_mp(skill_data.mp_cost)
# 获取目标
var targets = custom_targets if !custom_targets.is_empty() else _get_targets_for_skill(skill_data)
# 处理直接效果
var effect_results = {}
if not skill_data.effects.is_empty():
effect_results = await apply_effects(skill_data.effects, caster, targets)
# 发送技能执行信号
skill_executed.emit(caster, targets, skill_data, final_results)
return final_results
4.2 状态效果的应用与触发
协作流程:
- 状态效果处理器处理状态应用
- 状态成功应用后,触发其初始效果
- 状态效果处理器通过战斗管理器调用
_apply_skill_effects_to_targets() - 战斗管理器处理状态的初始效果
代码示例:
# 在 ApplyStatusProcessor 中
if results.success and is_instance_valid(applied_status_instance):
# 状态成功应用/更新后,触发其初始效果
if not applied_status_instance.initial_effects.is_empty():
if _battle_manager and _battle_manager.has_method("_apply_skill_effects_to_targets"):
await _battle_manager._apply_skill_effects_to_targets(
applied_status_instance.get_initial_effects(),
applied_status_instance.source_char,
[target]
)
5. 系统架构设计评估
5.1 优点
- 模块化设计:效果处理器与战斗管理器分离,职责清晰
- 可扩展性:新效果类型只需添加新的处理器类
- 灵活性:效果处理逻辑集中在各自的处理器中,便于维护
- 一致性:所有效果处理遵循相同的接口和流程
5.2 潜在改进点
- 错误处理:可以增加更完善的错误处理机制
- 效果组合:可以考虑支持效果组合和连锁反应
- 性能优化:对于大量效果的处理可以考虑批处理
- 可视化调试:可以添加更多的调试信息和可视化工具
6. 总结
战斗效果处理系统采用了基于处理器模式的设计,通过 EffectProcessor 基类和具体实现类,实现了对不同类型效果的统一处理。战斗管理器作为中心协调者,负责初始化、注册和调用效果处理器,同时管理整个战斗流程。
这种设计使得系统具有良好的可扩展性和可维护性,能够轻松添加新的效果类型和处理逻辑。同时,通过信号机制和回调函数,实现了各组件之间的解耦和高效通信。
整体架构清晰、层次分明,为战斗系统提供了灵活而强大的效果处理能力。
