本次提交实现了游戏内完整的计时性增益/减益系统,包含: 1. 新增Buff配置表与运行时数据组件 2. 实现属性修饰器与最终属性计算逻辑 3. 完成Buff管理、计时结算与周期效果处理 4. 兼容旧有控制状态系统,支持平滑过渡 5. 附带完整的系统设计文档 同时关闭了两个闲置的任务界面节点。
5.2 KiB
5.2 KiB
计时性 Buff/Debuff 系统设计
日期:2026-07-23 状态:已批准,待实现
1. 目标与范围
为英雄/怪物添加通用的计时性 buff/debuff 机制,覆盖四类需求:
- 限时的属性加成/削弱(如"10秒内攻击+30%")
- 周期性跳伤害/治疗 DoT/HoT(如中毒、灼烧、再生)
- 统一现有冰冻/击晕的计时框架,便于扩展更多控制效果
- 数据层完整记录 buff 状态(UI 展示本期不做,预留脏标记)
2. 核心架构决策
| 维度 | 决策 |
|---|---|
| 属性计算模型 | 修饰器模型(读取时重算,无残留风险) |
| 叠加规则 | 可叠层,每层独立计时 |
| 配置组织 | 独立 BuffSet.ts 配置表 |
| 架构方案 | 方案 A:原字段=base + 修饰器表 |
3. 模块划分
assets/script/game/
├── common/config/
│ ├── BuffSet.ts 新增:buff 静态配置表(id → 定义)
│ └── HeroAttrs.ts 复用现有 Attrs 枚举
└── hero/
├── HeroAttrsComp.ts 改造:新增 getFinalXxx() getter
├── BuffComp.ts 新增:ECS Comp,持有 buffs 运行时实例
├── BuffSystem.ts 新增:ECS System,tick 驱动
└── BuffManager.ts 新增:对外 API(apply/dispel/查询)
职责边界:
BuffSet.ts:纯静态配置,无运行时状态BuffComp.ts:挂实体上的运行时数据,不写逻辑BuffSystem.ts:固定 tick 推进 buff(倒计时/DoT/到期)BuffManager.ts:对外门面,屏蔽 ECS 细节
4. 数据模型
BuffSet.ts — 静态配置
export enum BuffCategory {
Buff = 0, // 正面增益
Debuff = 1, // 负面减益
Control = 2, // 控制(冰冻/击晕/减速)
}
export enum ModOp {
Flat = 0, // 固定值
PercentAdd = 1, // 百分比(加算)
PercentMul = 2, // 百分比(乘算,预留)
}
export interface BuffModifier {
attr: Attrs;
op: ModOp;
value: number;
}
export interface BuffTickEffect {
interval: number; // 触发间隔(秒)
damage_or_heal: number; // 正=治疗,负=伤害
scale_by_ap_pct: number; // 按施法者 ap 缩放百分比,0=不吃加成
}
export interface BuffConfig {
id: number;
name: string;
icon: string;
category: BuffCategory;
duration: number; // 单层持续时间(秒),0=永久
max_stack: number; // 默认 0=无限叠加;1=不可叠层;N=最多N层
modifiers?: BuffModifier[];
tick?: BuffTickEffect;
is_control?: boolean;
control_kind?: 'frost' | 'stun' | 'none';
info: string;
}
export const BuffList: Record<number, BuffConfig> = { /* 配置项 */ };
BuffComp.ts — 运行时实例
export class ActiveBuff {
config_id: number;
remaining: number;
source_uuid: number;
source_ap_snapshot: number; // 施法时锁定
tick_acc: number = 0;
}
@ecs.register('Buff')
export class BuffComp extends ecs.Comp {
buffs: Map<number, ActiveBuff[]> = new Map();
dirty_buffs: boolean = false; // 预留 UI 消费
}
5. 修饰器模型与最终值计算
HeroAttrsComp 原字段(ap/hp_max/critical 等)保持原语义(基础值 + 永久加成),新增 getter:
final = (base + Σ_flat_timed) × (1 + Σ_pctAdd_timed / 100)
- flat 优先于 pct:先加固定值,再乘百分比
- 驻场加成并入 getter,避免"忘记乘驻场"
add_ap/add_hp_max/add_special_attr语义不变,继续改 base- getter 不缓存,避免脏数据;实测有热点再加 dirty
6. BuffSystem 驱动逻辑
- 固定步长 0.1s(与现有 HeroBuffSystem 一致),用 Timer(0.1)
- 每层独立倒计时,到期 splice 移除
- DoT/HoT:每层独立 tick_acc,到 interval 跳一次,
tick_acc -= interval保留余数 - DoT 数值按施法时 ap 快照缩放,不在结算时重读施法者
- 控制类 buff 双写兼容:施加时同时调用现有 toFrost/toStun;现有字段继续由 BuffSystem.tickLegacyControl 驱动
- 永久 buff(duration=0)不递减 remaining,只能被 dispel 清除
7. BuffManager API
applyBuff(target, buffId, sourceUuid?, sourceAp?, overrides?): void
dispel(target, category?, excludeIds?): number
getStack(target, buffId): number
hasControl(target, kind): boolean
叠层规则(max_stack 默认 0=无限叠加):
- max_stack=0:每次施加都新增层
- 未满:新增层
- 已满(≥1):替换最老层(刷新)
驱散后控制类需 recomputeControlState:同 kind 还有任意层则保持,否则清除。
8. 迁移策略
- Step 1:新增 buff 基础设施(纯加法,零风险)
- Step 2:HeroAttrsComp 新增 getter,不改旧字段
- Step 3:SkillSet 新增 timed_buff_id,SCastSystem 新旧路径并存,消费方逐个改用 getter
- Step 4:控制类 buff 迁移(双写),最后删除 HeroBuffSystem 与旧字段
9. 风险与兜底
| 风险 | 兜底 |
|---|---|
| getter 未覆盖所有读取点 | grep 全量排查 |
| max_stack=0 无限叠加内存泄漏 | 超阈值打 warning |
| BuffComp 未 reset 残留 | reset() 清空,英雄回收时调用 |
| 双 System 冲突 | 兼容期 BuffSystem 接管,HeroBuffSystem 改空或删除 |