4
\$\begingroup\$

I'm getting some attacking to work with my units and I noticed that the coroutines seem to start adding up even after the StopCoroutine(IEnumerator) as need called.

I dug around on ways to perform an attack loop, most of the time I found "Use InvokeRepeating" and "Don't use InvokeRepeating". Since invoke uses reflection it's performance heavy anyways, so I'd rather not go down that road.

How do I get my coroutine to stop? Is it because it's in a while loop inside the coroutine? If so, how do I fashion an attack loop without using invoke?

//Recieves call when a new enemy is added to the list
public void NewEnemyDetected()
{
    if(currentlyAttacking == null)
    {
        SelectEnemyToAttack();
    }
}

//Recieves call when an enemy leaves the collider area
public void EnemyGone()
{
    SelectEnemyToAttack();
}

//Will select an enemy from the array to attack
private void SelectEnemyToAttack()
{
    if(enemyObjects.Count > 0)
    {
        StopCoroutine(AttackEnemy());
        currentlyAttacking = enemyObjects.ElementAt(0);
        isAttacking = true;
        StartCoroutine(AttackEnemy());
    }
    else
    {
        currentlyAttacking = null;
        isAttacking = false;
    }
}

//Partially hacked together attacking loop
private IEnumerator AttackEnemy()
{
    while(isAttacking)
    {
        Vector3 angle = currentlyAttacking.DetectedGameObject.transform.position - transformV.position;
        angle = angle.normalized;
        GameObject projectile = (GameObject)Instantiate(AttackType, transformV.position, transformV.rotation);
        BlasterAttack projectileAttack = projectile.GetComponent<BlasterAttack>();
        projectileAttack.OnCreation(10, player);
        projectile.GetComponent<Rigidbody>().AddForce(angle * 100);
        yield return new WaitForSeconds(1);
    }
    yield return false;
}

Thanks!

\$\endgroup\$

2 Answers 2

7
\$\begingroup\$

StopCoroutine takes an IEnumerator object (the routine you want to stop) as parameter.

Here's:

StopCoroutine(AttackEnemy());

AttackEnemy will return a new IEnumerator object, so any other coroutine created before won't be stopped (they are different objects).

The right way to stop is to store a reference to the currently running coroutine, for example:

IEnumerator attackEnemy = AttackEnemy();
StartCoroutine(attackEnemy);

....

StopCoroutine(attackEnemy);

Side note looking at the comments:

  1. Yes string version is slower and does use reflection
  2. Even without considering performances, IEnumerator overload is more flexible (It hasn't always been there a was a serious limitation). In fact you can have multiple running coroutine created using the same method and selectively stop only one of them.
\$\endgroup\$
0
\$\begingroup\$

Turns out I need to call the method by name with a string instead of the method itself to stop the coroutine.

 StartCoroutine("AttackEnemy");
 ...
 StopCoroutine("AttackEnemy");
\$\endgroup\$
5
  • \$\begingroup\$ You can use either by string, or keep a reference to the Coroutine object you're getting as a return value from StartCoroutine. Then pass said reference into StopCoroutine to stop the coroutine. \$\endgroup\$
    – bummzack
    Mar 28, 2015 at 7:23
  • \$\begingroup\$ Ah, how do you get the reference back from the coroutine? It was my understanding that it did not actually return a reference? \$\endgroup\$ Mar 28, 2015 at 20:06
  • \$\begingroup\$ StartCoroutine returns a Coroutine Object, which is an accepted parameter for StopCoroutine (at least in Unity 5) \$\endgroup\$
    – bummzack
    Mar 28, 2015 at 20:14
  • \$\begingroup\$ Ahhh, I'll give that a shot. Out of curiosity does the by string method use reflection? If so then performance would be an incentive to use the return value to stop the coroutine. \$\endgroup\$ Mar 28, 2015 at 20:20
  • \$\begingroup\$ Yes, the string method is slower. \$\endgroup\$
    – bummzack
    Mar 28, 2015 at 21:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .