I have made this ObjectPooler, but sometimes when i call the instanciateFromPool function to spawn bullets, some of them are missing, and when i log the position of the bullet in OnEnable, it shows a different position in the log than in the transform component implying that the bullet was taken from the already instanciated ones that are active. the longer the game runs, the worse this problem gets, and i have no idea what causes it.
i don’t use any multithreading in my game, and bullets are only spawned from coruntines or update loops.
using System;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
public static Dictionary<string, Queue<GameObject>> poolDictionary;
public static Dictionary<string, List<GameObject>> instanciatedDictionary;
[RuntimeInitializeOnLoadMethod]
public static void RunOnStart()
{
instanciatedDictionary = new Dictionary<string, List<GameObject>>();
poolDictionary = new Dictionary<string, Queue<GameObject>>();
}
public static GameObject InstanciateFromPool(string tag_, GameObject wantedInstance, Vector3 position, Quaternion rotation)
{
if (poolDictionary.ContainsKey(tag_))
{
if (poolDictionary[tag_].Count > 0)
{
//take wanted object from list
GameObject instance = poolDictionary[tag_].Dequeue();
//set position, rotation, activate
instance.transform.position = position;
instance.transform.rotation = rotation;
instance.SetActive(true);
if (!instanciatedDictionary.ContainsKey(instance.tag))
{
//add to instanciated objects
instanciatedDictionary.Add(instance.tag, new List<GameObject>());
}
//add to instanciated list
instanciatedDictionary[instance.tag].Add(instance);
//Return
return instance;
}
else
{
//instanciate
GameObject instance = Instantiate(wantedInstance, position, rotation);
if (!instanciatedDictionary.ContainsKey(instance.tag))
{
//add to instanciated objects
instanciatedDictionary.Add(instance.tag, new List<GameObject>());
}
//add to instanciated objects
instanciatedDictionary[instance.tag].Add(instance);
return instance;
}
}
else
{
//create needed list
poolDictionary.Add(tag_, new Queue<GameObject>());
//spawn wanted object
GameObject instance = Instantiate(wantedInstance, position, rotation);
if (!instanciatedDictionary.ContainsKey(instance.tag))
{
//add to instanciated objects
instanciatedDictionary.Add(instance.tag, new List<GameObject>());
}
instanciatedDictionary[instance.tag].Add(instance);
return instance;
}
}
public static void DestroyToPool(string tag_, GameObject gameObject)
{
if (poolDictionary.ContainsKey(tag_) && instanciatedDictionary.ContainsKey(gameObject.tag))
{
//deactivate
gameObject.SetActive(false);
//remove from instanciated
instanciatedDictionary[gameObject.tag].Remove(gameObject);
//add to list to reuse later
poolDictionary[tag_].Enqueue(gameObject);
}
else
{
//create needed dictionarys
if (!poolDictionary.ContainsKey(tag_))
poolDictionary.Add(tag_, new Queue<GameObject>());
if (!instanciatedDictionary.ContainsKey(gameObject.tag))
instanciatedDictionary.Add(gameObject.tag, new List<GameObject>());
//set inactive to reuse later
gameObject.SetActive(false);
//add to list to reuse later
poolDictionary[tag_].Enqueue(gameObject);
//remove from instanciated
instanciatedDictionary[gameObject.tag].Remove(gameObject);
throw new Exception("tag not found, new que created");
}
}
}
In case you need to see how it is used, here is a basic tower that spawnes bullets (yes it is a PVZ bootleg)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GeneralStuff;
public class FlyFungusAI : MonoBehaviour
{
[Tooltip("Tag of the fired projectile")]
public string tag_;
[SerializeField] private RowFinder rowFinder;
[SerializeField] private float cooldown, shakeIntensity, shakeDuration;
[SerializeField] private GameObject projectile;
[SerializeField] private Transform firePoint;
[SerializeField] private Animator animator;
[SerializeField] private ParticleSystem shootEffect;
[SerializeField] private AudioClip sound;
private float cooldownTimer;
private int hashShoot;
private void Awake()
{
hashShoot = Animator.StringToHash("Shoot");
}
private void Update()
{
cooldownTimer += Time.deltaTime;
if (cooldownTimer > cooldown)
{
cooldownTimer = 0;
if (EnemyFinder.ghoulInRow[rowFinder.row])
{
GameObject instance = ObjectPooler.InstanciateFromPool(tag_, projectile, firePoint.position, Quaternion.identity);
StartCoroutine(ShootEffects());
}
}
}
private IEnumerator ShootEffects()
{
animator.SetBool(hashShoot, true);
shootEffect.Play();
CameraShake.Shake(shakeDuration, shakeIntensity);
SoundManager.PlaySound(sound);
yield return new WaitForSeconds(0.3f);
animator.SetBool(hashShoot, false);
}
}