Чтобы понять как они работают, нужно понять как работает Enumerator. Если коротко, то это некий объект, у которого есть метод MoveNext(), если его вызвать, то произойдет переключение на следующий шаг:
// step yield return null; // step 2
А теперь каким образом юнити собственно это делает. Раз у нас есть объект, мы можем добавить его в какой-нибудь список.
IEnumerator MyMethod() { // step 1 yield return null; // step 2 } list.Add(MyMethod());
А теперь в update мы просто переключаем шаги:
for (int i = 0; i < list.Count; ++i) { if (item.MoveNext() == false) { item.Current // тут мы можем проверить возвращаемое значение, например, если там внутренняя корутина, то ее тоже хорошо бы выполнить 🙂 list.RemoveAt(i); --i; } }
Таким образом, мы будем выполнять шаги, пока есть чего выполнять.
Корутины в юнити работают примерно таким образом. Еще я бы добавил, что при вызове StartCoroutine сразу выполняется первый шаг, т.е. вызывается MoveNext(), если он возрващает true, то значит дальше что-то есть и нужно добавлять корутину в список выполнения.
Т.е. нужно понять главное: корутины - это не какая-то особенная штуковина, которая работает только в юнити, это стандартный синтаксис и коллекции C#.