Coroutine Waits, But Calls Multiple Times per Update. Is There a Way to Fix This?
In developing a shooting AI for a third-person shooter project, it made sense to me to try using coroutines and WaitForSeconds in order to create a fire rate for my AI. I believe I have WaitForSeconds set up properly,
IEnumerator doShoot() { yield return new WaitForSeconds(2f); Debug.Log("Shooting"); RaycastHit hit; if (Physics.Raycast(transform.position, (transform.forward + new Vector3(Random.Range(-.1f, .1f), Random.Range(-.1f, .1f), Random.Range(-.1f, .1f))), out hit, shootRange)) { Debug.Log(hit.transform.name); hit.transform.GetComponent<TPSController>().health -= damage; } }
however when I call the coroutine in my Update void, it calls multiple times after waiting 2 seconds is up. For reference, I'll attach my whole AI script file here. If there's anything else I can do to improve the script, let me know, but at the moment, my main focus is the delay between shots the enemy AI can take. Everything else works, and that's good enough right now for me.
Thanks for your help, everyone.
Answer by BradyIrv · Nov 29, 2019 at 03:32 PM
When you call a Coroutine, it establishes a new thread to run the code on. When you use StartCoroutine(...); you are creating a coroutine for every call made. So if you have this code in the update loop then you are calling and creating multiple coroutines (which can be bad for performance).
What you could do instead is either have a boolean to see if the coroutine is currently running or have the coroutine call itself recursively. Either could work, boolean is probably safer.
// For example
bool canShoot = false;
Update: if(canShoot) Shoot();
~End of Shoot, StartCoroutine(Wait())
IEnumerator Wait() { yield return new WaitForSeconds(x); canShoot = true; }
So basically just have a bool that asks if the Coroutine is already running. If it isn't, you run it. If it is, you don't. Simple as that?
Yeah, that's one way. You can also use the coroutine to call itself recursively if you'd like, but then you wouldn't be able to control if the AI can shoot or not and it will always shoot after the coroutine is completed:
private void Shoot()
{
// Fire Projectile or Raycast
StartCoroutine(ShootTimer());
}
IEnumerator ShootTimer()
{
yield return new WaitForSeconds(x);
Shoot();
}
It's still kind of nice to separate it this way though because then you can use the logic inside the Shoot function ins$$anonymous$$d of the update Loop, example:
private void Update()
{
Shoot();
}
private void Shoot()
{
if(canShoot && hasAmmo)
{
// Fire Projectile or Raycast
StartCoroutine(ShootTimer());
}
}
Happy Coding!
I feel like I understand what you're getting at here. After testing my revised code, it looks like everything works! Thanks a bunch for the help.
Just a note, coroutines are certainly not multithreaded, they just have their own small Update that calls any late coroutines.