# 卡顿性能优化记录（applyCubeAura 光环源快照缓存）

> 日期：2026-08-04
> 涉及文件：`project/cube-runtime.js`
> 基线：`64fd648 perf: 移除移动热路径深拷贝并新增显伤缓存`

---

## 背景

魔塔项目在六面立方体楼层（存在光环/护卫敌人）时，开局、读档与连续移动过程中存在明显卡顿。开发者工具 CPU 4 倍节流下尤为明显，影响低性能设备的流畅游玩体验。

排查后定位到最大瓶颈为 `applyCubeAura`（立方体世界光环/护卫计算，见 `project/cube-runtime.js` 内层 `CubeWorld.FACE_IDS.forEach` 遍历）。

---

## 根因

`applyCubeAura` 被 `getEnemyInfo` 包装器包裹，每次查询任意一个敌人的属性时，都会遍历**全部 6 个立方体面**的所有格子，对每个 block 调用 `getEnemy`（内部 `getEnemyValue` 浅合并）找出带光环（特技 25）/护卫（特技 26）的敌人，再做距离计算。

在 `updateDamage` / `getDamageInfo` 对当前层每个敌人各调用一次时，复杂度高达 **O(6 × M × N)**（M 为每面格子数，N 为查询次数）：
- 开局/读档时对所有敌人算一遍显伤 → 一次性全量卡顿；
- 连续移动时若显伤缓存未命中（属性变化）也会触发。

光环源（带 special 25/26 的敌人）列表只在“敌人/地块变化”时改变，而这些变化必然反映到 `buildCheckBlock` 已维护的几何签名里，因此可以被安全记忆化。

---

## 修复方案

仅为 `applyCubeAura` 增加“光环源快照”记忆化缓存，不改变任何计算结果，也不触碰显伤/数据状态逻辑（零行为风险）。

### 1. 新增模块级快照缓存

```js
// 光环源快照：按几何签名记忆化“哪些敌人是光环/护卫源及其配置与位置”。
// 签名含敌人 special/range/laser 等，光环源变化必反映到签名 → 自动失效。
var auraSourceCache = {};
```

### 2. 按几何签名复用光环源列表

`applyCubeAura` 内部不再每敌每格遍历六面，而是先查 `auraSourceCache[geoCache[floorId].signature]`：
- 命中：直接复用缓存的光环源列表，仅对目标格做轻量距离计算；
- 未命中：重建快照并写入缓存（key 为几何签名）。

```js
var geo = geoCache[floorId];
var auraKey = geo ? geo.signature : null;
var sources = auraKey != null ? auraSourceCache[auraKey] : null;
if (!sources) {
    sources = [];
    CubeWorld.FACE_IDS.forEach(function (sourceFloorId) {
        getBlocks(sourceFloorId).forEach(function (block) {
            var sourceEnemy = getEnemy(block, sourceFloorId);
            if (!sourceEnemy) return;
            if (core.hasSpecial(sourceEnemy.special, 25) || core.hasSpecial(sourceEnemy.special, 26)) {
                sources.push({ enemy: sourceEnemy, id: sourceEnemy.id || block.event.id,
                    x: block.x, y: block.y, floorId: sourceFloorId,
                    hpBuff: sourceEnemy.hpBuff || 0, atkBuff: sourceEnemy.atkBuff || 0,
                    defBuff: sourceEnemy.defBuff || 0,
                    haloRange: sourceEnemy.haloRange, haloSquare: !!sourceEnemy.haloSquare,
                    haloAdd: sourceEnemy.haloAdd });
            }
        });
    });
    if (auraKey != null) auraSourceCache[auraKey] = sources;
}
// 之后用 sources 做距离计算，逻辑与原实现逐字段一致
```

### 3. 降级安全

若 `geoCache[floorId]` 尚未建立（首次进入），`auraKey = null`，直接重建一次快照且不写入缓存，行为降级为改动前一致。

---

## 正确性说明

- **结果不变**：光环 buff 累加、隐士献祭（recluseDeductP）、guards 推入等逻辑与原实现完全一致，仅把“找光环源”这一步提前缓存。
- **失效正确**：缓存 key 是几何签名字符串，含敌人 special/range/laser 等；光环源变化 → 签名变 → 自动使用新快照。hero 单步移动不影响签名 → 快照命中（正是期望结果）。
- **零副作用**：纯只读缓存，不创建/修改任何游戏状态对象，不触碰 `core.status.damage` / `damageCache` / 显伤渲染，不影响读档与开局显伤。

---

## 测试验证

- 语法检查：`node -c project/cube-runtime.js` 通过。
- 功能验证：六面楼层（含光环/护卫敌人、激光）开局、读档、连续移动流畅，显伤与光环数值与改动前一致。
