Undesired behavior when firing projectiles from an object pooler

I’ve been working on this project for a while now trying to learn my way through Unity but I’ve kind of hit a wall with my most recent issue.

My game is a 3D top-down space shooter. I have an object pooler that is holding the ship’s projectile and scripts that activate/deactivate, retrieve them, and apply force to them when pulling the trigger of a controller.

What I can’t get to happen is for the game to let me cycle through my whole pool. I want to be able to hold the trigger down and have it fire bolts until the pool drains or. What it keeps doing is releasing 3-8 bolts per trigger pull every time. My pool never gets exhausted and I never get nice, evenly spaced bolts. I’ve tried a few different rate of fire techniques from the internet and a few of my own without any success. At best I can get it to evenly space 5-6 bolts per trigger hold, but inevitably it stops firing bolts well before the pool is empty. It’s also never a consistent amount of bolts before it stops shooting and some times there is a long pause (of a length I know I didn’t set any where) before I can shoot again.

I’ll attach my code and a video of what it’s doing below.

The script controlling the player (handles ship movement and weapon firing), the commented out code are different methods I’ve tried, not necessarily relevant to this issue.

public class PlayerController : MonoBehaviour {

    public float agility;
    public float speed;
    public float tilt;
    public float fireRate;
    public float projectileForce;   
    public Rigidbody rb;

    private GameObject BS;

    private float nextFire = 0;
    GameObject tempObj;
    void Start()
    {
       BS = GameObject.FindGameObjectWithTag("weapon");
       rb = GetComponent<Rigidbody>();
    }

    void Fire()
    {
        Debug.Log("fire called");
        GameObject obj = BS.GetComponent<ObjectPoolerWeapon>().GetPooledObject();
        Rigidbody rb;
        rb = obj.GetComponent<Rigidbody>();


        obj.transform.position = BS.transform.position;
        obj.transform.rotation = BS.transform.rotation;
        obj.SetActive(true);
        rb.AddForce(transform.forward * projectileForce);
    }

   void Update()
    {
        nextFire = Time.time + nextFire;
       
    }
    void FixedUpdate()
    {
        if ((Input.GetAxis("PrimaryAttack") > 0) && Time.time < nextFire)
        {

            Fire();

        }

        float moveHorizontal = Input.GetAxis("HorizontalJ");
        float moveVertical = Input.GetAxis("VerticalJ");
        float y = Input.GetAxis("RightH");
        float nextShot;
      
       // Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        rb.transform.Rotate(0, (y * agility), 0);
        if (moveHorizontal  > 0)
        {
            rb.AddForce(transform.right * speed);
        }
        if (moveHorizontal < 0)
        {
            rb.AddForce((transform.right * -1) * speed);
        }
        if (moveVertical >0)
        {
            rb.AddForce((transform.forward * speed) * moveVertical);
        }
        if (moveVertical < 0)
        {
            float reverseSpeed;
            reverseSpeed = ((speed) * -1);
            rb.AddForce(transform.forward * reverseSpeed);
        }
      //  rb.MovePosition(transform.position + transform.forward * (moveVertical * speed));
        rb.position = new Vector3
            (
                   GetComponent<Rigidbody>().position.x,
                   10.0f,
                   GetComponent<Rigidbody>().position.z
            );
        // GetComponent<Rigidbody>().rotation = Quaternion.Euler(0.0f, 0.0f, GetComponent<Rigidbody>().velocity.x * -tilt);

       
    }

   
}

The object pooler I’m using

public class ObjectPoolerWeapon : MonoBehaviour
{

    public GameObject pooledObject;
    public int pooledAmount = 20;
    public bool willGrow = true;
    public GameObject weapon; //represents the object that this script is attached to

    private GameObject obj;
    private float nextFire;

    List<GameObject> pooledObjects;


    void Start()
    {
        pooledObjects = new List<GameObject>();

        for (int i = 0; i < pooledAmount; i++)
        {
            GameObject obj = (GameObject)Instantiate(pooledObject);
            obj.SetActive(false);
            pooledObjects.Add(obj);
        }
    }

    public GameObject GetPooledObject()
    {
        for (int i = 0; i < pooledObjects.Count; i++)
        {
            if (!pooledObjects[i].activeInHierarchy)
            {
                return pooledObjects[i];
            }
        }

        if (willGrow)
        {
            GameObject obj = (GameObject)Instantiate(pooledObject);
            pooledObjects.Add(obj);
            return obj;
        }

        return null;
    }

}

And finally on a side note besides this issue, any advice on how I can get the bolts to travel at the same speed/direction as the ship and then ADD more force so they move ahead of the moving ship? I spent a good 6 hours trying things this past weekend and got no where. The end of the video shows the exact behavior I’d like to avoid.

Try this:

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 = BS.GetComponent<ObjectPoolerWeapon>().GetPooledObject();
        Rigidbody rb;
        rb = obj.GetComponent<Rigidbody>();


        obj.transform.position = BS.transform.position;
        obj.transform.rotation = BS.transform.rotation;
        obj.SetActive(true);
        rb.AddForce(transform.forward * projectileForce);
    }

   void Update()
    {
        nextFire += Time.deltaTime;  // Use deltaTime here
        // Dont put this in fixedupdate, it isn't needed.  Also FixedUpdate is called a bunch/ton/MANY more times than update.  Not needed and should be for physics only
        if ((Input.GetAxis("PrimaryAttack") > 0) && nextFire >= firerate)
        {
            nextFire = 0.0f;
            Fire();
        }
    }

Thank you juicyz, that helped the bolt spacing and I appreciate the tips.

It still isn’t consistently firing for me though. If I hold the trigger it will release 3-8 bolts (properly activating and deactivating them) out of 20 depending on the firerate then pause and maybe fire another bolt before pausing again. Sometimes I need to pull my finger off the trigger for a second or two to kind of reset it to get it to fire again.

Any thoughts?

Ummm check your inspector and make sure pooledamount is 20.
During the trigger holding part when it only releases 3-8 bolts, do all 20 instantiated bolts get set to active or is there still some not active? (Answer should be that there are some not active but making 100% sure)

The pooled amount is 20, and only the 3-8 bolts out of the 20 activate, the rest stay deactivated. The activated ones even deactivate as they should and are then reused as the 3-8 that get activated.

This kind of sounds like another script is doing some computational heavy stuff maybe and taking some time up. I would run the profiler and during the time that it isn’t shooting I would check what is taking up the main threads time. That’s my best guess at the moment.

You could also do print out statements for nextFire and Input.GetAxis(“PrimaryAttack”) to see if somehow they aren’t what is expected.

PS. Your first video shows that at least 12 are getting shot, I’m guessing that it was all 20. Can you maybe make a new video if that isn’t too much trouble of the shooting now? Be sure to show as many of the object pool bullets in the inspector as possible.

The only thing that seemed to be using a lot of CPU was the camera. Honestly there isn’t much going on in the game right now as I’m trying to solve the basic issues like this before moving on.

Here is another video, from the start until 0:30 I never took my finger off the trigger. The subsequent bursts of fire are after I increased the fire rate to something unreasonably high.

Hmmm honestly… I can’t tell you much but I’m interested in something. Now that I think about. Typically GetAxis isn’t used that often for input like this, it’s usually for movement.

Input.GetAxis("PrimaryAttack") > 0

Try any of the following instead and see if there is any difference:

Input.GetAxisRaw("PrimaryAttack") > 0
Input.GetButton("PrimaryAttack") // set this up however
Input.GetMouseButton(0) // left click, but set this up however

See if those do anything different… There might be some smoothing thing going on with GetAxis…

Thanks again for the insight juicyz.

I tried your suggestions without any change but they did help push me in a different direction input wise.

I rewrote the class to clean it up and remove some junk and opted for a while loop which I had previously avoided due them crashing unity. Now the input is handled perfectly, nice and consistent but the ship stops shooting short of it’s object pool capacity (number of shots it actually fires is dependent on the firerate variable). The weird part about it though is that after timestamping the console output as far as the game is concerned it doesn’t miss a beat. There are no time gaps at all in the method calls.

Here is my updated player control class:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerControl : MonoBehaviour
{

    public float speed;
    public float rotSpeed;
    public float maxSpeed;
    public float projectileForce;
    public float fireRate;

    private GameObject projectileSpawner;
    private ObjectPoolerWeapon pooler;
    private Rigidbody rb;
    private Transform tr;
    private float nextFire = 0;


    void Update()
    {
        projectileSpawner = GameObject.FindGameObjectWithTag("Enemy");
        pooler = projectileSpawner.GetComponent<ObjectPoolerWeapon>();
        float y = Input.GetAxis("RightH");

        rb = GetComponent<Rigidbody>();
        tr = GetComponent<Transform>();

        transform.Rotate(0, (y * rotSpeed), 0);
  
      
        if ((Input.GetAxis("VerticalJ") > 0))
        {
            rb.AddForce((transform.forward.normalized * speed) * (Input.GetAxis("VerticalJ")));
           // this.rb.AddForce(direction.forward * speed);
        }

  
        /* 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))
        {

            if (nextFire >= fireRate)
            {
                nextFire = 0.0f;
                Fire();
                Debug.Log("triggered");
                break;
            }
            break;
         
        }





    }

    void Fire()
    {
        Debug.Log("fired");
        GameObject obj = pooler.GetPooledObject();
        Rigidbody rbb = obj.GetComponent<Rigidbody>();
        obj.transform.position = projectileSpawner.transform.position;
        obj.transform.rotation = projectileSpawner.transform.rotation;
        obj.SetActive(true);
        rbb.AddForce(transform.forward * 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);

        }
    }
}

Alright so I think I figured out what is happening but I’m still at a loss as to how to fix it.

I spent some time firing the bolts while playing with the length of time they stay active. It seems that the object pool will continue to dispense objects until the first one gets deactivated. After the first is deactivated I have to wait for the res of the objects to be deactivated before I can pull from the pool again.

Anyone have any suggestions on how to fix that? I’d like to be able to pull objects back out of the pool as soon as they are done being used and deactivated. Ideally I’d like to be able to set a projectile life and fire rate/force that will allow a continuous stream of fire without ever needing more than the set number of pooled objects.