Unity对象池问题:Get()函数持续实例化新对象求助
Unity对象池Get()持续实例化新对象问题排查
问题现象
使用Unity内置ObjectPool实现对象池时,调用Get()函数会持续实例化新对象,无报错,但对象池未复用已有对象。
相关代码
生成器类(OPool)
public class OPool : MonoBehaviour { public Bullet bulletPrefab; public UnityEngine.Pool.ObjectPool<Bullet> _bulletPool; public static OPool _OPool; public void Start() { _bulletPool = new UnityEngine.Pool.ObjectPool<Bullet>(createFunc: InstantiatePoolBullet, actionOnGet: GetBulletFromPool, actionOnRelease: ReturnBulletToPool,DestroyBullet, true, 15,20); } public void Update() { transform.position = transform.parent.position + new Vector3(0f, 3.5f, 2.5f); } public Bullet InstantiatePoolBullet() { Bullet bullet = Instantiate(bulletPrefab, transform); bullet.SetPool(_bulletPool); return bullet; } public void GetBulletFromPool(Bullet bullet) { bullet.transform.position = transform.position; bullet.transform.rotation = transform.rotation; bullet.gameObject.SetActive(true); } public void ReturnBulletToPool(Bullet bullet) { bullet.gameObject.SetActive(false); } public void DestroyBullet(Bullet bullet) { Destroy(bullet.gameObject); } }
子弹类(Bullet)
public class Bullet : MonoBehaviour { public float speed; private ObjectPool<Bullet> _pool; public float _deadtime = 0.7f; private float _currentTime; private Rigidbody rb; public int damage; public static Bullet bullet; private void Awake() { rb = GetComponent<Rigidbody>(); _currentTime = _deadtime; } private void Start() { bullet = this; ApplyVelocity(); } private void Update() { if (_currentTime > 0) { _currentTime -= Time.deltaTime; } else { DestroyBullet(); } } public void SetPool(ObjectPool<Bullet> pool) { _pool = pool; } private void OnEnable() { ApplyVelocity(); _currentTime = _deadtime; } private void ApplyVelocity() { rb.velocity = speed * transform.forward; } private void DestroyBullet() { if (_pool != null) { _pool.Release(this); } else { Destroy(gameObject); } } }
Player脚本调用Get()的代码
void Update() { if (transform.IsChildOf(RunPlayer.Instance.transform) && RunPlayer.Instance._state == NumState.Running) { _State = State.Free; } if (_State == State.Free) { canShoot = true; charAnim.SetBool(Starting, true); StartCoroutine(ShootR()); } } public enum State { Idle, Free } public IEnumerator ShootR() { while (canShoot) { if (downShootRate) { yield return new WaitForSeconds(.150f); } yield return new WaitForSeconds(.145f); bulletPool._bulletPool.Get(); } }
排查方向
- 协程重复启动问题:Player的Update方法中,每次
_State == State.Free都会调用StartCoroutine(ShootR()),导致同时运行多个射击协程,疯狂调用Get()。当调用频率远高于子弹回收速度时,对象池空闲对象被耗尽,只能持续实例化新对象。 - 子弹回收逻辑失效:
- 在
DestroyBullet中添加日志,确认是否执行了_pool.Release(this); - 检查
_pool是否为null,避免子弹被直接销毁而非放回池; - 打印
_currentTime值,验证倒计时是否正常触发回收。
- 在
- 对象池参数限制:当前对象池最大空闲对象数为20,如果同时存在的子弹数量超过20,回收的子弹会被销毁(空闲池已满),导致后续Get()只能创建新对象。
- 子弹运动异常:检查Rigidbody设置(如是否勾选Kinematic),若
rb.velocity不生效,虽不影响倒计时回收,但如果子弹被其他逻辑误销毁,也会导致池中空闲对象不足。
开发建议
- 修复协程重复启动:添加标志位控制协程仅启动一次:
private bool _isShooting; void Update() { // 原有状态判断逻辑 if (_State == State.Free && !_isShooting) { canShoot = true; charAnim.SetBool(Starting, true); _isShooting = true; StartCoroutine(ShootR()); } } public IEnumerator ShootR() { while (canShoot) { // 原有等待逻辑 bulletPool._bulletPool.Get(); } _isShooting = false; } - 移除静态Bullet实例:
public static Bullet bullet;会被所有子弹覆盖,可能引发未知错误,建议直接删除。 - 增加调试日志:在对象池的
InstantiatePoolBullet、ReturnBulletToPool、DestroyBullet中添加日志,跟踪对象创建、回收、销毁流程。 - 调整参数匹配需求:根据射击频率和子弹生命周期,调整对象池的
defaultCapacity和maxSize,或延长射击间隔,降低Get()调用频率。
内容的提问来源于stack exchange,提问作者user21596799
相关产品推荐
相关产品推荐

