com.alicizax.unity.framework/Runtime/ABase/GameObjectPool/fe.md
2026-04-17 21:01:20 +08:00

723 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. 运行时统计与调优导出