Need help coding an automatic rifle!

I’m trying to code an automatic assault rifle, but I’m having trouble. The rifle shoots much faster than my set fire rate, and I tried the code on an old thread on the forum, and that made it outright refuse to shoot. Can someone please help me with this?

Here’s the code:

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

public class AssaultRifle : MonoBehaviour
{
    public float assaultRifleDamage = 5f;
    float range = 50f;
    float fireRate = 0.5f;
    float reloadTime = 1.5f;
    float fadeTime = 0.1f;

    public int currentAmmo;
    public int maxAmmo = 20;

    [SerializeField] Camera cam;
    [SerializeField] GameObject hitmarker;

    void Start()
    {
        //currentAmmo = maxAmmo;
    }

    void Update()
    {
        if(Input.GetButton("Fire1"))
        {
            Shoot();
        }

        /*if (currentAmmo == 0 || Input.GetKeyDown(KeyCode.R))
        {
            StartCoroutine(Reload());
        }*/
    }

    void Shoot()
    {
        RaycastHit hit;
        if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, range))
        {
            Enemy enemy = hit.transform.GetComponent<Enemy>();

            if (enemy != null)
            {
                 enemy.TakeDamage(assaultRifleDamage);
                 StartCoroutine(Fade());
                 Debug.Log("I hit a thing!");
            }
            //currentAmmo--;
           
        }
        Debug.Log("PewPew!");
StartCoroutine(WaitToFire());
    }
    IEnumerator WaitToFire()
    {
        yield return new WaitForSeconds(fireRate);
    }

    IEnumerator Reload()
    {
        yield return new WaitForSeconds(reloadTime);
        currentAmmo = maxAmmo;
    }

    IEnumerator Fade()
    {
        hitmarker.SetActive(true);
        yield return new WaitForSeconds(fadeTime);
        hitmarker.SetActive(false);
    }
}

Update() gets call every frame, even if a coroutine is running. So you shoot a bullet, start a coroutine that waits for fireRate seconds, but then the next frame you shoot another bullet and kick off another coroutine. You end up with a bunch of coroutines running at the same time, all of which wait for a certain amount of time and then just do nothing.

Here’s a better way to rate-limit something that’s easier and more reliable than coroutines:

public float fireRate = 0.5f;
private float lastFiredAt = -999f;

void Update() {
if (Input.GetButton("Fire1") && Time.time > lastFiredAt + fireRate) {
Shoot();
}
}

void Shoot() {
lastFiredAt = Time.time;
//all your raycast stuff here
}

Coroutines are great for running complex sequences of code over time, but for simple loops, it’s best to avoid them; they’ll just overcomplicate things.

2 Likes

Works like a charm! Thanks so much!