Using Unity 2019.1.9f1
I just fell into this problem with a simple Coroutine manager I wrote, the problem was strangling me so i decided to write an example to be sure the problem was not my fault. Or else I made a wrong assumption on how the Coroutine system is meant to work.
The problem is:
- I start a coroutine and save it reference
- I stop the coroutine and start a new one using the same reference by changing it’s target IEnumerator
- !!! The coroutine actually won’t stop, the new one starts (on the same reference?)
Here is the code, it’s simple, a phone is ringing, the user by pressing D (running on script 2) invokes an unity event, the telephone (SHOULD) stop ringing and the user should answer.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DEBUGCoroutines : MonoBehaviour
{
//DIFFERENT STATES
public enum DebugState { Ringing, Pickup, Stop }
public DebugState state = DebugState.Ringing;
Coroutine stateRoutine = null;
void Start()
{
//START EXAMPLE BY RINGING
UpdateState(state, true);
}
//STATE UPDATER, ON CALL CHANGES STATE
void UpdateState(DebugState s, bool force = false)
{
if (s != state || force)
{
state = s;
CustomStopAllCoroutines();
switch (state)
{
case DebugState.Ringing:
CustomStartCoroutine(stateRoutine, Ring());
break;
case DebugState.Pickup:
CustomStartCoroutine(stateRoutine, Pickup());
break;
case DebugState.Stop:
CustomStartCoroutine(stateRoutine, Stop());
break;
default:
break;
}
}
}
//STATE EVENTS
//
IEnumerator Pickup()
{
Debug.Log("HELLO!?");
yield return new WaitForSeconds(5f);
Debug.Log("Ok, Good bye");
UpdateState(DebugState.Stop);
}
IEnumerator Ring()
{
while (state == DebugState.Ringing)
{
Debug.Log("ring");
yield return new WaitForSeconds(2f);
}
Debug.Log("ERROR"); //THIS SHOULDN'T PLAY!
}
IEnumerator Stop()
{
yield return new WaitForSeconds(10);
UpdateState(DebugState.Ringing);
}
//METHOD CALLED BY THE EVENT
public void GetCall()
{
UpdateState(DebugState.Pickup);
}
//COROUTINE MANAGERS
//
void CustomStopAllCoroutines()
{
QuitRoutine(stateRoutine);
}
void CustomStartCoroutine(Coroutine cr, IEnumerator routine)
{
if (cr == null) cr = StartCoroutine(routine);
else
{
StopCoroutine(cr);
cr = null;
cr = StartCoroutine(routine);
}
}
void QuitRoutine(Coroutine cr)
{
if (cr != null) StopCoroutine(cr);
cr = null;
}
}
And here the simple event caller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class DEBUGCaller : MonoBehaviour
{
public UnityEvent debugEvent;
void Update()
{
if(Input.GetKeyDown(KeyCode.D)) debugEvent.Invoke();
}
}