14 KiB
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
- 隐藏字符串路径、组名、预热细节
- 承载特定对象的默认策略
示例:
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 核心接口
public interface IGameObjectPoolable
{
void OnPoolCreate();
void OnPoolGet(in PoolSpawnContext context);
void OnPoolRelease();
void OnPoolDestroy();
}
语义建议:
OnPoolCreate仅在实例第一次创建后调用一次OnPoolGet每次借出时调用,用于注入上下文、重置运行态OnPoolRelease每次归还时调用,用于停止逻辑、清理状态OnPoolDestroy实例真正销毁前调用,用于释放自持资源
5.2 可选扩展接口
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 需要统一上下文,避免靠外部脚本到处手动赋值。
建议结构:
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. 池实例状态机
每个实例建议具备明确状态:
UninitializedInactiveActiveReleasingDestroying
状态转换:
Create -> Inactive -> Active -> Releasing -> Inactive
Create -> Inactive -> Destroying -> Destroy
Active -> Destroying -> Destroy
价值:
- 避免重复回收
- 避免回收中再次借出
- 便于统计与调试
- 便于做自动回收与异步保护
7. 配置模型设计
当前 PoolConfig 维度不够,建议升级为两层配置:
PoolProfilePoolRule
7.1 PoolProfile
定义一类对象的默认策略,例如:
BulletProfileFxProfileEnemyProfileUiWorldProfile
示例字段:
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
定义具体资源命中规则。
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 对象类型
public enum PoolObjectKind
{
Default = 0,
Bullet = 1,
Effect = 2,
Enemy = 3,
Npc = 4,
Pickup = 5,
WorldUi = 6,
AudioProxy = 7,
SceneProp = 8
}
8.2 池满策略
public enum PoolOverflowPolicy
{
FailFast = 0,
InstantiateOneShot = 1,
AutoExpand = 2,
RecycleOldestInactive = 3,
DropNewestRequest = 4
}
8.3 清理策略
public enum PoolTrimPolicy
{
None = 0,
IdleOnly = 1,
IdleAndPriority = 2,
AggressiveOnLowMemory = 3
}
8.4 激活模式
public enum PoolActivationMode
{
SetActive = 0,
SleepWake = 1,
Custom = 2
}
8.5 重置模式
public enum PoolResetMode
{
TransformOnly = 0,
PoolableCallbacks = 1,
FullReset = 2,
Custom = 3
}
8.6 预热阶段
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 预热阶段
预热必须是分阶段的,而不是只靠初始化时一次性完成。
建议阶段:
AppInitializeSceneLoadBattlePrepareWavePrepareOnDemand
10.2 预热预算
预热不允许无限制同步创建。
建议加入:
- 每帧最大创建数
warmupBatchPerFrame - 每帧时间预算
warmupFrameBudgetMs - 大对象延迟到非关键帧创建
10.3 自适应预热
建议收集以下统计并用于反哺配置:
peakActivepeakTotalmissCountexpandCountpoolExhaustedCount
由此生成:
- 推荐
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:
ReturnToPoolAfterSecondsReturnToPoolOnParticleStoppedReturnToPoolOnAnimatorStateExitReturnToPoolOnAudioFinishedReturnToPoolOnDistanceExceededReturnToPoolOnCollision
目标:
- 业务不重复造轮子
- 自动回收行为可视化配置
- 减少“忘记 Release”导致的泄漏
14. 运行时统计设计
最优方案必须带统计闭环。
每个池建议统计:
acquireCountreleaseCounthitCountmissCountpeakActivepeakTotalexpandCountexhaustedCountautoRecycleCountdestroyCountavgLifetimeavgIdleTimeavgAcquireCostMsavgReleaseCostMs
调试用途:
- Inspector 面板
- 运行时快照
- CSV / JSON 导出
- 自动生成调优建议
15. 推荐默认配置区间
以下是默认建议值,不是硬编码结论,应由运行时统计修正。
15.1 通用默认
minRetained = 2 ~ 8softCapacity = 8 ~ 32hardCapacity = softCapacity * 1.5 ~ 2idleTrimDelay = 15s ~ 60sprefabUnloadDelay = 60s ~ 300strimBatchPerTick = 1 ~ 4
15.2 子弹默认
minRetained = 16softCapacity = 64hardCapacity = 128overflowPolicy = AutoExpandprefabUnloadDelay = 300s
15.3 普通特效默认
minRetained = 4softCapacity = 16hardCapacity = 32overflowPolicy = DropNewestRequestidleTrimDelay = 20s
15.4 怪物默认
minRetained = 2softCapacity = 8hardCapacity = 16overflowPolicy = AutoExpandactivationMode = 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 池为底层
- 以生命周期协议为核心
- 以分类策略为手段
- 以预算调度为性能保障
- 以统计闭环为调优依据
如果后续按本文档实施,建议优先级如下:
IGameObjectPoolable与PoolSpawnContextPoolProfile + PoolRulePoolOverflowPolicy- 自动回收 helper
- 分帧预热与预算清理
- 运行时统计与调优导出