Site logo

Triceraprog
La programmation depuis le Crétacé

  • Dans la prison hantée sur AgonLight ()

    Comme présenté dans un article précédent, j'ai participé à la game jam Retro Programmers United for Obscure Systems, organisée par Olipix. Le principe est de développer un jeu pour une machine qui n'a pas eu une grande ludothèque (moins de 100 titres).

    Après avoir terminé ma contribution sur le Lynx, je me suis dit qu'un portage pour l'AgonLight serait intéressant et plutôt facile. Les capacités graphiques sont bien supérieures, et le processeur est un Z80, supporté par la même toolchain que j’avais utilisée pour le Lynx (z88dk).

    Un petit mois plus tard, c'est chose faite. Le jeu est disponible sur itch.io.

    J'en ai profité pour ajouter une version en anglais et une version en esperanto. Puisque tout est développé depuis les mêmes sources, ces versions sont aussi disponibles sur le Lynx.

    Au passage, la taille de l'exécutable a été un tout petit peu réduite. Pas assez pour entrer sur la version Lynx 48k malheureusement. Et en projetant le gain potentiel en travaillant la compression des données, j'ai assez peu d'espoir d'y arriver. Cela pourrait probablement être possible en reprogrammant le jeu en assembleur, mais j'ai déjà passé assez de temps sur ce projet et j'ai envie de passer à autre chose. De plus, je perdrai la possibilité d'un portage facile sur une machine avec un autre processeur.

    J'ai aussi publié les sources du jeu sur GitHub.

    À bientôt pour de nouvelles aventures !

    Dans la prison hantée sur AgonLight2


  • VG5000µ, Schémas de principe mis à jour en v1.5 ()

    Et voici une nouvelle mise à jour du schéma de principe.

    Il y a une seule modification par rapport à la version 1.4, qui est l'ajout du mode international lorsque la diode 6602 relie le signal NMI/ au Y3/ de 7807. Avec cette diode présente, le VG5000µ passe en anglais.

    Merci à Etno pour cette information.

    Ce qui donne, mis à jour.

    La platine principale

    Image cliquable pour une version en haute définition. (mise à jour 21 novembre 2023)

    Platine principale

    La platine K7/Son

    Image cliquable pour une version en haute définition. (mise à jour 9 sept. 2018)

    Platine K7/Son

    Rappel des versions précédentes


  • Un an de Retro Programmers United for Obscure Systems ()

    Un peu plus d'un an en fait, puisque Olipix lance ce groupe en juin 2022. L'idée, je le rappelle, est d'offrir à des machines qui en leur temps n'ont pas eu une grande ludothèque quelques titres supplémentaires, dans un format game jam de trois mois (souvent étendus à quatre).

    Dans cet article, je vais revenir rapidement sur les 4 jeux que j'ai développés à cette occasion, avec quelques commentaires.

    VG5000µ : La Maison dans la colline

    La première machine choisie a été le VG5000µ, une machine que, vous le savez si vous suivez ce blog, j'étudie depuis un moment. Pour un premier développement réel (autre que des tests), je voulais un affichage rapide, mais sans aller dans un jeu rapide. L'idée du jeu d'aventure graphique avec support de texte est arrivée rapidement.

    J'ai commencé ici une série d'articles sur le développement du jeu.

    Le jeu est développé avec z88dk, en C avec un peu d'assembleur pour l'affichage. J'ai aussi réalisé tous les graphismes (et ça se voit ?) avec Pixelorama. Pour les outils de données, c'est du Python avec un fichier de description du jeu, qui sort les données binaires injectées dans l'exécutable.

    Le jeu est disponible sur itch.io.

    Olipix fait une revue du jeu dans cette vidéo.

    La maison dans la colline

    EXL100 : Plouf... in space

    La deuxième machine choisie a été l'EXL100, une machine que je ne connaissais pas du tout. Et une machine plutôt exotique. Un processeur que je ne connais pas, l'essentiel de la mémoire utilisable non adressable par le processeur... et un synthétiseur vocal plutôt difficile à utiliser.

    J'avais quelques idées pour utiliser la machine, mais j'ai rapidement compris que le temps de me familiariser avec et de faire des tests, je n'aurais pas le temps de faire un jeu. J'ai donc changé d'avis et suis partie sur un jeu programmé en BASIC, qui permet facilement d'utiliser toute la mémoire. Et pour le type de jeu, un touché-coulé, mais avec un twist : les bateaux sont en mouvement. Je n'étais pas très certain que le gameplay donne quelque chose, mais au final et après quelques ajustements, ça fonctionne plutôt bien.

    Sur la fin, les calculs en BASIC commençaient à être un peu lents, et j'ai donc ajouté un peu d'assembleur dans le peu de mémoire adressable par le processeur. J'ai utilisé l'assembleur ASL qui est un des rares supportant le TMS7020. J'y associe un petit script en Python pour générer les DATA pour le BASIC. C'était assez manuel, je n'aime pas trop ça, mais comme souvent : manque de temps pour des choix assez tardifs.

    Puis les bateaux sont devenus des vaisseaux parce que... parce que.

    Je voulais aussi redéfinir quelques caractères pour un affichage plus sympa, mais je n'ai pas eu le temps. Peut-être un jour si je ressors l'idée ?

    Le jeu est disponible sur itch.io.

    Olipix fait une revue du jeu dans cette vidéo, dans laquelle nous avons aussi discuté du développement du jeu.

    Plouf... in space

    Aquarius : Le jardin des œufs

    La troisième machine a été l'Aquarius. Retour à du Z80 qui m'est familier. Et une machine simple. Dépouillée même. L'avantage pour le graphisme, c'est qu'il faut faire avec les caractères de la machine. Heureusement, un éditeur dédié à la machine est disponible en ligne, ce qui a bien simplifié les choses.

    Comme la fin de la game jam était autour de Pâques, j'ai voulu faire thématique avec une chasse au œufs. Dans la lignée des anciens jeux avec des plateformes qui n'ont pas vraiment de sens, j'ai designé un jardin étrange, avec quelques éléments de game design classiques.

    Dans tous ces jeux, je pars d'un principe : les joueurs de retro n'ont souvent pas vraiment de temps à passer sur ces machines, particulièrement celles qui ne sortent jamais. Je vise donc des jeux courts, quelque chose qui puisse se découvrir et se terminer en une vingtaine de minutes. Quitte à se qu'il se termine en moins de 5 minutes lorsque l'on connaît la solution.

    Puisque le jeu se termine rapidement, j'ai ajouté un compteur de mouvements, avec d'y associer une sorte de time attack... mais sans temps.

    Le jeu est entièrement en assembleur, utilisant sjasmplus, un assembleur que j'avais essayé auparavant et que je voulais creuser un peu plus. C'était l'occasion. Un jeu tout en assembleur, ça prend un peu plus de temps à développer, par contre, question mémoire, ça permettait de faire petit. Et encore, il y a de la marge d'optimisation.

    Le jeu est disponible sur itch.io.

    Olipix fait une revue du jeu dans cette vidéo.

    Le jardin des œufs

    Camputers Lynx : Dans la prison hantée

    Une nouvelle session, et une nouvelle machine sortie de nulle part. J'avais connaissance de la machine, mais je ne m'étais jamais penché dessus et... quel étonnement. Cette machine a été conçue avec des choix originaux. C'est du Z80, bon point pour moi. La partie vidéo est aussi très intéressante avec des pixels indépendants en couleur les uns des autres. Une rareté pour l'époque. Bien entendu, je me dis qu'il faudra absolument utiliser cette particularité.

    J'ai passé un bon moment sur la machine sur des tests variés. Les limitations d'accès à la RAM, avec un mapping vraiment pas simple, et des émulateurs pas très aboutis m'ont fait désespérer une paire de fois. J'ai souvent mis le projet sur le côté.

    Pour le jeu, je voulais aller du côté de Temple of Apshaï, un jeu qui m'avait marqué étant petit. Comme d'habitude, dans une formule courte. Vu tout le temps passés à faire des tests et à procrastiner, j'ai aussi fait le choix d'utiliser un set de sprites désigné par Kenney. Sur base d'un set noir et blanc, j'ai ajouté un peu de couleurs, puisque je voulais utiliser les capacités de la machine en la matière.

    Initialement, j'étais parti sur la réutilisation du format et des scripts de la Maison dans la Colline. Cependant, j'avais un peu luté avec le côté semi-manuel de la méthode. J'ai donc décidé d'utiliser Tiled pour la conception des niveaux, et un script Python pour générer les données. Tiled est vraiment sympa à utiliser et je pense que je le réutiliserai dans le futur pour des projets similaires.

    Pour le code, j'ai utilisé z88dk avec un peu d'assembleur pour l'affichage, mais beaucoup moins que pour le VG5000µ. Je voulais réutiliser le principe d'affichage rapide, mais les émulateurs Lynx ne supportent pas (encore ?) la redirection du vecteur d'interruption pour la VSYNC. J'ai rapidement fait une croix dessus. N'ayant pas accès à cette machine, je devais absolument faire avec les émulateurs.

    Aussi, rapidement, j'ai compris que le jeu n'entrerais pas en mémoire de la version 48k, mais uniquement sur la version 96k (cela semble beaucoup, mais de cette mémoire, 32ko sont réservés pour la vidéo).

    Le jeu est disponible sur itch.io.

    Retro VynZ a fait une partie filmée sur sa chaîne YouTube.

    Olipix présente le jeu, suivi d'un petit échange sur sa chaîne YouTube.

    Dans la prison hantée

    Et la suite ?

    J'ai passé de bons moments sur ces projets. Découvrir ces machines est un plaisir (même si elles sont parfois un peu énervantes), cela profite un peu (un tout petit peu) à leur visibilité, à leur redécouverte. Et si ça n'amuse que nous, c'est déjà ça.

    Je ne connais pas encore la prochaine machine, je ne sais donc pas vers où je vais aller, mais j'ai en tête deux défis : utiliser un peu plus de couleurs et, pour la première fois, ajouter du son !

    À bientôt pour de nouvelles aventures !


  • La palette de couleur de l'Agon Light ()

    Ces derniers temps, je m'amuse avec un AgonLight (ou plus exactement un AgonLight2, qui est la version Olimex).

    Cette machine est assez récente et possède une petite communauté. Sa documentation est par contre très éparse pour le moment. Du plus, la partie graphique de la machine se reposant sur FabGL, une partie des informations intéressantes sont en fait à déduire de cette bibliothèque. Mais d'autres se déduisent de l'implémentation pour la machine du BBC Basic.

    Je vais me servir de ce blog pour prendre quelques notes. Cette semaine, j'ai tourné en rond autour de la gestion de la palette et des modes graphiques disponibles.

    Modes graphiques

    Les modes graphiques, à cette date (MOS 1.03), sont :

    Mode Résolution (Pixels) Fréquence (Hz) Nb. de couleurs Palette?
    0 1024x768 60 2 Oui
    1 512x384 60 16 Oui
    2 320x200 75 64 Non
    3 640x480 60 16 Oui

    Palette de couleurs

    Dans les modes en palette, les couleurs se choisissent parmi l'espace de couleur complet RGB222. Les 4 niveaux pour chaque composante sont 0x00, 0x55, 0xAA et 0xFF. Ce qui donne :

    Palette RGB222 de l'AgonLight

    Attention, la palette est réinitialisée lors d'un changement de mode

    R,G,B ?

    Une chose étonnante, c'est qu'il est possible de spécifier les palettes, et il est obligatoire de spécifier les pixels des surfaces, en RGB888, alors que l'espace de couleur est RGB222. Le VDP (ou plutôt FabGL), va chercher à trouver les couleurs en fonction. Ce n'est pas hyper pratique au premier abord, et c'est un bon gâchis d'espace.

    Mieux vaut ne spécifier que des couleurs faisant partie de la palette.


  • La Maison dans la colline, partie 7 ()

    Dans ce septième article de la série sur le jeu « La maison dans la colline », il va être question de tests anti-regression.

    Regressions

    Mais qu'est-ce qu'une regression ? C'est un fonctionnement qui donnait toute satisfaction et qui, suite à un changement dans le système, se met à ne plus fonctionner comme attendu. Autrement dit, une apparition de bug !

    Les bugs n'arrivent jamais de nulle part, il y a toujours une raison. Mais plus un programme est grand, plus il se complexifie et plus le risque de programmer des morceaux qui entrent en conflit apparaît. C'est à peu près inéluctable et le développement d'un logiciel est, normalement, accompagné d'un certain nombre de règles pour éviter au mieux et surtout repérer au plus vite les défauts qui apparaissent.

    La vitesse de détection est importante, car il une regression peut ne pas être immédiatement flagrante. Il est possible que quelque chose casse sur une partie « éloignée » de ce sur quoi on travaille sur le moment. Il est aussi possible que le défaut soit subtile ; présent, mais pas évident. Tout à l'air de bien fonctionner en apparence, mais pas dans les détails. Et si on continue à développer avec ce défaut présent, il se peut très bien que l'on amplifie le problème, ajoutant du bug à du bug.

    La vitesse est aussi importante pour une question de contexte. Lorsque l'on a en tête une partie en train d'être travaillée, il est plus simple de corriger ce qui vient d'être modifié que lorsque l'on s'en rend compte plus tard, lorsqu'on est passé à autre chose.

    Dans le contexte de « La maison dans la colline », je suis tout seul et, comme je l'ai déjà dit auparavant, j'essaie de maximiser mon temps libre passé sur le projet. Ainsi, une session à essayer de trouver et corriger la raison d'un bug introduit deux sessions avant ne m'enchante pas du tout.

    J'ai donc mis en place deux systèmes pour m'aider à détecter rapidement les bugs

    Tests unitaires

    Le premier système est un système simpliste de tests unitaires. Le principe est de mettre le système dans un certain état, de faire une opération, puis de vérifier que le système est dans l'état attendu.

    J'ai mis en place ce système après avoir débuté le projet et lorsque celui-ci commençait à devenir un peu complexe. Malheureusement ou heureusement, j'avais pris quelques raccourcis qui rendaient certains tests un compliqués. Cela m'a pris un peu de temps pour nettoyer ça ; au passage, j'en suis sorti avec quelques nettoyages bienvenue dans le code.

    Je n'ai pas non plus beaucoup de tests. Majoritairement, je fais des tests sur la gestion de l'inventaire, qui m'a causé quelques soucis et qui a été ce qui m'a décidé à mettre des tests unitaires. Je fais aussi quelques tests sur mon micro-allocateur de mémoire dynamique (celui de z88dk ne me convenant pas).

    Lorsque je lance le programme avec une option de compilation, les tests sont exécutés dans une page spéciale, au démarrage du jeu. Je peux ainsi m'assurer que les tests passent et que donc le fonctionnement de mon système d'inventaire et d'allocation sont toujours d'aplomb.

    Voilà à quoi ressemble le test des objets, une fois que j'ai mis en place un environnement de tests avec des objets dans des pièces :

        CHECK(object_count_in_room(ROOM(1)) == 2);
        CHECK(object_count_in_room(ROOM(254)) == 1);
        CHECK(object_count_in_room(ROOM(255)) == 0);
    

    Et voilà une capture d'écran lors d'une regression :

    Liste terminée des fonctions du jeu.

    Mode automatique

    L'autre système que j'ai mis en place est un mode de jeu automatique. Au bout d'un moment, et alors que le nombre d'actions et de pièce commençait à grandir, il me fallait régulièrement jouer toute une première partie du jeu. Je vérifiais les mouvements, les portes et changements de pièce, la prise d'objet dans l'inventaire, l'utilisation d'un objet.

    Ça a rapidement été long, pas très amusant et source d'erreurs : est-ce que je fais bien les mêmes étapes ? Est-ce que j'ai encore envie de le faire parce que ça m'ennuie ? Est-ce que quelque chose ne va pas casse pile la fois où j'aurai la flemme de conduire les tests ?

    Solution : automatiser le test.

    Lorsque je compile le jeu avec le mode automatique inclue, je déroule un petit script qui va exécuter les actions à ma place. C'est assez simpliste, je n'ai pas de retour d'erreur automatisé. Mais au moins, si je vois quelque chose d'étrange, ou bien si le personnage se retrouve bloqué sur une étape, je peux le voir rapidement. Et je peux alors conduire un test manuel pour comprendre dans les détails ce qu'il se passe.

    Il n'y a pas beaucoup d'étapes dans ce test automatique, car c'est un peu fastidieux à maintenir. J'ai hésité un moment à rendre le système plus malin, avec une création de script depuis les données du jeu. Par exemple en trouvant le chemin pour aller d'un point A à un point B automatiquement. Mais je ne suis pas allé jusque là, j'ai trouvé un compromis avec un système qui « tente » d'aller vers une position, mais de manière simpliste, que je dois aider manuellement parfois.

    Mais que de temps gagné au final !

    Voici à quoi ressemble le début du script, qui est stocké dans un tableau de caractères dans le code source :

    unsigned char script[] =
            {
                    'W', 'P', 4, // Wait page 4 (test page)
                    'W', 'T', 20,// Wait frames
                    'K', ' ',    // Press ' '
                    'W', 'P', 0, //
                    'K', ' ',    // Press ' '
                    'W', 'P', 1, //
                    'K', 'A',    // Press 'A'
                    'W', 'T', 5,
                    // Go to Kitchen
                    'K', KEY_UP,
                    'K', KEY_UP,
                    'K', KEY_UP,
                    'K', KEY_UP,
                    'G', 'R', 2,// Go to Room 2
    

    Conclusion

    J'ai déjà évoqué mon goût pour les outils et les automatisation dans les articles précédents. L'automatisation des tests est un outil de plus pour se simplifier la vie. Cela demande un peu d'effort en amont, mais force parfois à mieux réfléchir son code pour le rendre flexible, ce qui est bénéfique lors de la mise au point du jeu, et permet un gros gain de temps sur la durée du projet, pour peu que l'on trouve le bon équilibre entre le temps passé à créer les outils et le gain de temps espéré grâce à eux.


« (précédent) Page 2 / 21 (suivant) »