W silniku gry Unity3D powszechna sekwencja kodów do uzyskiwania zdalnych danych jest następująca:
WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;
Jaki jest tutaj mechanizm podstawowy?
Wiem, że używamy mechanizmu wydajności, aby umożliwić przetwarzanie następnej klatki podczas pobierania. Ale co się dzieje pod maską, kiedy to robimy yield return www
?
Jaka metoda jest wywoływana (jeśli istnieje) w klasie WWW? Czy Unity używa wątków? Czy „górna” warstwa jedności bierze instancję www i coś robi?
EDYTOWAĆ:
- To pytanie dotyczy w szczególności elementów wewnętrznych Unity3D. Nie interesują mnie wyjaśnienia dotyczące działania
yield
instrukcji w języku C #. Zamiast tego szukam wewnętrznego spojrzenia na to, jak Unity radzi sobie z tymi konstrukcjami, aby na przykład umożliwić WWW pobieranie fragmentu danych w sposób rozproszony w kilku ramkach.
yield return
do operacji asynchronicznych to włamanie. W „prawdziwym” programie C # użyłbyśTask
do tego celu. Unity prawdopodobnie ich nie używa, ponieważ został stworzony przed .Net 4.0, kiedyTask
został wprowadzony.Odpowiedzi:
Jest to słowo kluczowe C # w działaniu - nie robi nic specjalnego z
www
obiektem, a raczej oznacza coś specjalnego dla metody w nim zawartej. W szczególności tego słowa kluczowego można użyć tylko w metodzie, która zwracaIEnumerable
(lubIEnumerator
) i jest używana wskazujący, który obiekt zostanie „zwrócony” przez moduł wyliczający, gdy zostanie wywołane MoveNext .Działa, ponieważ kompilator konwertuje całą metodę na osobną klasę, która implementuje
IEnumerable
(lubIEnumerator
) za pomocą automatu stanów - wynik netto jest taki, że ciało samej metody nie jest wykonywane, dopóki ktoś nie wyliczy wartości zwracanej. Działa to z każdym typem, nie ma w tym absolutnie nic specjalnegoWWW
, a raczej metoda zawierająca, która jest wyjątkowa.Rzuć okiem za kulisy słowa kluczowego wydajności C #, aby uzyskać więcej informacji na temat tego, jaki rodzaj kodu generuje kompilator C #, lub po prostu eksperymentuj i sprawdź kod samodzielnie, używając czegoś takiego jak IL Spy
Aktualizacja: w celu wyjaśnienia
yield return
instrukcję, dzieje się tak, że zwracany jest moduł wyliczający - w tym momencie nie jest wykonywana żadna treść metodyMoveNext
iterator w celu uzyskania pierwszej wartości w sekwencji. Powoduje to, że metoda wykonuje się do pierwszejyeild return
instrukcji, w którym to momencie osoba dzwoniąca wznawia (i prawdopodobnie Unity renderuje resztę ramki)MoveNext
metodę na iteratorze co każdą kolejną ramkę, powodując, że metoda wykonuje się ponownie aż do następnejyield return
instrukcji po każdej ramce, aż do osiągnięcia końca metody lubyield break
instrukcji (wskazując koniec sekwencji)Jedynym specjalny bit tutaj (oraz w kilku z pozostałych przypadkach ) jest to, że Unity nie posuwa tej konkretnej iterator następną ramkę, zamiast tylko przesuwa iterator (powodując metodę, aby kontynuować wykonywanie) po zakończeniu pobierania. Chociaż wydaje się, że istnieje podstawowa klasa YieldInstruction, która prawdopodobnie zawiera ogólny mechanizm sygnalizowania Unity, kiedy iterator powinien być zaawansowany,
WWW
klasa nie wydaje się dziedziczyć po tej klasie, więc mogę jedynie założyć, że istnieje specjalny przypadek ta klasa w silniku Unity.Żeby było jasne -
yield
słowo kluczowe nie robi nic specjalnego dlaWWW
klasy, a raczej specjalną obsługę, jaką Unity zapewnia członkom zwróconego wyliczenia, co powoduje takie zachowanie.Zaktualizuj drugi: Jeśli chodzi o mechanizm, który
WWW
używa do asynchronicznego pobierania stron internetowych, prawdopodobnie używa albo metody HttpWebRequest.BeginGetResponse, która wewnętrznie użyje asynchronicznego We / Wy lub alternatywnie może użyć wątków (albo tworząc dedykowany wątek, albo używając puli wątków).źródło
WWW
przedmiotem, gdy jest on poddawany, patrzWWW
odnośnik .yield
wydaje się być stosowany głównie w Unity w kontekście korporacyjnym. Aby przeczytać więcej o coroutines i dlaczego używają C #yield
polecam ten artykuł na blogu: Coroutines Unity3D szczegółowo . Większość badań w tej odpowiedzi pochodzi z tego artykułu.Coroutines in Unity służą do enkapsulacji zadań, które:
Przykładami tego rodzaju zadań są obliczenia (ponowne) odnajdywania ścieżek lub, jak w przypadku twojego pytania, pobieranie danych ze strony internetowej.
Aby odpowiedzieć na pytania (w nieco zmodyfikowanej kolejności):
WWW
Klasa Unity została zaprojektowana w taki sposób, aby pochodzić z koroutyn. Zgodnie z komentarzami do powyższego artykułu na blogu, spekulatywny blok kodu („górna” warstwa) oYieldInstruction
s zawiera przełącznik, który również sprawdza, czy uzyskanoWWW
s. Ten kod gwarantuje, że coroutine zakończy się automatycznie po zakończeniu pobierania, jak opisano wWWW
odnośniku .W takim przypadku, aby pobrać dane „bez blokowania reszty gry”: tak, najprawdopodobniej. (A wątkowanie jest zdecydowanie używane do dekompresji pobranych danych, o czym świadczy
WWW.threadPriority
.)źródło
Niestety WWW jest implementowane wewnętrznie jako kod macierzysty, co oznacza, że nie możemy na niego patrzeć. Z eksperymentów mogę to powiedzieć
WWW
nie jest pochodnąYieldInstruction
, więc niezależnie od tego , co się dzieje, gdy trzebayield
to zrobić, należy użyć specjalnego kodu.Nigdy nie zauważyłem żadnej różnicy między
i
Myślę, że jest to najbardziej logiczny sposób na jego wdrożenie i najprawdopodobniej dzieje się to pod maską. Ale nie wiem na pewno.
Unity nie rozpoczyna nowego wątku do pobrania, przynajmniej na niektórych platformach (iOS, webplayer). A jeśli tak, ustawia
WWW.isDone
się na głównym wątku. Wiem to, ponieważ ten kod:nie działa
Nie sądzę, abyś mógł uzyskać bardziej szczegółowe odpowiedzi, chyba że przyjedzie ktoś z dostępem do kodu źródłowego Unity3d.
źródło
Ponieważ Unity3D używa C # jako silnika skryptowego, przypuszczam, że jest to standardowe słowo kluczowe wydajności, które jest wbudowane w C #. Zasadniczo oznacza to, że zwraca już wartość www, aby można było kontynuować, podczas gdy następna iteracja zwróci następną wartość itp. Yield zasadniczo tworzy maszynę stanu i iterator w tle.
źródło
WWW
odnośnik . Co więcej, nie jestem pewien, czyyield
coś tworzy. Kontekst iteratora jest tworzony przez implementacjęIEnumerable
lub użycie go jako typu zwracanego. „Maszyna państwowa” też wydaje się wyłączona. Jasne, istnieje państwo, ale samo to nie jest wystarczającą własnością, prawda? Może mógłbyś to rozwinąć.