Mirrored from my blog.
I’m currently developing a game in C# using
MonoGame. It’s been great so far, I really love this language and the features it has.
One thing I’ve found myself missing from my home grown C# engine though is a simple
Coroutine system, one
like Unity has.
Read the pages linked above to learn a bit more about how Coroutines (can) work if you haven’t used them before. But basically, they provide an awesome tool for controlling logic flow in your game, and are wonderful for managing things like scripted/sequential events, behavior trees, and motion tweening.
So to gain this power back, I wrote my own Coroutine system in C#, and I’ve written it into a nice little standalone class that anyone is welcome to use in their own projects if they like!
Coroutines.csusing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace CoroutineTest
{
public class Coroutines
{
List<IEnumerator> routines = new List<IEnumerator>();
public Coroutines()
{
}
public void Start(IEnumerator routine)
{
routines.Add(routine);
}
public void StopAll()
{
routines.Clear();
}
public void Update()
{
for (int i = 0; i < routines.Count; i++)
{
if (routines[i].Current is IEnumerator)
if (MoveNext((IEnumerator)routines[i].Current))
continue;
if (!routines[i].MoveNext())
routines.RemoveAt(i--);
}
}
bool MoveNext(IEnumerator routine)
{
if (routine.Current is IEnumerator)
if (MoveNext((IEnumerator)routine.Current))
return true;
return routine.MoveNext();
}
public int Count
{
get { return routines.Count; }
}
public bool Running
{
get { return routines.Count > 0; }
}
}
}
So if you're writing a game in SFML.Net, OpenTK, MonoGame, XNA, or whatever, you should be able to use this.
Here's a simple test file if you want to try it out. Just start a new Console Application solution, include the Coroutines.cs file above, and make this the main program file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Diagnostics;
namespace CoroutineTest
{
class Program
{
//"Death" by John Donne
const string poem = "\"Death\" by John Donne\n\n" +
"Death be not proud, though some have called thee\n" +
"Mighty and dreadfull, for, thou art not so,\n" +
"For, those, whom thou think'st, thou dost overthrow,\n" +
"Die not, poore death, nor yet canst thou kill me.\n" +
"From rest and sleepe, which but thy pictures bee,\n" +
"Much pleasure, then from thee, much more must flow,\n" +
"And soonest our best men with thee doe goe,\n" +
"Rest of their bones, and soules deliverie.\n" +
"Thou art slave to Fate, Chance, kings, and desperate men,\n" +
"And dost with poyson, warre, and sicknesse dwell,\n" +
"And poppie, or charmes can make us sleepe as well,\n" +
"And better then thy stroake; why swell'st thou then;\n" +
"One short sleepe past, wee wake eternally,\n" +
"And death shall be no more; death, thou shalt die.";
static void Main(string[] args)
{
var coroutines = new Coroutines();
coroutines.Start(ReadPoem(poem));
while (coroutines.Running)
coroutines.Update();
}
static IEnumerator ReadPoem(string poem)
{
//Read the poem letter by letter
foreach (var letter in poem)
{
Console.Write(letter);
switch (letter)
{
//Pause for punctuation
case ',':
case ';':
yield return Pause(0.5f);
break;
//Long pause for full-stop
case '.':
yield return Pause(1);
break;
//Short pause for anything else
default:
yield return Pause(0.05f);
break;
}
}
//Wait for user input to close
Console.WriteLine("\nPress any key to exit");
Console.ReadLine();
}
static IEnumerator Pause(float time)
{
var watch = Stopwatch.StartNew();
while (watch.Elapsed.TotalSeconds < time)
yield return 0;
}
}
}
Hope somebody finds this useful