本文最后更新于15 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
不再让一个动作的逻辑一步执行到底而是在关键节点向整个系统广播一个事件
监听事件 条件 触发之后的动作(skillEffect)
比如反伤甲以及将攻击、防御都作为一个技能,其中,防御是一个触发类技能,它需要满足1.监听受到攻击,2.正在处于防御状态 3.将攻击减少固定百分比
上下文模式详解
一、什么是上下文模式?
核心思想:将一次操作所需的所有相关数据打包成一个对象,在系统各模块之间传递,而不是散乱地传递多个参数。
❌ 传统方式:process_damage(source, target, damage, element, is_crit, can_modify, ...)
✅ 上下文模式:process_damage(damage_context) // 所有数据都在里面
二、项目中的上下文继承体系
ContextBase (基类 - 预留扩展)
│
├── EventContext (事件上下文)
│ └── DamageEventContext (伤害事件上下文)
│
└── SkillExecutionContext (技能执行上下文)
三、两个核心上下文的实际应用
1. SkillExecutionContext – 技能执行上下文
用途:在技能执行过程中传递数据
创建位置:battle_manager.gd:59
params.merge({"skill_context": SkillExecutionContext.new(self)}, true)
current_turn_character.execute_action(action_type, target, params)
func process_effect(source: Character, target: Character, _context : SkillExecutionContext) -> Dictionary:
# 通过 context 访问战斗管理器或其他数据
2. DamageEventContext – 伤害事件上下文
用途:在伤害事件触发时传递数据,允许各系统响应和修改伤害
创建位置:character_combat_component.gd:103-104
var damage_info: DamageInfo = DamageInfo.new(base_damage, source, get_parent(), p_element)
var damage_event_context : DamageEventContext = DamageEventContext.new(source, get_parent(), damage_info)
SkillSystem.trigger_game_event(get_parent(), &"on_damage_taken", damage_event_context)
修改位置:modifiy_damage_effect_data.gd:28-36
var damage_info : DamageInfo = context.damage_info
damage_info.modify_damage("percent", damage_mod_percent, damage_mod_min, damage_mod_max)
四、数据流转图解
┌─────────────────────────────────────────────────────────────────────┐
│ 技能执行流程 │
└─────────────────────────────────────────────────────────────────────┘
玩家选择攻击
│
▼
┌──────────────────┐
│ BattleManager │ 创建 SkillExecutionContext
│ │──────────────────────────────┐
└──────────────────┘ │
│ ▼
│ ┌───────────────────────┐
│ │ SkillExecutionContext │
│ │ - battle_manager │
│ │ - damage_info │
│ └───────────────────────┘
▼ │
┌──────────────────┐ │
│ Character │ 执行技能效果 │
│ execute_action() │◄─────────────────────────────┘
└──────────────────┘
│
▼
┌──────────────────┐
│ DamageEffectData │ 处理伤害效果
│ process_effect() │
└──────────────────┘
│
▼
┌──────────────────┐ 创建 DamageInfo
│ take_damage() │──────────────────────────────┐
└──────────────────┘ │
│ ▼
│ ┌───────────────────────┐
│ │ DamageEventContext │
│ │ - source │
│ │ - target │
│ │ - damage_info ──────►│──┐
│ └───────────────────────┘ │
│ │ │
▼ │ │
┌──────────────────┐ 触发事件 │ │
│ SkillSystem │◄─────────────────────────────┘ │
│ trigger_event() │ │
└──────────────────┘ │
│ │
▼ │
┌──────────────────┐ 响应事件,修改伤害 │
│ 状态效果/Buff │──────────────────────────────────────────────┘
│ (如护盾、减伤) │ 通过 damage_info.modify_damage() 修改
└──────────────────┘
│
▼
应用最终伤害
五、上下文模式的优势
| 优势 | 项目中的体现 |
|---|---|
| 参数封装 | DamageInfo 包含 7 个伤害相关属性,避免函数签名过长 |
| 数据共享 | SkillExecutionContext 让所有技能效果都能访问 battle_manager |
| 可修改性 | DamageEventContext 传递 DamageInfo 引用,多个系统可修改同一份数据 |
| 可追溯性 | DamageInfo.modifications_log 记录所有修改过程 |
| 扩展性 | 新增事件类型只需继承 ContextBase 或 EventContext |
| 解耦 | 技能效果不需要知道伤害如何计算,只需操作 context.damage_info |
六、关键设计细节
DamageInfo 的引用传递机制
# character_combat_component.gd
var damage_info: DamageInfo = DamageInfo.new(base_damage, ...) # 创建
var context = DamageEventContext.new(source, target, damage_info)
SkillSystem.trigger_game_event(..., context) # 传递引用
final_damage = damage_info.final_damage # 获取被修改后的值
关键点:DamageInfo 是引用类型(继承 RefCounted),传递的是引用而非副本,所以任何地方对 damage_info 的修改都会反映到原始对象上。
七、总结
这个项目的上下文模式实现了一个灵活的伤害修改系统:
- 伤害发生时 → 创建
DamageInfo+DamageEventContext - 触发事件 → 各种效果(护盾、Buff、装备)监听并修改伤害
- 应用伤害 → 使用最终修改后的值
这种设计让伤害计算逻辑与效果系统完全解耦,新增一个减伤 Buff 只需监听事件并修改 damage_info,无需改动任何核心战斗代码。
为什么要让攻击和防御技能化?
因为这样会有更高的可拓展性,比如如果要将一个狂战士的普通攻击设计为由一定几率造成流血那如果把攻击看作一个技能这样就很好实现。
