# 计时性 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 — 静态配置 ```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 = { /* 配置项 */ }; ``` ### BuffComp.ts — 运行时实例 ```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 = 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 ```ts 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 改空或删除 |