В игровом движке Unity3D общая последовательность кода для получения удаленных данных такова:
WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;
Каков основной механизм здесь?
Я знаю, что мы используем механизм yield для обработки следующего кадра во время завершения загрузки. Но что происходит под капотом, когда мы делаем yield return www
?
Какой метод вызывается (если есть, в классе WWW)? Unity использует потоки? Получает ли «верхний» слой Unity экземпляр www и что-то делает?
РЕДАКТИРОВАТЬ:
- Этот вопрос конкретно о внутренностях Unity3D. Меня не интересуют объяснения того, как
yield
оператор работает в C #. Вместо этого я ищу внутреннее представление о том, как Unity работает с этими конструкциями, чтобы позволить, например, WWW загружать фрагмент данных распределенным образом по нескольким кадрам.
yield return
для асинхронных операций является взломом. В «настоящей» C # -программе вы бы использовалиTask
для этого. Unity, вероятно, не использует их, потому что он был создан до .Net 4.0, когда онTask
был представлен.Ответы:
Это ключевое слово yield C # в действии - оно не делает ничего особенного с
www
объектом, скорее оно означает что-то особенное для метода, в котором он содержится. В частности, это ключевое слово может использоваться только в методе, который возвращаетIEnumerable
(илиIEnumerator
), и используется чтобы указать, какой объект будет «возвращен» перечислителем при вызове MoveNext .Это работает, потому что компилятор преобразует весь метод в отдельный класс, который реализует
IEnumerable
(илиIEnumerator
) с использованием конечного автомата - в результате получается, что тело самого метода не выполняется до тех пор, пока кто-то не перечислит через возвращаемое значение. Это будет работать с любым типом, в этом нет абсолютно ничего особенногоWWW
, скорее это особый содержащий метод.Взгляните за кулисы ключевого слова C # yield для более глубокого понимания того, какой код генерирует компилятор C #, или просто экспериментируйте и проверяйте код самостоятельно, используя что-то вроде IL Spy.
Обновление: уточнить
yield return
инструкцию, все, что происходит, - это то, что возвращается перечислитель - в этот момент тело метода не выполняетсяMoveNext
итератор, чтобы получить первое значение в последовательности. Это заставляет метод выполняться до первогоyeild return
оператора, после чего вызывающая сторона возобновляет работу (и, по-видимому, Unity продолжает визуализацию остальной части кадра)MoveNext
метод на итераторе один раз в каждом последующем кадре, в результате чего метод выполняется снова до следующегоyield return
оператора после каждого кадра, пока неyield break
будет достигнут конец метода или оператора (указывая конец последовательности)Единственный специальный бит здесь (и в нескольких из других случаев ) является то , что Unity не продвигает эту конкретную итератор следующего кадра, вместо этого он продвигает только итератор (вызвавший метод для продолжения выполнения) , когда загрузка завершена. Хотя, похоже, есть базовая YieldInstruction класс который, предположительно, содержит общий механизм для сигнализации Unity, когда итератор должен быть расширен,
WWW
класс не наследуется от этого класса, поэтому я могу только предположить, что существует особый случай для этот класс в движке Unity.Просто чтобы прояснить:
yield
ключевое слово не делает ничего особенного дляWWW
класса, скорее его специальная обработка, которую Unity предоставляет членам возвращаемого перечисления, вызывает такое поведение.Обновите второе: что касается механизма, который
WWW
использует для загрузки веб-страниц асинхронно, он, вероятно, использует либо метод HttpWebRequest.BeginGetResponse, который будет внутренне использовать асинхронный ввод-вывод, либо, альтернативно, он мог бы использовать потоки (либо создавая выделенный поток, либо используя пул потоков).источник
WWW
объектом, когда он получен, см.WWW
Ссылку .yield
кажется, в основном используется в Unity в сопрограммном контексте. Чтобы узнать больше о сопрограммах и почему они используют C #,yield
я рекомендую эту статью в блоге: сопрограммы Unity3D подробно . Большинство исследований в этом ответе происходит из этой статьи.Сопрограммы в Unity используются для инкапсуляции задач, которые:
Примерами таких задач являются поиск пути (пере) вычисления или, как в вашем вопросе, получение данных с веб-сайта.
Чтобы ответить на ваши подвопросы (в слегка измененном порядке):
WWW
Класс Unity предназначен для получения сопрограмм. Согласно комментариям к статье в блоге, на которую ссылаются выше, спекулятивный блок кода («верхний» уровень) оYieldInstruction
s на самом деле содержит переключатель, который также проверяет наличиеWWW
s. Затем этот код обеспечивает автоматическое завершение сопрограммы после завершения загрузки, как описано вWWW
справочном руководстве .В этом случае для загрузки данных «без блокировки остальной части игры»: да, скорее всего. (И потоки определенно используются для распаковки загруженных данных, о чем свидетельствует
WWW.threadPriority
.)источник
К сожалению, WWW реализован внутри как нативный код, что означает, что мы не можем смотреть на код. Из экспериментов могу сказать, что
WWW
не является производным отYieldInstruction
, поэтому, что бы ни происходило с вами,yield
оно должно обрабатываться кодом особого случая.Я никогда не замечал никакой разницы между
и
Я думаю, что это самый логичный способ реализовать это, и, скорее всего , это то, что происходит под капотом. Но я не знаю точно.
Unity не запускает новый поток для загрузки, по крайней мере, на некоторых платформах (iOS, веб-плеер). Или, если это так, он устанавливает
WWW.isDone
на основной поток. Я знаю это, потому что этот код:не работает
Я не думаю, что у вас могут быть более конкретные ответы, если кто-то, имеющий доступ к исходному коду Unity3d, не придет сюда.
источник
Поскольку Unity3D использует C # в качестве механизма сценариев, я предполагаю, что это стандартное ключевое слово yield , встроенное в C #. По сути, это означает, что он уже возвращает значение www, так что вы можете продолжить, пока на следующей итерации он вернет следующее значение и т. Д. ... Выход в основном создает конечный автомат и итератор в фоновом режиме.
источник
WWW
Ссылку . Кроме того, я не уверен, чтоyield
создает что-либо. Контекст итератора создается путем его реализацииIEnumerable
или использования в качестве возвращаемого типа. «Конечный автомат» тоже кажется выключенным. Конечно, есть государство, но одного этого недостаточно, верно? Может быть, вы могли бы уточнить это.