Starting Coroutines more than once

The “A” key is responsible for disabling the sprite renderer and box collider of one game object and enabling the same on another. It all works well, but only once. Is there a way to start coroutines more than once?

{

public GameObject ExtendedHook;
public GameObject ShortHook;

IEnumerator DisableHook()
{
    yield return new WaitForSeconds (Time.deltaTime * 10);

    (ShortHook).gameObject.GetComponent<SpriteRenderer>().enabled = true;
    ExtendedHook.SetActive(false);
    ShortHook.SetActive(true);
}

IEnumerator HookCatch()
{
    yield return new WaitForSeconds (0);

    gameObject.GetComponent<SpriteRenderer>().enabled = true;
    gameObject.GetComponent<BoxCollider2D>().enabled = true;
    (ShortHook).gameObject.GetComponent<SpriteRenderer>().enabled = false;
}

void Update()
{
    if (Input.GetKey("a"))
    {
        StartCoroutine(HookCatch());
        StartCoroutine(DisableHook());
    }

}  

},

Hello there,

Based on the code shown above, there is no reason why the coroutines would only run once.

A few improvements I would suggest:

• Are you sure Input.GetKey is what you want here? Keep in mind that this will return true every frame as long as A is pressed. Which means you could potentially start a hundred coroutines if you just held it down for a little too long. Instead, try using Input.GetKeyDown(). Additionally, if you just want to get an A key press, I think you should use KeyCode.A instead of “a”.

• Yield return new WaitForSeconds(0) probably isn’t what you’re looking for here. If you want no delay at all, then remove it. If you want a one-frame delay, then replace it with yield return null;

• “ShortHook” is already a gameobject, there is no need to do ShortHook.gameObject or to put it in between parentheses.

[EDIT: IGNORE THIS LINE] For good practice, try putting a yield return null; at the end of your coroutines.

• You don’t need to do “gameObject.GetComponent” if trying to refer to the object this script is attached to. You can just use GetComponent directly. [EDIT: As mentioned in the comment below, this is more of a style choice, not an actual improvement.]


Hope that helps!

Cheers,

~LegendBacon

There are countless things wrong in this code, apart from the fact that it’s really difficult to determine what the intended action should be.

  • First of all you’re using “GetKey” which returns true while you’re holding down the button. That means at the moment you start those two coroutines every frame as long as the key is held down. It’s nearly impossible that you start those only once. You should have used GetKeyDown which only fires once whenever the key is pressed down.
  • It seems to make no sense that you use two coroutines here. It seems like one should enable some objects while the other should disable the objects. One reason why it doesn’t work a second time might be that the coroutines do not do exactly the opposite.
  • You’re starting “HookCatch” and “DisableHook” at the very same time. So both coroutines will run in parallel. Since you start those again each frame you actually have a mess of running coroutines at the same time. Since each coroutine manipulates the same objects it’s impossible to determine what actually happens while any of those coroutines is running.
  • Next thing is this line new WaitForSeconds (Time.deltaTime * 10); makes not much sense. Time.deltaTime is a value that is related to the current frame. Saving it or using it for later use make no sense. The current frame could have taken quite long (heavy load). You’re just multiply the current dt by 10 and use it as wait time. This does not ensure a 10 frame delay. It may be way longer or way shorter.
  • Also pointless is using new WaitForSeconds (0); it just wastes memory. If you want to wait one frame, use yield return null;
  • “ShortHook” is a GameObject. using .gameObject on it makes no sense. This is like saying ShortHook.gameObject.gameObject.gameObject.gameObject.GetComponent ... which is just completely pointless. just do ShortHook.GetComponent .... Though since you use the same Components several times you may want to actually get those components once in start and store them in a variable. This makes the code shorter, faster and easier to read.

Finally as i mentioned above you probably want to create a single coroutine that does everything at once. Also for “repeatability” keep in mind that both of your coroutine change the state of some objects. However you don’t ever seem to reset those states. For example In HookCatch you enable this objects BoxCollider and Renderer. However at no point do you disable them again. So once enabled they stay enabled.

The code after the yield in “DisableHook” most likely will run after the code in “HookCatch”. Here you disable the “ExtendedHook” gameobject and enable the “ShortHook” gameobject. This is also never undone. Once this code has run the objects stay in that state.

It’s generally a bad sign if the description of what is wrong with a script is longer than the script itself. Your question and code is very cryptic and hard to follow. Terms like “It all works well” or “it doesn’t work” may only make sense to you. We can’t really deduce the point of the script from your code. Either it’s missing some crucial part or it is just plain wrong.

It’s obvious that you want to implement some sort of grappling hook (Thanks to the naming of your coroutines). However we have no idea what each object is responsible for, where this script is attached to and what the actual behaviour should be. Is it a toggle hook, should you hold down the button and release the hook when you release the button? All those things we can’t answer because of lacking information.