- Home /
Making Coroutine Repeat?
I'm currently trying to code a charge shot for a weapon, just like you'd see in a Mega Man game. However, while everything else seems to work just fine for the first shot, the next ones do not. It's like this: the coroutine I set up works just fine; makes you hold the button down for two seconds to fire off the charged shot. Great. But, after that, the charged shots fire off immediately with each GetButtonUp use. Think semi-auto fire, as fast as you can pull the trigger. Clearly, that won't do for a charged shot. I believe the issue is with the coroutine. They are only used once per script correct? So, how can I make it to where the coroutine can be used as many times as I want? I tried a while loop with yield, but it doesn't work. Just makes it fire off four or five charged shots in one button press, still semi-auto. Is it because I didn't put it in the right place? Or, is it not a good function to use for this altogether? Here is my current script:
public Transform chargeSpawn;
public GameObject chargeShot;
private float chargeTime = 0;
private float chargeRate = 2f;
public float fireRate1;
public float nextFire1;
void Update()
{
StartCoroutine(TimerRoutine());
}
IEnumerator TimerRoutine()
{
while (true)
{
if (Input.GetButtonDown("Fire1"))
{
yield return new WaitForSeconds(2f);
chargeTime += chargeRate;
}
else if (Input.GetButtonUp("Fire1") && Time.time > 2f)
{
Instantiate(chargeShot, chargeSpawn.transform.position, chargeSpawn.transform.rotation);
GetComponent<AudioSource>().Play();
chargeTime = 0;
}
else if (Input.GetButtonUp("Fire1") && Time.time < 2f)
{
chargeTime = 0;
}
yield return new WaitForSeconds(5f);
}
}
Answer by Kauppasarvi · Aug 21, 2017 at 11:39 AM
Hello, there's multiple problems, mainly, you have too much stuff, there, so here is stripped version.
public Transform chargeSpawn;
public GameObject chargeShot;
public float chargeRate = 2f; // make this public, maybe give it better name like "chargeTime"
void Start()
{
StartCoroutine(TimerRoutine());
}
IEnumerator TimerRoutine()
{
while (true)
{
if (Input.GetButtonDown("Fire1"))
{
yield return new WaitForSeconds(chargeRate);
}
else if (Input.GetButtonUp("Fire1"))
{
Instantiate(chargeShot, chargeSpawn.transform.position, chargeSpawn.transform.rotation);
GetComponent<AudioSource>().Play();
}
yield return null;
}
}
Also, made my own version, to show another way to approach this, with ton of comments
public Transform chargeSpawn;
public GameObject chargeShot;
public float chargeTime = 2f;
private bool readyToShoot = false;
[ Range (0f, 1f)] // this gives it a nice slider in Inspector, it can still be given any value in scripts
public float chargeLevel;
void Update()
{
if (Input.GetButtonDown ("Fire1")) {
// Start charging
StartCoroutine (Charge());
}
if (Input.GetButtonUp ("Fire1") && readyToShoot) {
// Shoot
readyToShoot = false;
chargeLevel = 0;
Instantiate(chargeShot, chargeSpawn.transform.position, chargeSpawn.transform.rotation);
}
}
IEnumerator Charge () {
while (chargeLevel < 1) {
// Track charge level, and use it in other scripts if desired
chargeLevel += Time.deltaTime / chargeTime;
if (Input.GetButtonUp ("Fire1")) {
// fail and quit Charge
Debug.Log ("Charge failed.");
chargeLevel = 0;
yield break;
}
// wait here for next frame
yield return null;
}
// Charge succeeded, and we're ready to shoot
Debug.Log ("Weapon Charged!");
readyToShoot = true;
// Also clamp chargeLevel to full = 1
chargeLevel = 1;
}
Hope this helps!
Wowwwww! You are a GODSEND. I've been losing my $$anonymous$$d trying to get this to work. Thank you, so much. I'll study this script in and out, so I'll know what I'm doing in the future. Thanks again, man!
Thank you $$anonymous$$auppasarvi for your help I understand better the second version!
Answer by fafase · Aug 20, 2017 at 01:58 PM
You are creating a new coroutine every frame. No good.
Instead, give up on the coroutine and make it all happen in the update.
void Update()
{
if (Input.GetButton("Fire1"))
{
chargeTime += chargeRate;
if(chargeTime > timerToShoot){
Shoot();
chargeTime = 0f;
}
else if (Input.GetButtonUp("Fire1")
{
chargeTime = 0;
}
}
Did I do it wrong? It's only firing off the shots like a semi-auto fashion, without any wait time.
public Transform chargeSpawn;
public GameObject chargeShot;
private float chargeTime = 0;
private float chargeRate = 0.1f;
public float fireRate1;
public float nextFire1;
private float timerToShoot;
//private float Shoot;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
chargeTime += chargeRate;
//timerToShoot = Time.time;
if (chargeTime > timerToShoot)
{
Instantiate(chargeShot, chargeSpawn.transform.position, chargeSpawn.transform.rotation);
GetComponent<AudioSource>().Play();
chargeTime = 0f;
}
else if (Input.GetButtonUp("Fire1"))
{
chargeTime = 0;
}
}
}
}
Ok I edited the answer. I guess with GetButton ins$$anonymous$$d of GetButtonDown, it should go better. You want to increase every frame not just on press.
Nah man, doing that just makes it fire off an endless stream of them, like a machine gun. Doesn't even wait for the two seconds.
Answer by AbominationGames · Aug 20, 2017 at 06:03 AM
You can create a bool, canCharge and have it toggle it between shots then you can say that you can't charge at the beginning of the coroutine then at the end of the timer it should toggle the can charge bool back to true. This should be sufficient to double check everything and enforce the timer. If it doesn't work you could always drop another coroutine or method timing it all out correctly. I think that just adding the extra bool should be enough though. It would still have to run those sections of the code so adding it might just be enough to make the difference.
Could you give an example in scripting? I'm trying it, but nothing's changing. Not sure if I did something wrong, or if it's just not what I need.
Your answer
Follow this Question
Related Questions
Coding A Megaman-like Charge Shot? 1 Answer
Problem with a charged projectile direction 1 Answer
How to make two enemies appear in sequence? 1 Answer
WaitForSeconds behaves weirdly 1 Answer
"Charge" shot script help 1 Answer