com.alicizax.unity.framework/Runtime/ABase/GameObjectPool/fe.md

723 lines
14 KiB
Markdown
Raw Normal View History

2026-04-17 21:01:20 +08:00
# GameObjectPool 最优架构设计文档
## 1. 文档目标
本文档定义 `GameObjectPool` 模块的目标形态,用于支撑游戏内所有 `GameObject` 相关对象的统一池化,包括但不限于:
- 怪物 / NPC
- 子弹 / 投射物
- 特效 / 粒子 / VFXGraph
- 掉落物 / 临时交互物
- 飘字 / World UI
- 音效壳对象
- 可复用的场景装饰物
目标不是做一个“能复用 prefab 的基础对象池”,而是做一个“可覆盖全项目高频对象生命周期管理”的统一池化体系。
---
## 2. 现状结论
当前实现可以作为基础版对象池使用,但不是最优方案,主要原因如下:
- 只处理 `SetActive`、`SetParent`、Transform 复位,不具备统一的业务状态重置协议
- 池满时只会返回 `null`,没有按对象类型区分溢出策略
- 预热只支持静态固定值,不支持场景级、战斗级、波次级预热
- 淘汰策略只基于时间,不包含优先级、预算、低内存策略
- 清理调度依赖全量扫描,不适合大量池并存
- 配置维度不足,无法精细支撑怪物、特效、子弹等不同对象类型
- 缺少自动回收能力
- 缺少运行时统计闭环,无法逼近最优容量配置
结论:
- 当前实现可以继续作为底层雏形
- 不建议直接扩写成最终方案
- 最优方向应升级为“分层池化架构”
---
## 3. 设计目标
### 3.1 功能目标
- 统一管理所有 `GameObject` 池化对象
- 支持同步获取、异步获取、预热、回收、自动回收
- 支持对象在复用前后执行自定义重置
- 支持按对象类型定义不同的容量与淘汰策略
- 支持运行时统计与调优建议导出
- 支持低内存场景下的激进回收
### 3.2 性能目标
- 高频借还路径保持 O(1) 或近似 O(1)
- 避免重复加载同一 prefab
- 避免单帧大量 Instantiate / Destroy
- 清理与预热都采用预算驱动,而不是无上限循环
- 复杂对象允许使用轻量休眠,而非强制全量 `SetActive`
### 3.3 工程目标
- 配置清晰,可由策划 / TA / 程序共同维护
- 业务接入方式统一,不让每个系统各自发明对象池
- 支持逐步迁移,兼容现有 `GameObjectPoolManager`
---
## 4. 总体架构
推荐采用三层结构。
### 4.1 第一层Prefab 池层
职责:
- prefab 资源加载与引用持有
- 单 prefab 实例池管理
- 容量控制
- 预热
- 淘汰
- 调度
建议保留当前 `RuntimePrefabPool` 的职责方向,但增强其策略能力。
### 4.2 第二层:实例生命周期层
职责:
- 统一对象创建后初始化
- 对象借出前重置
- 对象归还前清理
- 对象销毁前释放内部资源
- 管理自动回收与上下文注入
该层是当前模块最缺失的部分,也是怪物、子弹、特效能够统一纳入池化的关键。
### 4.3 第三层:业务门面层
职责:
- 向业务提供强类型 API
- 隐藏字符串路径、组名、预热细节
- 承载特定对象的默认策略
示例:
```csharp
Enemy enemy = enemyPool.Spawn(enemyId, spawnContext);
Bullet bullet = bulletPool.Spawn(bulletConfigId, spawnContext);
FxHandle fx = fxPool.Play(fxId, position, rotation, autoRecycle: true);
```
不建议把字符串 `assetPath` 作为业务层唯一入口。
字符串路径池应作为底层资源定位方式,而不是最终业务接口。
---
## 5. 生命周期协议设计
统一池化必须提供明确的生命周期协议。
### 5.1 核心接口
```csharp
public interface IGameObjectPoolable
{
void OnPoolCreate();
void OnPoolGet(in PoolSpawnContext context);
void OnPoolRelease();
void OnPoolDestroy();
}
```
语义建议:
- `OnPoolCreate`
仅在实例第一次创建后调用一次
- `OnPoolGet`
每次借出时调用,用于注入上下文、重置运行态
- `OnPoolRelease`
每次归还时调用,用于停止逻辑、清理状态
- `OnPoolDestroy`
实例真正销毁前调用,用于释放自持资源
### 5.2 可选扩展接口
```csharp
public interface IPoolAutoRecycle
{
bool TryGetAutoRecycleDelay(out float delaySeconds);
}
public interface IPoolResettablePhysics
{
void ResetPhysicsState();
}
public interface IPoolResettableVisual
{
void ResetVisualState();
}
public interface IPoolResettableAnimation
{
void ResetAnimationState();
}
public interface IPoolSleepable
{
void EnterSleep();
void ExitSleep(in PoolSpawnContext context);
}
```
说明:
- `IPoolSleepable` 主要给怪物 / NPC 这类重对象使用
- `IPoolResettablePhysics` 适合子弹、投射物、掉落物
- `IPoolResettableVisual` 适合特效、Trail、VFXGraph、Renderer 状态清理
### 5.3 PoolSpawnContext
`OnPoolGet` 需要统一上下文,避免靠外部脚本到处手动赋值。
建议结构:
```csharp
public readonly struct PoolSpawnContext
{
public readonly string AssetPath;
public readonly string Group;
public readonly Transform Parent;
public readonly Vector3 Position;
public readonly Quaternion Rotation;
public readonly object UserData;
public readonly int OwnerId;
public readonly int TeamId;
public readonly uint SpawnFrame;
}
```
说明:
- `UserData` 用于挂通用扩展参数
- 高频场景可以进一步拆成强类型上下文
---
## 6. 池实例状态机
每个实例建议具备明确状态:
- `Uninitialized`
- `Inactive`
- `Active`
- `Releasing`
- `Destroying`
状态转换:
```text
Create -> Inactive -> Active -> Releasing -> Inactive
Create -> Inactive -> Destroying -> Destroy
Active -> Destroying -> Destroy
```
价值:
- 避免重复回收
- 避免回收中再次借出
- 便于统计与调试
- 便于做自动回收与异步保护
---
## 7. 配置模型设计
当前 `PoolConfig` 维度不够,建议升级为两层配置:
- `PoolProfile`
- `PoolRule`
### 7.1 PoolProfile
定义一类对象的默认策略,例如:
- `BulletProfile`
- `FxProfile`
- `EnemyProfile`
- `UiWorldProfile`
示例字段:
```csharp
public sealed class PoolProfile
{
public string profileName;
public PoolObjectKind objectKind;
public PoolOverflowPolicy overflowPolicy;
public PoolTrimPolicy trimPolicy;
public PoolActivationMode activationMode;
public PoolResetMode resetMode;
public int minRetained;
public int softCapacity;
public int hardCapacity;
public float idleTrimDelay;
public float prefabUnloadDelay;
public float autoRecycleDelay;
public int trimBatchPerTick;
public int warmupBatchPerFrame;
public float warmupFrameBudgetMs;
public bool allowRuntimeExpand;
public bool preloadOnInitialize;
public bool keepPrefabResident;
public bool aggressiveTrimOnLowMemory;
}
```
### 7.2 PoolRule
定义具体资源命中规则。
```csharp
public sealed class PoolRule
{
public string group;
public string assetPath;
public PoolMatchMode matchMode;
public PoolResourceLoaderType loaderType;
public string profileName;
public bool overrideCapacity;
public int minRetained;
public int softCapacity;
public int hardCapacity;
public bool overridePrewarm;
public int prewarmCount;
public PoolPrewarmPhase prewarmPhase;
public int priority;
}
```
设计原则:
- `Profile` 管类型策略
- `Rule` 管具体命中
- 高频热点对象优先使用 `Exact`
- `Prefix` 只做默认兜底
---
## 8. 核心枚举草案
### 8.1 对象类型
```csharp
public enum PoolObjectKind
{
Default = 0,
Bullet = 1,
Effect = 2,
Enemy = 3,
Npc = 4,
Pickup = 5,
WorldUi = 6,
AudioProxy = 7,
SceneProp = 8
}
```
### 8.2 池满策略
```csharp
public enum PoolOverflowPolicy
{
FailFast = 0,
InstantiateOneShot = 1,
AutoExpand = 2,
RecycleOldestInactive = 3,
DropNewestRequest = 4
}
```
### 8.3 清理策略
```csharp
public enum PoolTrimPolicy
{
None = 0,
IdleOnly = 1,
IdleAndPriority = 2,
AggressiveOnLowMemory = 3
}
```
### 8.4 激活模式
```csharp
public enum PoolActivationMode
{
SetActive = 0,
SleepWake = 1,
Custom = 2
}
```
### 8.5 重置模式
```csharp
public enum PoolResetMode
{
TransformOnly = 0,
PoolableCallbacks = 1,
FullReset = 2,
Custom = 3
}
```
### 8.6 预热阶段
```csharp
public enum PoolPrewarmPhase
{
None = 0,
AppInitialize = 1,
SceneLoad = 2,
BattlePrepare = 3,
WavePrepare = 4,
OnDemand = 5
}
```
---
## 9. 不同对象类型的推荐策略
### 9.1 子弹
建议:
- `softCapacity` 较高
- `hardCapacity` 可比 `softCapacity` 高 1.5 到 2 倍
- 默认支持 `AutoExpand``InstantiateOneShot`
- 必须支持自动回收
- 必须清理 `Rigidbody / Collider / TrailRenderer`
- prefab 通常常驻,不建议频繁 unload
### 9.2 特效 / 粒子
建议:
- 区分关键特效与普通特效
- 普通特效允许 `DropNewestRequest`
- 回收时必须清理所有粒子系统、Trail、音效播放状态
- 支持“播完自动回池”
- 预热由场景和技能装配共同驱动
### 9.3 怪物 / NPC
建议:
- 不建议只依赖通用 `SetActive`
- 必须实现 `IGameObjectPoolable`
- 推荐支持 `SleepWake`
- 必须有 `minRetained`
- 池满时不建议直接 `null`
- 场景切换 / 波次结束后再做结构性收缩
### 9.4 掉落物 / 临时交互物
建议:
- 适合通用池
- 支持超时自动回收
- 支持来源信息重置
### 9.5 飘字 / World UI
建议:
- 高度适合池化
- 必须自动回收
- 必须重置 tween / alpha / scale / text / follow target
---
## 10. 预热策略设计
### 10.1 预热阶段
预热必须是分阶段的,而不是只靠初始化时一次性完成。
建议阶段:
- `AppInitialize`
- `SceneLoad`
- `BattlePrepare`
- `WavePrepare`
- `OnDemand`
### 10.2 预热预算
预热不允许无限制同步创建。
建议加入:
- 每帧最大创建数 `warmupBatchPerFrame`
- 每帧时间预算 `warmupFrameBudgetMs`
- 大对象延迟到非关键帧创建
### 10.3 自适应预热
建议收集以下统计并用于反哺配置:
- `peakActive`
- `peakTotal`
- `missCount`
- `expandCount`
- `poolExhaustedCount`
由此生成:
- 推荐 `softCapacity`
- 推荐 `prewarmCount`
- 推荐 `minRetained`
---
## 11. 淘汰策略设计
### 11.1 默认原则
淘汰不能只看时间,还应结合:
- 空闲时间
- 对象优先级
- 当前是否战斗中
- 当前内存压力
- 当前池容量是否超过软上限
### 11.2 推荐容量模型
- `minRetained`
最低保有量,正常回收不低于该值
- `softCapacity`
常态推荐容量
- `hardCapacity`
绝对上限
### 11.3 销毁预算
回收策略必须预算化:
- 每 tick 最多回收 `trimBatchPerTick`
- 必要时可以带时间预算,例如每帧不超过 `X ms`
这样可避免同一帧大量 `Destroy`
### 11.4 低内存策略
当收到 `Application.lowMemory` 时:
- 忽略部分 `minRetained`
- 优先回收低优先级池
- 优先卸载不常用 prefab
- 进入短时激进回收模式
---
## 12. 调度策略设计
当前全量扫描策略不适合作为最终版。
推荐:
- 用最小堆维护池的下次清理时间
- 池状态变化时只更新自己的 `nextDueTime`
- 调度器只处理到期池
- 清理和预热都按 budget 驱动
这样可以避免:
- 每次回收都扫描所有池
- 大量池同时存在时的调度浪费
---
## 13. 自动回收设计
推荐提供一组通用 helper / component
- `ReturnToPoolAfterSeconds`
- `ReturnToPoolOnParticleStopped`
- `ReturnToPoolOnAnimatorStateExit`
- `ReturnToPoolOnAudioFinished`
- `ReturnToPoolOnDistanceExceeded`
- `ReturnToPoolOnCollision`
目标:
- 业务不重复造轮子
- 自动回收行为可视化配置
- 减少“忘记 Release”导致的泄漏
---
## 14. 运行时统计设计
最优方案必须带统计闭环。
每个池建议统计:
- `acquireCount`
- `releaseCount`
- `hitCount`
- `missCount`
- `peakActive`
- `peakTotal`
- `expandCount`
- `exhaustedCount`
- `autoRecycleCount`
- `destroyCount`
- `avgLifetime`
- `avgIdleTime`
- `avgAcquireCostMs`
- `avgReleaseCostMs`
调试用途:
- Inspector 面板
- 运行时快照
- CSV / JSON 导出
- 自动生成调优建议
---
## 15. 推荐默认配置区间
以下是默认建议值,不是硬编码结论,应由运行时统计修正。
### 15.1 通用默认
- `minRetained = 2 ~ 8`
- `softCapacity = 8 ~ 32`
- `hardCapacity = softCapacity * 1.5 ~ 2`
- `idleTrimDelay = 15s ~ 60s`
- `prefabUnloadDelay = 60s ~ 300s`
- `trimBatchPerTick = 1 ~ 4`
### 15.2 子弹默认
- `minRetained = 16`
- `softCapacity = 64`
- `hardCapacity = 128`
- `overflowPolicy = AutoExpand`
- `prefabUnloadDelay = 300s`
### 15.3 普通特效默认
- `minRetained = 4`
- `softCapacity = 16`
- `hardCapacity = 32`
- `overflowPolicy = DropNewestRequest`
- `idleTrimDelay = 20s`
### 15.4 怪物默认
- `minRetained = 2`
- `softCapacity = 8`
- `hardCapacity = 16`
- `overflowPolicy = AutoExpand`
- `activationMode = SleepWake`
---
## 16. 与当前实现的映射关系
当前模块中可保留的部分:
- `GameObjectPoolManager`
继续作为总入口和调度器雏形
- `RuntimePrefabPool`
继续作为单 prefab 池雏形
- `PoolConfig`
升级为新配置模型的一部分
- `IResourceLoader`
可继续作为资源加载抽象
需要重点重构的部分:
- 实例生命周期协议
- 池满策略
- 预热与清理预算调度
- 统计系统
- 自动回收组件
- 怪物/特效/子弹的分类默认策略
---
## 17. 迁移路线
建议分四个阶段落地。
### 阶段 1补生命周期协议
目标:
- 引入 `IGameObjectPoolable`
- Acquire / Release 时统一回调
- 允许对象自定义重置
### 阶段 2补配置模型
目标:
- 引入 `PoolProfile + PoolRule`
- 加入 `minRetained / softCapacity / hardCapacity / overflowPolicy`
- 兼容旧 `PoolConfig`
### 阶段 3补自动回收与预算调度
目标:
- 自动回收 helper
- 分帧 warmup
- 预算式 trim
- 低内存模式
### 阶段 4补统计与业务门面
目标:
- 运行时统计面板
- 容量推荐导出
- `EnemyPool / BulletPool / FxPool` 业务门面
---
## 18. 最终结论
最优对象池方案不是单一 `GameObject` 缓存容器,而是:
- 以 prefab 池为底层
- 以生命周期协议为核心
- 以分类策略为手段
- 以预算调度为性能保障
- 以统计闭环为调优依据
如果后续按本文档实施,建议优先级如下:
1. `IGameObjectPoolable``PoolSpawnContext`
2. `PoolProfile + PoolRule`
3. `PoolOverflowPolicy`
4. 自动回收 helper
5. 分帧预热与预算清理
6. 运行时统计与调优导出