Le modèle : projet, bundle, groupe, bibliothèque, release¶
Note
Technical reference — French only for now. / Référence technique, en français pour l'instant.
Ce document répond à la question qu'on se repose toutes les semaines : qu'est-ce qu'un bundle, où vit quoi, et qu'est-ce qui part sur la carte ? Tranché avec Olivier le 2026-09-01. Si le code et ce document divergent, l'un des deux est faux — le dire.
Le vocabulaire, en une table¶
| Mot | Ce que c'est | Où il vit |
|---|---|---|
| Projet | le jeu en cours d'écriture : ses .lua (main.lua, toujours) et ses .mask |
table project, project_file |
| Bibliothèque RAW | les images chargées telles quelles, en vraies couleurs (RGB/RGBA) : tilesets, sprites, sprites animés | asset, asset_frame |
| Bibliothèque PROCESSED | à venir — les assets que l'éditeur graphique a découpés, alignés, nommés, retouchés. Toujours du RGBA : l'éditeur fabrique des assets, pas des bundles | — |
| Bundle | une série d'assets avec sa palette calculée sur l'ensemble, et ses variantes compatibles (mêmes index, autres couleurs : jour/nuit). C'est la seule forme indexée de l'art. Un jeu peut en avoir plusieurs | bundle, bundle_asset (la recette), bundle_image (le résultat) |
| Attache | « ce projet utilise le bundle B, vu par le jeu sous le nom G » — une référence, pas une copie | project_bundle |
| Groupe (côté console) | ce que la carte voit d'un bundle : G.pal, G.atlas, ses PNG indexés, ses variantes G.<v>.pal, ses fonds G-<f>.map + G-<f>-tiles.png |
le dossier du jeu sur la carte |
| Release | l'instantané publié : un dossier complet — les fichiers du projet plus les pièces de ses bundles attachés — figé, identifié par son empreinte de manifeste | release, release_file |
Donc : un bundle de l'App = un groupe sur la console, et le « nom vu par le
jeu » de l'attache est le nom du groupe — gfx.palette("G"), gfx.id("G/perso").
Ce nom suit la règle des groupes : minuscules, chiffres, souligné, pas de tiret
(le tiret sépare un groupe de son fond).
Le firmware dit « le dossier du jeu » pour l'ensemble que gfx.bundle() compile
d'un coup — tous les groupes. Il a dit « un bundle = un jeu » jusqu'au 2026-09-01 ;
c'est le même objet que la release ici, et pas un bundle de l'App.
Ce qui va où, et pourquoi¶
.luaet.maskdans le projet. Le.maskest la géométrie de collision du niveau, dessinée à la main en ASCII : c'est du code, pas de l'art..mapdans le bundle. La carte de tuiles d'un fond ne veut rien dire hors de son tileset ; les deux sont une pièce du groupe (G-<f>.map+G-<f>-tiles.png). Les fonds dans les bundles restent à faire ; en attendant, un.mapimporté avec un exemple est toléré comme fichier de projet.- Une seule palette par bundle, calculée sur tous ses assets. Un pixel indexé n'a de sens que dans la palette qui l'a produit : c'est pour cela qu'on ne quantifie jamais un asset seul, et que la console n'affiche à la fois que des sprites d'un même groupe (une seule palette active).
- Publier matérialise. Une release doit se poser telle quelle sur une carte
micro-SD ; elle embarque donc les pièces des bundles attachés, nommées comme
le groupe. L'export
.zipdu brouillon fait pareil. Le projet, lui, ne garde que la référence : retoucher un bundle change la prochaine publication, pas la précédente. - Un asset employé par un bundle ne se supprime pas, un bundle attaché non plus — la base le garantit (clés différées) : sinon un jeu cesserait de se charger loin de la cause.
Ce que chaque onglet fait¶
Projets — la liste de mes projets ; pour le projet choisi : ses fichiers
(créer, envoyer, supprimer — jamais main.lua), ses bundles attachés (voir,
éditer → onglet assets, délier, lier), publier, télécharger, supprimer. Un
troisième panneau montre le contenu du bundle choisi, en lecture seule.
Assets graphiques — la bibliothèque RAW (charger, voir, supprimer) et les bundles : en créer un, y ajouter et retirer des assets avec leurs réglages (alpha en additif / translucide / tramé / seuil, « derrière »), Calculer la palette → palette, atlas, pages indexées ; en éditer un (rouvrir la recette, recalculer) ; en supprimer un s'il n'est attaché nulle part. Puis les variantes et les fonds.
Cartes (Map editor) — un mini Tiled : on y dessine des tuiles de 8×8 (RGBA,
la tuile 0 est le trou) et on les peint sur un monde aux bornes de la console
(de 80×60 à 256×128 tuiles, 704 tuiles au plus). La carte est la source,
éditable à volonté (table carte, octets en base) ; « Envoyer comme fond »
l'aplatit en image et la dépose dans la bibliothèque comme asset fond du même
nom (le dépôt remplace) — la chaîne des bundles est inchangée et retrouve
d'elle-même les tuiles dessinées, puisqu'elles se répètent. Supprimer la carte
ne supprime pas l'asset envoyé.
Au-dessus du 8×8, les tampons (16, 24, 32) : des grilles d'ids de tuiles de
base — le modèle des métatuiles. Le monde reste une grille d'ids 8×8, toute la
chaîne aval l'ignore. Un tampon se compose dans l'atelier en peignant sa grille
avec un pinceau plus petit, posé au pas de 8 px (et rogné aux bords) ; le
même moteur de peinture sert le monde, où le pinceau est une tuile ou un tampon
de n'importe quel côté. La peinture au pixel est réservée au 8×8, la source de
vérité — retoucher une tuile de base se répercute dans tous les tampons et
toute la carte. « Importer une planche » (_T.png, T ∈ {8, 16, 24, 32}) la
décompose en tuiles de base dédupliquées — importer du 24 remplit aussi la
barre 8×8 — et en tampons ; un import qui déborderait les 704 est refusé avec
le compte, avant d'écrire. Depuis le 2026-09-03, l'onglet cartes est le seul
producteur de fonds et de tilesets : les boutons « Charger Fond » et
« Charger Tileset » ont quitté les assets graphiques (la bibliothèque ne reçoit
que des sprites, un animé = une ligne d'animation par chargement) ; le flux
« image continue → regroupement » passe désormais par le calcul du bundle
uniquement.
Chaque tuile porte un tag solide ; « Écrire <carte>.mask » génère le
masque de collision dans un projet du compte, à côté du code qui fera
phy.map("<carte>"). Une case du masque = une tuile (8×8, depuis le
2026-09-02 — la case 8×16 couvrait deux tuiles empilées et débordait) : la
géométrie du masque est exactement celle du dessin. Une seule géométrie,
dessinée une fois : le décor et le masque ne peuvent plus se contredire.
Éditeur graphique — à venir, un onglet de plus : lit RAW, écrit PROCESSED.
La recette d'un bundle¶
Un bundle se rouvre : GET /gfx/bundles/{nom} remet dans l'éditeur ce que
bundle_asset a gardé — les assets dans l'ordre, le nom vu par le jeu, le
mode d'alpha (additif / translucide / tramé / seuil), le contour (aucun /
fondu / sombre), « derrière le fond », et l'animation en trois états :
auto (la mesure du wasm tranche, c'est le défaut), oui (une animation, une
ligne), non (des sprites distincts, une ligne chacun). Le plan des rangées
est sur bundle. « Calculer la palette » rejoue tout dans le navigateur
(/sim/pdgfx.mjs, le core/ du simulateur) ; « Enregistrer » poste les
pièces et la recette, et remplace le bundle sous le même nom — un bundle
ne se renomme pas, un autre nom serait un autre bundle. GET /gfx/bundles
ouvre un bundle vide.
Un bundle attaché à des projets le dit (et les nomme) : le recalculer change ce
qu'ils publieront la prochaine fois, et il ne se supprime pas tant qu'il est
attaché (ErrBundleAttache). Un asset employé par un bundle ne se supprime
pas non plus (ErrAssetEmploye).
Les fonds¶
Un fond est un asset de genre fond : le monde entier en une image RGBA,
dimensions multiples de 8, 2048×1024 au plus (la carte de RP3 fait 256×128
tuiles) ; les pixels transparents sont le trou. Dans la recette d'un bundle il
n'a pas de réglage : il est quantifié avec les sprites du groupe (une seule
palette, c'est toute la raison de sa présence) puis tuilé par core/tiles.c
(la référence ; tools/png2map.py en est la version ligne de commande, restée à
la coupe simple) — tuiles 8×8 distinctes par fréquence décroissante, la tuile 0 =
le trou, 704 au plus ; au-delà, regroupement par similarité (k-moyennes en
couleur, moyenne de groupe reprojetée sur les index employés) : une image
continue devient une mosaïque douce au lieu d'un remplacement brutal, et la
perte est comptée. Hors atlas, hors pool.
Il produit deux pièces, rangées dans bundle_fond sous son nom court
(grotte) : la carte texte (papydeck map 1, relue par pd_bg_parse_map) et
la planche des tuiles (PNG indexé, 32 tuiles par rangée). À la sortie elles
prennent le préfixe du groupe : niveau1-grotte.map et
niveau1-grotte-tiles.png dans une release (le nom de l'attache), caverne-…
dans le zip du bundle — parce que gfx.bundle() rattache un fond à son groupe
par ce préfixe. Le jeu écrit gfx.background("niveau1-grotte").
Les variantes¶
Une variante — <groupe>.<variante>.pal — c'est les mêmes index, d'autres
couleurs : jour/nuit, flash ; aucune image, et gfx.palette("<groupe>.<nom>")
bascule en temps réel. Dans l'App, une variante est définie par ses
réglages (teinte, saturation, luminosité appliqués à la palette du bundle)
et le .pal dérivé (table bundle_variante) ; la page redérive les couleurs
à chaque « Calculer la palette », donc un recalcul du bundle ne désynchronise
jamais ses variantes. Les modes (octets 512–767) restent ceux du bundle —
une variante ne change jamais les rangées, c'est sa définition, et le serveur
le refuse. L'aperçu « sur la console » peut s'afficher dans une variante
(pd_gfx_apercu_variante). À la sortie, le préfixe est celui du groupe :
niveau1.nuit.pal dans la release, caverne.nuit.pal dans le zip.
Les noms, et leur borne¶
Un nom complet — <groupe>/<région> pour gfx.id(), <groupe>-<fond> pour
gfx.background() — tient dans les tampons de 48 octets de la console et du
simulateur : 47 caractères au plus. Ce nom n'existe pas tant que le groupe
(le nom vu de l'attache) n'est pas choisi ; la somme se vérifie donc aux deux
moments où elle peut naître — à l'attache, et au recalcul d'un bundle
déjà attaché — avec un refus qui nomme le fautif. Chaque moitié garde sa
règle propre (60 max), aucune paire qui tient n'est interdite.
État (2026-09-01)¶
Fait : projet restreint au code, main.lua indestructible, attaches avec
délier / lier / voir / éditer, publication et export qui embarquent les pièces,
la composition des bundles dans l'onglet assets (liste, nouveau, recette qui se
rouvre, plan, calcul de la palette en wasm, enregistrer, supprimer si non
attaché), l'aperçu « sur la console » par la table de mélange, les fonds
(genre fond, tuilage dans le navigateur avec regroupement au-delà de 704,
pièces préfixées à la sortie, images paginées ≤ 65 536 px), l'onglet Cartes
(Map editor, 2026-09-02). Les variantes sont faites (2026-09-03), et l'import de planche aussi (grille N×M →
bande _N, détection par autocorrélation — sheet_to_strip.py intégré à la page des
assets). PROCESSED est abandonné : l'édition redéposera l'asset en place, la bannière
« à recalculer » prévient l'aval — deux étagères RAW/PROCESSED étaient de la comptabilité
sans lecteur, et l'appli graphique autonome avec. Restent, si le besoin naît : un éditeur
de frames (pelure d'oignon) dans la page d'un asset.