Базовий механізм у «віддачі прибутковості www» Unity3D Game Engine


14

У ігровому двигуні Unity3D поширена кодова послідовність для отримання віддалених даних така:

WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;

Який тут основний механізм?

Я знаю, що ми використовуємо механізм виходу, щоб дозволити обробці наступного кадру, поки завантаження завершується. Але що відбувається під капотом, коли ми робимо yield return www?

Який метод викликається (якщо такий є у класі WWW)? Чи використовує Unity теми? Чи "верхній" шар Unity влаштовує примірник www і щось робить?

Редагувати:

  • Це питання стосується внутрішніх справ Unity3D. Мене не цікавлять пояснення того, як yieldоператор працює в C #. Натомість я шукаю внутрішнє уявлення про те, як Unity поводиться з цими конструкціями, щоб, наприклад, до WWW дозволити завантажувати частину даних розподіленим чином через декілька кадрів.

1
Зауважте, що використання yield returnдля асинхронних операцій - це злом. У "справжній" програмі C # ви використовуєте Taskдля цього. Unity, ймовірно, не використовує їх, оскільки він був створений раніше .Net 4.0, коли він Taskбув представлений.
BlueRaja - Danny Pflughoeft

Відповіді:


8

Це ключове слово прибутковості C # в дії - воно не робить нічого особливого з wwwоб'єктом, скоріше означає щось особливе для методу, який міститься в ньому. Зокрема, це ключове слово можна використовувати лише в методі, який повертає IEnumerable(або IEnumerator), і використовується щоб вказати, який об'єкт буде повернутий перелічувачем при виклику MoveNext .

Це працює тому, що компілятор перетворює весь метод в окремий клас, який реалізує IEnumerable(або IEnumerator) за допомогою машини машини - чистий результат полягає в тому, що тіло методу не виконується, поки хтось не перерахує через повернене значення. Це буде працювати з будь-яким типом, в цьому немає абсолютно нічого особливого WWW, скоріше його особливий метод, що містить.

Погляньте на те, що за кадром ключового слова врожаю C #, щоб дізнатися, який код генерує компілятор C #, або просто експериментуйте та огляньте код, використовуючи щось на зразок IL Spy


Оновлення: для уточнення

  • Коли Unity викликає підпрограму, яка містить yield returnоператор, все, що трапляється, - це повернення нумератора - жоден з методів не виконується в цей момент
  • Щоб отримати тіло методу для виконання Unity, необхідно зателефонувати MoveNextна ітератор, щоб отримати перше значення в послідовності. Це призводить до того, що метод виконує до першого yeild returnоператора, після чого абонент поновлюється (і, мабуть, Unity продовжує виводити решту кадру)
  • Як я це розумію, Unity зазвичай продовжує викликати MoveNextметод на ітераторі раз у кожному наступному кадрі, змушуючи метод виконувати знову до наступного yield returnоператора, коли кожен кадр, доки не буде досягнуто кінця методу чи yield breakоператора (із зазначенням кінець послідовності)

Єдиний спеціальний біт тут (і в декількох з інших випадків ) є те , що Unity не просувається цю конкретну итератор наступного кадру, замість цього він просуває тільки итератор (що викликав метод для продовження виконання) , коли завантаження завершена. Хоча, здається, існує базовий клас YieldInstruction, який, імовірно, містить загальний механізм передачі сигналу Unity, коли ітератор повинен бути вдосконалений, WWWклас, схоже, не успадковує цей клас, тому я можу лише припускати, що існує особливий випадок для цього класу в двигуні Unity.

Щоб було зрозуміло - yieldключове слово не робить нічого особливого для WWWкласу, скоріше, це особлива обробка, яку Unity надає членам повернутого перерахування, що викликає таку поведінку.


Оновіть друге: Що стосується механізму, який WWWвикористовує для асинхронного завантаження веб-сторінок, він, ймовірно, використовує або метод HttpWebRequest.BeginGetResponse, який буде внутрішньо використовувати асинхронний IO, або в якості альтернативи, він може використовувати нитки (або створюючи виділений потік, або використовуючи пул потоків).


5
Насправді в Єдності щось особливе відбувається з WWWоб'єктом, коли він виходить, див WWW. Посилання .
Ерік

9

yieldЗдається, в основному використовується в Unity в контексті підпрограми. Щоб ознайомитись докладніше з питаннями та чому вони використовують C #, yieldя рекомендую цю статтю в блозі: Unity3D coroutines докладно . Більшість досліджень у цій відповіді походить із цієї статті.

Супроводи в Єдності використовуються для інкапсуляції завдань, які:

  1. Може зайняти більше часу, ніж кадр для відтворення (тим самим спричиняючи уповільнення), і
  2. Можна виконувати окремо від ігрового циклу (оскільки результат не повинен бути доступним для поточного кадру).

Прикладами подібних завдань є обчислення (повторне) обчислення маршруту або, як це стосується вашого питання, отримання даних з веб-сайту.

Щоб відповісти на запитання (у дещо зміненому порядку):

Який метод викликається (якщо такий є у класі WWW)? Чи "верхній" шар Unity влаштовує примірник www і щось робить?

WWWКлас Unity розроблений таким чином, щоб отримати його за допомогою програми. Відповідно до вищезазначених коментарів до статті блогу, спекулятивний блок коду ("верхній" шар ") про YieldInstructions фактично містить перемикач, який також перевіряє наявність вихідних WWWs. Цей код потім гарантує, що підпрограма автоматично завершиться після завантаження, як описано в WWWпосиланні .

Чи використовує Unity теми?

У цьому випадку завантажувати дані ", не блокуючи решту гри": так, швидше за все. (І нитка напевно використовується для декомпресії завантажених даних, про що свідчить WWW.threadPriority.)


приємно! Я побачив коментар altdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail/… і, схоже, до WWW звертаються спеціально. Тож я хотів би знати, як це здійснюється для того, щоб дозволити завантаження здійснюватися через кілька кадрів, не використовуючи потоки?
thyandrecardoso

Гарна думка! Я припускаю, що на цьому рівні має відбуватися якась нитка, я відредагую свою відповідь, щоб це відобразити.
Ерік

1
@thyandrecardoso я б здогадувався, що він використовує HttpWebRequest.BeginGetResponse або подібне, однак ви можете декомпілювати збірку, щоб підтвердити це, якщо воно дійсно має значення для вас.
Джастін

на даний момент я дійсно чекаю лише "найкращого пояснення" ... було б чудово, якби хтось із команди розробників Unity дав "правильну відповідь" 8 -) ... врешті-решт, я думаю, що реальної реалізації не буде далеко не ті, що вже були дані тут ... У мене немає фактичної потреби декомпілювати збірку і все це точно знати. Але я можу спробувати пізніше :)
thyandrecardoso

7

На жаль, WWW реалізований внутрішньо як нативний код, це означає, що ми не можемо дивитися на код. З експерименту я можу це сказати

  1. WWWце НЕ походять від YieldInstruction, тому все , що відбувається , коли ви yieldйого повинен оброблятися спеціальним випадок кодом.
  2. Я ніколи не одержими різницею між

    yield return www;

    і

    while(!www.isDone)
        yield return null;

    Я думаю, що це найбільш логічний спосіб його втілення, і, мабуть , саме це відбувається під капотом. Але я не знаю точно.

  3. Unity не запускає нову нитку для завантаження, принаймні на деяких платформах (iOS, веб-плеєр). Або якщо так, то він встановлюється WWW.isDoneна головну нитку. Я знаю це тому, що цей код:

    while(!www.isDone)
        Thread.Sleep(500);

    не працює.

Я не думаю, що ти можеш мати більш конкретні відповіді, якщо сюди не прийде хтось із доступом до вихідного коду Unity3d.


Так. Дійсно приємні уявлення! Спасибі! Незважаючи на те, що не запускає нитку сама по собі, найбільш вірогідною справою, що може статися, є WWW (або шар двигуна) за допомогою HttpWebRequest.BeginGetResponse (або щось подібне) ... правда? Що-небудь повністю асинхронне повинно відбуватися в будь-якому випадку ... завантаження не може бути "призупинено".
thyandrecardoso

** я не можу бути "призупинено" між кадрами, я маю на увазі.
thyandrecardoso

2

Оскільки Unity3D використовує C # в якості механізму сценаріїв, я вважаю, що це стандартне ключове слово, яке вбудоване в C #. В основному, це означає, що він повертає значення www вже так, що ви можете продовжувати, а під час наступної ітерації він поверне наступне значення тощо. Вихід в основному створює стан машини та ітератор у фоновому режимі.


Так, я думаю, що я маю основні поняття про ключове слово. Однак, що відбувається під час конструктора WWW, який дозволяє використовувати цю "державну машину"? Я маю на увазі, що "return return www", схоже, не називає нічого всередині класу WWW ... Коли ви говорите "це означає, що він вже повертає значення www", яке значення є примірником www? Що буде робити цей екземпляр при наступній "ітерації"?
thyandrecardoso

1
У спільнотах Unity, вихід WWW є особливим випадком, див WWW. Посилання . Крім того, я не впевнений, що yieldщось створює. Контекст ітератора створюється шляхом його впровадження IEnumerableабо використання як типу повернення. "Державна машина" теж здається вимкненою. Звичайно, є держава, але це одне не є достатнім майном, правда? Можливо, ви могли б детальніше розглянути це.
Ерік

1
Ось хороша довідка про нормальну поведінку C #. shadowcoding.blogspot.nl/2009/01/yield-and-c-state-machine.html . Вихід генерує багато коду, щоб відстежувати, де він знаходиться в ітераторі. Про особливий випадок Unity, що приносить WWW, я не знав, але, згідно з документами, він не має нічого із типовим ключовим словом C #, це дуже заплутано, вони могли просто зробити метод асинхронізації.
Рой Т.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.