Issues with my Object Pooler
Hello,
In my game I'm using an object pooler to pool projectile for the player's space ship. When the player pulls the trigger a script pulls an object from the pooler, positions it, and applies force to it. A lifetime variable is also attached to the projectile itself to deactivate it after a set amount of time.
The functionality of the whole setup is pretty much exactly where I want it but I'm having an issue with the way the pooler itself is functioning.
Basically, the problem is that if the player is holding down the fire trigger the projectiles will be plucked, positioned, and activated properly from the pooler but regardless of how many 'ready to go' objects are available in the pool as soon as the first active projectile is set to deactivated the pooler will stop releasing more projectiles until all of the currently active ones are set to deactive.
Here is the code for the player controller that handles the firing:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
public float speed;
public float rotSpeed;
public float maxSpeed;
public GameObject engine;
public GameObject Shields;
public GameObject repairBots;
public Weapon weapon;
public GameObject booesters;
public Reactor reactor;
public Transform projectileSpawner;
private float availablePower;
private GameObject projectileSpawn;
private ObjectPoolerWeapon pooler;
private Rigidbody rb;
private Transform tr;
private float nextFire = 0;
private float refireRate;
private Vector3 projectileSpeed;
private GameObject bolt;
private GameObject obj;
private Rigidbody rbb;
void Start()
{
}
void Update()
{
float y = Input.GetAxis("RightH");
rb = GetComponent<Rigidbody>();
tr = GetComponent<Transform>();
transform.Rotate(0, (y * rotSpeed), 0);
if (Input.GetAxis("HorizontalJ") > 0)
{
rb.AddForce(transform.right * speed);
}
if (Input.GetAxis("HorizontalJ") < 0)
{
rb.AddForce((transform.right * -1) * speed);
}
if ((Input.GetAxis("VerticalJ") > 0))
{
rb.AddForce((transform.forward * speed) * (Input.GetAxis("VerticalJ")));
projectileSpeed = (transform.forward * speed);
}
if ((Input.GetAxis("VerticalJ") < 0))
{
float reverseSpeed;
reverseSpeed = ((speed) * -1);
rb.AddForce(transform.forward * reverseSpeed);
}
/* tr.Translate(
new Vector3(
((Input.GetAxis("HorizontalJ") / (speed / 2) )),
0f,
((Input.GetAxis("VerticalJ") / (speed * 3)))
));
rb.transform.Rotate(0, (y * rotSpeed), 0); */
nextFire += Time.deltaTime;
while ((Input.GetAxisRaw("PrimaryAttack") != 0))
{
Debug.Log("triggered");
weapon.RefireRate();
Debug.Log(reactor.GetAvailablePower());
if (nextFire >= weapon.RefireRate()) /*&& (reactor.GetAvailablePower() > weapon.EnergyCost())*/
{
Debug.Log("should fire");
nextFire = 0.0f;
Fire();
// reactor.ConsumePower(weapon.EnergyCost());
// Debug.Log("5 nrg consumed");
break;
}
break;
}
}
void Fire()
{
Debug.Log("fired");
obj = weapon.GetPooledObject();
rbb = obj.GetComponent<Rigidbody>();
obj.transform.position = projectileSpawner.transform.position;
obj.transform.rotation = projectileSpawner.transform.rotation;
obj.SetActive(true);
rbb.AddForce(transform.forward * weapon.ProjectileForce());
}
//void Fire()
// {
// Debug.Log("fire called");
// // You Should make a variable for the ObjectPoolerWeapon. GetComponent is decently slow and since you are calling this alot, you should in your Start() do the BS.GetComponent<ObjectPoolerWeapon>() call.
// GameObject obj = weapon.GetPooledObject();
// Rigidbody rb;
// rb = obj.GetComponent<Rigidbody>();
// obj.transform.position = projectileSpawn.position;
// obj.transform.rotation = projectileSpawn.rotation;
// obj.SetActive(true);
// rb.AddForce((transform.forward * weapon.ProjectileForce()));
// }
void FixedUpdate()
{
rb = GetComponent<Rigidbody>();
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = rb.velocity.normalized * maxSpeed;
}
}
public class Debug
{
public static void Log(object obj)
{
UnityEngine.Debug.Log(System.DateTime.Now.ToLongTimeString() + " : " + obj);
}
}
}
The code for the object pooler:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour {
public float damage;
public float refireRate;
public float energyCost;
public float projectileLife;
public float projectileForce;
public GameObject projectile;
public int pooledAmount = 20;
public bool willGrow = true;
public GameObject projectileSpawn; //represents the object that this script is attached to
private GameObject obj;
List<GameObject> pooledObjects;
void Start()
{
pooledObjects = new List<GameObject>();
for (int i = 0; i < pooledAmount; i++)
{
GameObject obj = (GameObject)Instantiate(projectile);
obj.SendMessage("SetDamage", damage);
obj.SendMessage("SetLife", projectileLife);
obj.SetActive(false);
pooledObjects.Add(obj);
}
}
public float GetDamage()
{
return damage;
}
public float RefireRate()
{
return refireRate;
}
public float EnergyCost()
{
return energyCost;
}
public float ProjectileForce()
{
return projectileForce;
}
//public void Fire()
//{
// Debug.Log("fired");
// GameObject obj = GetPooledObject();
// Debug.Log("pooled bolt collected");
// Rigidbody rbb = obj.GetComponent<Rigidbody>();
// Debug.Log("bolts RB assigned");
// obj.transform.position = projectileSpawn.transform.position;
// Debug.Log("bolt positioned");
// obj.transform.rotation = projectileSpawn.transform.rotation;
// Debug.Log("bolt rotated");
// obj.SetActive(true);
// Debug.Log("bolt activated");
// rbb.AddForce(transform.forward * projectileForce);
// Debug.Log("force applied to bolt");
//}
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObjects.Count; i++)
{
if (!pooledObjects[i].activeInHierarchy)
{
Debug.Log("Object dispensed");
return pooledObjects[i];
}
}
Debug.Log("bolt dispensed");
if (willGrow)
{
GameObject obj = (GameObject)Instantiate(projectile);
obj.SendMessage("SetDamage", damage);
obj.SendMessage("SetLife", projectileLife);
pooledObjects.Add(obj);
return obj;
}
return null;
}
}
And my projectile code, just to be thorough:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProjectileMover : MonoBehaviour
{
private float life;
public float damage;
private GameObject BS;
private Vector3 shipSpeed;
void Update()
{
}
void Start()
{
//GetComponent<Rigidbody>().velocity = transform.forward * speed;
BS = GameObject.FindGameObjectWithTag("Enemy");
}
void OnTriggerEnter(Collider target)
{
target.SendMessage("UpdateHitPoints", damage);
CancelInvoke();
gameObject.SetActive(false);
}
void TurnOff()
{
gameObject.SetActive(false);
gameObject.GetComponent<Rigidbody>().velocity = Vector3.zero;
//gameObject.transform.position = BS.transform.position;
// gameObject.transform.rotation = BS.transform.rotation;
}
void SetDamage(float dmg)
{
damage = dmg;
}
void SetLife(float lifeInput)
{
life = lifeInput;
}
void FixedUpdate()
{
if (gameObject.activeInHierarchy)
{
Invoke("TurnOff", life);
}
}
}
Thanks in advance!
Answer by mh_vitor · Mar 13, 2017 at 06:56 AM
Hello @Julac,
I've analyzed your classes and I can't precisely point where the problem is, though I have observed some parts that seemed odd to me. Since I can't currently run the code, this is just a small contribution to your troubleshooting, and an attempt to gather more information for someone else that runs by your question. I'd rather post this as a comment, but apparently I cannot.
The Object Pooler itself appears to be correct based on the Unity tutorial video about the matter, so the problem could be somewhere else.
You mention there is a "lifetime variable" for the projectile, though it doesn't appear in the ProjectileMover class, so it may be in another class or somewhere else. It's odd, though, that there is an if statement "if (gameObject.activeInHierarchy)" in that class that is called every frame (because it is inside FixedUpdate) and it seems to deactivate the projectile every frame. That's a strange behavior for a projectile.
There is a "while" inside the Update method in the PlayerControl class. If the game can't get out of the while, that frame is never ended and the game freezes. Removing the while, in theory, wouldn't solve the object pooling problem, but testing to see what happens could be worth a shot.
I hope this helps you finding the source of the problem.