1
\$\begingroup\$

i use StopCoroutine to stop "AttackPlayer", but their keep exculte the TakeDamage() function, even i stop it.

how to totally stop the IEnumerator , use bool to turn the whileloop on and off?

   void OnTriggerEnter(Collider other)
    {
        if (other.TryGetComponent(out Player player))
        {
            // Attack the player
            StartCoroutine(AttackPlayer(other));
        }
    }

   void OnTriggerExit(Collider other)
    {
        if (other.TryGetComponent(out Player player))
        {
            // Stop attacking the player
            StopCoroutine("AttackPlayer");
        }
    }

    IEnumerator AttackPlayer(Collider playerCollider)
    {
        Player player = playerCollider.GetComponent<Player>();
        while (player != null)
        {
              TakeDamage();
              yield return new WaitForSeconds(AttackSpeed);
        }
    }
\$\endgroup\$
4
  • \$\begingroup\$ Would it not be easier to use OnTriggerStay instead of the while loop? \$\endgroup\$
    – Zibelas
    Mar 9 at 6:48
  • \$\begingroup\$ It seems like your coroutine never yields. \$\endgroup\$
    – tkausl
    Mar 9 at 7:08
  • \$\begingroup\$ OnTriggerStay ? I never hear that before, i try , thanks \$\endgroup\$ Mar 9 at 11:10
  • \$\begingroup\$ This is basically the same as gamedev.stackexchange.com/questions/204784/… where the triggerstay just needs a timer and calls your attack player. Or you use the coroutines version. Which you feel more comfortable with. \$\endgroup\$
    – Zibelas
    Mar 9 at 11:26

2 Answers 2

1
\$\begingroup\$

S. Tank Çetin's answer correctly shows why your code doesn't work. What you can do instead is capture a reference to the coroutine in progress when you start it, then stop that:

Coroutine _attackInProgress;

void OnTriggerEnter(Collider other)
{
    if (_attackInProgress == null && other.TryGetComponent(out Player player))
    {
        // Attack the player
        _attackInProgress = StartCoroutine(AttackPlayer(player));
    }
}

void OnTriggerExit(Collider other)
{
    if (_attqckInProgress != null && other.TryGetComponent(out Player player))
    {
        // Stop attacking the player
        StopCoroutine(_attackInProgress);
        _attackInProgess = null;
    }
}

IEnumerator AttackPlayer(Player player)
{
    while (player != null)
    {
          TakeDamage();
          yield return new WaitForSeconds(AttackSpeed);
    }

    _attackInProgress = null;
}

You'll notice I (again) changed your AttackPlayer function to accept a Player, not a Collider. It's unnecessarily wasteful to search for the Player component twice, once in OnTriggerEnter and again in AttackPlayer, when the calling function could have just given it an instance of the type it needs directly.

\$\endgroup\$
2
  • \$\begingroup\$ if add _attackInProgress == nul conditionl in OnTriggerEnter , it will only attack one enemy. i remove it, and work find \$\endgroup\$ Mar 9 at 15:15
  • \$\begingroup\$ If you can attack multiple targets at once/get attacked by multiple attackers, then you also need to store multiple coroutine instances — one associated with each attacker/target — so you know which one to end in OnTriggerExit. \$\endgroup\$
    – DMGregory
    Mar 9 at 15:41
1
\$\begingroup\$

You can't start with the function call syntax and end with the string syntax.

Note: Do not mix the three arguments. If a string is used as the argument in StartCoroutine, use the string in StopCoroutine. Similarly, use the IEnumerator in both StartCoroutine and StopCoroutine. Finally, use StopCoroutine with the Coroutine used for creation.

See here: https://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html

\$\endgroup\$

You must log in to answer this question.

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