Restarting a Coroutine ignore the yield WaitForSeconds - c#

I have an IEnumerator for a coroutine that spawn object like this
public IEnumerator spawnCoroutine;
private float spawnObstacleTimer;
void Start () {
spawnObstacleTimer = GameObject.Find("EventSystem").GetComponent<gameManager>().spawnObstacleTimer;
spawnCoroutine = spawnObsCoroutine(spawnObstacleTimer);
startSpawnCoroutine();
}
public IEnumerator spawnObsCoroutine(float timer)
{
while (true)
{
yield return new WaitForSeconds(timer);
spawnObs();
}
}
public void startSpawnCoroutine()
{
StartCoroutine(spawnCoroutine);
}
public void stopSpawnCoroutine()
{
StopCoroutine(spawnCoroutine);
}
And I start it on the Start() which it run fine. But I've put a collision detection to stop this Coroutine when triggered. Again this is running fine but it's when I call the OnCollisionExit() that restart the Coroutine with the function startSpawnCoroutine(); a new object is spawned immediatly and is ignoring the yield return new WaitForSeconds()
So how this happen? StopCouroutine should stop it all and when I restart it, it should wait for seconds before executing the spawn.
Thanks for your help

The function StopCoroutine(spawnCoroutine) could be better named PauseCoroutine(spawnCoroutine), when you call start again it restarts from the last yield inside the routine.
Change your startSpawnCoroutine() function to start a new instance of the routine to get it to "start from scratch"
public IEnumerator spawnCoroutine;
private float spawnObstacleTimer;
void Start () {
spawnObstacleTimer = GameObject.Find("EventSystem").GetComponent<gameManager>().spawnObstacleTimer;
startSpawnCoroutine();
}
public IEnumerator spawnObsCoroutine(float timer)
{
while (true)
{
yield return new WaitForSeconds(timer);
spawnObs();
}
}
public void startSpawnCoroutine()
{
//Moved the creation of the IEnumerable in to this function.
spawnCoroutine = spawnObsCoroutine(spawnObstacleTimer);
StartCoroutine(spawnCoroutine);
}
public void stopSpawnCoroutine()
{
StopCoroutine(spawnCoroutine);
}

Related

Start, Pause, Resume and Stop Unity Coroutines?

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++;
}
}
}
}
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);
}
}
}

How to repeatedly execute a method until said otherwise

I have an EventSystem for managing my turn-based game in Unity.
public class EventSystem : MonoBehaviour
{
private static List<Action> _commandsQueue = new List<Action>();
private bool _canExecuteCommand = true;
public void AddToQueue(Action command)
{
_commandsQueue.Add(command);
}
private void StartCommandExecution()
{
_commandsQueue[0]();
_canExecuteCommand = false;
}
public void CommandExecutionComplete()
{
_canExecuteCommand = true;
}
public void PlayFirstCommandFromQueue()
{
if (_commandsQueue.Any() && _canExecuteCommand)
{
StartCommandExecution();
}
else
{
Debug.LogError("No Command In Queue");
}
}
}
How do I put a method in Update() until _canExecuteCommand is true again but only for some methods?
It is quite broad what you are trying to do but in general you would use an endless loop within a Coroutine.
You can create a generic routine which invokes any Action you pass in as parameter once a frame like e.g.
private IEnumerator InvokeEveryFrame(Action action)
{
// This looks strange but is okey in a Coroutine as long as you yield somewhere within
while(true)
{
action?.Invoke();
// This tells Unity to "pause" this routine here
// render the current frame and continue from here in the next frame
yield return null;
}
}
So all that's left is starting the routine using MonoBehaviour.StartCoroutine like e.g.
Coroutine routine = StartCoroutine(SomeParameterlessMethod);
or if you need parameters
Coroutine routine = StartCoroutine(() => SomeMethod(x, y, z));
and then at some point later stop it using MonoBehaviour.StopCoroutine and the stored Coroutine reference like e.g.
StopCoroutine(routine);
how exactly you store that reference is up to you of course up to you.

How to make fade transition when I leave a scene

In my game I have LevelManager and LevelManager.cs and it's not a singleton.
I wanna make fade transition between scenes. I made animations for Fade_In and Fade_Out. And for the first it works fine but when I'd like to leave a scene, it (Fade_Out) doesn't work.
Some of LevelManager.cs:
private void Start() {
if (autoLoadNextLevelAfter != 0) {
Invoke("FadeOut", autoLoadNextLevelAfter);
}
}
public void LoadLevel(string name) {
SceneManager.LoadScene(name);
}
public void LoadNextLevel() {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
private void FadeOut() {
animator.SetTrigger("FadeOut");
}
In the Start method I call Invoke of animation and at the end of Fade_Out I call LoadNextLevel method. In this case, Fade_Out works fine.
But If I wanna make transition with LoadLevel (it calls when I push the button) it breaks. I tried to make IEnumerator LoadLevel:
public void NewLoadLevel(string name) {
FadeOut();
StartCoroutine(CoroutLoadLevel(name));
}
private IEnumerator CoroutLoadLevel(string name) {
yield return new WaitForSeconds(1f);
SceneManager.LoadScene(name);
}
The console says: Coroutine couldn't be started because the the game object 'LevelManager' is inactive!. I guess it's because LevelManager is not a singleton.
Make sure the LevelManager is active and enabled. A coroutine needs an active object to run (because if it is not active update logic will not be called).

Making Threads in Unity using frames

I have an assignment to make a thread safe logging class that writes to a file. Every ten frames I am supposed to push some information of my choice to the logging class from a separate script. I was wondering how to do that. Here is my code so far.
public class Threading
{
public bool Execute = true;
public Vector3 player;
public Vector3 WriteTime;
System.Collections.Generic.Queue<float> values;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
public void execute()
{
while (Execute)
{
System.Threading.Thread.Sleep(500);
values.Enqueue(player.x);
UnityEngine.Debug.Log("value");
}
System.IO.StreamWriter write = new System.IO.StreamWriter("values.txt"); // writes to file every 5 seconds
while (values.Count > 0)
{
WriteTime = write.WriteLine(values.Dequeue().ToString());
}
write.Close();
}
public void Lock() // applied Lock threading
{
while(true)
{
lock (this)
{
// dont have anything here yet. Trying to figure out locks
}
}
}
Thank you.

Hold or Wait while Coroutine finishes

In the example below, how can I get FinishFirst() to complete first before running DoLast(), while still retaining the 'public void StartPage()' signature?
I'm trying to avoid making "StartPage()" return an IEnumerator as that would force me to change it in the interface. It would be great if my Interface for StartPage() supported both IEnumerator and Void without needing to implement both.
public void StartPage()
{
print("in StartPage()");
StartCoroutine(FinishFirst(5.0f));
DoLast();
print("done");
}
IEnumerator FinishFirst(float waitTime)
{
print("in FinishFirst");
yield return WaitForSeconds(waitTime);
print("leave FinishFirst");
}
void DoLast()
{
print("do after everything is finished");
}
Use of a lock-object should work:
object lockObject = new object();
public void StartPage()
{
print("in StartPage()");
StartCoroutine(FinishFirst(5.0f));
DoLast();
print("done");
}
IEnumerator FinishFirst(float waitTime)
{
lock(lockObject)
{
print("in FinishFirst");
yield return WaitForSeconds(waitTime);
print("leave FinishFirst");
}
}
void DoLast()
{
lock(lockObject)
{
print("do after everything is finished");
}
}
I know this is an old question, but if I understand the question correctly, something like this works. DoLast() will run at the end of the co-routine.
IEnumerator FinishFirst(float waitTime)
{
print("in FinishFirst");
yield return WaitForSeconds(waitTime);
print("leave FinishFirst");
DoLast();
}
I use a lot of similar code inside co-routines to clean and kill the routine if another one was created when this one is running (mainly for typed-text dialog boxes where the text might change at any time).

Categories

Resources