⌚Корутины
Автор: Дмитрий Прокопьев
Что такое корутина?
Слово "Корутина" (от англ. Coroutine
) можно дословно перевести как "сопроцесс", то есть процесс, работающий параллельно основному.
Корутина это механизм Unity
, который позволяет нам выполнять растянутые во времени действия удобным образом. По названию "Сопроцесс" можно ошибочно предположить, что корутины выполняются в отдельном процессе, но это не так. Они создают иллюзию параллельности для нашего удобства, но под капотом они выполняются в основном цикле обновления кадров - Update
, с некоторыми исключениями.
С точки зрения производительности корутины чуть более затратны чем вызов Update
, однако несущественно. По этой причине, если удобнее использовать корутину чем Update
, - то она предпочтительна.
Зачем нужны корутины?
Допустим, мы хотим реализовать отсчет времени от 10 до 1 с помощью счетчика на экране. Реализация будет выглядеть так:
public class Timer : MonoBehaviour
{
private float _elapsedTime = 0f;
private int _counter = 10;
private void Update()
{
_elapsedTime += Time.deltaTime;
if (_elapsedTime >= 1)
{
_elapsedTime = 0;
if (_counter > 0)
{
DisplayCountdown(_counter--);
}
}
}
private void DisplayCountdown(int countdown)
{
// отобразить на экране данное число
}
}
Ужасно неудобно, так ведь? Создали два фактически лишних поля и выстроили двухэтажное условие в Update. А если у таких действий ещё и сложная логика, то вообще грустно. Здесь нас и спасут корутины!
Как можно упростить эту задачу?
В каком-нибудь счастливом сне мы могли бы написать такое:
private void MagicCountdown(int start = 10)
{
for (int i = start; i > 0; i--)
{
DisplayCountdown(i);
// магическим образом подождать одну секунду
// и потом продолжить выполнение
}
}
Насколько проще это было бы сделать! А секрет в том, что в корутинах это так и работает.
Синтаксис корутин
Вот так будет выглядеть готовая корутина, делающая то же самое:
private IEnumerator Countdown(int start = 10)
{
for (int i = start; i > 0; i--)
{
DisplayCountdown(i);
yield return new WaitForSeconds(1f);
}
}
Можно увидеть, что корутина - это метод с возвращаемым типом IEnumerator
. Это связано с технической особенностью реализации корутин.
Инструкция yield return
позволяет указать задержку во времени на данной строке. Мы будто говорим исполняещему процессу "Выйди и зайди нормально" "Выйди, подожди сколько сказали, а потом продолжи". Когда исполнение корутины доходит до такой инструкции, оно временно прерывается и возобновляется после исполнения переданной инструкции.
Можно заметить, что на каждой итерации цикла мы заново создаем объект WaitForSeconds
. Так как эти объекты одинаковые, нет смысла делать множество копий и засорять ими оперативную память. А также нам может потребоваться изменить время ожидания. Поэтому мы закешируем этот объект в целях оптимизации и выделим параметр:
private IEnumerator Countdown(float delay, int start = 10)
{
var wait = new WaitForSeconds(delay);
for (int i = start; i > 0; i--)
{
DisplayCountdown(i);
yield return wait;
}
}
Что еще нужно знать?
После инструкции yield return можно передать любой объект наследующий тип YieldInstruction
. Класс-наследник можно реализовать самостоятельно, однако есть множество уже реализованных классов, подробнее про них можно прочитать здесь.
Помимо написания корутин нужно также уметь ими управлять, о чем можно почитать здесь.
Мы разобрались с синтаксисом, но чтобы пользоваться корутинами поистине эффективно, нужно понимать, как они работают! Об этом можно почитать здесь.
Важно. Когда исполнение корутины доходит до конца, она автоматически завершается - явно останавливать ее не нужно.
Last updated
Was this helpful?