# Stopping coroutine

**URL:** https://discussions.unity.com/t/stopping-coroutine/787547
**Category:** Scripting
**Tags:** Scripting
**Created:** [April 25, 2020, 3:25pm UTC](https://discussions.unity.com/t/stopping-coroutine/787547 "2020-04-25T15:25:42Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![beerinbox](https://avatars.discourse-cdn.com/v4/letter/b/df705f/32.png) [@beerinbox](https://discussions.unity.com/u/beerinbox)
#### Post date: [April 25, 2020, 3:25pm UTC](https://discussions.unity.com/t/stopping-coroutine/787547/1 "2020-04-25T15:25:42Z")

</div>

Hello.

My game involves two castles and enemy character spawns from one, and player spawns his characters from the other. The characters meet in the middle, they fight, winner goes to the castle to destroy it provided it does not meet another enemy on its way.

When the character reaches the other castle, it starts to damage the castle. And the other player can still spawn a new character. What I am trying to do is, stop character from damaging the castle while there is another enemy colliding with it.

I tried to add the contact to an array list, then check if an enemy character is in the array list, make the enemy stop damaging castle and start damaging the player again. I use coroutines to start damaging the castle and/or other character.

Here is my code:

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyChar : MonoBehaviour
{

    private float charSpeed;
    private float charHP;
    private float charAttack;
    private float attackSpeed;
    private bool shouldStop = false; //Checks whether the char should move
    private bool isPlayerDamageRunning = false; //Checks if Player damage coroutine runs
    private bool isPlayerCastleRunning = false; //Checks if Castle damage coroutine runs

    private ArrayList contacts = new ArrayList();
    private bool playerHasContact;

    void Start()
    {
        charSpeed = 5f;
        charHP = 150f;
        charAttack = 40f;
        attackSpeed = 5f;
    }

    void Update()
    {
        if (!shouldStop) //Colliders are triggers, so I manually stop them from moving.
        {
            transform.Translate(Vector3.left * charSpeed * Time.deltaTime);
        }
        if (charHP <= 0)
        {
            Destroy(gameObject);
        }
    }

    public void GetDamaged(float damage) //PlayerChar calls this to damage the enemy chars.
    {
        charHP -= damage;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        shouldStop = true;
        contacts.Add(collision.gameObject);

        if (!isPlayerDamageRunning && collision.tag == "Player")
        {
            StartCoroutine(DamagePlayer(collision));
        }

        if (!playerHasContact && !isPlayerCastleRunning && collision.tag == "PlayerCastle")
        {
            StartCoroutine(DamageCastle(collision));
            Debug.Log("Damage castle coroutine started");
        }
        if(collision.tag == "EnemyCastle") //Char spawn point is behind the castle collider, so we are ignoring it
        {
            Physics2D.IgnoreCollision(gameObject.GetComponent<Collider2D>(), collision);
        }

    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        shouldStop = false;
        contacts.Remove(collision.gameObject);
        if (isPlayerDamageRunning)
        {
            StopCoroutine(DamagePlayer(collision));
            isPlayerDamageRunning = false;
        }
        if (isPlayerCastleRunning)
        {
            StopCoroutine(DamageCastle(collision));
            isPlayerCastleRunning = false;
        }
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        bool playerOnScene = FindObjectOfType<PlayerChar>() != null;
        if (playerOnScene && contacts.Contains(FindObjectOfType<PlayerChar>().gameObject))
        {
            playerHasContact = true;
            StopCoroutine("DamageCastle"); //This actually does not stop that coroutine
            Debug.Log("Stopped DamageCastle coroutine?"); //NOT REALLY
        }
    }

    IEnumerator DamagePlayer(Collider2D collision)
    {
        isPlayerDamageRunning = true;
        while (true)
        {
            if (null != collision.gameObject)
            {
                PlayerChar player = collision.GetComponent<PlayerChar>();
                player.GetDamaged(charAttack);
            }
            yield return new WaitForSeconds(attackSpeed);
        }
    }

    IEnumerator DamageCastle(Collider2D collision)
    {
        isPlayerCastleRunning = true;
        Debug.Log("DamageCastle coroutine started");
        while (true)
        {
            if (null != collision.gameObject)
            {
                PlayerCastle player = collision.GetComponent<PlayerCastle>();
                player.GetDamaged(charAttack);
            }
            yield return new WaitForSeconds(attackSpeed);
        }
    }
}

```

I realized that the coroutine actually does not stop although the Debug message below is working.

I think because the coroutine takes a Collider2D parameter, and when it gets different parameter, it is basically looking for another coroutine which took that particular parameter (PlayerChar instead of PlayerCastle or vice versa). Is that correct? If so, do I have any other solution other than finding a way to calling that coroutine without any parameters?

---

<div class="post-metadata">

### Author: ![Kurt-Dekker](https://dub1.discourse-cdn.com/unity/user_avatar/discussions.unity.com/kurt-dekker/32/101578_2.png) [@Kurt-Dekker](https://discussions.unity.com/u/Kurt-Dekker)
#### Post date: [April 25, 2020, 10:13pm UTC](https://discussions.unity.com/t/stopping-coroutine/787547/2 "2020-04-25T22:13:51Z")

</div>

When you call a coroutine you can assign that to a variable and call StopCoroutine() with that variable.

Might be easier to implement multiple behavioral states and then detection logic to switch between the states. You CAN use a coroutine here but it can be awkward to stop.

---

<div class="post-metadata">

### Author: ![beerinbox](https://avatars.discourse-cdn.com/v4/letter/b/df705f/32.png) [@beerinbox](https://discussions.unity.com/u/beerinbox)
#### Post date: [April 26, 2020, 12:55am UTC](https://discussions.unity.com/t/stopping-coroutine/787547/3 "2020-04-26T00:55:22Z")

</div>

> [@Kurt-Dekker](#):
>
> When you call a coroutine you can assign that to a variable and call StopCoroutine() with that variable.
> 
> Might be easier to implement multiple behavioral states and then detection logic to switch between the states. You CAN use a coroutine here but it can be awkward to stop.

Hello, thank you for answering.  
Assigning the coroutine to a variable also did not work.  
I created two IEnumerator variables called castleDamager and playerDamager. Because they require a Collider2D parameter, I had to initialize them in OnTriggerEnter2D method. That did not create any new problems, however it also did not solve mine. The coroutine is still not stopped.

While I was at it, I tried InvokeRepeating as well, but it also did not work. Did not even get to invoke, maybe because methods require a parameter.

So, I did this:

```csharp
private void OnTriggerStay2D(Collider2D collision)
    {
        bool playerOnScene = FindObjectOfType<PlayerChar>() != null;
        if (!threeContact && playerOnScene && contacts.Contains(FindObjectOfType<PlayerChar>().gameObject))
        {
            playerHasContact = true;
            threeContact = true; //A bool so that I can make this if false
            StopAllCoroutines();
            isPlayerCastleRunning = false;
            isPlayerDamageRunning = false;
            Debug.Log("Stopped All Coroutines");
        }

        if (!isPlayerDamageRunning && collision.tag == "Player")
        {
            Debug.Log("Damage player coroutine started");
            shouldStop = true;
            isPlayerDamageRunning = true;
            StartCoroutine(playerDamager);
        }

        if (!isPlayerCastleRunning && collision.tag == "PlayerCastle")
        {
            Debug.Log("Damage castle coroutine started");
            shouldStop = true;
            isPlayerCastleRunning = true;
            StartCoroutine(castleDamager);
        }
        if (collision.tag == "EnemyCastle") //Char spawn point is behind the castle collider, so we are ignoring it
        {
            Physics2D.IgnoreCollision(gameObject.GetComponent<Collider2D>(), collision);
        }
    }

```

I stop all the coroutines, and then start the one I need once again.  
But now I have new problems. So, after the char goes to the castle, I spawn a playerChar, it kills it and then starts the castleDamager coroutine again. But it throws a MissingReferenceException, right where I try to check to see if it is null or not 🙂

It shows 147th line and here it is:

```csharp
    IEnumerator DamageCastle(Collider2D collision)
    {
        isPlayerCastleRunning = true;
        while (isPlayerCastleRunning)
        {
            if (null != collision.gameObject) //LINE 147
            {
                PlayerCastle castle = collision.GetComponent<PlayerCastle>();
                castle.GetDamaged(charAttack);
            }
            yield return new WaitForSeconds(attackSpeed);
        }

    }

```

When you say behavioral states, do you mean “If player and castle are in do this, if only castle is in do that, and if only player is in, do that” kind of thing?

EDIT: So, TriggerExit is called, but that does not mean TriggerEnter will be called again, hence the Collider2D DamageCastle is getting belongs to PlayerChar that I just destroyed… Right? Where do I go from that?

---

<div class="post-metadata">

### Author: ![Kurt-Dekker](https://dub1.discourse-cdn.com/unity/user_avatar/discussions.unity.com/kurt-dekker/32/101578_2.png) [@Kurt-Dekker](https://discussions.unity.com/u/Kurt-Dekker)
#### Post date: [April 26, 2020, 3:03am UTC](https://discussions.unity.com/t/stopping-coroutine/787547/4 "2020-04-26T03:03:08Z")

</div>

> [@beerinbox](#):
>
> When you say behavioral states, do you mean “If player and castle are in do this, if only castle is in do that, and if only player is in, do that” kind of thing?

I mean in your Update() loop you have a variable that controls what mode the agent is in.

If he is in mode1, he calls one function. If he is in mode2, he calls another, etc.

Periodically (even every frame if you want), the agent decides if they should change modes.

The primary way this differs from using coroutines is that you must have your own timers and/or counters to decide how long to do something, or how often, etc., as in Update you cannot be yielding.

BUT! The cool part is anytime you decide “Hey, I better start acting like mode2”, it can happen immediately, there is no messy coroutine to kill, etc. Next time through Update() you just start doing the other thing.

Classic use case for a melee enemy:

1. how far am I from the player?  
a. in range - then go to melee attack mode  
b. out of range - then walk towards the player

It can get as complex as you want. Before you evaluate the above you could also evaluate something like this:

1. how healthy am I?  
a. fit as a fiddle - see if I can advance upon or attack the player (decision above)  
b. hurt badly - RUN AWAY!

etc.

---

<div class="post-metadata">

### Author: ![beerinbox](https://avatars.discourse-cdn.com/v4/letter/b/df705f/32.png) [@beerinbox](https://discussions.unity.com/u/beerinbox)
#### Post date: [April 26, 2020, 7:36am UTC](https://discussions.unity.com/t/stopping-coroutine/787547/5 "2020-04-26T07:36:15Z")

</div>

I wish the coroutine approach just worked, but I think I’ll waste my time there if I dwell on it more.

However, there are things that I do not get regarding your approach. Just tried to implement it lazily. I wrote two methods (Damage player and damage castle). These methods need to get the object they are colliding with. As far as I know, this only happens in one of the OnTrigger… methods. I can’t think of a way to call those methods from Update method. as I do not have a reference to the colliding object.

So I tried to call the methods from OnTriggerStay2D. But there is something funny happening that I am sure is my fault.

```csharp
    private void DamageCastle(Collider2D collision)
    {
        if(Time.time >= timeManagerCastle)
        {
            if (collision.gameObject.GetComponent<PlayerCastle>() != null)
            {
                collision.gameObject.GetComponent<PlayerCastle>().GetDamaged(charAttack);
            }
            timeManagerCastle = Time.time + attackSpeed;
        }
        Debug.Log("TimeManagerCastle: " + timeManagerCastle + " , Time.time: " + Time.time);
    }

```

This code does not work after Time.time is 3,5.

---

<div class="post-metadata">

### Author: ![Kurt-Dekker](https://dub1.discourse-cdn.com/unity/user_avatar/discussions.unity.com/kurt-dekker/32/101578_2.png) [@Kurt-Dekker](https://discussions.unity.com/u/Kurt-Dekker)
#### Post date: [April 26, 2020, 5:38pm UTC](https://discussions.unity.com/t/stopping-coroutine/787547/6 "2020-04-26T17:38:16Z")

</div>

> [@beerinbox](#):
>
> These methods need to get the object they are colliding with

Just make a copy of the reference to the given object at the point where you decide to go into that mode.

```auto
Collider2D AttackTarget;

```

It might be useful to also check the object is still valid always in your attack, because there could be some other reason that destroys it without changing the attack mode to something else.

---

<div class="post-metadata">

### Author: ![beerinbox](https://avatars.discourse-cdn.com/v4/letter/b/df705f/32.png) [@beerinbox](https://discussions.unity.com/u/beerinbox)
#### Post date: [April 26, 2020, 7:07pm UTC](https://discussions.unity.com/t/stopping-coroutine/787547/7 "2020-04-26T19:07:15Z")

</div>

> [@Kurt-Dekker](#):
>
> Just make a copy of the reference to the given object at the point where you decide to go into that mode.
> 
> ```auto
> Collider2D AttackTarget;
> 
> ```
> 
> It might be useful to also check the object is still valid always in your attack, because there could be some other reason that destroys it without changing the attack mode to something else.

Alright. Thank you. I am going to try to do that. I’ll be back if I can’t figure it out.

But I am also curious about why the coroutine did not stop. Is it because of what I suspected, or something else? Any ideas?
