16.1 为什么需要对象池 在游戏开发中,频繁创建和销毁对象会导致:
GC 压力 :大量内存分配触发垃圾回收,造成卡顿
性能开销 :Instantiate / Destroy 是开销较大的操作
对象池 通过预先创建一批对象实例,复用而不是销毁,显著提升性能。
适合对象池的场景
子弹、弹幕
粒子特效
敌人波次
掉落物
临时 UI 提示
16.2 基础对象池实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 using System.Collections.Generic;using UnityEngine;public class ObjectPool <T > where T : MonoBehaviour { private readonly T prefab; private readonly Queue<T> pool = new Queue<T>(); private readonly Transform parent; public ObjectPool (T prefab, int preloadCount, Transform parent = null ) { this .prefab = prefab; this .parent = parent; for (int i = 0 ; i < preloadCount; i++) { T obj = CreateNew(); obj.gameObject.SetActive(false ); pool.Enqueue(obj); } } private T CreateNew () { T obj = Object.Instantiate(prefab, parent); obj.name = $"{prefab.name} _Pooled" ; return obj; } public T Get () { if (pool.Count > 0 ) { T obj = pool.Dequeue(); obj.gameObject.SetActive(true ); return obj; } T newObj = CreateNew(); newObj.gameObject.SetActive(true ); return newObj; } public void Return (T obj ) { obj.gameObject.SetActive(false ); pool.Enqueue(obj); } }
16.3 实战:子弹池 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class Bullet : MonoBehaviour { [SerializeField ] private float speed = 20f ; [SerializeField ] private float lifetime = 3f ; private float timer; private ObjectPool<Bullet> pool; public void Initialize (ObjectPool<Bullet> owner ) { pool = owner; } void OnEnable () { timer = 0f ; } void Update () { transform.Translate(Vector3.forward * speed * Time.deltaTime); timer += Time.deltaTime; if (timer >= lifetime) { ReturnToPool(); } } void OnTriggerEnter (Collider other ) { if (other.CompareTag("Enemy" )) { other.GetComponent<Enemy>().TakeDamage(10 ); ReturnToPool(); } } public void ReturnToPool () { pool?.Return(this ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Gun : MonoBehaviour { [SerializeField ] private Bullet bulletPrefab; [SerializeField ] private Transform firePoint; [SerializeField ] private int preloadCount = 20 ; private ObjectPool<Bullet> bulletPool; void Start () { bulletPool = new ObjectPool<Bullet>(bulletPrefab, preloadCount); } void Update () { if (Input.GetButtonDown("Fire1" )) { Shoot(); } } void Shoot () { Bullet bullet = bulletPool.Get(); bullet.transform.SetPositionAndRotation(firePoint.position, firePoint.rotation); bullet.Initialize(bulletPool); } }
16.4 使用 Unity 内置池(需要 2021.1+) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 using UnityEngine.Pool;public class ModernGun : MonoBehaviour { [SerializeField ] private Bullet bulletPrefab; private ObjectPool<Bullet> pool; void Start () { pool = new ObjectPool<Bullet>( createFunc: () => Instantiate(bulletPrefab), actionOnGet: (bullet) => bullet.gameObject.SetActive(true ), actionOnRelease: (bullet) => bullet.gameObject.SetActive(false ), actionOnDestroy: (bullet) => Destroy(bullet.gameObject), collectionCheck: false , defaultCapacity: 20 , maxSize: 50 ); } void Shoot () { Bullet bullet = pool.Get(); bullet.transform.position = firePoint.position; bullet.transform.rotation = firePoint.rotation; } void Update () { if (Input.GetKeyDown(KeyCode.R)) { pool.Trim(); } } void OnDestroy () { pool.Clear(); pool.Dispose(); } } public class Bullet : MonoBehaviour { private IObjectPool<Bullet> pool; public void SetPool (IObjectPool<Bullet> pool ) { this .pool = pool; } void OnDisable () { pool?.Release(this ); } void OnTriggerEnter (Collider other ) { pool.Release(this ); } }
16.5 性能对比
操作
耗时
Instantiate(首次)
高(资源加载)
pool.Get(池中有对象)
极低(仅 SetActive)
pool.Get(池已满)
中(Instantiate 新对象)
Destroy(立即回收)
中(内存释放)
pool.Return(池复用)
极低(仅 SetActive)
实测:1000 颗子弹场景,对象池方案比即时创建销毁快 10-20 倍 ,且无 GC 卡顿。
16.6 本章小结
对象池通过复用对象减少 GC 和 Instantiate 开销
核心操作:Get(获取)和 Return(归还)
对象默认失活,取出时激活
Unity 2021+ 提供内置 ObjectPool<T>
适合子弹、粒子、敌人等高频创建/销毁场景
练习题
实现一个简单的子弹池,发射 100 发子弹观察 GC 情况
对比 Instantiate/Destroy 和对象池方案在连续生成 500 个对象时的帧率差异
为对象池添加动态扩容和缩容功能(闲置对象超过 N 分钟自动释放)
使用 Unity 内置 ObjectPool<T> 重构一个现有项目中的对象创建逻辑