
1 Année 1 Jeu - Splits Manager
Concept de 1 Année 1 Jeu#
1 Année 1 jeu est un défi que au quel je participe depuis quelques années maintenant. Je fais une liste de jeux en en sélectionnant un pour chaque année en partant de celle de ma naissance à l’actuelle. Je suis né en 1991, c’est donc mon année de départ. Je choisis un jeu sorti en 91, puis un autre sorti en 92, ainsi de suite jusqu’à arriver à l’année actuelle. Ça donne une liste de mon age + 1 jeux auxquels jouer.
Il y a quelques surprises en plus:
- Je dois choisir des jeux que je n’ai jamais finis. Je peux les avoir commencés puis abandonnés, ou jamais touchés. Du moment que je ne suis jamais arrivé à la fin, ils sont éligibles.
- Un seul jeu par séries. Si je choisis Crash Bandicoot 2, je ne peux plus prendre Crash Bandicoot 3.
L’idée derrière tout ça étant de découvrir des nouveaux jeux mais aussi d’enfin jouer à ceux que j’ai achetés et jamais lancés. Ça m’aide aussi beaucoup avec la paralysie du choix et avoir du mal à simplement lancer un jeu. J’ai fait ma liste donc je n’ai pas besoin d’hésiter sur quel jeu choisir et puisque tout a déjà été sélectionné au préalable, il n’y a pas d’hésitation au moment de lancer non plus, je peux juste jouer. Ça prend généralement une bonne partie de l’année mais ça a toujours été amusant et je suis très content d’avoir commencé à faire ça.
Le concept vient d’un marathon que Shisheyu (un streamer français sur Twitch) fait chaque année pour son anniversaire, qu’il appelle “N ans N jeux” où N représente son age. Pendant l’édition 2024, quelqu’un dans son chat disait qu’il pourrait choisir un jeu pour chaque année entre sa naissance et l’année en cours et ça m’est resté en tête. J’en ai ensuite parlé dans le serveur discord d’AngleDroit (une streameuse française) et quelques membre de la communauté et moi avons commencé à créer nos listes et à jouer. Tout ça s’est passé le 6 Aout, c’est donc “l’anniversaire” de l’événement, date à laquelle nous démarrons désormais chaque année une nouvelle saison.
Pour chaque saison, on a un Google Sheet dédié qui ressemble à ça:

Le besoin d’un application#
Quand je fais ce défi, j’aime mesurer combien de temps je mets à finir chaque jeu. Pour la première saison, j’ai utilisé LiveSplit (un outil de speedrun) dans lequel je changeais le temps de départ pour correspondre à où j’en étais dans le défi. Ça marchait mais je n’étais pas entièrement satisfait.
Pour la saison 2025, je voulais utiliser LiveSplit différemment et avoir un chronomètre global pour toute la liste pour voir ma progression, mais aussi voir le temps de chaque session de jeu. J’ai donc commencé à réfléchir à comment mettre tout ça en place. Pour l’utiliser convenablement, je devais trouver une solution à plusieurs problèmes:
- Le premier était assez simple. J’avais besoin de mettre en pause et reprendre ma “run” puisque ça allait s’étaler sur une longue période de temps. J’ai trouvé un plugin qui permet de faire exactement ça en sauvegardant et rechargeant un fichier json qui contient toutes les informations de la run en cours et où elle s’est mise en pause.
- Le second était plus compliqué. Quand ils speedrunnent un jeu, les runners ont souvent des segments dans leur LiveSplit appelés “splits”. Ils permettent de mieux voir où du temps est gagné et perdu. Je voulais avoir un split par session de jeu, ce qui donne un grand nombre de splits/sessions à la fin d’un défi et un moyen de voir où j’ai passé le plus de temps et d’autres stats rigolotes. Le problème c’est que les splits sont fait pour être réglés avant le lancement d’une run, ils ne sont pas sensé être ajoutés ou enlevé une fois que le chrono à démarré.
Puisque j’avais un json avec toutes les informations de la run et un fichier xml contenant les splits, je me suis dit qu’une solution serait de faire tourner un programme qui peut ouvrir ces fichiers, ajouter un split dans le xml et régler les temps dans le json et les sauvegarder. J’ai pensé le faire à la main mais les fichiers étaient un peu gros et il y avait beaucoup de possibilité pour moi de faire des erreurs. Il y avait un bon nombre de choses à éditer à différents endroits dans les deux fichiers pour ajouter un split, sans prendre en compte les cas où je lançait un jeu plus loin dans la liste avant l’ordre établi (ce qui n’arrive pas dans un speedrun, les splits se font dans l’ordre). Il y avait déjà un élément de calcul de temps avec une édition simple, mais considérer les sessions dans le désordre en impliquait encore plus. Ça pouvait entraîner de plus longues éditions et donc des erreurs de timing de ma part, ce n’était pas la meilleure façon de procéder. J’étais assez impatient de créer mon petit programme pour faire toutes ces choses pour moi avec mon moteur pour pouvoir faire tout ça sans y penser, juste appuyer sur un bouton.
C’est comme ça que le Splits Manager à vu le jour et pourquoi il s’appelle comme ça, c’était un moyen de gérer (manager) mes splits de LiveSplit.



Premières versions#

Au début, j’avais seulement prévu d’avoir la liste des jeux en sections qui se déplieraient en cliquant dessus pour révéler toutes les sessions passées dessus. À la fin de la section d’un jeu, il y aurait eu un champ pour écrire ma dernière session et un bouton pour l’ajouter. L’application aurait alors réécrit les deux fichiers que j’ai mentionné.
Comme souvent, j’ai utilisé la SFML et ImGui dans mon moteur C++, FaZoN, pour créer cette application. J’ai d’abord utilisé l’ImGui dans un but de debug dans les jeux sur lesquels je travaillais mais j’ai réalisé que cette librairie était très bonne pour créer des applications en dehors des jeux aussi. Je m’appuie très fortement dessus depuis un moment, toutes les apps que je crée l’utilisent.

Contenu des fichiers#
La première chose que j’ai eu à faire a été de comprendre comment marchent les deux fichiers que j’allais avoir à manipuler. L’export de la run était assez explicite, la seule chose présente dedans étant les splits avec leur temps (globaux à la run entière), l’index du split en cours, et le temps total de la run.
Le fichier des splits était plus compliqué. Premièrement, j’ai essayé de l’alléger le plus possible. Il y a beaucoup d’options dans LiveSplit et un bon nombre d’entre elles finissent dans ce fichier, méthodes de chronométrage, historique de la run, nombre d’essais, etc… Je voulais enlever tout ce dont je n’avais pas besoin et qui n’était pas obligatoire pour que le fichier fonctionne correctement. J’ai donc pris une approche d’essais et d’erreurs en éditant le fichier à la main, enlevant des tags et en essayant de charger le fichier dans LiveSplit et voir si tout fonctionnait correctement. J’ai réussi à enlever une bonne partie de son contenu, j’étais assez content. J’ai aussi détourné l’utilisation de certains paramètres. Entre autres choses, la catégorie de la run qui est généralement quelque chose comme “Any%”, “Glitchless”, ou autre, est devenu l’année de la saison. Le nombre d’essais est devenu le nombre de sessions.
Mettre à jour la run#
Avec l’étape précédente terminée, j’ai pu travailler à ajouter des sessions à la run.
Le cas d’utilisation de base était d’ajouter une session au jeu courant. Ce que j’appelle “jeu courant” dans cette application est, évidemment, le jeu auquel je joue actuellement. Depuis le début, je savais que je pouvais avoir plusieurs jeux en cours en même temps car ça m’était arrivé pendant la saison précédente. J’avais donc prévu d’ajouter cette gestion là dans les fonctionnalités de l’app. À ce moment là, le jeu en cours était simplement le premier en cours que je trouvais en lisant mes fichiers.
Si le jeu courant n’était pas fini, il n’y avait pas grand chose à faire. Juste ajouter un split au bon endroit et c’était fini. Mais si le jeu était terminé, il y avait des complications. Le principe de base était, et est toujours, de trouver le prochain jeu qui est soit non démarré soit en cours. Si il y avait d’autres jeux finis sur le chemin, je devais les dépasser et continuer la recherche. Le processus n’était pas compliqué mais la gestion des deux fichiers le rendait difficile par moments.
Comme j’ai dit précédemment, ce n’est pas toujours le jeu courant que je voulais mettre à jour. Je voulais aussi avoir la possibilité d’ajouter des sessions en avance sur des jeux plus loin dans la liste. C’était à peu près les mêmes étapes que pour le jeu courant, sans mettre à jour le split courant (un index représentant où j’en suis dans la run). J’avais juste à détourner une autre fonctionnalité du fichier de splits pour sauvegarder ces temps parce que je ne pouvais pas les mettre dans le fichier json d’export puisque ce n’était pas où en était la run.
Un changement de philosophie#
Ma première idée avec cette application était simplement d’éviter de recréer LiveSplit en faisant un autre chronomètre, avec probablement moins de fonctionnalités et/ou moins fiable. Mais au fil du temps, j’ai réalisé que beaucoup des fonctionnalités dont j’avais besoin n’étaient pas faisables dans LiveSplit ou l’étaient, mais pas comme je le voulais. Ce n’est vraiment pas un problème de LiveSplit, il n’est juste pas fait pour ce que je voulais faire. J’avais aussi un petit problème d’UI, vu que ma “run” était très longue, certains des textes de temps se chevauchaient et ce n’était pas très lisible.
Gérer les deux fichiers devenait un peu lourd aussi, et l’édition manuelle dans les cas où je n’avais pas accès à l’application était un peu compliquée.
J’ai décidé qu’il était temps d’intégrer le chronomètre directement dans l’application, ce qui simplifierai beaucoup le processus. Ça voulait dire que je pourrai utiliser une application au lieu de deux, et gérer seulement un fichier dont je déciderai le contenu.

J’ai créé une petite classe de chronomètre dans mon moteur en utilisant la librairie chrono et j’ai un peu retravaillé mon interface pour l’ajouter dans la partie droite. J’ai fait en sorte que le chronomètre et d’autres informations changent de couleur quand il tourne et j’ai ajouté des raccourcis pour démarrer ou arrêter une session.
La vidéo au dessus montre le chronomètre en action après plus de travail sur ses fonctionnalités, plus particulièrement comment j’ai géré le delta quand le temps passé sur un jeu dépasse son temps estimé (le temps supposé qu’il faudrait pour finir le jeu). Dans la section du haut, on trouve le chronomètre en lui même, et en dessous, le temps passé sur le jeu (“Played”), son estimation, et la différence de temps entre les deux, c’est ce qui nous intéresse ici. Dans la section juste en dessous, on voit l’estimation de temps pour toute la liste, le temps total passé sur tous les jeux, la différence entre les deux, le temps restant jusqu’à la fin (calculé à partir de l’estimation), et une prédiction du temps final. Quand une session est en cours, toutes les informations changées par le chronomètre deviennent vertes. Elles deviennent aussi grises quand le chronomètre est en pause. Au début, le delta est négatif car on a passé moins de temps sur le jeu que ce qui était estimé. Le temps passé total augmente en même temps que celui du chrono. Quand le temps passé sur le jeu dépasse son estimation, le delta devient positif et augmente, ainsi que le temps restant sur la liste. Il y a deux nouvelles informations qui sont mises à jour maintenant, le delta sur toute la liste diminue (au augmentera si il dépasse zéro), et le temps restant augmente.
Note: Le temps estimé du jeu devient vert aussi malgré qu’il ne change pas, purement pour des raisons esthétiques, mais je changerai peut-être ça plus tard.
Comme dit au début de cette section, ajouter le chronomètre au Splits Manager m’a permis de me débarrasser du fichier de splits et de l’export de la run pour créer mon propre fichier où sauvegarder les données de la liste. Pour chaque jeu, j’avais ces informations à garder:
- Couverture: L’utilisateur peut sélectionner une couverture pour ses jeux. J’ai appris à utiliser l’encodage 64bit pour pouvoir faire comme LiveSplit et sauvegarder les images directement dans le fichier pour n’avoir besoin de les choisir qu’une fois puis l’image ne serait plus requise ensuite. Ses données sont sauvegardées sous la forme d’une string qui est lue est décodé pour la recréer et la charger en mémoire à l’ouverture du fichier. Je suis très content d’avoir réussi à faire marcher ça.
- Estimation de temps
- Nom (je choisis d’ajouter l’année de sortie au nom mais ce n’est pas requis)
- État: Avant de créer ce fichier, je devais déduire l’état d’un jeu avec les informations que je trouvais dans les fichiers de splits et d’export de run, ce n’était pas toujours fiable. Ajouter cette information directement dans le nouveau fichier a simplifié beaucoup de choses.
- Chronos: J’ai décidé que sauvegarder les temps des sessions au lieu de l’état du chrono global était mieux et plus simple. Dans l’export, si ma run avait duré 10 heures et j’avais joué deux fois, j’aurais eu un premier temps à 4 heures et le deuxième à 10 et j’aurais du calculer le temps des sessions. Ça voulait aussi dire que je devais calculer quel temps je devais mettre si je voulais ajouter une session à la main, ce qui n’était pas génial. En sauvegardant les temps des sessions, sur le même exemple, j’ai une session de 4 heures et une autre de 6 dans le fichier. Je laisse l’application faire la somme des sessions pour avoir le temps total, ce qui est très simple, et je peux rapidement ajouter une nouvelle session en écrivant juste combien de temps j’ai joué si j’en ai besoin.
- Dates: Elles sont regroupées avec les chronos pour un parsing plus facile et ça permet aussi d’avoir un fichier plus court. Elles n’étaient pas là dans les premières versions du nouveau fichier mais j’ai voulu en parler ici puisqu’elles sont visible dans la capture d’écran en dessous. Je détaillerai leur gestion dans la prochaine partie de cette page.

J’étais vraiment satisfait de ce qu’a donné le retravail de l’application. J’ai fini par l’aimer encore plus et j’avais hâte de continuer à y ajouter des fonctionnalités.
Statistiques#
Depuis le début, j’avais l’idée de calculer toutes les stats auxquelles je pouvais penser dans l’application. Jeu au temps le plus court et le plus long, pareil pour les sessions, combien de sessions en moyenne par jeu, ainsi de suite. On peut déjà en voir certaines dans des captures d’écran précédentes sur la page. Une donnée qui me manquait et m’aurait permis de calculer encore plus de statistiques était les dates des sessions. Avec ça je pouvais savoir combien de temps je jouais chaque jour, le jeu qui a pris la plus longue période de temps, et je pouvais même essayer de prédire à quelle date le défi pourrait se finir et voir la date évoluer au fur et à mesure de ma progression.
Ajouter la gestion des dates a été une entreprise relativement importante. J’ai utilisé la classe year_month_day disponible dans la librairie chrono que j’utilisais déjà. Grâce à ça, j’ai créé un bon nombre de fonctions se servant des dates, comme retrouver la date du jour. J’ai ajouté la possibilité de donner une date (au format ISO 8601, YYYY-MM-DD) en ajoutant une session manuellement. En utilisant le chronomètre directement, la date est récupérée automatiquement en validant la session. Pour celles dépassant minuit, je considère que le jour contenant le plus gros pourcentage de temps de la session sera le “jour principal”. Si je finis une session de jeu à 1 heure du matin mais j’ai joué 3 heures au total, il y a deux heures dans la journée précédente donc c’est là que la session sera.
Avec la date sauvegardée avec le temps de chaque session, il est possible de savoir combien d’heure je joue chaque jour en moyenne et quels jours j’ai joué. Ça m’a permis d’aller plus loin dans le calcul de stats. J’ai mis en place une prédiction qui utilise mon temps de jeu moyen pour déterminer le nombre de jours restants, et en l’ajoutant à la date de la dernière session, je peux estimer quand mon défi pourra potentiellement se finir.

Sur la capture d’écran au dessus, il y a un groupe de statistiques affiché deux fois. Le premier en haut concerne le jeu courant, le second tout en bas est pour toute la liste.
Création de la liste#
Maintenant que toutes les fonctionnalités principales étaient présentes et que j’étais satisfait de ce que j’avais, il était temps d’ajouter un moyen de créer une liste dans l’application. Je n’avais pas eu besoin de ça tout de suite parce que j’avais mon fichiers de splits qui m’avait servi a générer mon fichier personnalisé, mais si je voulais partager l’app ou simplement l’utiliser pour la prochaine saison, j’allais avoir besoin d’avoir une création de quelque sorte que ce soit pour ne pas avoir à faire le fichier de zéro.
J’avais différentes idées sur comment procéder, trois d’entre elles se sont révélées être celles que j’avais envie d’implémenter:
- Importation depuis un copié/collé de contenu Google Sheet.
- Importation depuis un fichier CSV généré par le Google Sheet.
- Création depuis zéro dans l’application en elle même.
De ces possibilités, j’ai décidé que la méthode du copié/collé serait la meilleure à faire en premier pour une combinaison de facilité d’utilisation et de vitesse d’implémentation de ma part. Même si ça n’allait pas être la manière de faire la plus robuste, au moins j’aurais quelque chose d’utilisable pour tout le monde.
J’ai ajouté une popup à mon menu qui contient un gros champ texte où coller la table venant de Google Sheet. En dessous, j’ai mis des cases à cocher qui correspondent aux colonnes disponibles dans la feuille. L’utilisateur·ice doit les cocher en fonction de ce qui est présent dans le texte collé, qui correspond aux colonnes présentes sur leur feuille. Puisque je le faisais pour moi, j’ai ajouté une option pour combiner l’année et le nom des jeux ensemble dans un format <année> - <nom> que j’aime bien car il permet de servir de rappel rapide de l’année de sortie d’un jeu.
Une fois que toute cette mise en place est faite, il y a un bouton pour générer la liste et un aperçu permettant de vérifier si tout va bien.
Une chose que la création ne peut pas gérer simplement parce que la donnée n’existe pas, c’est les sessions de jeux. Si l’un deux a du temps passé dessus au moment de l’importation, il sera compté dans une seule session. Cette manière de créer la liste est plus pensée pour être faite au début du défi quand aucune session n’a encore été faite.
C’est une méthode de faite. Au moment où j’écris cette page, c’est toujours la seule disponible car j’ai préféré concentrer mes efforts sur d’autres choses depuis. Je compte toujours faire la création dans l’application à un moment mais je suis plus très sûr de la méthode utilisant le CSV maintenant.
Partage de l’application#
Maintenant qu’elle était prête, je devais trouver un moyen de partager mon application sous une forme utilisable. Quand on donne aux gens un exe créé avec Visual Studio, il peut y avoir des problèmes si iels n’ont pas les bonnes redistribuables sur leur ordinateur et le logiciel ne démarrera pas. Je devais créer un installeur.
Après un peu de recherche, j’ai appris que Visual Studio proposait un moyen de faire ça via un type de projet. Tout ce que je devais faire était de créer le projet et lui donner mon exe. Il s’est avéré que c’était plus compliqué que ça car il ne détectait pas toutes les dll nécessaires donc j’ai du en ajouter à la main mais dans l’ensemble il fait ce qu’il est sensé faire. J’ai fini par avoir un exécutable que je pouvais partager.
Cependant, alors que j’écris cette page, je suis en train de chercher une alternative à ça car j’ai quelques problèmes avec la méthode de Visual Studio. Il ne peut pas remplacer une installation précédente, elle doit être manuellement désinstallée avant d’installer la nouvelle. Le chemin qu’on règle (potentiellement avec des dossiers intermédiaires) est effacé à la seconde où l’utilisateur·ice choisis un autre emplacement pour l’installation, ce qui veut dire que tous les fichiers de l’application peuvent se retrouver dans le dossier racine indiqué au lieu d’en créer un. Le projet crée deux fichiers, un msi et un exe, ce qui peut être déroutant. Ils font à peu de choses près la même chose sauf que c’est l’exe qui installe les redists dont je parlais plus tôt. Ça veut dire que si l’utilisateur·ice a le malheur de faire l’installation en utilisant le msi, iel aura l’impression d’avoir fait l’installation mais l’application ne démarrera quand même pas.
J’ai quelques candidats que j’essaierai d’utiliser pour la prochaine version publique, j’espère très prochainement.
Prochaines mises à jour#
Je prévois de continuer d’ajouter des fonctionnalités dans l’application allant de la qualité de vie comme des petites modifications d’UI à des choses que je voulais implémenter mais n’en ai jamais pris le temps comme du code manquant dans certains menus contextuels.
Mais parmi les plus gros chantiers sur lesquels j’aimerais travailler il y a:
- La création de liste complète depuis l’application
- L’ajout de graphiques dans la section des statistiques, pour peut-être montrer la fréquence des sessions ou l’évolution de la date de fin, je ne suis pas encore sûr.
Conclusion#
Je suis très content de ce que j’ai fait sur ce projet et j’ai hâte de voir ce que je vais y ajouter dans le futur. Partager l’app publiquement est une bonne opportunité entraînement car ça m’a permis de réfléchir à ma manière de nommer mes versions et comment créer un installeur. Une bonne expérience d’apprentissage. Ça me permet aussi d’utiliser plus mon moteur et continuer d’y ajouter des éléments. Je crée même une sorte de template pour les applications que je pourrai réutiliser pour des futurs projets. Penser à ce que je peux y ajouter et comment me plait vraiment beaucoup et j’aime penser à mon expérience sur mes futures créations que sera de plus en plus agréable.
Si ça vous intéresse, vous pouvez regarder mes listes de chaque saisons ici: 2024, 2025, 2026
Les jeux listés ont des notes sur eux contenant ce que j’ai pensés d’eux en y jouant.
