Quel est le point commun entre un plat de spaghettis et League of Legends ? Eh bien, les deux sont composés de spaghettis. Mais dans le cas de League of Legends, c'est une métaphore de développeur pour dire que le code n'est pas optimisé, ou totalement pourri. Et League of Legends contient encore beaucoup de code du genre, fonctionnant avec diverses rustines, qui pénalisent l'avancement du jeu. Aussi, Riot a commencé depuis assez longtemps à refaire les divers éléments du jeu à la manière d'une grande entreprise. Cela se voit avec la construction de nouveaux datacenters, la reconstruction de l'architecture serveur, la nouvelle map, le nouveau client en préparation, mais aussi via des éléments plus subtils et moins visibles des joueurs, comme le système de projectiles. Un article intéressant qui présente les nouvelles possibilités et les problèmes résolus, traduit depuis le site officiel de Riot.
Bienvenue ! Brian "Penrif" Bossé est là pour parler avec vous de quelques nouveaux aspects techniques derrière le système de projectiles. Nous avons développé un nouveau système pendant les derniers mois (Comment signalé dans un récent patchnote). Si nous avons bien fait notre travail, vous n'avez pas remarqué le changement en jouant. Mais même si c'est le cas, nous sommes très contents des possibilités que cela donne au jeu, et nous souhaitions partager un peu contexte en coulisse avec vous.
L'ancien système était un véritable fouillis, et il était presque impossible de travailler avec, alors que le nouveau nous permet de faire des choses comme celles-ci facilement :
Évidemment, nous n'allons pas faire en sorte que la salve d'Ashe fasse ça. C'est simplement un exemple de ce que nous pouvons faire en quelques heures sans trop d'efforts.
Les projectiles : Étonnamment complexes
Pour vous donner une idée de ce avec quoi nous travaillions, voici un diagramme qui représente le lancement d'un projectile étape par étape dans le système
Il n'est pas possible de zoomer beaucoup, mais c'est probablement volontaire...
Bien que pas aussi complexe qu'un véritable système de guidage de missile, ce diagramme montre le risque à effectuer le moindre changement juste par sa nature. En résumé, ce que nous cherchons ici est une hiérarchie de types de projectiles, chacun offrant un pack de projectiles différents. Fonctionnellement, ces différents types sont parfois partagés, parfois dupliqués, et fréquemment confus. Ce diagramme brise les types de projectiles horizontalement, avec des fonctionnalités similaires. Par exemple, si vous vouliez savoir comment les projectiles détectaient les collisions, vous deviez examiner tout ce qui était en rose. Et il y en a partout.
Le bloc en haut du diagramme est ce que nous appelons un "projectile de base", le plus bas de la hiérarchie. Ce sont les projectiles les plus simples, ils vont jusqu'à une position ou une unité et activent ce qu'ils doivent activer quand leur mouvement est terminé. Les attaques de base et les tirs de tours en font partie, ainsi que les projectiles de base comme la bombe au phosphore (A/Q) de Corki ou l'ultime de Ziggs. Leur fonctionnement est très simple, ils traquent leur cible, et évaluent ensuite où le projectile devrait aller, et voient ensuite s'ils ont touché leur cible.
Le deuxième bloc dans le diagramme contient les "projectiles en ligne", qui comprend la majeure partie de ce que les joueurs appelleraient un skillshot, comme la salve d'Ashe, ou presque tout ce que peut faire Ezreal. Il y a quelques surprises dans cette catégorie aussi, comme le mur de vent de Yasuo, qui est lui aussi un projectile en ligne. Ironique.
Ce genre de projectiles a un comportement plus compliqué que les projectiles de base. Par exemple, ils peuvent toucher des choses pendant leur trajet, retourner au lanceur (Comme l'ultime de Draven), ou suivre le sol (Comme racines fixatrices (E) de Zyra). Tasser tous ces différents comportements dans une seule entité est déjà une erreur, mais les projectiles en ligne ont en plus un facteur aggravant, car ils héritent de toutes les fonctionnalités des projectiles de base par défaut, mais les utilisent différemment. Ce qui nous place dans une situation frustrante où les changements sur les projectiles de base apportent le risque d'affecter négativement les projectiles en ligne.
Le petit groupe en bas représente nos 3 projectiles circulaires, (Croissant lunaire (A/Q) et corps célestes (Z/W) de Diana, et lucioles (Z/W) d'Ahri). Ils agissent d'une manière similaire aux projectiles en ligne, mais leur trajectoire est circulaire. Ils dérivent des projectiles de base et ressemblent en tout point aux projectiles en ligne, en dehors du mouvement. En réalisant ce diagramme, j'ai trouvé des correctifs sur les projectiles en ligne qui n'avaient pas été ré-appliqués à ces projectiles circulaires. Dupliquer du code duplique également les bugs. C'est la cruauté qu'apporte le démon du copier/coller.
Une bonne alternative pour visualiser l'ancien système de projectile...
En résumé, le système de projectile de League of Legends inutilement complexe, confus, et fragile. Comme les projectiles représentent un large domaine de la conception pour le jeu, nous devions clairement améliorer cela pour qu'il soit plus facile de travailler avec.
La méthode que nous avons choisie pour accomplir cela fut de détruire ce système, et de le réécrire entièrement. C'est le seul moyen pour que le système soit sûr.
Refaire tout depuis le début...
Faire table rase
Cette réécriture avait 4 principes fondamentaux :
- Les fonctionnalités devaient être co-localisée dans le code pour en faciliter la compréhension
- Les dépendances entre chaque groupe fonctionnel devaient être minimales et explicites
- Le raisonnement sur ce que les projectiles font en général devait être facile
- Les cas spéciaux de comportements devaient être séparés correctement de l'exécution standard
L'équipe a commencé à identifier les aspects clés d'un projectile, et a planifié l'écriture de composants pour corriger les problèmes de manière indépendante. Apparemment, j'aime les listes, donc, voyons ce que fait un projectile avec une liste :
- Mouvement : Les projectiles vont quelque part
- Collision : Les projectiles touchent quelque chose
- Suivi de cible : Les projectiles vont vers quelque chose qui peut bouger
- Script : Les projectiles ont besoin de concepteurs pour leur dire quoi faire
- Visibilité : Les projectiles sont vus par certaines choses mais pas par d'autres
- Aspect visuel et son : Vous allez voir et entendre ce qu'il se passe
Nous avons donc commencé à réécrire le code avec cette liste, en ayant un composant pour chaque objet que nous pouvions construire en plein vol, ou même modifier au milieu d'un trajet. Au fur et à mesure que nous travaillions avec cette méthode, nous avons continuellement ré-évalué ces séparations et fait des ajustements lorsque des abstractions n'avaient aucun intérêt. Par exemple, nous avons placé le suivi d'une cible dans le code pour le mouvement parce que nous avons vu que leurs objectifs étaient liés, et nous avons placé l'interface pour réaliser des scripts dans la base commune des projectiles, parce que nous avons vu qu'il n'y avait pas besoin de la différencier à travers les différents types de projectiles.
Finalement, nous avons un système qui ressemble à ça :
Riot appelle ce premier diagramme un "Vomi d'arc-en-ciel"
Les arcs-en-ciel sont d'excellent matériaux de construction
Ce que cela signifie pour League of Legends
Il y a deux principaux avantages avec ce nouveau système. D'abord, le code des projectiles ne devrait plus continuer à faire peur à nos programmeurs, avec une facilité à trouver des bugs et à l'améliorer plus grande. C'est déjà visible avec la résolution de certains bugs ou défauts considérés comme incorrigibles pendant longtemps (Avec l'ancien système)
Par exemple, nous nous sommes arrachés les cheveux sur des vidéos montrant des projectiles traversant leur cible. Même si c'était rare, cela impactait considérablement les parties quand cela arrivait. Avec aucune possibilité de reproduire le problème et pas vraiment d'indicateur sur où regarder dans le code, la seule chose à faire était de mettre ça dans un coin de notre tête et de continuer. Cependant, avoir toutes les collisions de projectiles en un seul endroit, clair et détaillé, rend la détection et la correction de bugs facile à trouver. Après avoir fait cela, nous avions une grande confiance dans le fait que les projectiles allaient toucher ceux qu'ils étaient supposés toucher.
Pour garder quelques réserves et nous protéger de reproches à venir, certaines situations où les projectiles traversent une unité existent probablement encore. Mais je pense que cela vient d'erreurs de synchronisation de la position des unités, plutôt que de la détection des collisions.
Si avoir des programmeurs plus heureux et moins de bugs est une bonne chose, le deuxième et plus grand avantage ici est que nous sommes capables de faire de nouvelles choses avec les projectiles, beaucoup plus rapidement. Sans avoir à créer une nouvelle classe de projectile pour en avoir un qui se déplace différemment, nous sommes libre de faire en sorte qu'ils fassent tous des choses complètement folle. Je vous laisse quelques exemples que nous avons mis ensemble pour vous faire une démonstration. Ils ne représentent pas des idées à venir, mais simplement le résultat de quelques idées folles d'ingénieurs.
Une attaque de Draven qui fait un looping avant d'atteindre sa cible
Une flèche de Varus qui décrit la forme d'un cœur.
Une salve d'Ashe en forme d'hexagone !