perf(英雄属性): 使用脏标签模式优化属性UI更新性能

在 HeroAttrsComp 中添加脏标签标记,仅在属性变化时更新UI
移除 HeroViewComp 中每帧不必要的UI更新调用
添加文档说明优化方案
This commit is contained in:
panw
2025-12-31 14:49:53 +08:00
parent bb0ed6a9c3
commit 05b82a912a
4 changed files with 110 additions and 29 deletions

View File

@@ -14,39 +14,27 @@ import { HeroAttrEvent } from "./HeroAttrEvent";
*
* 系统职责:
* 1. 处理当角色收到attr变更事件时更新角色属性并处理动画展示
*
/**
* 使用方式:
* 在 RootSystem 中注册此系统,它会自动每帧更新所有拥有 HeroAttrsComp 的实体
*/
@ecs.register('HeroAttrEventSystem')
export class HeroAttrEventSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem {
implements ecs.ISystemUpdate, ecs.IEntityEnterSystem {
/**
* 过滤器:只处理拥有 HeroAttrsComp 的实体
*/
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;
// 移除事件组件(事件处理完成)
}
/**
* 启用调试模式(调试时使用)
*/

View File

@@ -39,6 +39,11 @@ export class HeroAttrsComp extends ecs.Comp {
//数值型天赋buff
BUFFS_TAL: Record<number, {count:number,BType:BType,attrIndex:number,value: number}> = {};
// ==================== 脏标签标记 ====================
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;
}

View File

@@ -149,11 +149,21 @@ export class HeroViewComp extends CCComp {
// ✅ View 层职责:处理表现相关的逻辑
this.processDamageQueue(); // 伤害数字显示队列
// ✅ 更新 UI 显示(数据由 HeroAttrSystem 更新
// 移除了每帧调用的 hp_show改为仅在需要时调用
// ✅ 按需更新 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;
}
}

View File

@@ -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开销
- **架构优化**:实现按需更新模式,解耦数据层和视图层
- **可维护性提升**:脏标签逻辑简单清晰,易于理解和维护
- **代码简洁**:无需引入复杂的事件系统,实现成本低