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
    Commented Mar 9, 2023 at 6:48
  • \$\begingroup\$ It seems like your coroutine never yields. \$\endgroup\$
    – tkausl
    Commented Mar 9, 2023 at 7:08
  • \$\begingroup\$ OnTriggerStay ? I never hear that before, i try , thanks \$\endgroup\$ Commented Mar 9, 2023 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
    Commented Mar 9, 2023 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\$ Commented Mar 9, 2023 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
    Commented Mar 9, 2023 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 .