Tutoriel Attract-Mode (Partie 2 sur 3)

Deux articles en deux semaines ? Un exploit. Bref, voici la seconde partie de mon tutoriel en trois volets concernant Attract-Mode. Au programme : le développement d’un layout personnalisé en Squirrel. Pour les retardataires, la première partie est ici.

PARTIE II – DÉVELOPPER UN LAYOUT PERSONNALISÉ POUR ATTRACT-MODE

TABLE DES MATIÈRES

INTRODUCTION

Dans cette deuxième partie de mon tutoriel concernant Attract-Mode, j’ai décidé de vous expliquer comment réaliser un layout personnalisé pour Attract-Mode. Pourquoi s’embêter alors que de nombreux layouts sont proposés gratuitement pour Attract-Mode, et qu’ils proposent souvent un rendu très convaincant ? Essentiellement pour comprendre comment fonctionne le frontend, ce qui permet d’adapter un layout existant à ses propres exigences ou de reprendre le développement d’un layout abandonné. Développer son propre layout permet également de mieux comprendre comment Attract-Mode gère les médias, ce qui assure à terme d’obtenir le meilleur du modèle sélectionné.

Oh, et puis parce que Bob aime la bidouille informatique aussi. Surtout, en fait !

Comme les développeurs de frontends aiment également la bidouille, ils ne peuvent supporter de faire comme les autres : à chaque frontend ou presque, son langage dédié. Attract-Mode a choisi de s’appuyer sur le Squirrel, un énième langage de script dont on peut constater sur la page officielle que la première version date de 2011 et la dernière à ce jour de 2016… encore un projet à l’avenir très incertain ! [Apparté] Bob ne comprend pas pourquoi les projets open-source s’obstinent à réinventer la roue à chaque fois : en matière de script léger et facile d’apprentissage, le Python est dans la place depuis longtemps et pour encore longtemps. Il serait plus sage (et plus simple) de développer une bibliothèque spécifique en Python que de vouloir égaler les pionniers de la programmation. Bref.[Apparté].

Bon, de toute façon, ce n’est pas comme si nous avions le choix : développer pour Attract-Mode impose de passer par le Squirrel. Voyez le bon côté des choses : le langage est assez souple et permet un développement tranquille, loin de l’usine à gaz proposée – notamment – par HyperSpin. Bob va donc vous expliquer les bases, libre à vous d’approfondir. Mais la documentation n’est pas super exhaustive. Et comme l’avenir du Squirrel – voir même d’Attract-Mode – ne s’inscrit probablement pas dans les décennies à venir, il conviendra d’avoir une organisation assez modulaire pour être en mesure de conserver le fruit de son travail même en cas de migration. La troisième partie de ce tutoriel vous expliquera tout ça. Mais d’abord, regardons ce fameux Squirrel de plus prêt.

LE LANGAGE SQUIRREL

D’après Wikipédia, le Squirrel est un langage de programmation haut niveau impératif et orienté objet. Mouais. Ça nous fait une belle jambe. Pour faire (très) simple, Squirrel est un langage de script comme Python ou un simple fichier batch pour Windows : vous rédigez des commandes dans un fichier texte, et ces commandes sont ensuite exécutées par le l’interpréteur.

Vous n’avez pas besoin de comprendre en détail ce que je viens d’écrire (mais je vous conseille tout de même de vous renseigner à l’occasion sur les différents types de langage de programmation); pour développer en Squirrel, vous avez besoin de deux choses : un éditeur de texte (le Bloc-notes Windows suffit, je vous conseille Geany qui rajoute pas mal de petits outils et est multi-plateformes) et la documentation. Pour développer un layout pour Attract-Mode, il faut en plus la documentation spécifique.

Le Squirrel fait partie de ces langages relativement clairs à la lecture (mais il faut quand même quelques notions d’anglais). Comme pour tout langage de programmation, il sera nécessaire d’être très rigoureux sur la syntaxe : un point en trop ou en moins, et ça ne fonctionnera pas ! Dernière chose, le fichier texte devra être enregistré avec l’extension .nut (des noix pour l’écureuil… lolilol !), sinon… ça ne fonctionnera pas ! Il est donc nécessaire d’afficher les extensions dans votre explorateur de fichier (si vous ne savez pas comment faire, une petite recherche avec votre moteur préféré s’impose !).

Enfin, notez qu’il n’y a rien à télécharger, l’interpréteur Squirrel étant intégré à Attract-Mode. C’est toujours ça de pris ! Voyons maintenant comment s’organise un layout dans Attract-Mode.

LE LAYOUT DANS ATTRACT-MODE

Ouvrez votre dossier d’installation d’Attract-Mode. Vous devez y trouver un dossier « layouts »

Dans ce dossier « layouts », vous devriez trouver dix dossiers : il s’agit des dix layouts de base fournis avec Attract-Mode.

Si vous téléchargez un layout, c’est donc dans ce dossier que vous devrez le copier. Et pour créer un nouveau layout, il faudra donc créer un nouveau dossier, lui donner le nom de votre layout et placer à l’intérieur votre fichier .nut (au minimum).
Voyons voir ce qui se trouve dans le dossier « basic », soit celui du layout de base utilisé dans la première partie du tutoriel.

Un fichier layout.nut et deux images : ces images sont utilisées par le script pour former l’environnement visuel. Mais quid des visuels téléchargés dans la première partie du tutoriel et propres à chaque jeu ? Retournez dans le dossier d’installation d’Attract-Mode et rechercher le dossier « scraper ». Ouvrez-le.

Vous devriez y trouver un dossier au nom de la base de données choisie (par défaut thegamesdb.net) et surtout un dossier au nom de l’émulateur configuré en première partie (pour ce tuto : Snes_9X).
Dans ce dossier, vous devriez trouver cinq dossiers « flyer », « marquee », « overview », « snap » et « wheel », qui correspondent aux médias suivants : jaquette, enseigne avec le logo (comme en partie haute des bornes d’arcade), description (en anglais, of course), capture d’écran et logo du jeu.

Dans chacun de ces dossiers, vous retrouvez un fichier média par jeu (pour rappel, trois dans ce tuto), portant le nom du jeu (ou plus exactement de la ROM qui a été listée).

Quatre remarques :

  • Un média a été créé pour l’émulateur dans le dossier « flyer » uniquement.
  • Si il existe plusieurs médias de la même catégorie pour un même jeu, un dossier est alors créé, portant le nom de la ROM, et les fichiers médias y sont copiés (Attract-Mode affichera un fichier au hasard à chaque fois). Allez regarder dans le dossier « snap ».
  • Le nom du fichier média doit être strictement identique à celui de la ROM (sauf l’extension), à défaut rien ne s’affichera dans Attract-Mode. Si vous changez le nom de la ROM, il faudra changer le nom de chaque média qui s’y rapporte (ou procéder à une nouvelle aspiration de données).
  • Si un fichier média est absent, mal nommé ou corrompu, Attract-Mode fonctionnera normalement, mais le média ne sera pas affiché.

Gardez à l’esprit que ces cinq types de médias sont les types standards d’Attract-Mode, qui seront toujours accessibles sans configuration particulière (mais n’oubliez pas qu’il faut les « aspirer » préalablement, comme expliqué dans la partie 1 du tutoriel).
Nous allons pour l’instant créer un layout en utilisant ces types de média uniquement, et je vous expliquerai en dernière partie puis dans le 3ème volet de ce tutorial comment étoffer l' »offre ».

DÉVELOPPER SON LAYOUT : ENVIRONNEMENT COMMUN

Ce tutoriel ne sera pas exhaustif et demeurera simple. Ça tombe bien, Squirrel et Attract-Mode permettent de faire les choses simplement (même si on peut toujours optimiser). Notamment, il est possible de définir la résolution de référence du layout, et de positionner les éléments dans la fenêtre par rapport à cette résolution de référence, ce qui permet d’économiser du temps et du paracétamol : Attract-Mode se charge d’adapter l’affichage à la résolution de l’écran.
Par ailleurs, le layout sera spécifique à un système (ici la Super Nintendo), mais il est possible de développer un layout passe-partout. Deux options existent alors : faire l’impasse sur les aspects spécifiques du système ou développer quelque chose de compliqué qui s’adapte automatiquement au système.
Enfin, le layout n’utilisera pas d’effet « whaou ! » et se contentera d’afficher une illustration de la SNIN, le logo de la console, la liste des jeux, le logo et la jaquette du jeu ainsi que deux captures d’écran, avec une police de caractère personnalisée. Simple et efficace (normalement).

Pour commencer, retournez dans le dossier « layouts » et créez un dossier que vous nommerez comme votre layout (pour moi ce sera « BL_SNIN » pour « Bob’s Layout SNIN »).
Dans ce dossier, créez un fichier layout.nut (si Windows vous dit que changer l’extension rendra le fichier inutilisable, ce n’est pas un problème); je vais également y copier les éléments visuels de la console (préalablement préparés avec amour un logiciel de dessin) : le logo (logo_console.png), l’illustration de la SNIN (Snin_PA.png), un fond de 1280×720 (BG.jpg – 1280×720, qui correspond à la norme HD, sera ma définition de référence) et ma police de caractère (Nintender-Mm1B.ttf). Mon dossier ressemble désormais à ça :

Bon, il est temps de mettre les mains dans le code. Ouvrez le fichier layout.nut et inscrivez-y les lignes suivantes :

/*
####################

BOB'S LAYOUTS - SNIN

Theme pour Attract Mode par Bob Dupneu
Decembre 2021
www.bobdupneu.fr
Reproduction, redistribution et modification autorisées
en rappelant que Bob est le meilleur !
####################
*/


//RESOLUTION :  1280x720 (HD)
fe.layout.width = 1280
fe.layout.height = 720


//##########
//VARIABLES COMMUNES
//Police de caractère Nintendo NES
fe.layout.font = "Nintender-Mm1B"
//Marge régulière pour décoller les éléments (padding)
local marge = fe.layout.width / 100

Les premières lignes (entre /* et */) seront ignorées par l’interpréteur, vous pouvez donc y mettre ce que vous voulez (ou même les supprimer complètement).
Les lignes qui commencent par un double slash (//) sont également ignorées et servent à commenter le code (pour mieux s’y retrouver par la suite ou permettre à un tiers de le comprendre).
Notez que vous pourriez écrire simplement :

fe.layout.width = 1280
fe.layout.height = 720
fe.layout.font = "Nintender-Mm1B"
local marge = fe.layout.width / 100

Ça fonctionnerait exactement pareil, mais ce serait beaucoup moins lisible. Et en matière de programmation, la lisibilité du code est la meilleure défense contre les erreurs !

Que dit le code ?
Les deux premières lignes définissent la largeur et la hauteur (donc la résolution) de référence du layout : ici 1280 pixels de large pour 720 pixels de haut, ce qui est la norme HD. A partir de maintenant, tous les éléments devront être placés selon cette référence.
Pour rappel (ou pas, selon votre niveau), l’origine (le point 0,0) se trouve en haut à gauche; le point 1280,720 est donc en bas à droite. Imaginez une grille de bataille navale qui ferait 1280 cases en largeur et 720 cases en hauteur.

Ensuite (sous « Variables communes »), je définis la police de caractère qui sera utilisée par défaut (fe.layout.font = « Nintender-MmlB »); notez que la police Nintender-MmlB se trouve dans le même dossier que layout.nut. Si elle était dans un dossier « police » de ce dossier, il faudrait écrire fe.layout.font = « police/Nintender-MmlB » (chemin relatif; un chemin absolu fonctionnerait mais c’est très déconseillé). Il n’est pas nécessaire d’indiquer l’extension, mais les guillemets sont indispensables pour indiquer un chemin extérieur (sans guillements, l’interpréteur recherchera une variable Nintender-MmlB qui n’existe pas).

Je définis ensuite une marge relative à la largeur du layout, qui me servira par la suite pour espacer les éléments visuels : je crée une variable locale que j’appelle marge (local marge) et je définis sa taille comme étant un centième de la largeur de l’écran (fe.layout.width / 100) soit 12,8 pixels (arrondi à 13 dans ma définition de référence).

Passons à l’environnement visuel commun, c’est-à-dire à celui de la console.
Remarque importante : Squirrel et Attract-Mode « empilent » les éléments dans l’ordre du script : le premier est affiché, le second est affiché au-dessus et ainsi de suite. Il faut donc prévoir rigoureusement l’ordre d’appel des éléments graphiques pour éviter les mauvaises surprises.
Ici, l’image de fond est donc affichée en premier, puis les deux autres images, plus petites et avec de la transparence.
Dans layout.nut, inscrivez ceci à la suite des lignes précédentes :

//##########
//ENVIRONNEMENT GRAPHIQUE DE LA CONSOLE

//Image de fond - Même taille que la définition de base
local bg_image = fe.add_image("BG.jpg", 0, 0, fe.layout.width, fe.layout.height)
//Logo de la console - Alignement en haut à gauche - 1/4 de la largeur
local logo_console = fe.add_image("logo_console.png")
logo_console.width = fe.layout.width / 4
logo_console.preserve_aspect_ratio = true
logo_console.x = marge
logo_console.y = marge
//Illustration de la console - Alignement en bas à droite - 1/4 de la largeur
local ill_console = fe.add_image("Snin_PA.png")
ill_console.width = fe.layout.width / 4
ill_console.preserve_aspect_ratio = true
ill_console.x = fe.layout.width - marge - ill_console.width
ill_console.y = fe.layout.height - marge - ill_console.width

Le but est d’avoir l’image de fond avec le logo de la console en haut à gauche sur un quart de la largeur de l’écran, et l’illustration de la console en bas à droite, sur un quart de la largeur de l’écran également. Rien de compliqué mais il faut réfléchir un peu et faire preuve de logique.

Pour l’image de fond, c’est simple : je crée une variable que j’appelle bg_image (local bg_image) et je la définis comme étant une image (fonction fe.add_image); je donne le chemin vers l’image que je veux utiliser (entre guillemets, avec l’extension : ici il s’agit de l’image « BG.jpg » présente dans le dossier du layout (voir plus haut). Mon image fait la même taille que le layout : je l’affiche donc à l’origine (0,0) et lui donne la largeur (fe.layout.width) et la hauteur (fe.layout.height) du layout. Pour rappel, la largeur et la hauteur du layout ont été définis plus haut.

Pour le logo de la console, c’est un poil plus compliqué (j’ai donc découpé les arguments en plusieurs lignes pour plus de lisibilité, mais c’est exactement la même méthode que pour l’image de fond).
Je crée mon image logo_console et lui donne une largeur d’un quart de la largeur du layout.
Avec la fonction preserve_aspect_ratio, je m’assure que les proportions de l’image demeurent inchangées même avec le redimensionnement.
Enfin, j’affiche mon image en haut à gauche, avec un décalage de 13 px, soit la valeur de la marge, définie plus haut.

Pour l’illustration de la console, c’est la même chose avec une ruse de sioux : je veux afficher mon image à l’extrémité gauche (soit 1280). Mais l’origine de mon image est en haut à gauche : si j’affiche l’image a 1280, elle commencera à 1280 pixels, et s’affichera donc en-dehors de l’écran. Je dois donc la décaler vers la gauche afin de la voir. Ce décalage doit faire la largeur de l’image, auquel je rajoute ma marge. L’image est donc entièrement visible et légèrement décollée du bord.
Pour la hauteur, c’est exactement la même logique, avec une difficulté : la hauteur de mon image n’a pas été définie et est par conséquent inconnue; je ne peux donc pas me référer à cette valeur (inexistante) pour le décalage vers le haut. La résolution de ce problème peut s’avérer passablement complexe, mais le vieux Bob a plus d’un tour dans son sac : mon illustration a un rapport de 1:1 (c’est un carré). Comme j’ai utilisé la méthode preserve_aspect_ratio, ma hauteur (inconnue) est donc toujours égale à ma largeur (connue) : j’utilise donc la largeur de mon image pour le décalage vers le haut, auquel je rajoute ma marge.

Je vous laisse relire tout ça tranquillement, et surtout analyser le code qui est au final assez simple.
Maintenant, si vous lancez Attract-Mode et définissez votre layout comme le layout d’affichage, vous devriez avoir quelque chose qui ressemble à ça :

Pas mal non ? Bon, l’environnement de la console est en place (et il est statique); voyons voir l’affichage dynamique des jeux.

DÉVELOPPER SON LAYOUT : MÉDIAS SPÉCIFIQUES

Sélection du jeu

Personnellement, j’aime bien avoir une liste textuelle, car cela assure une lisibilité qui facilite la navigation.
Ça tombe bien, une fonction a été justement prévue pour cela : add_listbox. Cette fonction crée une liste de tous les jeux scannés pour le système en cours (voir partie 1 du tutoriel – 3 dans mon cas), et la liste peut être mise en forme.
Je veux que ma liste fasse un tiers de la hauteur du layout et soit placée sous le logo de la console, avec la même largeur. Pour des raisons esthétiques, je souhaite également qu’elle soit affichée en bas à gauche (avec le décalage de ma marge) sur l’écran et que son texte soit aligné à gauche. Rien de compliqué :

local list = fe.add_listbox (marge, fe.layout.height - (fe.layout.height / 3) - marge, logo_console.width, fe.layout.height / 3)
list.align = Align.Left

Lancez Attract-Mode et vous devriez avoir quelque chose de ce genre :

Vous remarquerez que la liste utilise la police de caractère définie plus tôt. Par défaut, le texte est blanc et jaune encadré de bleu lorsqu’il est sélectionné. Tout ça peut se modifier : set_rgb pour le texte, set_sel_rgb pour le texte sélectionné et set_selbg_rgb pour l’encadré (pour rappel, RGB signifie Red Green Blue, et permet de faire un mélange de ces trois couleurs primaires pour obtenir n’importe quelle autre couleur entre le blanc (255,255,255) et le noir (0,0,0)).
Essayons un texte noir qui devient blanc encadré de noir lorsqu’il est sélectionné :

Pas si mal. Pour terminer le côté droit du layout, pourquoi ne pas afficher le logo du jeu (pour rappel il s’agit d’un des types de média standard, accessible sous le mot-clef « wheel » et qui se trouve dans le sous-dossier « wheel » du dossier « scraper ».
Les médias spécifiques au jeu ne sont pas affichés au moyen de la fonction add_image utilisée (et pour laquelle il est nécessaire d’indiquer le chemin – relatif ou absolu – qui pointe vers l’image) mais au moyen de la fonction add_artwork (qui dispense notamment d’indiquer le chemin, le média étant nécessairement dans un dossier connu d’Attract-Mode). Pour le reste, les mêmes attributs que pour une image sont disponibles, le code ci-après devrait donc vous être familier :

local i_logo_jeu = fe.add_artwork("wheel")
i_logo_jeu.width = logo_console.width
i_logo_jeu.preserve_aspect_ratio = true
i_logo_jeu.x = marge
i_logo_jeu.y = 200

Mon logo fait la largeur du logo de la console (et donc de la liste), et il est décalé du bord de la largeur de la marge. Reste la position verticale : la hauteur de mon logo est inconnue, ainsi que celle du logo de la console; on a défini la hauteur de la liste, mais si j’affiche mon logo en haut de la liste, il se superposera… Comme pour l’illustration de la console, le positionnement précis peut être un peu embêtant à régler, et je ne peux pas utiliser ma ruse de l’image carrée, car les logos ne le sont pas nécessairement !
Pour rester simple, j’ai donc arbitrairement décidé de placer le logo a une position verticale de 200 px, ce qui donne un résultat satisfaisant. On pourra toujours améliorer le code plus tard, si cela s’avère nécessaire.
Vous devriez obtenir un résultat proche de celui-ci :

Avec les commentaires, le code de la partie gauche devrait donc être le suivant :

//##########
//SELECTION DU JEU

//Liste des jeux en bas à gauche, un tiers de la hauteur texte aligné à gauche, noir, blanc sur fond noir si sélection
local list = fe.add_listbox (marge, fe.layout.height - (fe.layout.height / 3) - marge, logo_console.width, fe.layout.height / 3)
list.align = Align.Left
list.set_rgb(0,0,0)
list.set_sel_rgb(255,255,255)
list.set_selbg_rgb(0,0,0)

//Logo du jeu entre le logo de la console et la liste, même largeur
local i_logo_jeu = fe.add_artwork("wheel")
i_logo_jeu.width = logo_console.width
i_logo_jeu.preserve_aspect_ratio = true
i_logo_jeu.x = marge
i_logo_jeu.y = 200
[code]

Passons à la partie droite !

Visuels du jeu

Il reste 2/3 de l’écran à remplir. Restons simple et efficace : la jaquette du jeu sera affichée en partie centrale et fera un tiers de la largeur de l’écran, et le tiers droite affichera trois captures d’écran superposées.

Pour la jaquette, le positionnement précis nécessite de connaître la largeur et la hauteur; on a vu à deux reprises que ces informations pouvaient être inconnues… Il y a toujours une solution plus ou moins complexe – et élégante – en programmation. Comme ce tutoriel veut rester simple, la solution proposée appartient à la triste histoire des solutions « quick ‘n dirty » (vite fait, mal fait !). Mais elle a le mérite de fonctionner.
Les jaquettes Super Nintendo ont un ratio approximatif de 210×154 et je veux que l’affichage de la jaquette prenne un tiers de l’écran, dont la résolution de référence est 1280×720 : ma jaquette doit donc avoir une largeur de 426,67 px (1280/3); je retire la largeur de la marge des deux côtés soit 25,6 px (12,8×2) => ma jaquette doit avoir une largeur de 401,07 px, que nous arrondirons à 400 pixels. De là, je peux connaître la hauteur de ma jaquette par un simple produit en croix : 400/21×15 = 293,33 px, que nous arrondirons à 293 pixels (et dire que vous pensiez que les cours de maths étaient inutiles !).

Maintenant que je connais les dimensions précises de ma jaquette, il faut que je définisse ses coordonnées pour qu’elle soit parfaitement centrée. Le point central de mon écran de 1280×720 est 640, 360. Mais si j’affiche ma jaquette à ces coordonnées, c’est le coin supérieur gauche qui sera centré, et non la jaquette.
Je dois donc décaler ma jaquette de sa moitié de hauteur vers le haut et de sa moitié de largeur vers la gauche, afin que le centre de ma jaquette soit affiché au centre de l’écran. Bob s’est fendu d’un petit schéma pour que tout cela soit clair :

L’image verte est bien reculée et remontée de moitié par rapport à l’image rouge juste positionnée au centre de l’écran. Il faut donc reculer la jaquette de 200 px et la remonter de 146,5 px (arrondi a 147).
Nous disposons de toutes les données pour l’affichage de la jaquette.

// Jaquette du jeu, 1/3 de la largeur de l'écran, centrée
local i_jaquette_jeu = fe.add_artwork("flyer", 440, 213, 400, 293)

Oh yeah ! Ca fonctionne. Le code n’est pas le plus élégant, mais comme Attract-Mode adapte seul en fonction de l’écran de destination, le script fonctionnera sans difficulté partout. Bien sûr, le layout n’est pas adapté aux autres systèmes qui proposent un format de jaquette différent : il faudra le modifier pour le réutiliser. C’est la grande limite de la programmation « en dur » des dimensions, mais cela suffira pour ce tutoriel.

Il ne reste plus que les deux captures d’écran à afficher en colonne dans le tiers droit de l’écran. Petite remarque : si plusieurs captures sont présentes dans le dossier « snap », Attract-Mode en affichera une au hasard pour chaque appel; si il n’y a qu’une capture, elle sera affichée deux fois. Également, il arrive qu’Attract-Mode affiche plusieurs fois la même capture alors qu’il y en a plus de deux dans le dossier. Je n’ai pas trop cherché de solution a ce problème car j’ai une autre façon de faire qui sera expliquée dans la 3ème partie de ce tutoriel.

Dans l’immédiat, nous allons nous contenter de deux artworks du type « snap » qui seront affichés en colonne, à droite de la jaquette. Je veux que ces captures prennent tout l’espace entre le haut de l’écran et l’illustration de la SNIN en bas à droite (moins une marge a chaque extrémité et une entre chaque capture).
Je sais, et ça me suffira, que le ratio de la SNIN est de 8:7. Et ce coup-ci, je ne vais pas être quick ‘n dirty, mais vous montrer comment coder proprement. Des maths niveau collège seront le seul prérequis.

Je commence par définir la largeur de mon affichage, qui est un tiers de l’écran, moins deux marges :

fe.layout.width - (marge*2)

Je définis ensuite la hauteur de mon affichage, qui est la hauteur de l’écran moins la hauteur de l’illustration de la SNIN (soit, si vous vous rappelez mon astuce, sa largeur) et trois marges :

fe.layout.height - ill_console.width - (marge*3)

La hauteur de mes captures sera la moitié de mon affichage, et la largeur huit septième de cette hauteur. Pour avoir une répartition uniforme, la première capture sera collée au haut de l’écran (plus la marge) et la seconde capture sera collée au bas de l’écran (moins la hauteur de l’illustration de la SNIN et la marge). Vous remarquerez que l’ordre d’affichage des éléments – que j’évoquais plus haut – est importante car il est possible de positionner les nouveaux éléments par rapport aux anciens : la rigueur est donc de mise.
Les deux captures seront centrées horizontalement sur la zone d’affichage (par pur choix esthétique). Je vous invite à réfléchir au code par vous-même (toutes les notions nécessaires ont été vues) avant de faire un copier-coller.

//Affichage de 2 captures d'écran au ratio 8:7 en colonne dans le tiers droit
local largeur_aff = fe.layout.width / 3 - (marge*2)
local hauteur_aff = fe.layout.height - ill_console.width - (marge*3)
local hauteur_cap = hauteur_aff / 2
local largeur_cap = hauteur_cap / 7 * 8
local x_cap = fe.layout.width - marge - (largeur_cap / 2) - (largeur_aff / 2)
local y_cap2 = fe.layout.height - marge - ill_console.width - hauteur_cap

local i_capture1 = fe.add_artwork("snap", x_cap, marge, largeur_cap, hauteur_cap)
local i_capture2 = fe.add_artwork("snap", x_cap, y_cap2, largeur_cap, hauteur_cap)

Normalement, vous devriez désormais avoir un layout qui ressemble à ça :

Et un code final identique à ceci :

/*
####################

BOB'S LAYOUTS - SNIN

Theme pour Attract Mode par Bob Dupneu
Decembre 2021
www.bobdupneu.fr
Reproduction, redistribution et modification autorisées
en rappelant que Bob est le meilleur !
####################
*/


//RESOLUTION :  1280x720 (HD)
fe.layout.width = 1280
fe.layout.height = 720


//##########
//VARIABLES COMMUNES
//Police de caractère Nintendo NES
fe.layout.font = "Nintender-Mm1B"
//Marge régulière pour décoller les éléments (padding)
local marge = fe.layout.width / 100

//##########
//ENVIRONNEMENT GRAPHIQUE DE LA CONSOLE

//Image de fond - Même taille que la définition de base
local bg_image = fe.add_image("BG.jpg", 0, 0, fe.layout.width, fe.layout.height)
//Logo de la console - Alignement en haut à gauche - 1/4 de la largeur
local logo_console = fe.add_image("logo_console.png")
logo_console.width = fe.layout.width / 4
logo_console.preserve_aspect_ratio = true
logo_console.x = marge
logo_console.y = marge
//Illustration de la console - Alignement en bas à droite - 1/4 de la largeur
local ill_console = fe.add_image("Snin_PA.png")
ill_console.width = fe.layout.width / 4
ill_console.preserve_aspect_ratio = true
ill_console.x = fe.layout.width - marge - ill_console.width
ill_console.y = fe.layout.height - marge - ill_console.width

//##########
//SELECTION DU JEU

//Liste des jeux en bas à gauche, un tiers de la hauteur texte aligné à gauche, noir, blanc sur fond noir si sélection
local list = fe.add_listbox (marge, fe.layout.height - (fe.layout.height / 3) - marge, logo_console.width, fe.layout.height / 3)
list.align = Align.Left
list.set_rgb(0,0,0)
list.set_sel_rgb(255,255,255)
list.set_selbg_rgb(0,0,0)

//Logo du jeu entre le logo de la console et la liste, même largeur
local i_logo_jeu = fe.add_artwork("wheel")
i_logo_jeu.width = logo_console.width
i_logo_jeu.preserve_aspect_ratio = true
i_logo_jeu.x = marge
i_logo_jeu.y = 200

// Jaquette du jeu, 1/3 de la largeur de l'écran, centrée
local i_jaquette_jeu = fe.add_artwork("flyer", 440, 213, 400, 293)

//Affichage de 2 captures d'écran au ratio 8:7 en colonne dans le tiers droit
local largeur_aff = fe.layout.width / 3 - (marge*2)
local hauteur_aff = fe.layout.height - ill_console.width - (marge*3)
local hauteur_cap = hauteur_aff / 2
local largeur_cap = hauteur_cap / 7 * 8
local x_cap = fe.layout.width - marge - (largeur_cap / 2) - (largeur_aff / 2)
local y_cap2 = fe.layout.height - marge - ill_console.width - hauteur_cap

local i_capture1 = fe.add_artwork("snap", x_cap, marge, largeur_cap, hauteur_cap)
local i_capture2 = fe.add_artwork("snap", x_cap, y_cap2, largeur_cap, hauteur_cap)

Et qui – pour la petite histoire – pourrait être ramené à 34 lignes :

fe.layout.width = 1280
fe.layout.height = 720
fe.layout.font = "Nintender-Mm1B"
local marge = fe.layout.width / 100
local bg_image = fe.add_image("BG.jpg", 0, 0, fe.layout.width, fe.layout.height)
local logo_console = fe.add_image("logo_console.png")
logo_console.width = fe.layout.width / 4
logo_console.preserve_aspect_ratio = true
logo_console.x = marge
logo_console.y = marge
local ill_console = fe.add_image("Snin_PA.png")
ill_console.width = fe.layout.width / 4
ill_console.preserve_aspect_ratio = true
ill_console.x = fe.layout.width - marge - ill_console.width
ill_console.y = fe.layout.height - marge - ill_console.width
local list = fe.add_listbox (marge, fe.layout.height - (fe.layout.height / 3) - marge, logo_console.width, fe.layout.height / 3)
list.align = Align.Left
list.set_rgb(0,0,0)
list.set_sel_rgb(255,255,255)
list.set_selbg_rgb(0,0,0)
local i_logo_jeu = fe.add_artwork("wheel")
i_logo_jeu.width = logo_console.width
i_logo_jeu.preserve_aspect_ratio = true
i_logo_jeu.x = marge
i_logo_jeu.y = 200
local i_jaquette_jeu = fe.add_artwork("flyer", 440, 213, 400, 293)
local largeur_aff = fe.layout.width / 3 - (marge*2)
local hauteur_aff = fe.layout.height - ill_console.width - (marge*3)
local hauteur_cap = hauteur_aff / 2
local largeur_cap = hauteur_cap / 7 * 8
local x_cap = fe.layout.width - marge - (largeur_cap / 2) - (largeur_aff / 2)
local y_cap2 = fe.layout.height - marge - ill_console.width - hauteur_cap
local i_capture1 = fe.add_artwork("snap", x_cap, marge, largeur_cap, hauteur_cap)
local i_capture2 = fe.add_artwork("snap", x_cap, y_cap2, largeur_cap, hauteur_cap)

Mais je vous déconseille très fortement de travailler de la sorte car les commentaires sont indispensables à la maintenance et au développement de tout code !
Le layout est fonctionnel mais très simple; il a surtout vocation a vous présenter les notions de base du développement de layouts pour Attract-Mode. Voyons pour finir quelques pistes d’amélioration.

AMÉLIORATIONS ET OPTIMISATIONS

Améliorer le layout ne fait pas nécessairement appel à des concepts plus compliqués. Vous avez besoin de deux choses : des visuels un peu plus travaillés pour l’environnement visuel de base et une lecture attentive de la documentation qui présente toutes les fonctions Squirrel spécifiques à Attract-Mode. Ce sera suffisant et c’est tant mieux, car la documentation officielle de Squirrel n’apporte pas grand-chose au développement Attract-Mode et il y a peu d’aide sur les forums de discussion sur le sujet, même dans la langue de John Holmes.
Quelques fonctions permettent notamment des animations, et il est possible d’en développer soi-même en Squirrel. Le Squirrel accepte également la programmation modulaire (cad plusieurs scripts qui fonctionnent ensemble, utile pour découper le code et séparer des fonctions indépendantes). Le mieux reste encore de lire et d’essayer de comprendre les scripts présents dans les layouts plus avancés. Commencez par modifier un layout existant et vous serez assez vite en mesure de développer le layout de vos rêves.

Une autre piste pour l’amélioration du layout est l’utilisation d’une plus grande variété de médias, et un meilleur contrôle de ces derniers. En effet, l’aspirateur par défaut d’Attract-Mode est assez limité : seulement cinq types de médias, impossibilité d’affiner l’aspiration par région, etc. Il est assez facile d’améliorer le rendu, comme dans cet exemple :

Le rendu est plus sympa à droite, non ? Techniquement, si vous avez réussi a développer le layout du tutoriel, vous pourrez développer celui montré à droite du tableau.
Pour avoir une plus grande variété et un plus grand contrôle des médias, il faut utiliser un aspirateur plus performant que celui proposé par défaut dans Attract-Mode. C’est l’objet de la partie 3 de ce tutoriel. Pour vous faire saliver d’avance, une petite vidéo du résultat recherché !

Suite en partie 3.

Bob Dupneu

Leave a Reply

Votre adresse de messagerie ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.