Я понимаю принцип сопрограмм. Я знаю, как заставить стандарт StartCoroutine
/ yield return
шаблон работать на C # в Unity, например, вызвать метод, возвращающийся IEnumerator
через, StartCoroutine
и в этом методе что-то сделать, сделать, yield return new WaitForSeconds(1);
чтобы подождать секунду, а затем сделать что-то еще.
У меня вопрос: что на самом деле происходит за кулисами? Что на StartCoroutine
самом деле делает? Что IEnumerator
это WaitForSeconds
возвращение? Как StartCoroutine
вернуть управление «чему-то еще» в вызываемом методе? Как все это взаимодействует с моделью параллелизма Unity (где множество вещей происходит одновременно без использования сопрограмм)?
IEnumerator
/IEnumerable
(или универсальные эквиваленты) и содержащиеyield
ключевое слово. Найдите итераторы.Ответы:
Часто упоминаемая ссылка подробных сведений о сопрограммах Unity3D мертва. Поскольку это упоминается в комментариях и ответах, я опубликую здесь содержание статьи. Этот контент исходит из этого зеркала .
источник
Первый заголовок ниже - прямой ответ на вопрос. Два заголовка после них более полезны для обычного программиста.
Возможно скучные детали реализации сопрограмм
Сопрограммы описаны в Википедии и других местах. Здесь я просто представлю некоторые детали с практической точки зрения.
IEnumerator
,yield
и т. д. - это функции языка C # , которые используются в Unity для иных целей.Проще говоря, объект
IEnumerator
утверждает, что у него есть набор значений, которые вы можете запрашивать одно за другим, вроде какList
. В C # функция с подписью для возвратаIEnumerator
не обязана фактически создавать и возвращать ее, но может позволить C # предоставлять неявноеIEnumerator
. Затем функция можетIEnumerator
лениво предоставлять содержимое того, что будет возвращено в будущем, посредствомyield return
операторов. Каждый раз, когда вызывающий объект запрашивает другое значение из этого неявного выражения,IEnumerator
функция выполняется до следующегоyield return
оператора, который предоставляет следующее значение. Как побочный продукт этого, функция приостанавливается до тех пор, пока не будет запрошено следующее значение.В Unity мы не используем их для предоставления будущих значений, мы используем тот факт, что функция приостанавливается. Из-за этой эксплуатации многие вещи в сопрограммах в Unity не имеют смысла (причем здесь что-
IEnumerator
то общего? Что такоеyield
? Почемуnew WaitForSeconds(3)
? И т. Д.). Что происходит «под капотом»: значения, которые вы предоставляете через IEnumerator, используются,StartCoroutine()
чтобы решить, когда запрашивать следующее значение, которое определяет, когда ваша сопрограмма снова возобновит работу.Ваша игра Unity является однопоточной (*)
Сопрограммы - это не потоки. В Unity есть один основной цикл, и все те функции, которые вы пишете, вызываются одним и тем же основным потоком по порядку. Вы можете проверить это, поместив
while(true);
в любую из ваших функций или сопрограмм. Это заморозит все, даже редактор Unity. Это свидетельство того, что все работает в одном основном потоке. Эта ссылка, которую Кей упомянул в своем комментарии выше, также является отличным ресурсом.(*) Unity вызывает ваши функции из одного потока. Итак, если вы сами не создаете поток, написанный вами код будет однопоточным. Конечно, Unity использует другие потоки, и вы можете сами создавать потоки, если хотите.
Практическое описание сопрограмм для программистов игр
В основном, когда вы звоните
StartCoroutine(MyCoroutine())
, это не так же , как обычный вызов функции кMyCoroutine()
, до первогоyield return X
, гдеX
что - то вродеnull
,new WaitForSeconds(3)
,StartCoroutine(AnotherCoroutine())
,break
и т.д. Это когда он начинает отличаясь от функции. Unity «приостанавливает» эту функцию прямо на этойyield return X
строке, продолжает другие дела, и некоторые кадры проходят, и когда снова приходит время, Unity возобновляет эту функцию сразу после этой строки. Он запоминает значения для всех локальных переменных в функции. Таким образом, у вас может бытьfor
цикл, который, например, повторяется каждые две секунды.Когда Unity возобновит вашу сопрограмму, зависит от того, что
X
было в вашемyield return X
. Например, если вы использовалиyield return new WaitForSeconds(3);
, он возобновится через 3 секунды. Если вы использовалиyield return StartCoroutine(AnotherCoroutine())
, он возобновляется после того, какAnotherCoroutine()
будет полностью завершен, что позволяет вам вовремя вкладывать поведение. Если вы только что использовали ayield return null;
, он возобновится прямо в следующем кадре.источник
Это не может быть проще:
Unity (и все игровые движки) основаны на фреймах .
Вся суть, весь смысл существования Unity в том, что он основан на фреймах. Движок делает "каждый кадр" за вас. (Анимирует, отображает объекты, занимается физикой и т. Д.)
Вы можете спросить… «О, это здорово. Что, если я хочу, чтобы двигатель что-то делал для меня в каждом кадре? Как мне сказать движку делать то-то и то-то в кадре?»
Ответ ...
Именно для этого и нужна «сопрограмма».
Это так просто.
И учтите это ....
Вы знаете функцию «Обновить». Проще говоря, все, что вы туда вставляете, делается в каждом кадре . Это буквально то же самое, без каких-либо отличий от синтаксиса coroutine-yield.
Абсолютно никакой разницы.
Сноска: как все отметили, в Unity просто нет потоков . «Фреймы» в Unity или в любом игровом движке никак не связаны с потоками.
Coroutines / yield - это просто способ доступа к фреймам в Unity. Вот и все. (И действительно, это абсолютно то же самое, что и функция Update (), предоставляемая Unity.) Вот и все, это так просто.
источник
Update()
? Я имею в виду, что между этими двумя реализациями и вариантами их использования должна быть хотя бы небольшая разница, которая довольно очевидна.В последнее время копался в этом, написал здесь сообщение - http://eppz.eu/blog/understanding-ienumerator-in-unity-3d/ - которое проливает свет на внутренности (с примерами плотного кода), базовый
IEnumerator
интерфейс, и как это используется для сопрограмм.источник
Базовые функции Unity, которые вы получаете автоматически, - это функция Start () и функция Update (), поэтому Coroutine по сути являются функциями, такими же, как функции Start () и Update (). Любую старую функцию func () можно вызвать так же, как и сопрограмму. Очевидно, что Unity установила определенные границы для сопрограмм, которые отличают их от обычных функций. Одно отличие - вместо
Ты пишешь
для сопрограмм. Точно так же вы можете контролировать время в обычных функциях с помощью таких строк кода, как
У сопрограммы есть специальный дескриптор, позволяющий управлять временем.
Хотя это не единственное, что можно делать внутри IEnumerator / Coroutine, это одна из полезных вещей, для которых используются Coroutine. Вам нужно будет изучить API сценариев Unity, чтобы узнать о других конкретных применениях Coroutines.
источник
StartCoroutine - это метод вызова функции IEnumerator. Это похоже на простой вызов простой функции void, с той лишь разницей, что вы используете ее в функциях IEnumerator. Этот тип функции уникален, так как он позволяет использовать специальную функцию yield , обратите внимание, что вы должны что-то вернуть. Насколько я знаю. Здесь я написал простую игру с мерцанием над текстом в единстве
Затем я вызвал его из самого IEnumerator
Как видите, я использовал метод StartCoroutine (). Надеюсь, я как-то помог. Я сам новичок, поэтому, если вы поправите меня или оцените меня, любой отзыв будет отличным.
источник