Why are my projectiles spawning in batches?

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.

CSharp

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

CSharp

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*.activeInHierarchy)*

{
return pooledObjects*;*
}
}

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

return null;
}

}
]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 pooledObjects;

void Start()
{
pooledObjects = new List();

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*.activeInHierarchy)*
{
return pooledObjects*;*
}
}

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

return null;
}

_*}


*_</em></em>
<em><em>_*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.*_</em></em>

The problem is on the

void Update() {
 nextFire = Time.time + nextFire;     //1st problem
  }
  void FixedUpdate()
  {
      if ((Input.GetAxis("PrimaryAttack") > 0) && Time.time < nextFire)     //2nd
      {
 
          Fire();
 
      }

the way you have it Update() is called before fixedUpdate so nextFire is always >Time.time is always true.

float fireRate;
void Start(){ 
nextFire = Time.time;
}
void FixedUpdate()
      {
          if ((Input.GetAxis("PrimaryAttack") > 0) && Time.time > nextFire)     
          {
              Fire();
nextFire = Time.time + fireRate;
              }

Set fireRate to some number and remove the Update(), it should work. On GetPooledObject() it seems you have the willGrow always set on true so it will always instatiate more shots when all current shots are active. Now if willGrow= false then Fire() should throw some errors regarding that the obj you pull with the GetPooledObject might be null so you should add after the line you set the obj that

if(obj== null)
return;

Hope that helps.