Object keeps moving up even when StopCoroutine is called

Hello guys, I’m trying to move an object in the “up” direction whenever the player enters a collider using OnTriggerStay. I used the code below which includes a Coroutine.

if (other.gameObject.tag == "Puzzle2") // fence2 closes on puzzle2 smoothly
        {
            StartCoroutine("Fence2Up");
        }
    }

    IEnumerator Fence2Up()
    {
        fence2.transform.Translate(Vector3.up * Time.deltaTime * 3);
        new WaitForSeconds(6f);
        print("It should stop!");
        StopCoroutine("Fence2Up");
        yield return null;
    }

The if statement is in the "OnTriggerStay " function. Basically, the object correctly moving upward whenever I enter the collider. Yet, It doesn’t stop after 6 seconds which I specified in the IEnumerator function where I mentioned StopCoroutine. Also, the print function works properly after 6 seconds.

Your usage of the coroutine is all wrong.


Coroutine should contain a conditional loop (while) or conditional statement (if), and the code that should be executed continuously must be placed inside this loop/statement scope.
The scope then should end up with one of the yield return instructions determining when the next iteration should be executed again. Note that the yield return should be inside the loop/statement scope.


Once the condition return false, the coroutine is over, and the execution order will go to outside of the condition scope.


So, if you want to continuously move the transform up for 6 seconds (I think that’s what you’re trying to do), you should call the coroutine this way:

IEnumerator Fence2Up()
{
    // the counter will be substructed with Time.deltaTime after every coroutine iteration
    // and we'll execute next coroutine step every frame using yield return null
    float counter = 6f;
    // set up the condition that will determine if the coroutine should be executed again or not
    while (counter > 0)
    {
        print("Coroutine is running, counter=" + counter);
        fence2.transform.Translate(Vector3.up * Time.deltaTime * 3);
        counter -= Time.deltaTime;
        print("Gonna wait for next frame to continue");
        yield return null;
        // here the coroutine will be paused untill the next frame
        // and in the next frame it will execute the code inside the while scope again if the counter will still be more than 0
        // if you use WaitForSeconds here instead of yield return null,
        // the coroutine will be paused for 6 seconds and it will move the transform and substruct counter only every 6 secods
    }
    // if the counter is 0, then the coroutine will stop itself
     print("The coroutine has stopped because the counter is 0 or less. Counter=" + counter);
     // no need to stop the coroutine, it already has stopped itself
 }

And don’t use OnTriggerStay() since you use a coroutine here. Because it just doesn’t make sense. Coroutines are designed to be called just once and once you’ve called the coroutine, it will call itself automatically if the condition inside the coroutine is true. That’s what coroutines are made for.


So, if you want to move the fence for 6 seconds whenever player gets into the trigger area, use OnTriggerEnter() which is called just once, and you won’t have to stop and restart the coroutine manually. You call the coroutine once and it recalls itself automatically while the condition is true and then stops itself.

It seems like you are still staying in the trigger so you should only call the coroutine once. So just create a boolean named startedCoroutine and it should work.

if (other.gameObject.tag == "Puzzle2" && !startedCoroutine) 
         {
             StartCoroutine("Fence2Up");
             startedCoroutine = true;
         }
     }

EDIT

Another problem with the code is that new WaitForSeconds(6f); does not do much, you have to call yeild return new WaitForSeconds(6); for it to actually wait 6 seconds.

What I suggest is that you don’t use a coroutine at all and use Update instead, like this

float timeLeft = 6f;
bool runCode;

void Update(){
          if(runCode){
                   timeLeft -= Time.deltaTime;
                   if(timeLeft>0){
                             fence2.transform.Translate(Vector3.up * Time.deltaTime * 3);
                   }
          }
}

void OnTriggerStay(Collider other){
          if (other.gameObject.tag == "Puzzle2" && !runCode) 
          {
              runCode= true;
          }
}