So I’m working on a game, and I have a bullet firing script bound to z seen below:
if (shoot)
{
if (BusterAmmo > 0)
foreach (Gun gun in guns)
{
bulletTimer += Time.deltaTime;
if (bulletTimer > 0.11f)
{
gun.Shoot();
BusterCool = 0.25f;
BusterAmmo -= 1;
bulletTimer = 0;
}
else
{
shoot = false;
}
}
However, I want to make this code so it will repeat three times, since the way it is now will result in the script being buggy in how it manages the bulletTimer and ammo variables. Is there a way to do this?
I would consider using a coroutine for this, not sure if there’s better solutions. The basic premise would be that you have some code that looks like this
Ok, I gotcha. Will I need to make shootCoroutine() it’s own variable? I tried using the code you used as a template, but it just gave me an error message.
I would respectfully submit that a coroutine is NOT suited to this. I am paging in @GroZZleR too just because we love to banter about this.
The approach I would do here is as follows:
when you receive an intent to fire from the user, set a ShotsRequests integer variable to the number of shots in a burst (for example 3 shots)
use a standard cooldown timer, and when the cooldown has expired, check if that ShotsRequested integer is greater than zero. If it is greater than zero, decrement it and fire a shot, then restore the cooldown so each shot is spaced out.
Here is this precise mechanism in my Jetpack Kurt game’s combat mode:
Okay, but why? You’re basically just doing the exact same thing as the Coroutine, but with extra steps directly inside of Update. Why not just make it more readable and use the Coroutine?
I too would like to know this, it was never explained why coroutines were not suitable for this, and it doesn’t seem like it would run into any specific problems like “too many coroutines running” or things that would normally steer you away from them. I feel like it’s particularly nice because of how the shooting works, you would want to wait for the coroutine to finish running anyways.
I don’t find coroutines more readable in this case, and now you must keep track of if the coroutine is running already or not. Anytime you CARE about a coroutine’s running state to decide what to do next, it is not a good fit.
If your plan involves calling StopCoroutine() regularly (this application would not need that), then it is DEFINITELY not a job for coroutines.
To me, the idea coroutine is one that fires off and does its magic without any future interaction or coordination.
The only exception would be a coroutine with a delegate callback at the end of it. You COULD make this fit that description, but you get no benefit. Those properties are already local to your player or weapon class.
Not only that but when you have a bug, just look how much harder it is to reason about where your code is in any given frame!! How would you detect some bug in your logic allowed you to inadvertently start two or three instances of this coroutine?
I understand your reasoning, but I don’t understand it when taking into account the alternative you presented.
Not really? I just need to know which state the enemy is in, namely the “shooting” one. You also have to do that when using a timer though, so I’m not sure why that’s an argument against coroutines. You don’t want to keep track of a coroutines status, but you want to keep track of a timer’s status? Why?
What gave you the impression that this was not exactly what is needed here? You tell the enemy to shoot three times and you don’t really have to interact with it at all. It’ll shoot three times and that’s that. Again, exact same thing a timer would do.
I honestly think this is a matter of personal preference. My answer would be “just don’t start two or three instances of this”. I realize this is not a good answer to you, but I actually never came across the problem you’re describing.
I find it ten times more readable to have Coroutines for doing stuff over time than using timers inside of Update. Every time I see someone adding deltaTime to a variable every frame, the code that follows is so unreadable that it takes forever to understand what is even going on. And don’t even get me started on behavior that requires multiple timers. It becomes a complete mess. And in contrast to that you have this:
That’s infinitely more readable. Yeah, it becomes harder to reason what exactly happens in a frame, but there almost never comes a point where you even need to do that. And if you do, I’d argue that Coroutines don’t really hinder your problem finding process at all, since they still give you full control over the behavior.
In the end I’m not saying timers suck, but I’m saying that in this case both coroutines and timers are equally suited to the task and I don’t see any reason to antagonize coroutines here.