Dlaczego Unity używa refleksji, aby uzyskać metodę aktualizacji?

12

Dlaczego Unity użyć refleksji w celu uzyskania dostępu do MonoBehaviourmetod, takich jak wiadomości Awake, Update, Start, ...?

Czy nie byłoby wolno używać refleksji? Dlaczego nie wykorzystuje innych podejść takich jak Template Method? Może po prostu zdefiniować metody jako abstrakcyjne w MonoBehaviourklasie bazowej i zmusić podklasy do ich wdrożenia.

Emad
źródło

Odpowiedzi:

20

Tak nie jest

Jak nazywa się aktualizacja

Nie, Unity nie używa System.Reflection, aby znaleźć magiczną metodę za każdym razem, gdy musi ją wywołać.

Zamiast tego przy pierwszym dostępie do MonoBehaviour danego typu skrypt jest sprawdzany przez środowisko wykonawcze skryptów (Mono lub IL2CPP), czy ma zdefiniowane jakieś magiczne metody i czy informacje te są buforowane. Jeśli MonoBehaviour ma określoną metodę, jest dodawany do właściwej listy, na przykład jeśli skrypt ma zdefiniowaną metodę aktualizacji, jest dodawany do listy skryptów, które należy aktualizować w każdej ramce.

Podczas gry Unity po prostu iteruje te listy i wykonuje z nich metody - to takie proste. Również dlatego nie ma znaczenia, czy twoja metoda aktualizacji jest publiczna czy prywatna.

Jeśli chodzi o powody, dla których tak się dzieje, w dużej mierze odsyłam cię (czytelnika) do odpowiedzi DMGregory , która sprowadza się do równowagi dwóch konkurencyjnych rzeczy:

  1. Optymalizacja wydajności
  2. Łatwość wykorzystania nowego dewelopera

Nowy programista chce tylko, aby działał i nie musi wymyślać „jak podłączyć to do systemu zdarzeń?” ale powinno nadal działać szybko przy minimalnym obciążeniu.

Rozwiązanie jest prawdopodobnie najlepszym, jakie można osiągnąć w ramach tych dwóch ograniczeń. A przynajmniej najlepszy zespół deweloperów Unity w tym czasie. Możemy nigdy nie wiedzieć.

Draco18s nie ufa już SE
źródło
2
tl; dr ten sam proces, inny, szybszy interfejs API. Ale dlaczego nie wybrali innej metody, o którą poprosił OP?
wondra
10
Nie powinniśmy zaprzeczać, że istnieje prosty powód „złego projektu”. Po wydaniu kodu źródłowego c # użytkownicy znaleźli mnóstwo błędów i niewyjaśnionych decyzji projektowych. Niektórzy gracze mogli wreszcie zrozumieć te nieoczekiwane zachowania, z którymi walczyli przez lata.
Ćwiczenie
Poza kwestiami technicznymi jest to jedna z pierwszych rzeczy, których uczą się nowicjusze, więc musi być łatwe do dodania lub pominięcia.
Nikaas
6
Przyczyny tego projektu mogą być również historyczne. Unity zaczęło od własnego języka skryptowego, który następnie stopniowo wycofało się na korzyść C #. To naturalne, że chcieli nadal używać tych samych wzorów, nawet jeśli miało to na celu oszukać.
Philipp
3
Myślę, że możemy pożytecznie odpowiedzieć na „Co osiąga Jedność w ten sposób” - np. jaka jest przewaga nad zbiorem abstrakcyjnych metod publicznych, które muszą zostać zaimplementowane. Nie możemy odpowiedzieć „co myśleli twórcy Jedności?” ale możemy sobie poradzić „dlaczego to podejście jest przydatne dla użytkowników silnika?”
DMGregory
10

Może po prostu zdefiniować metody jako abstrakcyjne w klasie bazowej MonoBehaviour i zmusić podklasy do ich wdrożenia.

Jedną z zalet systemu, z którego korzysta Unity, jest to, że nie trzeba implementować każdej wiadomości MonoBehaviour, której jest ponad 60 .

Zwykle potrzebujemy tylko jednej lub kilku takich metod. Ponieważ niektóre są specyficzne dla fizyki 2D / 3D, niektóre unikalne dla kamer lub układów cząsteczkowych, jest bardzo mało prawdopodobne, aby jakikolwiek skrypt potrzebowałby ich wszystkich.

Pozostawiając wiadomości, których nie potrzebujemy jako niezaimplementowane, możemy wyraźnie zasygnalizować runtime: „nawet nie zawracaj sobie głowy wywołaniem OnCollisionStay2D na tym obiekcie - nie potrzebuję tego”

Może to być znaczna wygrana w zakresie wydajności. Teraz silnik może odfiltrować krótką listę, aby wywołać aktualizację tylko na ~ 20 obiektach w mojej scenie, które wymagają niestandardowej logiki aktualizacji w każdej ramce, a nie na wszystkich ~ 200 obiektach, które składają się na całą scenerię lub reagują tylko na zdarzenia fizyki.

DMGregory
źródło
Tylko pytanie ... o ile wiem (nie osobiście), jeśli masz tysiące GameObjectów, z których wszystkie używają skryptu z metodą aktualizacji, masz znaczący wpływ na wydajność. Alternatywą jest używanie list i powtarzanie ich za pomocą tylko jednej metody aktualizacji w skrypcie menedżera, która wydaje się znacznie bardziej wydajna. Czy tak jest nadal, czy już to się zmieniło? - Jeśli nic się nie zmieni, oznacza to, że sposób, w jaki Unity to robi, nie jest zbyt optymalny pod względem wydajności.
Bitwa
4
@Battle to, co opisujesz, jest zgodne z linkiem, który przytacza Draco18s z 2015 roku, i podejrzewam, że to nadal prawda. Możliwość pozostawienia niezdefiniowanej metody aktualizacji dla tych tysięcy obiektów jest dokładnie tym, co pozwala na optymalizację. Gdyby każdy MonoBehaviour miał metodę aktualizacji zdefiniowaną za pomocą abstrakcyjnej klasy bazowej, jak opisano w sekcji cytowanego przeze mnie pytania, wówczas nie mielibyśmy możliwości ograniczenia naszych wywołań aktualizacji tylko do iteracji listy menedżera. Zauważ, że te pytania i odpowiedzi nie dotyczą „Czy podejście Unity jest optymalne?” ale tylko w jaki sposób obecne podejście może być korzystne.
DMGregory
Ach, rozumiem, to ma sens. Dziękuję za odpowiedź!
Bitwa
@Battle Jest to nadal prawda. Podczas gdy Unity działa wydajniej niż alternatywy, wciąż ma więcej narzutu niż zwykłe wywołanie funkcji.
Draco18s nie ufa już
@ Draco18s - Tak. Już wdrożyłem UpdateManager dla moich projektów, który zajmuje się tą optymalizacją dawno temu. Chciałem tylko trochę potwierdzenia, że ​​nadal jest potrzebne / przydatne.
Bitwa