Home > Software engineering >  Start, Pause, Resume and Stop Unity Coroutines?
Start, Pause, Resume and Stop Unity Coroutines?

Time:01-27

I have a class Program which is a list of Nodes that contain a method IEnumerator Invoke() and the Program class iterates through each Node invoking it. I want to be able to provide methods to Start, Pause, Resume, and Stop execution. Starting would cause the invocation to start at the top of the list, Pausing would effectively 'Stop' the execution and allow Resume to be able to pick up wherever execution was when Pause was called, and Stop would cease all function and would require Start to be called to begin again. With Unity's built-in Coroutines is this even possible, and if it is how do I Pause/Resume a coroutine?

EDIT

what I'm looking for is how to essentially pause an instance of Program and be able to resume it at the same step.

If I understand one of the comments correctly the suggestion it makes would be something similar to this?

public abstract class Node {
 public abstract IEnumerator Invoke(ProgramCaller caller);
}

public class Program : Node {
 private List<Node> nodes;

 public override IEnumerator Invoke(ProgramCaller caller) {
  int index = 0;

  while(index < nodes.Count) {
   if(caller.Paused) {
    yield return null;
   }
   else {
    yield return nodes[index].Invoke(caller);
    index  ;
   }
  }
 }
}

CodePudding user response:

So from what I read is you have e.g.

public class Node
{
    public IEnumerator Invoke()
    {
        yield return null;
    }
}

Then a Unity Coroutine is basically using the IEnumerator and invoking MoveNext on certain intervals (Update by default except using the special ones like e.g. WaitForFixedUpdate etc).

So you could simply make Program implement that like e.g.

public class Program : IEnumerator
{
    public Node[] nodes;

    private int index = -1;
    private IEnumerator currentNode;

    public bool MoveNext()
    {
        if (nodes == null || nodes.Length == 0)
        {
            return false;
        }

        while (currentNode == null)
        {

            index  ;

            if (index >= nodes.Length)
            {
                return false;
            }

            currentNode = nodes[index]?.Invoke();
        }

        if (currentNode.MoveNext())
        {
            return true;
        }

        currentNode = null;
        return true;
    }

    public void Reset()
    {
        index = -1;
        currentNode = null;
    }

    public object Current => null;
}

and then you can link this up to a Coroutine from a MonoBehaviour like e.g.

public class Example : MonoBehaviour
{
    public Program program;
    private Coroutine currentRoutine;

    // just a name alias
    public void StartProgram() => RestartProgram();
    
    public void RestartProgram()
    {
        StopProgram();
        
        ResumeProgram();
    }

    public void ResumeProgram()
    {
        currentRoutine = StartCoroutine(program);
    }

    public void PauseProgram()
    {
        if (currentRoutine != null)
        {
            StopCoroutine(currentRoutine);
        }
    }

    public void StopProgram()
    {
        PauseProgram();

        program.Reset();
    }
}

as you see the only difference between Start/Stop and Pause/Resume is resetting or not resetting the Program.


Alternatively and maybe even more simple: A Coroutine is paused automatically when disabling according MonoBehaviour and resumed when enabling it again.

=> If it is an option for you to have a dedicated runner component for each program then all you need really is the resetting part and you could simply do

public class Program
{
    public Node[] nodes;

    public IEnumerator Run()
    {
        foreach (var node in nodes)
        {
            yield return node.Invoke();
        }
    }
}

This way you can run them all as a single IEnumerator and then

public class Example : MonoBehaviour
{
    public Program program;
    private Coroutine currentRoutine;

    // just a name alias
    public void StartProgram() => RestartProgram();
    
    public void RestartProgram()
    {
        StopProgram();
        
        currentRoutine = StartCoroutine(program.Run());
    }

    public void ResumeProgram()
    {
        enabled = true;
    }

    public void PauseProgram()
    {
        enabled = false;
    }

    public void StopProgram()
    {
        if (currentRoutine != null)
        {
            StopCoroutine(currentRoutine);
        }
    }
}
  • Related