【AI编码】【Buff/AI/战斗】P0 性能优化:Buff 系统 GC 优化、AI 列表复用、DamageRecordor 去除冗余分配
Made-with: Cursor # Conflicts: # ProjectNLD/Assets/Code/Scripts/GamePlay_Dll/GamePlay.dllmain
parent
4d74227f0b
commit
753d91e5e5
|
|
@ -0,0 +1,51 @@
|
|||
## P0-01: Buff 系统 — GetBuffsByType 复用 List ✅ 已完成
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Buff/UnitBuffManager.cs`
|
||||
**问题类型**: GC 分配(热路径)
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
### 问题描述
|
||||
|
||||
`GetBuffsByType()` 每次调用都 `new List<BaseBuff>()`,如果在战斗中被频繁调用,会产生大量 GC。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/UnitBuffManager.cs`
|
||||
行号: 147-160
|
||||
|
||||
```csharp
|
||||
public List<BaseBuff> GetBuffsByType(EBuffType buffType)
|
||||
{
|
||||
var findList = new List<BaseBuff>(); // 每次分配新 List
|
||||
for (var i = 0; i < totalBuffList.Count; ++i)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
return findList;
|
||||
}
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
在类中增加一个可复用的 `List<BaseBuff>` 成员变量,每次调用时 `Clear()` 后重新填充。
|
||||
|
||||
**注意事项**:
|
||||
- 调用方不应长期持有返回值引用,因为下次调用会清空内容
|
||||
- 如果存在嵌套调用(在遍历结果时再次调用此方法),需要特殊处理(调用方自行拷贝)
|
||||
- 修改前需全局搜索 `GetBuffsByType` 的所有调用点,确认无嵌套使用场景
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 战斗中 Buff 功能正常(添加、触发、移除)
|
||||
3. 可通过 Unity Profiler 对比修改前后 GC Alloc
|
||||
|
||||
### 完成记录
|
||||
|
||||
**修改内容**:
|
||||
1. 新增 `_reusableBuffList` 成员变量(构造函数中初始化,容量 `EXPECTED_MAX_BUFFS`)
|
||||
2. `GetBuffsByType()` 改为使用 `_reusableBuffList`,每次调用先 `Clear()` 再填充
|
||||
3. 方法添加了 XML 注释,提示调用方不应长期持有返回引用
|
||||
|
||||
**调用点分析**:全局搜索确认 `GetBuffsByType` 当前无外部调用者,无嵌套调用风险。
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
## P0-02: Buff 系统 — RemoveBuffById 去除 lambda 分配 ✅ 已完成
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Buff/UnitBuffManager.cs`
|
||||
**问题类型**: GC 分配(lambda 委托)
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
### 问题描述
|
||||
|
||||
`RemoveBuffById()` 使用 `List.Find(buff => ...)` 查找 Buff,每次调用都会分配一个新的委托对象。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/UnitBuffManager.cs`
|
||||
行号: 214
|
||||
|
||||
```csharp
|
||||
var findBuff = totalBuffList.Find(buff => buff.configData.ID == buffId);
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
将 `List.Find` + lambda 替换为普通 `for` 循环查找:
|
||||
|
||||
```csharp
|
||||
BaseBuff findBuff = null;
|
||||
for (int i = 0; i < totalBuffList.Count; i++)
|
||||
{
|
||||
if (totalBuffList[i].configData.ID == buffId)
|
||||
{
|
||||
findBuff = totalBuffList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 通过 Buff ID 移除 Buff 功能正常
|
||||
|
||||
### 完成记录
|
||||
|
||||
将 `totalBuffList.Find(buff => buff.configData.ID == buffId)` 替换为普通 `for` 循环查找,消除 lambda 委托分配。
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
## P0-03: Buff 系统 — _needRemoveList 改用 HashSet ✅ 已完成
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Buff/UnitBuffManager.cs`
|
||||
**问题类型**: O(n) 查找优化
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
### 问题描述
|
||||
|
||||
`AddNeedRemoveBuff()` 中 `_needRemoveList.Contains(buff)` 为 O(n) 操作,在 Buff 频繁增删时性能较差。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/UnitBuffManager.cs`
|
||||
行号: 411-415
|
||||
|
||||
```csharp
|
||||
if (_needRemoveList.Contains(buff))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_needRemoveList.Add(buff);
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
增加一个 `HashSet<BaseBuff> _needRemoveSet` 用于快速判重,同时保留 `_needRemoveList` 用于顺序遍历(在 `LogicUpdate` 中使用):
|
||||
|
||||
1. 新增字段 `private HashSet<BaseBuff> _needRemoveSet = new HashSet<BaseBuff>();`
|
||||
2. `Contains` 检查改为 `_needRemoveSet.Contains(buff)`
|
||||
3. `Add` 时同时添加到 `_needRemoveSet` 和 `_needRemoveList`
|
||||
4. `Clear` 时同时清空两者
|
||||
5. 在 `LogicUpdate` 方法(行号 417-446)的 `_needRemoveList.Clear()` 后面加上 `_needRemoveSet.Clear()`
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. Buff 移除逻辑正常,无遗漏或重复移除
|
||||
|
||||
### 完成记录
|
||||
|
||||
新增 `_needRemoveSet` (HashSet),`AddNeedRemoveBuff` 中用 `_needRemoveSet.Add(buff)` 返回值判重(O(1)),同时保留 `_needRemoveList` 用于顺序遍历。`LogicUpdate` 和 `ClearAllBuff` 中同步 Clear 两者。
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
## P0-04: Buff 系统 — totalBuffList.Contains 改用 HashSet ✅ 已完成
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Buff/UnitBuffManager.cs`
|
||||
**问题类型**: O(n) 查找优化
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
### 问题描述
|
||||
|
||||
`RemoveBuff()` 中 `totalBuffList.Contains(buff)` 为 O(n) 操作。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/UnitBuffManager.cs`
|
||||
行号: 188-189
|
||||
|
||||
```csharp
|
||||
public void RemoveBuff(BaseBuff buff, bool imdRemove = false)
|
||||
{
|
||||
var isInList = totalBuffList.Contains(buff);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
新增一个 `HashSet<BaseBuff> _totalBuffSet` 用于快速判断 Buff 是否在列表中:
|
||||
|
||||
1. 新增字段 `private HashSet<BaseBuff> _totalBuffSet = new HashSet<BaseBuff>();`
|
||||
2. 在 `totalBuffList` 添加 Buff 的地方,同时 `_totalBuffSet.Add(buff)`
|
||||
3. 在 `totalBuffList` 移除 Buff 的地方,同时 `_totalBuffSet.Remove(buff)`
|
||||
4. 将 `totalBuffList.Contains(buff)` 替换为 `_totalBuffSet.Contains(buff)`
|
||||
|
||||
**注意事项**:
|
||||
- 需要全局搜索所有对 `totalBuffList` 的 `Add` 和 `Remove`(包括 `RemoveAt`、`Insert` 等),确保 `_totalBuffSet` 同步
|
||||
- 在 `Clear` / `Dispose` 等清理方法中也要同步清理
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. Buff 添加、移除、查询功能正常
|
||||
|
||||
### 完成记录
|
||||
|
||||
新增 `_totalBuffSet` (HashSet),在 `_AddBuff`/`_RemoveBuff`/`ClearAllBuff` 中与 `totalBuffList` 同步增删清空。`RemoveBuff` 中 `totalBuffList.Contains` 替换为 `_totalBuffSet.Contains`(O(1))。
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
## P0-06: Buff 系统 — BuffAddSearchValue 去除 Keys.ToList() ✅ 已完成
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Buff/Impl/BuffAddSearchValue.cs`
|
||||
**问题类型**: GC 分配
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
### 问题描述
|
||||
|
||||
`_ChangeSearchValue()` 和 `_RevertSearchValue()` 中 `handleDict.Keys.ToList()` 每次调用都分配新 List。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/Impl/BuffAddSearchValue.cs`
|
||||
|
||||
位置1 — 行号 30-31:
|
||||
```csharp
|
||||
var keys = handleDict.Keys.ToList();
|
||||
foreach (var key in keys)
|
||||
```
|
||||
|
||||
位置2 — 行号 66-67:
|
||||
```csharp
|
||||
var keys = handleDict.Keys.ToList();
|
||||
foreach (var key in keys)
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
**方案A**(如果循环内不修改 `handleDict`):直接遍历 `handleDict.Keys`,去掉 `.ToList()`。
|
||||
|
||||
**方案B**(如果循环内会修改 `handleDict`):使用类级别的可复用 List 缓存 keys:
|
||||
|
||||
```csharp
|
||||
private static readonly List<XXX> _tempKeys = new List<XXX>();
|
||||
|
||||
// 使用时:
|
||||
_tempKeys.Clear();
|
||||
_tempKeys.AddRange(handleDict.Keys);
|
||||
foreach (var key in _tempKeys)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 需要先检查循环体内是否有对 `handleDict` 的增删操作,决定使用方案A还是方案B
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. Buff 的 SearchValue 加成和还原功能正常
|
||||
|
||||
### 完成记录
|
||||
|
||||
移除 `using System.Linq`,`handleDict.Keys.ToList()` 替换为 `static readonly List<int> _tempKeys` 复用方案(`Clear()` + `AddRange(handleDict.Keys)` + `for` 循环)。`_ChangeSearchValue` 和 `_RevertSearchValue` 两处均已修改。使用方案B(缓存 keys)而非方案A,因为循环内修改字典值会导致枚举器失效。
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
## P0-07: AI 系统 — UpdateBatchGroup 复用 List ✅ 已完成(部分)
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/AI/AIManager.cs`
|
||||
**问题类型**: GC 分配
|
||||
**状态**: ✅ 已完成(仅复用 `allTrees`,`group` 被长期持有不可复用)
|
||||
|
||||
### 问题描述
|
||||
|
||||
`UpdateUnifiedBatchGroup()` 和 `UpdateBatchGroup()` 中频繁 `new List<BehaviorTree>()`。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/AI/AIManager.cs`
|
||||
|
||||
- 行号 460:`var allTrees = new List<BehaviorTree>();`
|
||||
- 行号 514:`var group = new List<BehaviorTree>();`
|
||||
- 行号 547:`var group = new List<BehaviorTree>();`
|
||||
|
||||
### 修改方案
|
||||
|
||||
将临时 List 改为类成员变量,调用前 `Clear()` 复用:
|
||||
|
||||
1. 新增类成员:
|
||||
- `private List<BehaviorTree> _tempAllTrees = new List<BehaviorTree>();`
|
||||
- `private List<List<BehaviorTree>> _tempBatchGroups`(根据分组逻辑的需要来定)
|
||||
2. 每次调用 `UpdateUnifiedBatchGroup` / `UpdateBatchGroup` 时,先 Clear 再填充
|
||||
|
||||
**注意事项**:
|
||||
- 分组的 `group` 列表如果最终被存入其他数据结构长期持有,则不能复用,需保持 new;仅对不被外部持有的临时变量做复用
|
||||
- 需确认 `allTrees` 和 `group` 的生命周期
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. AI 分批更新逻辑正常,行为树执行顺序不变
|
||||
|
||||
### 完成记录
|
||||
|
||||
`UpdateUnifiedBatchGroup` 中的 `var allTrees = new List<BehaviorTree>()` 替换为类成员 `_tempAllTrees`(每次调用 `Clear()` 复用)。`group` 列表因被 `_unifiedBehaviorTreeGroups.Add(group)` 长期持有,保持 `new`。`UpdateBatchGroup` 中的 `group` 同理保持不变。
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
## P0-11: DamageRecordor — 去除 ToArray 和 new double[0] ✅ 已完成(部分)
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Fight/Debug/DamageRecordor/DamageRecordor.cs`
|
||||
**问题类型**: GC 分配
|
||||
**状态**: ✅ 已完成(`new double[0]` → `Array.Empty<double>()`,`ToArray()` 保留因为赋给不同 log 对象无法缓存)
|
||||
|
||||
### 问题描述
|
||||
|
||||
每次 Buff 触发/初始化时 `FloatParams.ToArray()` 分配新数组,空分支使用 `new double[0]`。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Fight/Debug/DamageRecordor/DamageRecordor.cs`
|
||||
|
||||
位置1 — 行号 394-401(`FillBuffTriggerBeforData`):
|
||||
```csharp
|
||||
if (buff.configData.FloatParams != null && buff.configData.FloatParams.Count > 0)
|
||||
{
|
||||
triggerLog.BuffAuxiliaryValues = buff.configData.FloatParams.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerLog.BuffAuxiliaryValues = new double[0];
|
||||
}
|
||||
```
|
||||
|
||||
位置2 — 行号 517-524(`FillBuffInitData`):
|
||||
```csharp
|
||||
// 同上结构
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
1. `new double[0]` → `Array.Empty<double>()`(零分配)
|
||||
2. 如果 `FloatParams` 内容不会变化,可在 `configData` 中缓存一次 `ToArray()` 的结果,避免重复转换;否则保持 `ToArray()` 不变(因为调试模块影响有限)
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 伤害记录功能正常
|
||||
|
||||
### 完成记录
|
||||
|
||||
`FillBuffTriggerBeforData` 和 `FillBuffInitData` 两处的 `new double[0]` 替换为 `Array.Empty<double>()`(零 GC 分配)。`FloatParams.ToArray()` 保留不变,因为结果赋给不同的 log 对象实例,无法安全共享缓存。
|
||||
|
|
@ -0,0 +1,726 @@
|
|||
# NLD 项目性能优化任务清单
|
||||
|
||||
> 本文档按"最小可独立执行单元"组织,每个任务可以独立完成、独立测试、独立提交。
|
||||
> AI 执行时请严格按照每个任务的描述进行修改,不扩展额外功能。
|
||||
> 修改 GamePlay 目录下的代码后需通过 Unity 菜单 `Tools/文件与路径/转换为Dll`(快捷键 F1)编译生成 DLL。
|
||||
> 已完成任务的详细内容见 `AI_Gen_Doc/Finish/` 文件夹。
|
||||
|
||||
### AI 自动执行规则
|
||||
|
||||
1. **风险评估优先**:执行每个任务前必须先评估改动风险(如跨类改动、生命周期管理、大型重构、遍历中修改集合等)。有风险的任务标记为 ⏭️ 跳过,并注明跳过原因,等待与用户确认后再决定是否执行。
|
||||
2. **完成后归档**:任务完成后,将该任务的完整详情(问题描述、修改方案、完成记录等)拆分到 `AI_Gen_Doc/Finish/` 文件夹下的独立文件,本清单中仅保留目录条目标记。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [P0-01] ✅ Buff 系统 — GetBuffsByType 复用 List
|
||||
- [P0-02] ✅ Buff 系统 — RemoveBuffById 去除 lambda 分配
|
||||
- [P0-03] ✅ Buff 系统 — _needRemoveList 改用 HashSet
|
||||
- [P0-04] ✅ Buff 系统 — totalBuffList.Contains 改用 HashSet
|
||||
- [P0-05] ⏭️ Buff 系统 — totalBuffList.IndexOf 优化(跳过:Dictionary 索引维护复杂度高,易引入 bug)
|
||||
- [P0-06] ✅ Buff 系统 — BuffAddSearchValue 去除 Keys.ToList()
|
||||
- [P0-07] ✅ AI 系统 — UpdateBatchGroup 复用 List(仅 allTrees,group 被长期持有不可复用)
|
||||
- [P0-08] ⏭️ AI 系统 — 缓存 BehaviorTree 引用(跳过:需跨多个类改动,需确认组件生命周期)
|
||||
- [P0-09] ⏭️ 战斗 UI — UIFightSceneController 血条对象池(跳过:大型重构,需拆分子任务单独实施)
|
||||
- [P0-10] ⏭️ 战斗 UI — UIFightSceneController Buff 图标对象池(跳过:大型重构,需拆分子任务单独实施)
|
||||
- [P0-11] ✅ DamageRecordor — 去除 ToArray 和 new double[0]
|
||||
- [P1-01] 每帧检查降频 — _UpdateBlackArea
|
||||
- [P1-02] 每帧检查降频 — _UpdateViewTransparent 脏标记
|
||||
- [P1-03] 每帧检查降频 — BuffAroundCheck 空间查询优化
|
||||
- [P1-04] Camera.main 缓存
|
||||
- [P1-05] AreaManager — 距离缓存 key 碰撞修复
|
||||
- [P1-06] AreaManager — GetUnitsByCellIndex 去除无效 new List
|
||||
- [P1-07] AIEventBusManager — 去除 ToArray 和 new 对象
|
||||
- [P2-01] 画报系统 — GetComponent 缓存
|
||||
- [P2-02] 画报系统 — Debug.Log 条件编译
|
||||
- [P2-03] ConditionModifier — Keys.Contains 改为 ContainsKey
|
||||
- [P2-04] StorageMgr — Keys 遍历优化
|
||||
- [P2-05] 非战斗 UI — CurrencyItem 等对象池化
|
||||
- [P2-06] RoadLineView — 路径线对象池化
|
||||
|
||||
---
|
||||
|
||||
## P0-05: Buff 系统 — totalBuffList.IndexOf 优化 ⏭️ 跳过
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/Buff/UnitBuffManager.cs`
|
||||
**问题类型**: O(n) 查找优化
|
||||
**状态**: ⏭️ 跳过
|
||||
**跳过原因**: 用 `Dictionary<BaseBuff, int>` 维护索引映射需要在每次 `RemoveAt` 后更新所有后续元素索引(仍为 O(n)),且 `_RelocateIndex` 在 Trigger 回调链中频繁调用,字典与 List 的中间状态难以保持一致,引入 bug 风险高。当前 buff 数量预期 <=10,`IndexOf` 开销可接受。
|
||||
|
||||
### 问题描述
|
||||
|
||||
`_RemoveBuff()` 和 `_RelocateIndex()` 中使用 `totalBuffList.IndexOf(buff)` 进行 O(n) 查找。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/UnitBuffManager.cs`
|
||||
|
||||
位置1 — `_RemoveBuff` 行号 376-378:
|
||||
```csharp
|
||||
var index = totalBuffList.IndexOf(buff);
|
||||
if (index < 0)
|
||||
{
|
||||
```
|
||||
|
||||
位置2 — `_RelocateIndex` 行号 798-801:
|
||||
```csharp
|
||||
var newIndex = totalBuffList.IndexOf(buff);
|
||||
i = newIndex < 0 ? i - 1 : newIndex;
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
维护一个 `Dictionary<BaseBuff, int> _buffIndexMap` 记录每个 Buff 在 `totalBuffList` 中的索引:
|
||||
|
||||
1. 新增字段 `private Dictionary<BaseBuff, int> _buffIndexMap = new Dictionary<BaseBuff, int>();`
|
||||
2. 在 Buff 添加时记录索引
|
||||
3. 在 Buff 移除后重建受影响的索引(仅更新被移除位置之后的元素)
|
||||
4. 将 `IndexOf` 替换为字典查找
|
||||
|
||||
**注意事项**:
|
||||
- 此优化与 P0-04 可合并实施
|
||||
- 需要仔细处理 `_RelocateIndex` 中的索引重排逻辑
|
||||
- 如果 `totalBuffList` 有排序操作,索引映射也需同步更新
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. Buff 优先级排序、移除、重定位功能正常
|
||||
|
||||
---
|
||||
|
||||
## P0-08: AI 系统 — 缓存 BehaviorTree 引用 ⏭️ 跳过
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/AI/AIManager.cs`
|
||||
**问题类型**: GetComponent 开销
|
||||
**状态**: ⏭️ 跳过
|
||||
**跳过原因**: 需要在 `GameUnit` 或其视图组件中新增字段缓存 `BehaviorTree` 引用,涉及跨多个类的改动。且需确认 `BehaviorTree` 组件是否会被动态添加/移除,当前 `EnableSingleAI`/`UnRegisterAI` 不是每帧调用,风险收益比不高。
|
||||
|
||||
### 问题描述
|
||||
|
||||
`EnableSingleAI()` 和 `UnRegisterAI()` 中使用 `go.GetComponent<BehaviorTree>()` 获取行为树组件。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/AI/AIManager.cs`
|
||||
|
||||
位置1 — `UnRegisterAI` 行号 394:
|
||||
```csharp
|
||||
var bt = go.GetComponent<BehaviorTree>();
|
||||
```
|
||||
|
||||
位置2 — `EnableSingleAI` 行号 404:
|
||||
```csharp
|
||||
var bt = go.GetComponent<BehaviorTree>();
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
在 `GameUnit` 或对应的单位数据结构中缓存 `BehaviorTree` 引用,在单位创建时获取一次,后续直接使用缓存引用。
|
||||
|
||||
具体步骤:
|
||||
1. 检查 `GameUnit` 或其视图组件是否有合适的字段存储 `BehaviorTree`
|
||||
2. 在单位初始化时(如 `Init` 或 `OnCreate`)获取并缓存
|
||||
3. 在 `AIManager` 中改为从缓存读取
|
||||
|
||||
**注意事项**:
|
||||
- `EnableSingleAI` 和 `UnRegisterAI` 不是每帧调用,但在大量单位进出战场时可能集中调用
|
||||
- 需确认 `BehaviorTree` 组件是否可能被动态添加/移除
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. AI 启用/禁用功能正常
|
||||
|
||||
---
|
||||
|
||||
## P0-09: 战斗 UI — UIFightSceneController 血条对象池 ⏭️ 跳过
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/UI/LevelFight/FightSceneUI/UIFightSceneController.cs`
|
||||
**问题类型**: Instantiate/Destroy 开销
|
||||
**状态**: ⏭️ 跳过
|
||||
**跳过原因**: 大型重构任务,涉及 4 处不同类型血条的 Instantiate 和 Destroy 改造,需要按 prefab 类型分池、处理回收时的状态重置。任务清单也标注"此任务较大,可拆分为子任务逐步实施",不适合作为原子操作一次完成。
|
||||
|
||||
### 问题描述
|
||||
|
||||
战斗中单位血条频繁 `Instantiate` / `Destroy`,在大量单位进出战场时会产生明显的性能峰值。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/UI/LevelFight/FightSceneUI/UIFightSceneController.cs`
|
||||
|
||||
**创建血条**(Instantiate):
|
||||
- 行号 2939:角色/载具血条
|
||||
- 行号 3002:召唤物血条
|
||||
- 行号 3037:建筑血条
|
||||
- 行号 3090:NPC 血条
|
||||
|
||||
**销毁血条**(Destroy):
|
||||
- 行号 3417:`GameObjectUtils.Destroy(dicGoBlood[uid])`
|
||||
|
||||
### 修改方案
|
||||
|
||||
使用项目已有的 `ObjectPool<T>`(`Framework/ObjectPool.cs`)或创建一个 `Dictionary<prefab, Queue<GameObject>>` 简单对象池:
|
||||
|
||||
1. 创建血条对象池,按血条类型(角色、召唤物、建筑、NPC)分池
|
||||
2. 创建时从池中取出,设置 `SetActive(true)` 并重置数据
|
||||
3. 销毁时回收到池中,设置 `SetActive(false)`
|
||||
4. 在战斗结束/场景切换时清空对象池
|
||||
|
||||
**注意事项**:
|
||||
- 血条创建时有不同的 prefab,需按 prefab 类型分池
|
||||
- 回收时需重置血条状态(血量、颜色、Buff 显示等)
|
||||
- 此任务较大,可拆分为子任务逐步实施
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 单位出场/退场时血条显示正常
|
||||
3. 血条数据(血量、名称等)正确刷新
|
||||
4. 对比修改前后 Instantiate/Destroy 调用次数
|
||||
|
||||
---
|
||||
|
||||
## P0-10: 战斗 UI — UIFightSceneController Buff 图标对象池 ⏭️ 跳过
|
||||
|
||||
**优先级**: P0(高)
|
||||
**影响范围**: `GamePlay/UI/LevelFight/FightSceneUI/UIFightSceneController.cs`
|
||||
**问题类型**: Instantiate/Destroy 开销
|
||||
**状态**: ⏭️ 跳过
|
||||
**跳过原因**: 与 P0-09 类似,属于大型 UI 重构任务,需与血条对象池化统一规划实施。
|
||||
|
||||
### 问题描述
|
||||
|
||||
战斗中 Buff 图标频繁 `Instantiate` / `Destroy`。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/UI/LevelFight/FightSceneUI/UIFightSceneController.cs`
|
||||
|
||||
- 行号 3542:创建 Buff 图标 `GameObjectUtils.Instantiate(prefab, _goMap.transform)`
|
||||
- 行号 3534:销毁旧 Buff 图标 `GameObjectUtils.Destroy(curGo)`
|
||||
- 行号 3423:销毁 Buff 图标 `GameObjectUtils.Destroy(dicGoShowBuff[uid])`
|
||||
|
||||
### 修改方案
|
||||
|
||||
与 P0-09 类似,使用对象池管理 Buff 图标:
|
||||
|
||||
1. 创建 Buff 图标对象池
|
||||
2. 创建时从池取出,销毁时回收
|
||||
3. 回收时重置图标状态
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. Buff 图标显示和隐藏正常
|
||||
|
||||
---
|
||||
|
||||
## P1-01: 每帧检查降频 — _UpdateBlackArea
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GamePlay/Unit/GameUnit.cs`
|
||||
**问题类型**: 每帧重复计算
|
||||
|
||||
### 问题描述
|
||||
|
||||
`_UpdateBlackArea()` 每帧为**每个敌方单位**调用 `BlackAreaManager.instance.CheckIsInLightArea()`,开销与敌方单位数量成正比。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Unit/GameUnit.cs`
|
||||
行号: 358-365
|
||||
|
||||
```csharp
|
||||
private void _UpdateBlackArea()
|
||||
{
|
||||
if (commonData.troopId == ETroopsId.Self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
statusData.isInBlackArea = !BlackAreaManager.instance.CheckIsInLightArea(transData.cellIndex);
|
||||
}
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
降低检查频率,例如每 5-10 帧检查一次,或仅在单位移动(cellIndex 变化)时检查:
|
||||
|
||||
```csharp
|
||||
private int _lastBlackAreaCheckCell = -1;
|
||||
|
||||
private void _UpdateBlackArea()
|
||||
{
|
||||
if (commonData.troopId == ETroopsId.Self) return;
|
||||
if (transData.cellIndex == _lastBlackAreaCheckCell) return;
|
||||
_lastBlackAreaCheckCell = transData.cellIndex;
|
||||
statusData.isInBlackArea = !BlackAreaManager.instance.CheckIsInLightArea(transData.cellIndex);
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 需要确认 `CheckIsInLightArea` 的结果是否可能在单位静止时变化(如光照区域本身在变化)
|
||||
- 如果光照区域会动态变化,则需要事件通知或定时刷新
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 敌方单位在黑区/光照区域的显隐切换正常
|
||||
3. 无明显延迟感
|
||||
|
||||
---
|
||||
|
||||
## P1-02: 每帧检查降频 — _UpdateViewTransparent 脏标记
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GamePlay/Unit/GameUnit.cs`
|
||||
**问题类型**: 每帧重复计算
|
||||
|
||||
### 问题描述
|
||||
|
||||
`_UpdateViewTransparent()` 每帧执行,含多层 if-else 条件判断和属性访问,但透明度状态不会每帧变化。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Unit/GameUnit.cs`
|
||||
行号: 341-379
|
||||
|
||||
### 修改方案
|
||||
|
||||
引入脏标记机制,仅在相关状态变化时重新计算透明度:
|
||||
|
||||
1. 新增 `_transparentDirty` 标记
|
||||
2. 在影响透明度的状态发生变化时(如 `isForceHide`、`needSkillCover`、`belongBuild` 变化等)设置标记为 true
|
||||
3. `_UpdateViewTransparent()` 中检查标记,无变化时直接返回
|
||||
|
||||
**注意事项**:
|
||||
- 需要找到所有修改 `isForceHide`、`needSkillCover`、`belongBuild` 的代码位置,在其中设置脏标记
|
||||
- 此优化需要对透明度相关的状态变化有全面了解
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 单位透明度切换(技能覆盖、强制隐藏、建筑归属等)正常
|
||||
|
||||
---
|
||||
|
||||
## P1-03: 每帧检查降频 — BuffAroundCheck 空间查询优化
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GamePlay/Buff/Impl/AroundCheck/BuffAroundCheckEnemyForDefense.cs` 及同目录下类似文件
|
||||
**问题类型**: 每帧重复计算 + 全场遍历
|
||||
|
||||
### 问题描述
|
||||
|
||||
`_Check()` 方法遍历 `GameUnitManager.instance.infightUnits.unitList` 全部单位来查找范围内的敌人,在大量单位场景下开销较大。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Buff/Impl/AroundCheck/BuffAroundCheckEnemyForDefense.cs`
|
||||
行号: 56-96
|
||||
|
||||
同类文件:
|
||||
- `BuffAroundCheckEnemyForMoraleDamageScale.cs`
|
||||
- `BuffDamageScaleWithAroundCheck.cs`
|
||||
|
||||
### 修改方案
|
||||
|
||||
**方案A**(简单):利用已有的 `AreaManager.GetUnitsByCellIndex()` 做近距离格子查询,而非遍历全场单位:
|
||||
|
||||
1. 通过 `AreaManager.GetCellIndexsAround()` 获取周围格子
|
||||
2. 对每个格子调用 `GetUnitsByCellIndex()` 获取单位
|
||||
3. 仅对范围内格子上的单位做距离判断
|
||||
|
||||
**方案B**(保守):保持全场遍历,但提前过滤已死亡和同阵营单位,减少 `GetDistance` 调用。
|
||||
|
||||
**注意事项**:
|
||||
- 需要确认 `_checkRange` 的范围与格子距离的换算关系
|
||||
- 类似的 AroundCheck 类需要统一修改
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 防御加成/士气伤害倍率等 Buff 效果数值正确
|
||||
|
||||
---
|
||||
|
||||
## P1-04: Camera.main 缓存
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GameWrapper/Camera/CameraStand.cs`
|
||||
**问题类型**: Unity API 性能陷阱
|
||||
|
||||
### 问题描述
|
||||
|
||||
`DefaultCamera` 属性中使用 `Camera.main`,每次访问都会执行 `FindGameObjectsWithTag("MainCamera")`。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GameWrapper/Camera/CameraStand.cs`
|
||||
行号: 48-49
|
||||
|
||||
```csharp
|
||||
public Camera DefaultCamera => FrameWorkUtils.GetSceneRootComponent<Camera>(gameObject.scene) ??
|
||||
Camera.main ?? CameraManager.Instance.MainCamera;
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
将 `DefaultCamera` 从计算属性改为带缓存的属性:
|
||||
|
||||
```csharp
|
||||
private Camera _cachedCamera;
|
||||
|
||||
public Camera DefaultCamera
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cachedCamera == null)
|
||||
{
|
||||
_cachedCamera = FrameWorkUtils.GetSceneRootComponent<Camera>(gameObject.scene)
|
||||
?? Camera.main
|
||||
?? CameraManager.Instance.MainCamera;
|
||||
}
|
||||
return _cachedCamera;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 如果相机可能被销毁重建,需提供一个 `InvalidateCameraCache()` 方法
|
||||
- 检查 `DefaultCamera` 是否在场景切换后仍需重新获取
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 相机功能正常
|
||||
|
||||
---
|
||||
|
||||
## P1-05: AreaManager — 距离缓存 key 碰撞修复
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GamePlay/Area/AreaManager.cs`
|
||||
**问题类型**: 逻辑缺陷 + 潜在性能问题
|
||||
|
||||
### 问题描述
|
||||
|
||||
`_distanceCache` 使用 `cellIndex1 * 10000 + cellIndex2` 作为 key,当 cellIndex 超过 10000 时会产生 key 碰撞,且缓存无限增长。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Area/AreaManager.cs`
|
||||
行号: 298-310
|
||||
|
||||
```csharp
|
||||
var key = cellIndex1 * 10000 + cellIndex2;
|
||||
if (_distanceCache.TryGetValue(key, out var distance))
|
||||
return distance;
|
||||
// ...
|
||||
_distanceCache.Add(key, newDistance);
|
||||
var invertedKey = cellIndex2 * 10000 + cellIndex1;
|
||||
_distanceCache.Add(invertedKey, newDistance);
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
将 key 改为 `long` 类型或 `(int, int)` 元组以避免碰撞:
|
||||
|
||||
```csharp
|
||||
private Dictionary<long, int> _distanceCache;
|
||||
|
||||
// 使用时:
|
||||
long key = ((long)cellIndex1 << 32) | (uint)cellIndex2;
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 先确认项目中 cellIndex 的最大值,如果确定小于 10000 则可暂不修改
|
||||
- 缓存无限增长问题可通过在关卡结束时清空来解决
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 距离计算结果正确
|
||||
|
||||
---
|
||||
|
||||
## P1-06: AreaManager — GetUnitsByCellIndex 去除无效 new List
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GamePlay/Area/AreaManager.cs`
|
||||
**问题类型**: GC 分配
|
||||
|
||||
### 问题描述
|
||||
|
||||
`GetUnitsByCellIndex()` 在 `_cellIndex2Units` 无对应 key 时会 `new List<GameUnit>()` 并加入字典,导致空格子也占用内存。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Area/AreaManager.cs`
|
||||
行号: 421-425
|
||||
|
||||
```csharp
|
||||
var newList = new List<GameUnit>();
|
||||
_cellIndex2Units.Add(cellIndex, newList);
|
||||
return newList;
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
使用 `TryGetValue` 并返回一个静态空列表,不插入字典:
|
||||
|
||||
```csharp
|
||||
private static readonly List<GameUnit> _emptyUnitList = new List<GameUnit>();
|
||||
|
||||
public List<GameUnit> GetUnitsByCellIndex(int cellIndex)
|
||||
{
|
||||
if (_cellIndex2Units.TryGetValue(cellIndex, out var list))
|
||||
return list;
|
||||
return _emptyUnitList;
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 需确认调用方是否会向返回的列表中添加元素(如果会,则不能返回共享空列表)
|
||||
- 如果调用方需要向空列表中添加单位,应在单位注册到格子时创建列表
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 格子单位查询功能正常
|
||||
|
||||
---
|
||||
|
||||
## P1-07: AIEventBusManager — 去除 ToArray 和 new 对象
|
||||
|
||||
**优先级**: P1(中)
|
||||
**影响范围**: `GamePlay/Level/AIEventBus/AIEventBusManager.cs`
|
||||
**问题类型**: GC 分配
|
||||
|
||||
### 问题描述
|
||||
|
||||
- `AddCacheSkill` 中 `listCellIndex.ToArray()` 分配新数组
|
||||
- `CachePlayerSkill.PreExecute` 中 `new CommanderSkillParams()` 每次创建新对象
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Level/AIEventBus/AIEventBusManager.cs`
|
||||
|
||||
- 行号 317:`AddCacheSkill(skill, false, true, listCellIndex.ToArray());`
|
||||
- 行号 86-90:`CommanderSkill.PreExecute(new CommanderSkillParams() { ... });`
|
||||
|
||||
### 修改方案
|
||||
|
||||
1. `listCellIndex.ToArray()` → 如果接收方支持 `IList<int>` 或 `IReadOnlyList<int>`,直接传 `listCellIndex`
|
||||
2. `CommanderSkillParams` → 如果是 class,考虑改为 struct 或使用对象池;如果已是 struct,则无需修改
|
||||
|
||||
**注意事项**:
|
||||
- 需确认 `AddCacheSkill` 的参数类型和后续用法
|
||||
- 需确认 `CommanderSkillParams` 是 class 还是 struct
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. AI 指挥官技能释放正常
|
||||
|
||||
---
|
||||
|
||||
## P2-01: 画报系统 — GetComponent 缓存
|
||||
|
||||
**优先级**: P2(低)
|
||||
**影响范围**: `Framework/UI/Pictorial/PictorialBackground.cs`、`PictorialItem.cs`
|
||||
**问题类型**: GetComponent 开销
|
||||
|
||||
### 问题描述
|
||||
|
||||
`PictorialBackground.Update()` 和 `PictorialItem.Update()` 中多处 `GetComponent<RectTransform>()` / `GetComponent<PictorialItem>()` 调用。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
**PictorialBackground.cs**:
|
||||
- 行号 128:`child.GetComponent<PictorialItem>()` (循环内)
|
||||
- 行号 153:`rectTransform = GetComponent<RectTransform>()`
|
||||
|
||||
**PictorialItem.cs**:
|
||||
- 行号 127, 189, 229, 256, 282:`transform.parent?.GetComponent<RectTransform>()` (多处)
|
||||
|
||||
### 修改方案
|
||||
|
||||
在 `Awake` 或 `OnEnable` 中缓存这些组件引用到字段中。
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 画报系统显示正常
|
||||
|
||||
---
|
||||
|
||||
## P2-02: 画报系统 — Debug.Log 条件编译
|
||||
|
||||
**优先级**: P2(低)
|
||||
**影响范围**: `Framework/UI/Pictorial/PictorialBackground.cs`、`PictorialItem.cs`
|
||||
**问题类型**: 字符串 GC 分配
|
||||
|
||||
### 问题描述
|
||||
|
||||
`Update()` 中大量 `Debug.Log($"[画报系统] ...")` 调用,在发布版本中也会执行字符串拼接。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
**PictorialBackground.cs** — Update 中的 Debug.Log:
|
||||
- 行号 95, 105, 139, 164, 182, 202, 211, 213
|
||||
|
||||
**PictorialItem.cs** — Update 中的 Debug.Log:
|
||||
- 行号 117, 133, 148, 154, 192, 203, 212
|
||||
|
||||
### 修改方案
|
||||
|
||||
用 `#if UNITY_EDITOR` 包裹这些调试日志,或使用 `[Conditional("UNITY_EDITOR")]` 标记的日志方法。
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 编辑器中日志仍然输出
|
||||
3. 发布版本中无此类日志
|
||||
|
||||
---
|
||||
|
||||
## P2-03: ConditionModifier — Keys.Contains 改为 ContainsKey
|
||||
|
||||
**优先级**: P2(低)
|
||||
**影响范围**: `Framework/Condition/Util/ConditionModifier.cs`
|
||||
**问题类型**: 不必要的 GC 分配
|
||||
|
||||
### 问题描述
|
||||
|
||||
`conditionNameCfg.Keys.Contains(type)` 会分配枚举器,而 `ContainsKey` 是 O(1) 且无 GC。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/Framework/Condition/Util/ConditionModifier.cs`
|
||||
行号: 155
|
||||
|
||||
```csharp
|
||||
if (ConditionUtil.conditionNameCfg.Keys.Contains(type))
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
```csharp
|
||||
if (ConditionUtil.conditionNameCfg.ContainsKey(type))
|
||||
```
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 条件判断功能正常
|
||||
|
||||
---
|
||||
|
||||
## P2-04: StorageMgr — Keys 遍历优化
|
||||
|
||||
**优先级**: P2(低)
|
||||
**影响范围**: `Framework/Storage/StorageMgr.cs`
|
||||
**问题类型**: 不必要的 GC 分配
|
||||
|
||||
### 问题描述
|
||||
|
||||
`foreach (var key in localStorageMap.Keys)` 遍历 `Keys` 集合会分配枚举器。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/Framework/Storage/StorageMgr.cs`
|
||||
行号: 193-196
|
||||
|
||||
```csharp
|
||||
foreach (var key in localStorageMap.Keys)
|
||||
{
|
||||
dataIOBase.LoadToDic(key, dataIOBase.Load(key), ref localStorageMap);
|
||||
}
|
||||
```
|
||||
|
||||
### 修改方案
|
||||
|
||||
改为遍历 Dictionary 本体:
|
||||
|
||||
```csharp
|
||||
foreach (var kv in localStorageMap)
|
||||
{
|
||||
dataIOBase.LoadToDic(kv.Key, dataIOBase.Load(kv.Key), ref localStorageMap);
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 如果 `LoadToDic` 会修改 `localStorageMap`,则需要先将 keys 缓存到列表中再遍历(但应使用复用列表,非 new List)
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 存储加载功能正常
|
||||
|
||||
---
|
||||
|
||||
## P2-05: 非战斗 UI — CurrencyItem 等对象池化
|
||||
|
||||
**优先级**: P2(低)
|
||||
**影响范围**: 多个 UI 控制器
|
||||
**问题类型**: Instantiate/Destroy 开销
|
||||
|
||||
### 问题描述
|
||||
|
||||
以下 UI 列表项频繁 `Instantiate` / `Destroy`,但未使用对象池:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `GamePlay/UI/Common/CurrencyItem.cs` 行号 60, 108 | 货币列表项 |
|
||||
| `GamePlay/UI/Character/UIAdvanceNewController.cs` 行号 257-260, 443-460 | 属性/技能文本 |
|
||||
| `GamePlay/UI/Character/UI_CultivateChangeController.cs` 行号 1153-1175 | 职业列表项 |
|
||||
| `GamePlay/UI/UI_VehiclePartTreeDetailController.cs` 行号 802-803 | 列表项 |
|
||||
|
||||
### 修改方案
|
||||
|
||||
使用 `Framework/ObjectPool.cs` 中的通用对象池对这些 UI 列表项进行池化管理。
|
||||
|
||||
每个文件可独立改造,建议按以下顺序:
|
||||
1. `CurrencyItem.cs`(最简单,改造成本低)
|
||||
2. `UIAdvanceNewController.cs`
|
||||
3. 其他
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 对应界面列表显示正常
|
||||
3. 界面开关时无明显内存增长
|
||||
|
||||
---
|
||||
|
||||
## P2-06: RoadLineView — 路径线对象池化
|
||||
|
||||
**优先级**: P2(低)
|
||||
**影响范围**: `GamePlay/Area/Path/RoadLineView.cs`
|
||||
**问题类型**: Instantiate/Destroy 开销
|
||||
|
||||
### 问题描述
|
||||
|
||||
路径线在创建/销毁时频繁 `Instantiate` / `Destroy`。
|
||||
|
||||
### 问题代码位置
|
||||
|
||||
文件: `Assets/Code/Scripts/GamePlay/Area/Path/RoadLineView.cs`
|
||||
|
||||
**Instantiate**:行号 59, 69, 105
|
||||
**Destroy**:行号 80, 86, 92
|
||||
|
||||
### 修改方案
|
||||
|
||||
使用对象池管理路径线 GameObject,回收时 `SetActive(false)`,取出时 `SetActive(true)` 并重置位置/旋转。
|
||||
|
||||
### 验证方法
|
||||
|
||||
1. 编译通过
|
||||
2. 路径线显示和隐藏正常
|
||||
3. 路径变更时线段正确更新
|
||||
Loading…
Reference in New Issue