I have an object pool system that instantiates a certain amount of deactivated bullet prefabs at start and cycles through them as they’re fired. Basic stuff.
The bullets are acting weird, though. The first bullet will fire just fine, hit a collider and disable or disable itself after 3 secs according to a coroutine. A bullet fired after this though will pass right through a collider and ignore the coroutine and continue to travel through the scene. It was also remain activated in the hierarchy. The next bullet will behave normally, and then the bullet after that will have the weird behavior. Basically, every other bullet isn’t working right. Sometimes the weird bullet will activate in the hierarchy but it won’t appear in the scene. This behavior has been consistent every time I tested it. But this DOESN’T appear to happen when I instantiate the bullets instead of using the object pool. Instantiated bullets act normally. Anyone know what could be causing this?
Here is my object pooler:
public GameObject[] objects;
public int[] number;
public List<GameObject>[] pool;
void Start () {
instantiate();
}
void instantiate(){
GameObject temp;
pool = new List<GameObject>[objects.Length];
for(int count = 0; count < objects.Length; count++){
pool[count] = new List<GameObject>();
for(int num = 0; num < number[count]; num++){
temp = (GameObject)Instantiate(objects[count]);
temp.transform.parent = this.transform;
pool[count].Add(temp);
}
}
}
public GameObject activate(int id){
for(int count = 0; count < pool[id].Count; count++){
if(!pool[id][count].activeSelf){
pool[id][count].SetActive(true);
return pool[id][count];
}
}
pool[id].Add((GameObject)(Instantiate(objects[id])));
pool[id][pool[id].Count - 1].transform.parent = this.transform;
return pool[id][pool[id].Count - 1];
}
And here is the shoot script:
public ObjectPooler pool;
private Transform myTransform;
private Transform cameraHeadTransform;
private Vector3 launchPosition = new Vector3();
public float fireRate = 0.2f;
public float nextFire = 0;
// Use this for initialization
void Start () {
myTransform = transform;
cameraHeadTransform = myTransform.FindChild("BulletSpawn");
}
void Update () {
if(Input.GetButton("Fire1") && Time.time > nextFire){
nextFire = Time.time + fireRate;
launchPosition = cameraHeadTransform.TransformPoint(0, 0, 0.2f);
GameObject current = pool.activate(0);
current.transform.position = launchPosition;
current.transform.rotation = Quaternion.Euler(cameraHeadTransform.eulerAngles.x + 90, myTransform.eulerAngles.y, 0);
}
}
1st bullet is a never used bullet. Its created with the original none used state. You modify the state to make it a projectile. It hits something and dies and is returned to pool.
2nd bullet is now fired, it grabs the first available bullet, that’s the one that recently died. Its state is not the original state, its state is the state of being a projectile, just disabled. You enable it again… modify the state… and the alteration stacks (maybe you ADD velocity instead of SET velocity). It is launched, bugs out, is never returned to pool.
3rd bullet is now fired, it grabs the first available bullet, this bullet is unused. We’re like step 1.
4th bullet is now fired, it grabs the first available bullet, this bullet was the one used in 3. We’re like step 2 now.
5th bullet is now fired, it grabs the first available bullet, this bullet is unused because the 4th bullet got stuck like the 2nd bullet. We’re like step 1 and 3.
… repeat, every other bullet being an unused, the others being a recycled bullet.
I created my own system as well a while back, and noticed a similar problem (wasn’t bullets as I didn’t use it for that, but still similar problem). And what I did was had a ‘despawn’ event/message that notified all the code that makes up the gameobject spawned (this means you need to support sending this message to ALL gameobjects inside the gameobject you’re spawning). This way those scripts can revert themselves back to original state, and their settings don’t stack.
The first line is supposed to instantiate a new object if there are no objects in the pool available to recycle. The second line is supposed to get the last object added to the list and set it as the parent of the empty game object Pool so any spare bullets instantiated are added as children to the Pool object with the rest of the bullets instead of cluttering up the hierarchy.
When my projectiles were pooled, I had to be careful to do things like reset velocity of the rigidbody, reset rotations, etc, to make sure they didnt get recycled with some extra force/ unwanted properties.
I would consider re-doing my pooling to follow a more per-formant style.
Rather than having all projectiles within an array and checking if they are active, a better approach is to only put a projectile into the pool when its ready for being re-used.
in psudo code
Gun requests bullet from pool
If pool has one available, return instance and remove from list, if not return new instance.
projectile is returned, added into a list of available bullets
This way it doesnt have to iterate over the list of projectiles checking each projectile everytime you want one, it simply looks at the list, sees there is one available, returns it, and removes it from the list of ‘available’ bullets.
It also has the benefit of not being limited to a maximum bullet count… if you need a bullet and the list is full, what do you do? What you will find is using the method outlined above is it will create as many bullets as you need… it will get to a point where you dont need to instantiate any more bullets, as the recycle rate is returning them before they all get used.
One other thing, make sure you put a Invoke(“ReturnToPool”, BulletLifeTime), so that bullets fired off into the sunset still get returned if they dont hit something for a while.
A caveat with that, is make sure you also CancelInvoke the function when you return it (just in case the bullet hits its target as expected and is returned)