[Actualité] [DevLog] Clash of the Daemons : la pelote et l'incident
par
, 09/09/2016 à 13h23 (2322 Affichages)
[DevLog] Clash of the Daemons : la pelote et l'incident
J'ai décidé de poursuivre la démo développée pour le week-end du jeu vidéo en août dernier, ce qui donnera lieu à des compte-rendus réguliers, du moins je l'espère, sur l'avancement de ce petit projet.
Le syndrome de la pelote de laine
Dans un article que j'ai traduit et récemment publié sur mon blog, Macoy Madson appelle le « Mur » ce moment fatidique où il devient très difficile, voire impossible, d'avancer sur son projet de jeu vidéo. Sauf à se mentir, quiconque s'est lancé dans l'aventure a un jour dû faire face à ce phénomène. Bien que l'image du « Mur » soit tout à fait représentative sur les conséquences du problème, à savoir le blocage presque systématique qui se produit au cours du développement solo d'un jeu vidéo, quand on est amateur, il n'explique pas vraiment la réelle nature de ce problème. Ça tombe bien, comme je suis à peu près en plein dedans, je vais essayer d'expliciter de quoi il en retourne.
L'une des caractéristiques du jeu vidéo par rapport à d'autres applications dites transactionnelles, est le degré d'interconnexion, pour ne pas dire d'intrication entre ses différentes composantes. Ce phénomène apparaît passé un certain stade, après avoir implémenté les traitements de bas niveau relativement linéaires et isolés comme le rendu graphique ou la gestion du contrôleur. Lorsqu'il s'agit d'implémenter les concepts liés au gameplay (eg. les interactions entre le joueur et son environnement ou le système d'IA), ce qu'on nommerait dans le monde de l'entreprise par les « règles de gestion » ou les « règles métiers », on ressent vite le besoin de relier, de faire communiquer, divers composants, d'une façon ou d'une autre. C'est à ce moment qu'on a recourt à diverses techniques de l'ingénierie du logiciel par exemple l'héritage par classe, par prototype, la composition, le pattern observateur, la file de messages, les fabriques (factories), les architectures MVC, MVVM et j'en passe. Quand ces termes apparaîssent dans l'esprit d'un développeur, c'est souvent le signe du début des réelles complications en général... Il va il y avoir du sport !
Évidemment, le code spaghetti qui a peut-être servi en tout début de projet pour réaliser les premiers prototypes montre à ce stade toutes ses limites. Lorsque les relations entre les composants d'une application se complexifient, et ça se produit toujours dans le cas d'un jeu vidéo (hormis les genres trivaux du style morpion ou Pong), le code spaghetti nous empêche d'y voir clair. C'est un obstacle majeur à l'ordre que nous cherchons à mettre en place. Ordre nécessaire pour faciliter le raisonnement sur le code, notamment lorsque des bugs se produisent ou lorsqu'on cherche à enrichir le gameplay, ce qui en phase de développement se produit fréquemment, cela va sans dire.
Tout un arsenal de techniques ont été mises au point pour déspaghettifier le code, d'où l'apparition du terme un peu pompeux de design pattern. Cependant, même si ces design patterns permettent de dépasser les défauts du code spaghetti, ils ne peuvent pas fondamentalement réduire la complexité intrinsèque du réseau de relations entre les multiples composants d'un jeu vidéo, sauf à revoir les exigences de gameplay à la baisse...
C'est cette complexité intrinsèque liée au gameplay, souvent sous-estimée par les débutants qui est selon moi la principale cause du « Mur » que décrit Macoy Madson. C'est d'ailleurs la raison pour laquelle, il est souvent préférable quand on débute, de s'attaquer aux parties les plus « linéaires » d'un jeu vidéo comme le rendu graphique. Cela a l'avantage de produire rapidement des résultats qui sont visibles. (Il n'en reste pas moins que produire un bon moteur graphique en partant de zéro reste tout de même un travail de longue haleine, qu'il n'y ait pas de méprise). Avoir rapidement un feedback visuel, c'est plus motivant que de se battre à déboguer des logiques de gameplay qui ne produiront pas forcément d'effets visibles.
En pratique, la complexité intrinsèque liée au gameplay se traduit souvent par ce que je nommerai « le syndrome de la pelote de laine ». Et je viens de l'expérimenter tout dernièrement pendant le développement de Clash of the Daemons...
Concrètement, je souhaitais améliorer à la marge un des comportements des PNJ, le « hit-and-run », dont le pattern peut être globalement décomposé en trois phases : la poursuite vers la cible (ie. le joueur), l'attaque au corps-à-corps, et la fuite.
Sans rentrer dans les détails au risque de devenir rasoir, ce comportement est lié au module d'IA d'une part et d'autre part au concept d'attaque. Pour améliorer le « hit-and-run », même modestement, j'ai été amené à :
- remodeler sensiblement mon module d'IA,
- ajouter une gestion d'inventaire,
- ajouter le concept d'arme,
- et changer le gameplay pour basculer de l'attaque à distance vers l'attaque au corps-à-corps et vice-et-versa.
Cela a même impacté le mapping des touches du joueur alors qu'il s'agissait à la base d'une modification d'un comportement de PNJ !
Un long détour qui peut décourager au premier abord. Mais même si au final cela n'a rien donné de très visible à l'écran, ces grosses modifications internes permettent au code d'être plus évolutif et donnent surtout de nouvelles possibilités pour le gameplay. Par exemple, désormais, je peux sereinement envisager d'avoir un nombre arbitraire d'armes aux effets variés, sans craindre de manquer de touches pour le joueur et sans modifier les classes existantes. Et concernant l'IA, grâce au remodelage, il devient très facile de combiner les comportements pour en produire un nouveau, à la manière des legos, chose qui n'était pas garantie précédemment.
En souhaitant apporter une modification relativement mineure sur le plan fonctionnel, je me suis donc retrouvé à modifier une large portion de mon code, un peu à l'image d'une pelote de laine qu'on dévide au fur et à mesure sans savoir quand on va tomber sur l'autre extrémité. Il y a clairement un effet tunnel où on s'embarque dans un voyage dont on ne connaît pas encore la durée et sans avoir le feedback visuel gratifiant qu'on peut avoir quand on travaille sur les graphismes ou l'animation.
Il ne faut donc pas sous-estimer la difficulté d'implémenter le gameplay, car de là surgit souvent le « Mur ». Il est possible de contourner le problème en restreignant le gameplay, ce qui peut tout à fait se justifier selon les situations. Cependant, si on souhaite conserver ses idées de gameplay, il faudra tout d'abord s'armer de patience et de courage, dévider petit à petit la pelote de laine (un outil de versionning comme Git est ici très utile pour sécuriser les innombrables refactoring qui interviendront), pour enfin briser le mur et en viser un autre. Car c'est un processus sans fin, ou presque ! En Asie, il existe un proverbe, « Après la montagne, la montagne ». C'est ce qu'il se produit quand on se lance dans le développement d'un jeu vidéo en solo : une succession continue d'obstacles à abattre.
Surtout que ces obstacles peuvent survenir de n'importe où et à n'importe quel moment, pour preuve...
L'incident OneDrive
L'ensemble de mes projets personnels sont synchronisés avec mon compte OneDrive, l'offre de cloud de Microsoft. Cela permet de bénéficier gratuitement d'une sauvegarde hors-les-murs avec un bon niveau de service. Du moins en théorie car depuis que j'ai appliqué la mise à jour Anniversaire de Windows 10, le processus OneDrive tend à perdre les pédales en se lançant par exemple en trois, quatre voire cinq exemplaires, ou bien en moulinant dans le vide. Mais à vrai dire, je ne m'en étais jusqu'à présent pas trop inquiété, après tout, du moment que la sauvegarde est faite sur le cloud, c'est bien le principal, n'est-ce pas ?
Il se trouve qu'il y a quelques jours, en ouvrant mon projet dans Visual Studio Code, le référentiel Git était corrompu. De plus, une moitié de mes sources avaient disparu, et l'autre semblaient complètement obsolète, out of date dirait-on en anglais. OneDrive semblait avoir fait des siennes en synchronisant (ou plutôt en désynchronisant) d'une curieuse façon les données du cloud vers mon disque en local. En gros, des dizaines et des dizaines d'heures de travail venaient de partir en fumée... C'est dans ces moments un peu désespérés, bien que rares toutefois, où on se bénit soi-même d'avoir mis en place une sauvegarde incrémentale sur autre disque dur. Je n'ai heureusement ainsi perdu que le delta depuis ma dernière sauvegarde, ce qui ne représentait que moins d'une heure (la sauvegarde est planifiée toutes les heures). C'est beaucoup plus acceptable que 50 heures de perdues !
Moralité : Même sur des projets personnels, ne jamais négliger la sécurité des données en doublant voire triplant les procédés de sauvegarde. Car dans mon cas, c'est bien un outil sensé garantir ces données, en l'occurrence OneDrive, qui a causé la perte d'une partie de ces données. Mais comme toutes les bonnes histoires, ça se termine sur un happy end.