- Home /
C#, some kind of coroutine that can pause?
Hi! I'd like to have some kind of tool to let me determine when an action will be triggered (in frames is the best way for me), there is the StartCoroutine that seems to work, but I can only use it with seconds and I cannot pause and resume this coroutine. Anyone has an idea on how to make something as easy to implement as the coroutine, but that would support frames and stop/resume?
Answer by whydoidoit · Sep 21, 2012 at 04:30 PM
You can absolutely do that with a coroutine - here is an example using a helper function to make it easier to control.
DelayedExecution.cs
public static class DelayedExecution
{
public static void StartCoroutine(this GameObject gameObject, IEnumerator coroutine)
{
var behaviour = gameObject.GetComponent<CoroutineHelper>();
if(!behaviour)
behaviour = gameObject.AddComponent<CoroutineHelper>();
behaviour.StartCoroutine(coroutine);
}
public class CoroutineHelper : MonoBehaviour {}
public class WaitController
{
public bool cancel;
public bool pause;
}
static IEnumerator WaitForANumberOfFrames(int numberOfFrames, Action thingToDo, WaitController controller)
{
while(numberOfFrames > 0)
{
if(!controller.pause)
numberOfFrames--;
if(controller.cancel)
yield break;
yield return null;
}
thingToDo();
}
static IEnumerator WaitForAPeriodOfTime(float timeToWait, Action thingToDo, WaitController controller)
{
while(timeToWait > 0)
{
if(!controller.pause)
timeToWait -= Time.deltaTime;
if(controller.cancel)
yield break;
yield return null;
}
thingToDo();
}
public static WaitController DoSomethingLater(this GameObject gameObject, Action thingToDo, int numberOfFrames)
{
var controller = new WaitController();
gameObject.StartCoroutine(WaitForANumberOfFrames(numberOfFrames, thingToDo, controller));
return controller;
}
public static WaitController DoSomethingLater(this GameObject gameObject, Action thingToDo, float timeToWait)
{
var controller = new WaitController();
gameObject.StartCoroutine(WaitForAPeriodOfTime(timeToWait, thingToDo, controller));
return controller;
}
public static WaitController DoSomethingLater(this MonoBehaviour behaviour, Action thingToDo, int numberOfFrames)
{
var controller = new WaitController();
behaviour.StartCoroutine(WaitForANumberOfFrames(numberOfFrames, thingToDo, controller));
return controller;
}
public static WaitController DoSomethingLater(this MonoBehaviour behaviour, Action thingToDo, float timeToWait)
{
var controller = new WaitController();
behaviour.StartCoroutine(WaitForAPeriodOfTime(timeToWait, thingToDo, controller));
return controller;
}
}
That's a static utility class - you would use if from your code like this:
TestIt.cs#
public class TestIt : MonoBehaviour
{
public DelayedExecution.WaitController controller;
void Start()
{
//Either
controller = gameObject.DoSomethingLater(DisplayMessage, 15);
//Or
controller = gameObject.DoSomethingLater(()=>{
//You can execute anything you like here e.g.
Destroy(gameObject);
}, 20);
//Or to wait for a period of time instead
controller = gameObject.DoSomethingLater(DisplayMessage, 5f); //Use a float to wait for time and an int for frames
}
void Update()
{
if(Input.GetKeyDown(KeyCode.P))
{
controller.pause = !controller.pause;
}
}
void DisplayMessage()
{
//Do something here
}
}
This is indeed awesome! You sir are a savior, but maybe there's something I don't understand, but GameObject does not have any method called StartCoroutine according to Unity...
@DaJuice - Oops - sorry - I forgot that's another extension I wrote! I've added it to the example (didn't have chance to test it - looks good though)
Do you think that putting a static $$anonymous$$onoBehaviour in the DelayedTask class, and always use this $$anonymous$$onoBehaviour to call StartCoroutine would be a good idea? Or would there be memory management issues maybe?
The reason I do it that way is that a) it doesn't take too much memory b) it gets destroyed if the GameObject is destroyed and c) it gets disabled if the GameObject get's SetActiveRecursively(false) - so I use it like this to not link one thing to a particular script. It would be valid to actually have a version that returned an IEnumerator so you could start it on any behaviour you wanted to make sure it was tied to that particular $$anonymous$$onoBehaviour's state.
I use my GameObject extension when I don't want to have it tied to a particular behaviour.