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