Pourquoi Unity utilise la réflexion pour obtenir la méthode de mise à jour?
12
Pourquoi une réflexion d'utilisation Unité pour l' accès des MonoBehaviourméthodes de message comme Awake, Update, Start, ...?
Ne serait-il pas lent d'utiliser la réflexion? Pourquoi ne tire-t-il pas parti d'autres approches comme Template Method? Il pourrait simplement définir les méthodes comme abstraites dans la MonoBehaviourclasse de base et forcer les sous-classes à l'implémenter.
Non, Unity n'utilise pas System.Reflection pour trouver une méthode magique à chaque fois qu'il doit en appeler une.
Au lieu de cela, la première fois qu'un MonoBehaviour d'un type donné est accédé, le script sous-jacent est inspecté par le biais de l'exécution du script (Mono ou IL2CPP), qu'il ait des méthodes magiques définies et que ces informations soient mises en cache. Si un MonoBehaviour a une méthode spécifique, il est ajouté à une liste appropriée, par exemple si un script a défini la méthode Update, il est ajouté à une liste de scripts qui doivent être mis à jour à chaque image.
Pendant le jeu, Unity parcourt ces listes et exécute des méthodes à partir de celle-ci - aussi simple que cela. Aussi, c'est pourquoi cela n'a pas d'importance si votre méthode de mise à jour est publique ou privée.
Quant aux raisons pour lesquelles cela se fait de cette façon, je vous renvoie largement (le lecteur) à la réponse de DMGregory , qui se résume à un équilibre de deux choses concurrentes:
Optimisation des performances
Facilité d'utilisation des nouveaux développeurs
Un nouveau développeur veut simplement que cela fonctionne et ne veut pas avoir à comprendre "comment puis-je le connecter au système d'événements?" mais il devrait toujours fonctionner rapidement avec un minimum de frais généraux.
La solution est probablement la meilleure qui puisse être obtenue avec ces deux contraintes. Ou du moins, le meilleur que l'équipe de développement Unity ait pu trouver à l'époque. Nous ne le saurons peut-être jamais.
tl; dr même processus, API différente et plus rapide. Mais pourquoi n'ont-ils pas choisi une méthode différente comme demandé par OP?
Wondra
10
Nous ne devons pas nier la possibilité d'une raison ancienne et «mauvaise conception». Après la sortie du code source c #, les utilisateurs ont trouvé des tonnes d'erreurs et de décisions de conception inexpliquées. Certains gamedev ont finalement pu comprendre ces comportements inattendus avec lesquels ils se battaient depuis des années.
Exerion
Mis à part les détails techniques, c'est l'une des premières choses que les nouveaux arrivants apprennent, il doit donc être facile à ajouter ou à sauter.
Nikaas
6
Il pourrait également y avoir des raisons historiques à cette conception. Unity a commencé avec son propre langage de script, qu'il a ensuite progressivement abandonné en faveur de C #. Il est tout simplement naturel qu'ils voulaient continuer à utiliser les mêmes modèles, même si cela signifiait tricher un peu.
Philipp
3
Je pense que nous pouvons utilement répondre "Qu'est-ce que l'unité accomplit par ce moyen" - par exemple. quel est l'avantage sur une collection de méthodes publiques abstraites qui doivent toutes être implémentées. Nous ne pouvons pas répondre "à quoi pensaient les développeurs d'Unity?" mais nous pouvons aborder "pourquoi cette approche est-elle utile aux utilisateurs du moteur?"
DMGregory
10
Il pourrait simplement définir les méthodes comme abstraites dans la classe de base MonoBehaviour et forcer les sous-classes à l'implémenter.
L'un des avantages du système utilisé par Unity est que vous n'avez pas à implémenter tous les messages MonoBehaviour, dont il existe plus de 60 .
Habituellement, nous avons juste besoin d'une ou d'une petite poignée de ces méthodes. Étant donné que certains sont spécifiques à la physique 2D / 3D, certains uniques aux caméras ou aux systèmes de particules, il est très peu probable qu'un seul script ait besoin de tous.
En laissant les messages dont nous n'avons pas besoin comme non implémentés, nous pouvons clairement signaler au runtime "ne vous embêtez même pas à appeler OnCollisionStay2D sur cet objet - je n'en ai pas besoin"
Cela peut être un gain d'efficacité substantiel. Maintenant, le moteur peut filtrer une liste restreinte pour appeler la mise à jour uniquement sur les ~ 20 objets de ma scène qui nécessitent une logique de mise à jour personnalisée à chaque image, et non sur les ~ 200 objets qui composent tout le paysage ou ne répondent qu'aux événements physiques.
Juste une question ... pour autant que je sache (pas personnellement), si vous avez des milliers de GameObjects, qui utilisent tous un script avec une méthode de mise à jour, vous avez un impact significatif sur les performances. L'alternative consiste à utiliser des listes et à les parcourir avec une seule méthode de mise à jour dans un script de gestionnaire, ce qui semble être beaucoup plus performant. Est-ce toujours le cas ou cela a-t-il déjà changé? - Si rien ne changeait, cela impliquerait que la façon dont Unity le fait est loin d'être optimale en termes de performances.
Bataille du
4
@Battle ce que vous décrivez est cohérent avec le lien que Draco18s cite de 2015, et je soupçonne que c'est toujours vrai. Pouvoir laisser la méthode Update indéfinie sur ces milliers d'objets est précisément ce qui permet à cette optimisation de fonctionner. Si chaque MonoBehaviour avait une méthode Update définie via une classe de base abstraite comme décrit dans la section de la question que j'ai citée, alors nous n'aurions pas la possibilité de limiter nos appels de mise à jour à l'itération de la liste du gestionnaire. Notez que cette Q&R ne traite pas de "l'approche d'Unity est-elle optimale?" mais seulement comment l'approche actuelle peut être avantageuse.
DMGregory
Ah je vois, ça a vraiment du sens. Merci d'avoir répondu!
Bataille du
@Battle C'est toujours vrai. Bien que ce que fait Unity soit plus performant que les alternatives, il a encore plus de surcharge qu'un appel de fonction normal.
Draco18 ne font plus confiance au SE
@ Draco18s - Ouais. J'ai déjà implémenté un UpdateManager pour mes projets qui s'occupe de cette optimisation depuis longtemps. Je voulais juste une confirmation qu'il est toujours nécessaire / utile.
L'un des avantages du système utilisé par Unity est que vous n'avez pas à implémenter tous les messages MonoBehaviour, dont il existe plus de 60 .
Habituellement, nous avons juste besoin d'une ou d'une petite poignée de ces méthodes. Étant donné que certains sont spécifiques à la physique 2D / 3D, certains uniques aux caméras ou aux systèmes de particules, il est très peu probable qu'un seul script ait besoin de tous.
En laissant les messages dont nous n'avons pas besoin comme non implémentés, nous pouvons clairement signaler au runtime "ne vous embêtez même pas à appeler OnCollisionStay2D sur cet objet - je n'en ai pas besoin"
Cela peut être un gain d'efficacité substantiel. Maintenant, le moteur peut filtrer une liste restreinte pour appeler la mise à jour uniquement sur les ~ 20 objets de ma scène qui nécessitent une logique de mise à jour personnalisée à chaque image, et non sur les ~ 200 objets qui composent tout le paysage ou ne répondent qu'aux événements physiques.
la source