diff --git a/assets/script/game/hero/HeroAttrEventSystem.ts b/assets/script/game/hero/HeroAttrEventSystem.ts index ad0fec63..f3c89edd 100644 --- a/assets/script/game/hero/HeroAttrEventSystem.ts +++ b/assets/script/game/hero/HeroAttrEventSystem.ts @@ -14,39 +14,27 @@ import { HeroAttrEvent } from "./HeroAttrEvent"; * * 系统职责: * 1. 处理当角色收到attr变更事件时,更新角色属性,并处理动画展示 - - * -/** - * 使用方式: - * 在 RootSystem 中注册此系统,它会自动每帧更新所有拥有 HeroAttrsComp 的实体 */ @ecs.register('HeroAttrEventSystem') export class HeroAttrEventSystem extends ecs.ComblockSystem - implements ecs.IEntityEnterSystem { - - /** - * 过滤器:只处理拥有 HeroAttrsComp 的实体 - */ + implements ecs.ISystemUpdate, ecs.IEntityEnterSystem { + filter(): ecs.IMatcher { return ecs.allOf(HeroAttrsComp,HeroAttrEvent); } - - /** - * 实体首次进入系统时调用(每个实体只调用一次) - */ entityEnter(e: ecs.Entity): void { if(!smc.mission.play || smc.mission.pause) return; - } - /** - * 系统首次更新前调用(整个系统只调用一次) - */ - firstUpdate(): void { - console.log("[HeroAttrEventSystem] 系统首次更新"); + update(e: ecs.Entity): void { + if(!smc.mission.play || smc.mission.pause) return; + const model = e.get(HeroAttrsComp); + if (!model || model.is_dead) return; + const event = e.get(HeroAttrEvent); + if (!event) return; + // 移除事件组件(事件处理完成) + } - - /** * 启用调试模式(调试时使用) */ diff --git a/assets/script/game/hero/HeroAttrsComp.ts b/assets/script/game/hero/HeroAttrsComp.ts index b29c49b3..16745d05 100644 --- a/assets/script/game/hero/HeroAttrsComp.ts +++ b/assets/script/game/hero/HeroAttrsComp.ts @@ -38,7 +38,12 @@ export class HeroAttrsComp extends ecs.Comp { Talents: Record = {}; //数值型天赋buff BUFFS_TAL: Record = {}; - + + // ==================== 脏标签标记 ==================== + dirty_hp: boolean = false; // 血量变更标记 + dirty_mp: boolean = false; // 魔法值变更标记 + dirty_shield: boolean = false; // 护盾变更标记 + // ==================== 技能距离缓存 ==================== maxSkillDistance: number = 0; // 最远技能攻击距离(缓存,受MP影响) minSkillDistance: number = 0; // 最近技能攻击距离(缓存,不受MP影响,用于停止位置判断) @@ -128,6 +133,7 @@ export class HeroAttrsComp extends ecs.Comp { } this.hp += addValue; this.hp = Math.max(0, Math.min(this.hp, this.Attrs[Attrs.HP_MAX])); + this.dirty_hp = true; // 标记血量需要更新 } add_mp(value:number,isValue:boolean){ let addValue = value; @@ -140,6 +146,7 @@ export class HeroAttrsComp extends ecs.Comp { } this.mp += addValue; this.mp = Math.max(0, Math.min(this.mp, this.Attrs[Attrs.MP_MAX])); + this.dirty_mp = true; // 标记魔法值需要更新 } add_shield(value:number,isValue:boolean){ let addValue = value; @@ -148,6 +155,7 @@ export class HeroAttrsComp extends ecs.Comp { } this.shield += addValue; this.shield = Math.max(0, Math.min(this.shield, this.Attrs[Attrs.HP_MAX])); + this.dirty_shield = true; // 标记护盾需要更新 } // ==================== BUFF 管理 ==================== /** @@ -561,6 +569,10 @@ export class HeroAttrsComp extends ecs.Comp { this.atk_count = 0; this.atked_count = 0; this.killed_count =0; + // 重置脏标签 + this.dirty_hp = false; + this.dirty_mp = false; + this.dirty_shield = false; } diff --git a/assets/script/game/hero/HeroViewComp.ts b/assets/script/game/hero/HeroViewComp.ts index f3d8b018..cf66f441 100644 --- a/assets/script/game/hero/HeroViewComp.ts +++ b/assets/script/game/hero/HeroViewComp.ts @@ -148,12 +148,22 @@ export class HeroViewComp extends CCComp { // ✅ View 层职责:处理表现相关的逻辑 this.processDamageQueue(); // 伤害数字显示队列 - - // ✅ 更新 UI 显示(数据由 HeroAttrSystem 更新) - // 移除了每帧调用的 hp_show,改为仅在需要时调用 - this.hp_show(); - this.mp_show(); - this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]); + + // ✅ 按需更新 UI(脏标签模式)- 只在属性变化时更新 + if (this.model.dirty_hp) { + this.hp_show(); + this.model.dirty_hp = false; + } + + if (this.model.dirty_mp) { + this.mp_show(); + this.model.dirty_mp = false; + } + + if (this.model.dirty_shield) { + this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]); + this.model.dirty_shield = false; + } } diff --git a/属性变更事件驱动优化方案.md b/属性变更事件驱动优化方案.md new file mode 100644 index 00000000..20b4f28a --- /dev/null +++ b/属性变更事件驱动优化方案.md @@ -0,0 +1,71 @@ +# 属性变更脏标签优化方案 + +## 一、现有实现存在的具体问题 + +### 1.1 性能问题 +- **HeroViewComp.ts** 的 `update(dt: number)` 方法中,每帧都调用 `hp_show()` 和 `mp_show()` +- 即使血量和魔法值没有变化,也会每帧更新UI进度条,造成不必要的性能开销 +- 缺少属性变更标记机制,采用轮询方式检查 + +### 1.2 架构问题 +- 属性变更与UI更新耦合在 `update` 循环中,违反按需更新原则 +- UI更新逻辑分散,缺少统一的变更标记管理 +- 属性变更通知机制不完善 + +## 二、需要进行的修改方案 + +### 2.1 核心设计 +- 采用脏标签(Dirty Flag)模式,属性变更时标记对应的脏标签 +- UI层在 `update` 中检查脏标签,只在属性变化时更新显示 +- 解耦数据层和视图层,提升性能和可维护性 + +### 2.2 实现步骤 +1. **添加脏标签标记**:在 `HeroAttrsComp` 中添加血量、魔法值、护盾的脏标签 +2. **修改属性变更方法**:在 `HeroAttrsComp` 的属性变更方法中设置脏标签 +3. **实现脏标签检查逻辑**:在 `HeroViewComp` 的 `update` 中检查脏标签并按需更新UI +4. **清理脏标签**:UI更新完成后清理对应的脏标签 + +## 三、需要修改的具体文件及对应模块 + +### 3.1 HeroAttrsComp.ts +**修改模块**: +- 类属性:添加脏标签字段 `dirty_hp`、`dirty_mp`、`dirty_shield` +- `add_hp()` 方法:设置 `dirty_hp = true` +- `add_mp()` 方法:设置 `dirty_mp = true` +- `add_shield()` 方法:设置 `dirty_shield = true` +- `reset()` 方法:重置所有脏标签 + +**修改内容**: +- 添加脏标签字段:`dirty_hp: boolean = false`、`dirty_mp: boolean = false`、`dirty_shield: boolean = false` +- 在属性变更方法中设置对应的脏标签为 `true` +- 在 `reset` 方法中重置所有脏标签为 `false` + +### 3.2 HeroViewComp.ts +**修改模块**: +- `update(dt: number)` 方法:添加脏标签检查逻辑,按需调用UI更新方法 + +**修改内容**: +- 在 `update` 中检查 `this.model.dirty_hp`、`this.model.dirty_mp`、`this.model.dirty_shield` +- 只在脏标签为 `true` 时调用对应的 `hp_show()`、`mp_show()`、`show_shield()` +- UI更新完成后清理脏标签为 `false` + +### 3.3 HeroAttrEventSystem.ts +**修改模块**: +- 无需修改 + +**修改内容**: +- 脏标签方案不需要事件系统,保持现有实现即可 + +### 3.4 HeroAttrEvent.ts +**修改模块**: +- 无需修改 + +**修改内容**: +- 脏标签方案不需要事件组件,保持现有实现即可 + +## 四、预期效果 + +- **性能提升**:消除每帧不必要的UI更新,只在属性变化时更新,减少CPU开销 +- **架构优化**:实现按需更新模式,解耦数据层和视图层 +- **可维护性提升**:脏标签逻辑简单清晰,易于理解和维护 +- **代码简洁**:无需引入复杂的事件系统,实现成本低