Warum verwendet Unity Reflection, um die Aktualisierungsmethode abzurufen?

11

Warum funktioniert Unity Verwendung Reflexion , um Zugang MonoBehaviourNachricht Methoden wie Awake, Update, Start, ...?

Wäre es nicht langsam, Reflexion zu verwenden? Warum nutzt es nicht andere Ansätze wie Template Method? Es könnte einfach die Methoden als abstrakt in der MonoBehaviourBasisklasse definieren und die Unterklassen zwingen, sie zu implementieren.

Emad
quelle

Antworten:

19

Das tut es nicht

Wie Update aufgerufen wird

Nein, Unity verwendet System.Reflection nicht, um jedes Mal eine magische Methode zu finden, wenn eine aufgerufen werden muss.

Stattdessen wird beim ersten Zugriff auf ein MonoBehaviour eines bestimmten Typs das zugrunde liegende Skript über die Skriptlaufzeit (entweder Mono oder IL2CPP) überprüft, ob magische Methoden definiert sind, und diese Informationen werden zwischengespeichert. Wenn ein MonoBehaviour über eine bestimmte Methode verfügt, wird diese zu einer geeigneten Liste hinzugefügt. Wenn für ein Skript beispielsweise die Aktualisierungsmethode definiert ist, wird sie zu einer Liste von Skripten hinzugefügt, die in jedem Frame aktualisiert werden müssen.

Während des Spiels durchläuft Unity nur diese Listen und führt Methoden daraus aus - so einfach. Aus diesem Grund spielt es auch keine Rolle, ob Ihre Update-Methode öffentlich oder privat ist.

Aus den Gründen, aus denen dies geschieht, verweise ich Sie (den Leser) weitgehend auf die Antwort von DMGregory , die sich auf ein Gleichgewicht zweier konkurrierender Dinge beschränkt:

  1. Leistungsoptimierung
  2. Einfache Nutzung neuer Entwickler

Ein neuer Entwickler möchte nur, dass es funktioniert, und möchte nicht herausfinden müssen, wie ich dies mit dem Ereignissystem verbinde. aber es sollte immer noch schnell mit minimalem Overhead laufen.

Die Lösung ist wahrscheinlich die beste, die innerhalb dieser beiden Einschränkungen erreicht werden kann. Oder zumindest das Beste, was das Unity-Entwicklerteam zu dieser Zeit hervorbringen konnte. Wir werden es vielleicht nie erfahren.

Draco18s vertraut SE nicht mehr
quelle
2
tl; dr gleichen Prozess, andere, schnellere API. Aber warum haben sie nicht eine andere Methode gewählt, als von OP gefordert?
Wondra
10
Wir sollten die Möglichkeit eines einfachen alten Grundes für "schlechtes Design" nicht leugnen. Nach der Freigabe von c # Quellcode Nutzer haben festgestellt Tonnen von Fehlern und unerklärlichen Design - Entscheidungen. Einige Gamedevs konnten endlich die unerwarteten Verhaltensweisen verstehen, mit denen sie jahrelang kämpften.
Exerion
Abgesehen von den technischen Details gehört dies zu den ersten Dingen, die Neulinge lernen. Daher muss es einfach sein, sie hinzuzufügen oder zu überspringen.
Nikaas
6
Es kann auch historische Gründe für dieses Design geben. Unity begann mit einer eigenen Skriptsprache, die sie dann schrittweise zugunsten von C # auslaufen ließen. Es ist nur natürlich, dass sie weiterhin die gleichen Muster verwenden wollten, auch wenn es bedeutete, ein bisschen zu schummeln.
Philipp
3
Ich denke, wir können sinnvoll antworten: "Was erreicht die Einheit auf diese Weise?" - z. Was ist der Vorteil gegenüber einer Sammlung abstrakter öffentlicher Methoden, die alle implementiert werden müssen? Wir können nicht antworten: "Was haben die Entwickler von Unity gedacht?" Aber wir können uns mit der Frage befassen, warum dieser Ansatz für Benutzer der Engine nützlich ist.
DMGregory
9

Es könnte einfach die Methoden als abstrakt in der MonoBehaviour-Basisklasse definieren und die Unterklassen zwingen, sie zu implementieren.

Einer der Vorteile des von Unity verwendeten Systems besteht darin, dass Sie nicht jede MonoBehaviour-Nachricht implementieren müssen, von denen es über 60 gibt .

Normalerweise brauchen wir nur eine oder eine kleine Handvoll dieser Methoden. Da einige spezifisch für die 2D / 3D-Physik sind, andere nur für Kameras oder Partikelsysteme, ist es höchst unwahrscheinlich, dass ein Skript jemals alle benötigt.

Indem wir die Nachrichten, die wir nicht benötigen, nicht implementiert haben, können wir der Laufzeit klar signalisieren, dass Sie nicht einmal OnCollisionStay2D für dieses Objekt aufrufen müssen - ich brauche es nicht.

Dies kann ein erheblicher Effizienzgewinn sein. Jetzt kann die Engine eine Auswahlliste herausfiltern, um Update nur für die ~ 20 Objekte in meiner Szene aufzurufen, für die in jedem Frame eine benutzerdefinierte Aktualisierungslogik erforderlich ist, und nicht für alle ~ 200 Objekte, die die gesamte Szenerie bilden oder nur auf physikalische Ereignisse reagieren.

DMGregory
quelle
Nur eine Frage ... Soweit ich weiß (nicht persönlich), haben Sie erhebliche Auswirkungen auf die Leistung, wenn Sie Tausende von GameObjects haben, die alle ein Skript mit einer Update-Methode verwenden. Die Alternative besteht darin, Listen zu verwenden und diese mit nur einer Update-Methode in einem Manager-Skript zu durchlaufen , was wesentlich leistungsfähiger zu sein scheint. Ist das noch so oder hat sich das schon geändert? - Wenn sich nichts ändert, bedeutet dies, dass die Vorgehensweise von Unity in Bezug auf die Leistung bei weitem nicht optimal ist.
Schlacht
3
@Battle, was Sie beschreiben, stimmt mit dem Link überein, den Draco18s aus dem Jahr 2015 zitiert, und ich würde vermuten, dass es immer noch wahr ist. Die Aktualisierungsmethode für diese Tausenden von Objekten undefiniert zu lassen, ist genau das, was diese Optimierung ermöglicht. Wenn für jedes MonoBehaviour eine Update-Methode über eine abstrakte Basisklasse definiert wäre, wie im Abschnitt der von mir zitierten Frage beschrieben, hätten wir nicht die Möglichkeit, unsere Update-Aufrufe auf die Listeniteration des Managers zu beschränken. Beachten Sie, dass in diesen Fragen und Antworten nicht "Ist der Ansatz von Unity optimal?" aber nur, wie der derzeitige Ansatz von Nutzen sein kann.
DMGregory
Ah ich verstehe, das macht in der Tat Sinn. Danke für die Antwort!
Schlacht
@Battle Es ist immer noch wahr. Unity ist zwar leistungsfähiger als die Alternativen, hat aber immer noch mehr Overhead als ein regulärer Funktionsaufruf.
Draco18s vertraut SE
@ Draco18s - Ja. Ich habe bereits vor langer Zeit einen UpdateManager für meine Projekte implementiert, der sich um diese Optimierung kümmert. Ich wollte nur eine Bestätigung, dass es noch benötigt / nützlich ist.
Schlacht