Object Pooling
Object Pooling 复用实例,避免每次生成都调用 Instantiate/Destroy,从而在发射子弹、特效或敌群时消除 GC 尖峰与卡顿。这是 Unity 高频生成场景的经典优化模式,帮助移动端与主机项目稳住帧率,并与 Prefab、Addressables 协同管理对象的完整生命周期。
想象一下...
与其每次买新咖啡杯 → 用完 → 扔掉(Instantiate → 使用 → Destroy),餐厅用餐具池:备好 20 个杯子,取一个用,再放回架子。不多买、不乱扔——零 Garbage Collection。在 Unity 里,GC.Collect() 像高峰时段冲进厨房的清洁工——掉一帧。池化能阻止这件事。
概念详解
Instantiate/Destroy 的问题:每次
Instantiate()
都会分配新 GameObject、复制组件数据并注册到场景。
Destroy()
则把内存标给 GC。射击游戏每秒可生成 30–50 发子弹——频繁 GC,典型卡顿(每隔几秒“突然掉帧”)。
Object Pool 模式:加载时预生成固定数量。需要时从池中取出 (此前已停用)而不是新建。用完后禁用并归还。内存平稳,不触发 GC。
从 Unity 2021+ 起,
UnityEngine.Pool
提供
ObjectPool<T>、
LinkedPool<T>、
GenericPool<T>
——内置,无需手写队列。回调:onCreate、onGet、
onRelease、onDestroy(池满时)。
典型用途:子弹、命中/爆炸特效、飘字伤害、敌人/NPC 生成、脚步音源、UI toast、粒子实例。 每分钟生成不到 1 次的对象不必池化——管理开销不值得。
对象池如何工作
动手步骤
创建 IPoolable 接口
OnGet()(从池取出时重置状态)与 OnRelease()(归还前清理)。
创建 ObjectPool
new ObjectPool<T>(onCreate, onGet, onRelease, onDestroy, collectionCheck, defaultCapacity, maxSize)——2021+ 内置。
使用 pool.Get() / pool.Release()
用 pool.Get() 替代 Instantiate,用 pool.Release() 替代 Destroy。在 onGet 中重置位置/速度/状态。
加载时预热池
游戏开始时生成并立即归还 N 个对象,避免空池导致首帧尖峰。
监控 pool.CountAll / pool.CountActive
打日志看池是否够大。countActive/countAll 偏高 → 提高 defaultCapacity。
交互模拟器
对比 Instantiate/Destroy 与 Object Pool——按 Fire 观察 GC 差异。
Pool: 5/12
Allocations
0
GC Events
0
Active Objects
0
代码示例
基础用 Queue 实现简单子弹池——无需 Unity 包。
using UnityEngine;
using System.Collections.Generic;
public class BulletPool : MonoBehaviour
{
public GameObject bulletPrefab;
public int poolSize = 30;
readonly Queue<GameObject> pool = new Queue<GameObject>();
void Awake()
{
// Pre-warm: create and disable
for (int i = 0; i < poolSize; i++)
{
var go = Instantiate(bulletPrefab);
go.SetActive(false);
go.transform.SetParent(transform);
pool.Enqueue(go);
}
}
public GameObject Get(Vector3 pos, Quaternion rot)
{
var go = pool.Count > 0
? pool.Dequeue()
: Instantiate(bulletPrefab); // Expand if empty
go.transform.SetPositionAndRotation(pos, rot);
go.SetActive(true);
return go;
}
public void Release(GameObject go)
{
go.SetActive(false);
pool.Enqueue(go);
}
}代码示例
进阶UnityEngine.Pool.ObjectPool<T>(Unity 2021+)——内置自动扩容与回调。
using UnityEngine;
using UnityEngine.Pool;
public class AdvancedBulletPool : MonoBehaviour
{
public Bullet bulletPrefab;
ObjectPool<Bullet> pool;
void Awake()
{
pool = new ObjectPool<Bullet>(
createFunc: () => Instantiate(bulletPrefab, transform),
actionOnGet: b => { b.gameObject.SetActive(true); b.OnGet(); },
actionOnRelease: b => { b.gameObject.SetActive(false); b.OnRelease(); },
actionOnDestroy: b => Destroy(b.gameObject), // When the pool is full
collectionCheck: false, // Skip duplicate checks (production)
defaultCapacity: 20,
maxSize: 50
);
}
public Bullet Spawn(Vector3 pos, Vector3 dir)
{
var bullet = pool.Get();
bullet.transform.position = pos;
bullet.Init(dir, pool); // Bullet calls pool.Release(this) when done
return bullet;
}
public void LogStats()
{
Debug.Log($"Pool: {pool.CountActive} active / {pool.CountAll} total");
}
}📌 快速记忆
- ▸Pool = 复用对象,避免 GC 分配
- ▸加载时预热,避免首帧尖峰
- ▸UnityEngine.Pool.ObjectPool<T>——2021+ 内置
- ▸在 onGet 中重置状态,不要在 Update 里
- ▸collectionCheck=true 仅用于 Editor 调试
⚠️ 常见错误
❌ 对已在池中的对象再次 Release(双重归还)
归还两次 → 池损坏 → 闪烁或崩溃
✅ 开发时 collectionCheck=true;Release() 前做空值检查
❌ Get 时未重置状态
对象保留上次使用的速度、颜色、血量
✅ 在 actionOnGet 回调中完整重置