← Tous les articles

Interfaces en pixel art : boîtes de texte, polices et japonais

La boîte de dialogue de Pokémon Version Émeraude est un cadre de 30 tuiles sur 6, sur toute la largeur de l’écran (240 pixels) et 30 % de sa hauteur, autour d’une fenêtre de texte de 27 tuiles sur 4 qui contient deux lignes d’une police de 16 pixels ; celle de Cristal fait 20 tuiles sur 6, deux lignes de 18 caractères.123 Émeraude affiche un caractère toutes les 8, 4 ou 1 images selon son réglage de vitesse, soit 7,5, 15 ou 60 par seconde à 60 hertz ; le réglage rapide de Cristal et Final Fantasy VI affichent eux aussi un caractère par image.14 Les scénaristes d’Émeraude ont coupé chaque ligne à la main : sur 24 100 lignes de texte de script, la plus large mesure 208 pixels pour une fenêtre de 216, et aucune ne déborde.1 Une police pixel japonaise qui couvre le jeu standard dessine environ sept mille caractères : JIS X 0208 en compte 6 879, Misaki les dessine tous dans une cellule de 8 sur 8, PixelMplus10 tous sauf trois points de code sur une grille de 10 pixels, et le producteur de Final Fantasy Pixel Remaster a chiffré la nouvelle police japonaise de son équipe à « around 7,500 characters » (environ 7 500 caractères).5678 Sur mon Mac, lissage des polices désactivé, Core Text a dessiné les polices pixel TrueType dont la grille repose sur la ligne de base avec des avances en pixels entiers (Misaki, PixelMplus10 et Press Start 2P) sans aucun pixel partiel aux multiples entiers de leur grille, et avec 19,4 % à 82,2 % des pixels encrés brouillés entre ces multiples ; PixelMplus12, dont la grille est décalée par rapport à la ligne de base, Silkscreen, avec des points de contour égarés, et DotGothic16, sans grille, sont sortis avec des pixels partiels à chaque taille mesurée, et k8x12 à chaque taille dès que ses glyphes étaient placés exactement ; avec le lissage par défaut du contexte de dessin, même PixelMplus10 à ses propres 10 pixels est sorti avec 178 pixels encrés partiels sur 272.79 En 3×, un em de 10 pixels ne tombe sur des pixels entiers de l’appareil qu’à une seule des douze tailles Body d’iOS, 40 points.710 Kiradex World compose aujourd’hui chaque mot dans la police à chasse fixe du système, ne parle qu’anglais et donne à VoiceOver les coordonnées des tests de marche.11 J’ai donc mesuré les boîtes, les polices et les vitesses du canon à partir des décompilations, mesuré cinq polices pixel japonaises et deux latines sous Core Text, et rédigé un cahier des charges : une police sous licence pour les deux langues, Dynamic Type arrondi à la grille de la police, un cadre dessiné par la forge, deux lignes et des pages, et une scène que VoiceOver sait décrire.

En bref

  • La boîte occupe le tiers inférieur et deux lignes. Le cadre de dialogue d’Émeraude fait 30 tuiles sur 6 (240 pixels sur 48, 30 % de la hauteur de l’écran) autour d’une fenêtre de 27 tuiles sur 4 qui contient deux lignes de 16 pixels ; la boîte de Cristal fait 20 tuiles sur 6, 33 % de la hauteur, deux lignes de 18 caractères, et celle de Rouge les mêmes 20 sur 6. La boîte d’Émeraude n’a pas de champ de nom, et 934 de ses 14 412 paragraphes de script (6,5 %) nomment l’interlocuteur dans le texte.12312
  • La vitesse est un nombre d’images. Émeraude et Rouge Feu prennent 8, 4 ou 1 images par caractère (7,5, 15 ou 60 par seconde à 60 hertz), et une image tant que A ou B est maintenu après une pression pendant l’affichage ; Cristal et Rouge attendent 5, 3 ou 1 image (12, 20 ou 60) ; Final Fantasy VI dessine les dialogues de terrain à une lettre par image, sans réglage pour cela (sa vitesse des messages à six crans règle le temps d’attente d’un message de combat). La flèche de suite d’Émeraude se décale de 0, 1, 2, 1 pixels depuis le repos, un pas toutes les 9 images, un cycle de 0,6 seconde ; le ▼ de Cristal clignote en alternance avec la tuile de bordure.1131415164
  • Cinq polices libres dessinent déjà le japonais dont un jeu a besoin. Misaki dessine les 6 879 caractères de JIS X 0208 en 8 sur 8, chaque kanji tenant dans 7 sur 7 ; k8x12 en 8 sur 12 ; PixelMplus12 et PixelMplus10 les dessinent tous sauf trois points de code à 12 et 10 pixels ; DotGothic16 les dessine tous, mais sur aucune grille de pixels. La licence en trois phrases de Misaki, k8x12 et PixelMplus autorise l’usage, la copie et la distribution « either commercially or noncommercially » (à des fins commerciales ou non).5671718
  • Une police pixel n’est nette qu’avec un nombre entier de pixels de l’appareil par pixel de police, et seulement si le fichier est propre. Mesuré avec Core Text sous macOS 27.0, pas sur un iPhone, lissage des polices désactivé : 0,0 % de pixels partiels aux multiples entiers d’une grille posée sur la ligne de base avec des avances en pixels entiers (Misaki, PixelMplus10, Press Start 2P), 19,4 % à 82,2 % entre ces multiples.7 Avec le lissage par défaut du contexte de dessin, PixelMplus10 à ses propres 10 pixels avait 178 pixels encrés partiels sur 272 ; le résultat propre aux multiples entiers ne tient donc que lissage désactivé.9 La grille de PixelMplus12 se trouve 0,39 pixel sous la ligne de base, et lissage désactivé elle bave à chaque taille ; l’avance des kana de k8x12, 8,01 pixels, bave dès que les glyphes sont placés exactement.719
  • Dynamic Type doit être arrondi, pas suivi. Sur les douze tailles Body d’iOS, de 14 à 53 points, un em de 10 pixels n’est entier en 3× qu’à 40 points, un em de 8 pixels à 16 et 40, un em de 12 pixels à 16, 28 et 40. Apple demande qu’une police personnalisée reproduise les comportements des polices système, Dynamic Type compris, et que le texte d’une app puisse idéalement être agrandi d’au moins 200 %.71020
  • La coupure des lignes japonaises est stricte sans balise de langue et souple avec. Dans Core Text sous macOS, un paragraphe de 131 caractères non balisé s’est coupé sans qu’aucune ligne ne commence par un caractère de la liste de début de ligne de la sonde, une liste partielle, surtout des caractères de JLREQ dont sept sous leur forme pleine chasse, à aucune largeur mesurée ; balisé ja, un petit kana a commencé une ligne à 6 largeurs sur 7 dans Hiragino Sans, Misaki, PixelMplus et DotGothic16, la règle moins stricte que décrivent les exigences du W3C pour la mise en page du japonais.721
  • Ce que cela apporte à Kiradex. Un cahier des charges, pas un build : PixelMplus10 pour chaque mot dessiné au-dessus du monde (des deux polices mesurées qui ont le japonais, une grille propre et des avances en pixels entiers, Misaki et PixelMplus10, la plus grande), des tailles arrondies à un nombre entier de pixels de l’appareil par pixel de police (16,67 points à la taille par défaut sur les iPhone 3×, 15 sur les 2×), un cadre propre dessiné par la forge, deux lignes et des pages, un affichage progressif à 30 caractères par seconde avec une marque qui avance par pas, des bulles d’une ligne dimensionnées pour l’iPhone le plus étroit que liste le simulateur d’iOS 27.0 quand ses côtés n’ont pas de marges intérieures, une réplique qui ne tient pas envoyée dans la boîte, des lignes japonaises relues par une personne qui parle japonais, et une scène que VoiceOver décrit avec des mots plutôt qu’avec des coordonnées, chaque point assorti d’une vérification qu’un script, une ligne de journal ou une capture peut valider ou invalider, à l’exception d’une vérification VoiceOver manuelle.2211

1. Émeraude et Rouge Feu : un cadre sur toute la largeur, deux lignes, coupées à la main

Cette série a commencé par le monde et a progressé vers l’intérieur. Des mondes en pixel art sur iPhone a fixé le système de tuiles, la recette RealityKit et une direction pour le texte en une règle, « Text is art » (le texte est un élément graphique), et une phrase de plan : « m5x7 at integer multiples in a 24-by-24 nine-slice box, two lines, typewriter reveal, a two-frame advance arrow. » (m5x7 en multiples entiers dans une boîte nine-slice de 24 sur 24, deux lignes, affichage façon machine à écrire, une flèche de suite à deux images.)23 Les articles suivants ont construit le collectionneur, les bâtiments, le mouvement, la vie propre de la ville, les pièces et les routes. Le texte en est resté à cette phrase. Cet article mesure d’où viennent ses chiffres, relève quatre endroits où l’article sur le monde est faux ou imprécis, et ajoute ce qui lui manquait : le japonais en 8, 10 et 12 points, ce que Core Text fait réellement d’une police pixel TrueType, comment Dynamic Type et une grille de pixels peuvent s’accorder, la coupure des lignes japonaises, et VoiceOver au-dessus d’une scène RealityKit.

Le texte de Kiradex World est aujourd’hui sobre. Chaque mot y est du SwiftUI dans la police à chasse fixe du système du thème. Les bulles au-dessus de la tête des collectionneurs sont une ligne du rôle caption du kit, en gras, sur papier crème avec une bordure d’encre de 2 points, affichée tant que la réplique a moins de 4 secondes. La ligne du lieu se trouve dans une boîte de texte crème en bas de l’écran. L’app est en anglais uniquement, les répliques prédéfinies vivent sur le serveur en anglais, le panneau de la forge est une planche de 12 sur 11 couverte de gribouillis d’encre sans lettres, et VoiceOver lit la scène comme un seul élément dont la valeur est la trace de pas des tests de marche.11242526272829 La section 10 donne le détail ; le cahier des charges de la section 12 est rédigé comme un différentiel par rapport à cet état.

Chaque chiffre Pokémon ci-dessous a été mesuré sur des clones superficiels des décompilations de pret, pokeemerald 731ad5b (1er octobre 2026), pokefirered 037335f, pokecrystal 5beda23 et pokered d2704a6, par un seul script Python, measure_gen_text.py, dont la sortie est enregistrée à côté ; les chiffres de Final Fantasy VI viennent d’un second script appliqué au désassemblage everything8215/ff6. Les deux se relancent en quelques secondes, et les notes nomment le fichier source derrière chaque chiffre.3014

Les polices

Émeraude déclare ses polices dans une seule table, sFontInfos dans src/text.c, chacune avec une largeur et une hauteur maximales de lettre en pixels. La petite police fait 5 sur 12 ; la police normale, celle de la boîte de dialogue, 6 sur 16 ; la police courte 6 sur 14, déclarée quatre fois (elle-même et trois copies) ; la police étroite 5 sur 16 ; la petite police étroite 5 sur 8 ; la police grasse 8 sur 8 ; et la police braille 8 sur 16 avec 8 pixels d’interligne. Toutes les autres polices ont un interligne de 0 : la cellule de 16 pixels est la ligne.113

La police latine normale est proportionnelle. Indexées via le charmap.txt du jeu dans gFontNormalLatinGlyphWidths de src/fonts.c, les minuscules de a à z mesurent en moyenne 5,77 pixels de large, de 4 à 6 ; les capitales de A à Z et les chiffres de 0 à 9 font tous 6 ; l’espace et le point 3, et le point d’exclamation 4.131 En texte courant, cela donne 5,25 pixels par caractère en moyenne, mesuré sur chaque ligne du script du jeu ci-dessous.1

La vitesse est un nombre d’images

Dans Émeraude, la vitesse du texte est un délai en images. Les trois vitesses de l’écran Options passent par sTextSpeedFrameDelays dans src/menu.c pour donner des délais de 8 (lent), 4 (moyen) et 1 (rapide). AddTextPrinter dans src/text.c stocke le délai moins un comme vitesse de l’imprimeur ; l’imprimeur règle son compteur sur cette vitesse stockée quand un caractère s’affiche et le décrémente d’une unité par image, et le caractère suivant s’affiche à l’image qui suit le passage du compteur à 0. Un caractère prend donc le délai lui-même : 8, 4 et 1 images. À 60 hertz, cela fait 7,5, 15 et 60 caractères par seconde.1213 Ce décompte est ma lecture de la machine à états de l’imprimeur (AddTextPrinter et le cas RENDER_STATE_HANDLE_CHAR dans src/text.c), que le script vérifie par assertion plutôt qu’il ne l’exécute.1 La Game Boy Advance ne tourne pas exactement à 60 hertz : GBATEK donne son image comme « Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz » (228 lignes au total, 16,743 ms, 280 896 cycles, environ 59,737 Hz), dont 160 lignes visibles, chacune large de 240 points ; le réglage rapide donne donc environ 59,7 caractères par seconde sur le matériel.3233

Maintenir un bouton accélère les réglages plus lents. Une nouvelle pression sur A ou B pendant le décompte du délai d’un caractère, dans un message qui le permet (gTextFlags.canABSpeedUpPrint), active hasPrintBeenSpedUp. Dès lors, l’imprimeur teste JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp et, si la condition est vraie, ramène le compteur à 0 : un caractère par image, 60 par seconde, le rythme même du réglage rapide.131

Le texte à défilement automatique a son propre délai. Quand gTextFlags.autoScroll est actif, l’imprimeur règle le compteur sur 3 plutôt que sur la vitesse stockée, soit 4 images par caractère, sauf dans les combats enregistrés : le test est if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll).131 Les messages de combat activent le défilement automatique dans les combats en link et les combats enregistrés, et la commande de script messageautoscroll l’active et impose la vitesse moyenne.34 Le délai de 3 peut donc s’appliquer à d’autres fenêtres de combat dans les combats en link et aux messages scriptés à défilement automatique, et jamais dans les combats enregistrés, dont les messages s’affichent à la vitesse donnée à l’imprimeur : pour les messages de combat, la vitesse du texte enregistrée avec la partie, sauf dans un combat en link enregistré, dont la fenêtre de message reçoit 1, comme celle d’un combat en link.341 Selon ma lecture, le compteur ne tourne que tant que la vitesse stockée de l’imprimeur est supérieure à 0, et la fenêtre de message d’un combat en link reçoit le délai de 1, ce qui laisse son texte à un caractère par image avec le défilement automatique actif.1334

Les trois codes de coupure font trois choses différentes. \n fait descendre l’imprimeur à la deuxième ligne ; \l attend une pression sous la flèche de suite, puis fait remonter la boîte d’une ligne ; \p attend de la même façon, puis vide la boîte et repart du haut.1335 Lors d’un défilement, la ligne de 16 pixels remonte de 1, 2 ou 4 pixels par image selon le réglage de vitesse (sWindowVerticalScrollSpeeds), si bien qu’un défilement prend 16, 8 ou 4 images.113

Barres horizontales des caractères par seconde à 60 hertz : Émeraude lent 7,5, moyen 15, rapide 60 et 60 avec A ou B maintenu ; Cristal et Rouge lent 12, moyen 20 et rapide 60 ; Final Fantasy VI 60 sur le terrain ; et les réglages Slow 12 et Normal 30 proposés par le cahier des charges de Kiradex.

La vitesse du texte dans chaque jeu mesuré, et les deux réglages proposés par le cahier des charges ; dessiné par figures/make_figures.py à partir des sorties de mesure.

La flèche

Quand une page est pleine, la boîte attend sous une petite flèche. Émeraude copie une cellule de flèche de 8 sur 16 à des décalages verticaux de { 0, 1, 2, 1 } pixels (sDownArrowYCoords) et la redessine quand son délai, remis à 8, expire. Cela fait un pas toutes les 9 images et un cycle de 36 images, 0,60 seconde à 60 hertz.113 La flèche ne clignote pas ; elle oscille, par trois positions et retour.

La boîte et son cadre

La boîte de texte de terrain d’Émeraude est définie par sStandardTextBox_WindowTemplates dans src/menu.c. La fenêtre de texte se trouve en colonne de tuile 2, rangée 15, et mesure 27 tuiles sur 4 : 216 pixels sur 32, deux lignes de la police normale de 16 pixels. Son cadre, dessiné par WindowFunc_DrawDialogueFrame et évalué par le script pour cette fenêtre, va de la colonne 0 à 29 et de la rangée 14 à 19 : 30 tuiles sur 6, 240 pixels sur 48, toute la largeur de l’écran et 30 % de ses 160 pixels de hauteur.12

Ce cadre n’est pas un nine-slice. LoadMessageBoxGfx le charge depuis message_box.png, une bande de 56 pixels sur 16, 14 tuiles, et le cadre est dessiné à partir de huit positions de tuiles, sa rangée inférieure étant la rangée supérieure retournée verticalement avec BG_TILE_V_FLIP.1362 Le script mesure l’étendue du cadre et la taille du PNG ; le retournement est lu dans les appels de remplissage.1

Les cadres au choix du joueur servent aux autres fenêtres : les menus et les boîtes oui ou non. WINDOW_FRAMES_COUNT vaut 20, et les vingt cadres sont des PNG de 24 pixels sur 24, trois tuiles de 8 sur trois, chargés par LoadUserWindowBorderGfx d’après le type de cadre que l’option FRAME de l’écran Options enregistre.136

Cela corrige l’article sur le monde. Sa règle 10 dit « Pokémon Emerald’s text-window frame is a 24-by-24 image, three by three hardware tiles, which is a nine-slice » (le cadre de la fenêtre de texte de Pokémon Émeraude est une image de 24 sur 24, trois tuiles matérielles sur trois, soit un nine-slice).23 Les vingt images de 24 sur 24 de graphics/text_window/ sont les cadres sélectionnables des fenêtres de menu. La boîte de dialogue, la boîte de texte dont se souviennent les joueurs, est dessinée à partir de message_box.png, de 56 pixels sur 16, comme un cadre de 30 tuiles sur 6 au bord inférieur en miroir, pas comme un nine-slice d’une image de 24 sur 24.136 L’idée du nine-slice tient, et les menus l’utilisent ; l’attribution à la boîte de dialogue, non.

Deux écrans dessinés comme des grilles de tuiles de 8 pixels sur 8 à la même échelle : Émeraude en 30 tuiles sur 20, avec le cadre de dialogue sur les 6 rangées du bas et la fenêtre de texte de 27 sur 4 à l'intérieur à partir de la colonne 2, rangée 15 ; et Cristal en 20 tuiles sur 18, avec la boîte de 20 sur 6 en bas et son intérieur de 18 sur 4.

Les deux boîtes de dialogue sous forme de grilles de tuiles, sans aucun graphisme de jeu ; dessiné par figures/make_figures.py, qui vérifie chaque étendue par rapport à measure_gen_text.out.

Le texte a été écrit pour la boîte

Pour voir à quel point la boîte se remplit, le script a lu chaque ligne du texte de script d’Émeraude : 504 fichiers, les 36 de data/text/*.inc et les scripts.inc des 468 cartes, 24 100 lignes sans emplacement réservé et 3 000 avec, découpées aux trois codes de coupure du jeu, \n, \l et \p. La ligne médiane fait 29 caractères et 150 pixels ; le 95e centile 38 caractères et 196 pixels ; la plus longue 53 caractères, la plus large 208 pixels. Aucune n’est plus large que la fenêtre de 216 pixels, et le caractère moyen mesure 5,25 pixels de large.137

À 5,25 pixels par caractère, la fenêtre en contient 41 ; les scénaristes se sont arrêtés à 38 pour 95 % des lignes.331 Chaque coupure est un code placé à la main. Le jeu ne coupe jamais une ligne de lui-même ; il fait confiance au scénariste.

La boîte a une seule fenêtre et pas de champ de nom. Quand les scénaristes voulaient un interlocuteur, ils tapaient le nom et deux-points dans la ligne, comme dans Bourg-en-Vol (Littleroot Town) avec « MOM: {PLAYER}, we’re here, honey! » (Maman : {PLAYER}, on est arrivés, mon chéri !). Sur 14 412 paragraphes, 934, soit 6,5 %, commencent par un nom en capitales suivi de deux-points.137 L’absence de champ de nom séparé est ma lecture du modèle de fenêtre, qui a de la place pour les deux lignes de texte et rien d’autre.2

Du japonais dans le jeu anglais

L’Émeraude anglais porte encore des glyphes japonais, pour les noms des créatures et des dresseurs venus de cartouches japonaises. L’imprimeur change de jeu de glyphes sur le code de contrôle EXT_CTRL_CODE_JPN et revient sur EXT_CTRL_CODE_ENG, que portent ces noms (international_string_util.c, contest.c et battle_tower.c les insèrent). Dans ce mode, la police normale fait 8 pixels de large pour chaque caractère et 15 de haut (DecompressGlyph_Normal, et GetGlyphWidth_Normal renvoie 8).11338

Les planches à partir desquelles la ROM anglaise est construite : japanese_normal.png mesure 128 pixels sur 512 en cellules de 8 sur 16, 302 de ses 512 cellules dessinées ; japanese_small.png la même taille, 307 dessinées ; japanese_bold.png 128 sur 256, 246 sur 256 dessinées ; et japanese_frlg_male.png de Rouge Feu, présent lui aussi dans l’arborescence d’Émeraude, 256 pixels sur 512 en cellules de 16 sur 16, 269 sur 512 dessinées, avec une table de largeurs de 512 entrées de 0 à 10 pixels, 10 pour 408 d’entre elles.138 pret construit les jeux anglais ; ce sont donc les planches des noms dans une ROM anglaise ; la boîte, les vitesses et la police d’aucune ROM japonaise n’ont été lues (section 11).30

Dans les termes mêmes du canon, un caractère japonais dans une ligne de 16 pixels fait donc 8 pixels de large dans Émeraude et le plus souvent 10 dans la planche de Rouge Feu, contre 4 à 6 pour une lettre latine.1

Rouge Feu

Rouge Feu conserve les délais d’Émeraude, 8, 4 et 1, stockés moins un par son propre AddTextPrinter dans src/text_printer.c comme ceux d’Émeraude, et une fenêtre de texte standard de 26 tuiles sur 4 en colonne 2 (src/new_menu_helpers.c), une tuile plus étroite que celle d’Émeraude.139 Son imprimeur a aussi le maintien d’Émeraude : if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp) ramène le compteur à 0, et une nouvelle pression sur A ou B pendant le décompte du délai d’un caractère, dans un message qui le permet, active hasPrintBeenSpedUp.14

2. Cristal et Rouge : dix-huit caractères et une marque qui clignote

La boîte de la Game Boy est l’ancêtre de celle de la Game Boy Advance, à une résolution plus faible. L’écran de Cristal fait 20 tuiles sur 18 (SCREEN_WIDTH 20, SCREEN_HEIGHT 18), 160 pixels sur 144. Sa boîte de texte fait TEXTBOX_WIDTH = SCREEN_WIDTH sur TEXTBOX_HEIGHT 6 : toute la largeur et un tiers de la hauteur, 33 %. Avec BORDER_WIDTH 2, l’intérieur fait 18 tuiles sur 4, et la police de 8 sur 8 donne 18 caractères par ligne. Le texte commence deux rangées à l’intérieur de la boîte, TEXTBOX_INNERY = TEXTBOX_Y + 2, et les lignes sont espacées de deux rangées, si bien que la boîte montre deux lignes séparées par une rangée vide ; cette mise en page à deux lignes est ma lecture de ces constantes.13 La boîte de message de Rouge est db MESSAGE_BOX, 0, 12, 19, 17 : les mêmes 20 tuiles sur 6 en bas.112

Ces deux jeux comptent eux aussi la vitesse en images. Les délais sont TEXT_DELAY_FAST 1, moyen 3 et lent 5 images par lettre : 60, 20 et 12 lettres par seconde à 60 hertz. Le PrintLetterDelay de Cristal le dit sans détour : « The text speed setting in wOptions is actually a frame count » (le réglage de vitesse du texte dans wOptions est en réalité un nombre d’images). Tant que A ou B est maintenu, il attend une image par lettre.115 Le rapide de Cristal égale donc celui d’Émeraude, 60 par seconde, et maintenir A ou B dans Émeraude donne le même rythme ; les réglages moyen et lent de Cristal, 20 et 12, sont plus rapides que les 15 et 7,5 d’Émeraude.1 Pan Docs donne l’image de la Game Boy comme « One frame: 70224 dots @ 59.7 fps » (une image : 70 224 points à 59,7 fps) et note qu’une image « takes ~16.74 ms instead of ~16.67 » (dure environ 16,74 ms au lieu d’environ 16,67).40

Cristal laisse le joueur choisir parmi huit cadres, FRAME_1 à FRAME_8, chacun TEXTBOX_FRAME_TILES, 6 tuiles de 1 bit, stockés en PNG de 24 pixels sur 16 ; un neuvième cadre dort inutilisé dans la ROM. La planche de police, gfx/font/font.png, mesure 128 pixels sur 64, 128 cellules de 8 sur 8, et celle de Rouge a la même taille.141

La marque clignote

La marque de suite de Cristal fait partie du cadre. WaitPressAorB_BlinkCursor attend A ou B pendant que BlinkCursor alterne la tuile en (18, 17), dans la rangée inférieure du cadre lui-même, entre '▼' et la tuile de bordure '─'.16 Les deux états sont lus dans le code ; la période du clignotement n’a pas été déduite (section 11).

C’est la deuxième imprécision de l’article sur le monde. Son plan pour le texte demande « a two-frame advance arrow » (une flèche de suite à deux images).23 Une marque à deux états, c’est le clignotement de Cristal. La flèche d’Émeraude, celle que suit le reste de ce plan (la ligne de 16 pixels, la boîte en bas), ne clignote pas : elle passe par trois positions, 0, 1, 2 et 1 pixels depuis le repos, sur un cycle de 36 images (le code décale les rangées source du bitmap, ce qui, selon ma lecture, soulève le chevron à l’écran ; je ne l’ai pas exécuté).113 Le cahier des charges de la section 12 reprend les pas d’Émeraude, pas leur sens.

3. Final Fantasy VI : une fenêtre que vous repeignez, une police qui revient à la ligne

Final Fantasy VI traite la fenêtre comme l’affaire du joueur. Ses chiffres viennent ici du désassemblage everything8215/ff6 à 8130132 (14 septembre 2026), qui contient du code et des listes d’extraction plutôt que les graphismes de la ROM ; les tailles viennent donc de plages d’octets et du code qui les lit.304

Huit fenêtres, sept couleurs chacune

Les listes d’extraction anglaise et japonaise donnent toutes deux les graphismes de fenêtre de 0xED0000 à 0xED1BFF, 7 168 octets : huit styles de 896 octets, chacun de 28 tuiles de 8 sur 8 à 4 bits par pixel. Leurs palettes suivent de 0xED1C00 à 0xED1CFF, huit palettes de 16 couleurs.442 L’écran de configuration liste 8 motifs de fenêtre (ConfigWindowNumPos).443

La couleur de la fenêtre revient au joueur. LoadWindowPal lit les couleurs du motif dans la mémoire de sauvegarde à $1d57, 14 octets par motif, soit sept couleurs de deux octets, et l’écran de configuration les modifie sur des curseurs R, G et B (ConfigColorBarTextList) ; une couleur de plus, pour la police, se trouve à $1d55.443 Le pas est lu dans le code et les trois canaux dans la liste des curseurs.4

La vitesse des messages de l’écran de configuration a six réglages, de 1 à 6 (ChangeConfigOption_02 s’arrête à $05), et une nouvelle sauvegarde démarre à « message speed = 3, battle speed = 3 » (vitesse des messages = 3, vitesse de combat = 3), comme le dit le commentaire de menu_sram.asm.443 Il s’agit de la vitesse des messages de combat, comme la nomme un commentaire de config.asm : « battle speed, battle message speed, magic order, and window pattern » (vitesse de combat, vitesse des messages de combat, ordre des magies et motif de fenêtre). En dehors de l’écran de configuration, le seul code qui lit ses bits de l’octet de configuration, $1d4d, est mess_wait dans src/btlgfx/gfx_cmd.asm, qui cherche le réglage dans MsgSpeedTbl, « wait duration for each battle message speed » (durée d’attente pour chaque vitesse des messages de combat), et attend autant d’images : 32, 48, 64, 80, 96 ou 112 pour les réglages 1 à 6. Aucun fichier du code de terrain ne lit $1d4d, si bien que les dialogues de terrain n’ont pas de réglage de vitesse ; ils s’affichent au rythme unique décrit plus bas.444

Une fenêtre ombrée sans tuile

La fenêtre de dialogue est ombrée sur sa hauteur par le calcul des couleurs du matériel. DlgWindowFixedColorTbl dans src/field/hdma.asm compte 200 octets de $E0 à $E7, une couleur fixe sur les trois canaux à des intensités de 0 à 7, et sa première série compte 72 valeurs qui partent de 7, descendent à 0 puis remontent à 7.445 La fenêtre s’ouvre près du haut ou du bas de l’écran, « dialog window y position (1 or 18) » (position y de la fenêtre de dialogue, 1 ou 18) dans le commentaire de event.asm.45 Que la table soit consommée à raison d’une valeur par ligne de balayage est lu dans des commentaires, pas tracé, et l’aspect du dégradé n’a pas été vu, puisque le désassemblage ne contient aucun pixel (section 11).

La police

La police de dialogue est à 1 bit par pixel, 22 octets par lettre : 11 rangées de 16 bits, une cellule de 16 pixels de large et 11 de haut. Son ombre portée n’est pas dessinée : LoadLetterGfx la produit en décalant chaque rangée d’un pixel (lsr).446 Les largeurs anglaises, tirées de FontWidth, sont proportionnelles : les capitales font en moyenne 8,04 pixels (5 à 12), les minuscules 7,73 (4 à 12), les chiffres 7,80 (5 à 9), et l’espace 5.446

La police de dialogue de la ROM japonaise, de 0xC494C0 à 0xC4FFFF dans sa liste d’extraction, compte 27 456 octets : 1 248 lettres de 22 octets. Sa table de largeurs a 1 280 entrées, en séries de 1 056 à 12 pixels, 180 à 8 et 44 à 12, et les tables de caractères du désassemblage listent 1 010 kanji et 173 kana.447 La plage de la grande police anglaise, 5 120 octets, n’est pas un nombre entier de lettres de 22 octets (232,7), ce que je ne peux pas expliquer à partir du désassemblage (section 11).4

Une ligne, un retour à la ligne, une lettre par image

Une ligne de dialogue va de x 4 à un x maximal de $e0, 224 : 220 pixels, 18 lettres japonaises de 12 pixels.446 Le texte anglais revient à la ligne par mot dans le code. CalcTextWidth ajoute la largeur du mot suivant, et NewLine est appelé quand elle dépasserait le maximum ; le script anglais n’a donc besoin d’aucune coupure placée à la main ; le script vérifie les appels par assertion, et la lecture est celle de UpdateDlgTextOneLine.446

Le texte s’affiche à une lettre par image. La boucle de terrain attend le retour vertical et appelle UpdateDlgText une fois par passage, et UpdateDlgTextOneLine dessine une lettre puis rend la main : 60 lettres par seconde à 60 hertz.448 Un passage par image est ma lecture de la boucle. Comme une seule source construit les deux ROM, les deux langues partagent cette boucle, ce qui, selon ma lecture, signifie le même nombre de lettres par seconde en anglais et en japonais.48

Le titre d’une carte est centré, ($e0 - width) / 2, et affiché pendant 100 images, 1,67 seconde.446

Face à Émeraude, Final Fantasy VI montre l’autre façon de construire la même boîte : Émeraude écrit chaque coupure à la main et laisse le joueur choisir le cadre des menus, jamais celui de la boîte de dialogue ; Final Fantasy VI revient à la ligne dans le code, laisse le joueur repeindre la fenêtre et l’ombre en matériel. La boîte de terrain d’Émeraude se trouve en bas, celle de Final Fantasy VI en haut ou en bas, et les deux affichent à un nombre fixe d’images.

Chrono Trigger

Je voulais placer Chrono Trigger à côté de Final Fantasy VI et je n’ai pas pu. Aucun désassemblage public n’a été cloné, et la carte de la ROM du jeu sur Data Crystal a renvoyé HTTP 403 lors de la récupération ; rien dans cet article sur les boîtes de texte n’en vient donc (section 11).49

4. Le japonais en 8, 10 et 12 points

Kiradex est une app de collectionneurs de cartes, et le Dex affiche déjà les noms de cartes japonais avec l’anglais en dessous, dans les polices du système.50 Le texte du monde aura lui aussi besoin du japonais, et une police pixel incapable de le dessiner n’est pas candidate. La question est de savoir jusqu’où un kanji peut descendre en taille, et qui en a déjà dessiné sept mille.

L’ampleur de la tâche

JIS X 0208, le jeu de caractères à l’aune duquel on mesure les polices pixel japonaises, compte 6 879 caractères selon la table de correspondance du Consortium Unicode : 524 non-kanji, dont 169 kana aux rangées 4 et 5 ; 2 965 kanji de niveau 1 aux rangées 16 à 47 ; et 3 390 kanji de niveau 2 aux rangées 48 à 84.576 La table est en version 2.0, et le consortium la classe parmi ses correspondances obsolètes ; c’est pourtant toujours la liste sur laquelle la couverture d’une police se compte le plus facilement.5

Misaki : 8 sur 8

Misaki (美咲フォント), de Num Kadoma, est la plus petite. Sa page la présente comme « 8×8 ドットの日本語ビットマップフォント » (une police bitmap japonaise de 8 points sur 8) qui prend en charge « JIS 第一・第二水準 » (les niveaux JIS 1 et 2), et précise « 一部の記号を除いた全ての文字は 7×7 ドットの範囲に収まっている » (tous les caractères, sauf quelques symboles, tiennent dans 7 points sur 7), afin que, poursuit la page, des caractères posés côte à côte gardent leur interligne et leur espacement.51 Elle a été conçue pour un pilote d’affichage japonais des ordinateurs de poche PC-E500, d’après l’historique de la page.51

Mesurée à partir des fichiers BDF de la version du 2021-05-05, Misaki Gothic compte 7 171 caractères : 168 en demi-chasse, larges de 4 pixels, et 7 003 en pleine chasse, larges de 8 pixels, dans une cellule de 8 sur 8 avec un jambage supérieur de 6 et un jambage inférieur de 2. Sur ses 6 356 glyphes de U+4E00 à U+9FFF (une plage de codes, d’où un compte différent des 6 355 kanji des deux niveaux JIS), aucun ne remplit la cellule de 8 points dans un sens ou dans l’autre, alors que 53 glyphes pleine chasse, des symboles, le font. Elle dessine les 6 879 caractères de JIS X 0208.6 Misaki Mincho, que sa page décrit comme Misaki Gothic avec les non-kanji redessinés dans un style Mincho, donne les mêmes mesures sur tous les comptes. Misaki Gothic 2nd, qui selon sa page utilise les 3 sur 7 pixels complets pour les caractères demi-chasse et 7 sur 7 pour les kana pleine chasse, compte 7 170 caractères (167 en demi-chasse), un jambage supérieur de 7 et un jambage inférieur de 1, et donne les mêmes mesures pour le reste.651

k8x12 : 8 sur 12

k8x12, du même auteur, garde la largeur de 8 points et gagne en hauteur : « 8×12 ドットの日本語ビットマップフォント » (une police bitmap japonaise de 8 points sur 12), niveaux JIS 1 et 2. Elle a deux variantes : k8x12L, dont les kana sont dessinés en hauteur, et k8x12S, dont les non-kanji font 8 points de haut, conçue, dit la page, en pensant aux limites des anciennes consoles : « 8×8 ドット単位でグラフィックを扱うような、昔のゲーム機等の制限を意識しています » (en ayant conscience des limites des anciennes machines de jeu qui traitent les graphismes par unités de 8 points sur 8).52

Mesurée à partir du BDF, k8x12 est une cellule de 8 sur 12 avec un jambage supérieur de 10 et un jambage inférieur de 2 : 7 170 caractères, 168 à 4 pixels et 7 002 à 8. Aucun kanji ne remplit la cellule ; 53 glyphes pleine chasse le font dans k8x12 et k8x12L, et 45 dans k8x12S. Les trois dessinent les 6 879 caractères de JIS X 0208.6

PixelMplus : 10 et 12, en contours

PixelMplus, d’itouhiro (2013), est l’autre approche : des contours TrueType qui ressemblent à une police bitmap, sans bitmaps incorporés (« 埋め込みビットマップは なし », pas de bitmaps incorporés), tous les kanji des niveaux JIS 1 et 2 (« JIS第1・第2水準のすべての漢字を収録 », contient tous les kanji des niveaux JIS 1 et 2) plus ISO-8859-1, en graisse normale et grasse et en deux tailles, PixelMplus12 et PixelMplus10, vectorisées à partir des M+ BITMAP FONTS.18 Mesurée avec Core Text, chacune compte 7 253 glyphes et dessine 6 876 des 6 879 caractères de JIS X 0208. Les trois qu’elle ne fait pas correspondre sont U+301C 〜, U+2016 ‖ et U+2212 −.7 Ce sont des caractères dont la correspondance Unicode diffère entre le JIS0208.TXT du consortium et les tables d’autres fabricants ; selon ma lecture, les polices portent donc probablement ces glyphes à d’autres points de code ; cela n’a pas été vérifié (section 11).5

DotGothic16 : une police à points sans grille

DotGothic16, de Fontworks, est sur Google Fonts sous SIL Open Font License 1.1 sans Reserved Font Name, listée avec les sous-ensembles cyrillic, japanese, latin et latin-ext, et ajoutée le 2020-12-15.53 Elle compte 9 362 glyphes et dessine les 6 879 caractères de JIS X 0208.7 Mais elle est dessinée en points séparés, pas en pixels. Dans son A, les bords x du contour vont par paires, 91 et 102, 153 et 163, 214 et 225, 276 et 287, 338 et 349, 400 et 410 unités sur 1 000 : des points espacés d’environ 62 unités, avec des écarts de 10 ou 11 unités entre eux. Aucune grille de pixels entiers ne correspond à cela, et la sonde n’en a trouvé aucune entre 4 et 40 pixels par em.197 Un pas d’environ 62 unités est proche de 16 points par em, le nombre qui figure dans le nom de la police ; la sonde la dessine donc à 16 points par em nominaux et à des multiples de cette valeur, un choix de taille qui est le mien, pas une grille que la police déclare. Core Text sous macOS 27.0 l’a dessinée avec 82,3 % de pixels partiels à 16 pixels et 37,3 % à 64, et avec des pixels partiels à chaque taille mesurée (section 6).7

Ce que mesurent les polices libres

La grille de conception du tableau est le plus petit nombre de pixels par em pour lequel 95 % des points de contour d’un échantillon de glyphes se trouvent à moins de 0,04 pixel, chaque axe pouvant avoir un décalage constant ; le script qui la trouve, measure_coretext.swift, a été compilé avec swiftc -O et exécuté sous macOS 27.0 (build 26A428) ; ce sont donc les chiffres de Core Text sur un Mac, pas sur un iPhone.7

Police Glyphes Tables bitmap Grille de conception (pixels par em) Avance latin / kana (pixels) JIS X 0208 dessiné
Misaki Gothic 7 174 EBDT, EBLC 8, sur la ligne de base 4 / 8 6 879
k8x12 7 173 EBDT, EBLC 12, sur la ligne de base 4 / 8,01 6 879
PixelMplus10 Regular et Bold 7 253 chacune aucune 10, sur la ligne de base 5 / 10 6 876
PixelMplus12 7 253 aucune 12, 0,39 pixel sous la ligne de base 6 / 12 6 876
DotGothic16 9 362 aucune aucune (dessinée à 16 points nominaux) 8 / 16 points nominaux 6 879

Selon cette mesure, PixelMplus10 Regular a 0 coordonnée de contour sur 1 032 échantillonnées hors de sa grille de 10 pixels et la Bold 0 sur 1 000 ; Misaki Gothic 0 sur 848 ; k8x12 0 sur 988 ; PixelMplus12 0 sur 1 192 une fois son décalage de 0,39 pixel admis, et DotGothic16 les 1 544 hors grille.7 Le décalage de PixelMplus12 se voit dans ses coordonnées brutes : son H va de y −33 à 717 unités sur 1 000, sur une grille de 83,3 unités par pixel, si bien que sa ligne de base tombe à 0,396 pixel à l’intérieur d’une rangée de la grille ; l’estimation de la sonde, 0,39, vient du tri des parties fractionnaires des coordonnées en classes larges d’un cinquantième de pixel, et c’est la valeur dont les rendus ci-dessous ont été décalés.19733

Dans chaque police mesurée capable d’afficher le japonais, un caractère japonais fait le double d’un caractère latin : 8 contre 4 dans Misaki et k8x12, 10 contre 5 et 12 contre 6 dans PixelMplus.7 Le canon se situe dans la même fourchette : le japonais d’Émeraude fait 8 pixels de large dans une ligne de 16 pixels, la planche de Rouge Feu le plus souvent 10, Final Fantasy VI 12.14

Les licences

Tout cela est libre, dans les termes les plus simples. La licence de Misaki et k8x12, sur la page des polices de l’auteur, tient en trois phrases : « These fonts are free software. Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially. THESE FONTS ARE PROVIDED “AS IS” WITHOUT WARRANTY. » (Ces polices sont des logiciels libres. Permission illimitée est accordée de les utiliser, de les copier et de les distribuer, avec ou sans modification, à des fins commerciales ou non. Ces polices sont fournies « en l’état », sans garantie.) Le readme de chaque archive reprend les mêmes conditions en des termes légèrement différents, et la page de Misaki nomme la licence qu’elle utilise comme la licence M+ FONTS.1751 PixelMplus est sous M+ FONT LICENSE, dont le texte anglais, LICENSE_E, est fait des trois mêmes phrases.18 DotGothic16 est sous SIL Open Font License 1.1, dont les conditions figurent dans son propre OFL.txt.53 Selon ma lecture, la licence en trois phrases ne demande rien en retour ; créditer les auteurs reste juste, et le cahier des charges le fait (section 12). Ce sont des lectures, pas un avis juridique.

Ce que coûte la lisibilité

Un kana fait 8 pixels de police de large dans Misaki, douze dans PixelMplus12. À la densité propre du monde sur l’iPhone 18 Pro Max, où un texel fait 8 pixels de l’appareil, une ligne de la boîte dessinée en Misaki contient 36 caractères latins ou 18 japonais.11 La ligne japonaise porte davantage par caractère ; la boîte n’a donc pas besoin de contenir autant de caractères pour dire la même chose ; c’est ma lecture, pas une mesure.

5. Les polices pixel latines, et celles que je n’ai pas pu mesurer

Deux polices pixel latines de Google Fonts ont été mesurées aux côtés des japonaises.30

Press Start 2P, de CodeMan38, est sous SIL Open Font License 1.1 « with Reserved Font Name “Press Start 2P” » (avec le Reserved Font Name « Press Start 2P »).54 Elle compte 658 glyphes sur une grille de 8 pixels posée sur la ligne de base, chaque caractère latin faisant 8 pixels de large, et les 95 caractères ASCII imprimables. Elle n’a ni kana ni kanji : de JIS X 0208, elle dessine 150 non-kanji (son grec, son cyrillique et ses symboles) et aucun des 6 355 kanji.7

Silkscreen, de Jason Kottke, est sous SIL Open Font License 1.1 sans Reserved Font Name.55 Elle compte 228 glyphes sur une grille de 8 pixels, dont 10 coordonnées de contour sur 588 échantillonnées s’écartent (son A et son o ont des valeurs y de 495 et 130 unités sur une grille de 125 unités), et son A fait 6 pixels de large. De JIS X 0208, elle dessine 21 non-kanji.719

Trois autres polices souvent recommandées pour les jeux en pixel art n’ont pas été mesurées, et je le dis plutôt que de les décrire de mémoire.

Pixel Operator, de Jayvee Enaguas : les notes de l’auteur sur DaFont disent « Changed licence to Creative Commons Zero (CC0) 1.0 » (licence changée pour Creative Commons Zero (CC0) 1.0) et que la police est « licensed under a Creative Commons Zero (CC0) 1.0 » (sous licence Creative Commons Zero (CC0) 1.0). DaFont la liste comme « Bitmap (16 px) », en 15 fichiers de police, avec 241 glyphes. Le dépôt de l’auteur, que les notes situent sur NotABug, a renvoyé HTTP 404, et la police n’a été ni téléchargée ni mesurée. Tout ce qui est dit ici vient de cette seule page.56

m5x7, de Daniel Linssen : sa page itch.io dit « use font size 16, 32, 48, etc » (utilisez une taille de police de 16, 32, 48, etc.) et « free to use but attribution appreciated » (libre d’utilisation, mention appréciée), indique « Asset license Creative Commons Zero v1.0 Universal » (licence de l’asset Creative Commons Zero v1.0 Universal) et donne une largeur moyenne de 5, un jambage supérieur de 7, un jambage inférieur de 2 et une hauteur d’x de 5. Elle décrit ses caractères comme « uppercase and lowercase letters, including many accented letters, numbers and most common symbols » (des lettres majuscules et minuscules, dont de nombreuses lettres accentuées, des chiffres et la plupart des symboles courants).57

m3x6, du même auteur : « use font size 16, 32, 48, etc » (utilisez une taille de police de 16, 32, 48, etc.) et « free to use with attribution » (libre d’utilisation avec mention) ; sa page n’affiche aucun champ de licence d’asset.58 Aucun des deux fichiers n’a été téléchargé, parce que les deux sont derrière un téléchargement à prix libre ; aucun n’a donc été mesuré.5758

C’est la troisième imprécision de l’article sur le monde. Son plan pour le texte nomme m5x7 comme police.23 D’après la description de sa propre page, m5x7 est une police latine, et rien sur la page ne dit qu’elle a des kana ou des kanji ; une app qui affichera du japonais ne peut pas composer le texte de son monde avec elle.57 Le cahier des charges remplace la police par PixelMplus10, l’une des deux polices mesurées qui ont le japonais, une grille propre et des avances en pixels entiers (Misaki est l’autre), et la plus grande des deux (sections 6 et 12). m5x7 elle-même reste non mesurée.

6. Ce que Core Text fait d’une police pixel

L’article sur le monde affirmait que « Bitmap fonts render at integer multiples only » (les polices bitmap ne se rendent qu’en multiples entiers).23 C’est la règle que répète chaque guide du pixel art, et je voulais la voir, chiffres à l’appui, sur le moteur de texte d’Apple lui-même.

La mesure

Core Text est la couche d’Apple pour « Create text layouts, optimize font handling, and access font metrics and glyph data » (créer des mises en page de texte, optimiser la gestion des polices et accéder aux métriques des polices et aux données des glyphes), disponible depuis iOS 3.2 et macOS 10.8.59 measure_coretext.swift charge chaque fichier TrueType candidat, dessine une ligne d’exemple (« Nice card! Which set? 0123 » pour les polices latines, « カードを見せて! Nice card! » pour les japonaises) dans un bitmap en niveaux de gris sur 8 bits, à des tailles qui sont des multiples entiers et fractionnaires de la grille de chaque police, et compte les pixels encrés qui sont partiels : entre 8 et 247 sur 255, à plus de 3 % du plein ou du vide. Il dessine chaque taille de trois façons : avec l’anticrénelage par défaut de Core Text, sans anticrénelage, et avec le positionnement subpixel activé et la quantification désactivée, pour que les glyphes tombent exactement là où leurs avances les placent.7

Deux limites comptent avant tout chiffre. L’exécution a eu lieu sous macOS 27.0, build 26A428, sur mon Mac, pas sous iOS, et elle dessine via Core Text dans un contexte bitmap, pas via le Text de SwiftUI ; ce qu’un iPhone dessine reste une prédiction jusqu’à ce que les vérifications par capture du cahier des charges y tournent (section 11).7 Et le script dessine avec le lissage des polices désactivé (setAllowsFontSmoothing(false)). Quand j’ai dessiné la figure ci-dessous avec le réglage par défaut du contexte bitmap, 178 des 272 pixels encrés de « Nice card! » en PixelMplus10 à 10 pixels sont sortis partiels, contre 0 sur 94 lissage désactivé.9 Le lissage des polices est un réglage de macOS ; savoir si quelque chose de semblable s’applique sous iOS revient aux vérifications par capture.

Les multiples entiers sont propres

Aux multiples entiers de la grille, anticrénelage activé (le réglage par défaut du contexte) et lissage des polices désactivé, Misaki à 8, 16, 24 et 32 pixels, PixelMplus10 Regular et Bold à 10, 20, 30 et 40, et Press Start 2P à 8, 16, 24 et 32 se rendent tous avec 0,0 % de pixels partiels.7 Chaque pixel est encre ou papier. Cela dépend du réglage de lissage : avec le lissage par défaut du contexte bitmap, PixelMplus10 à ses propres 10 pixels a dessiné partiels 178 des 272 pixels encrés de « Nice card! » ; sur le Mac, un multiple entier ne suffisait donc pas à rendre le texte net.9

Les pixels propres de PixelMplus10 Regular à 10 pixels, dessinés sans anticrénelage et agrandis neuf fois sur une grille : la ligne Nice card! en 50 sur 11 pixels de police et la ligne japonaise カードを見せて! en 80 sur 11, la ligne de base deux rangées au-dessus du bas.

PixelMplus10 Regular dessinée par Core Text sous macOS 27.0 à un pixel de l’appareil par pixel de police, présentée sans anticrénelage. Anticrénelage activé et lissage des polices désactivé, 0 des 94 pixels encrés de « Nice card! » sont partiels ; avec le lissage par défaut du contexte bitmap, 178 sur 272 le sont. La police est sous M+ FONT LICENSE, qui autorise la copie et la distribution ; dessinée par figures/dump_glyphs.swift et figures/make_figures.py.

Entre les deux, elles bavent

Entre les multiples entiers, les mêmes polices bavent, lissage désactivé comme dans toute la sonde. Misaki a 57,7 % de pixels partiels à 12 pixels (1,5 fois sa grille), 39,9 % à 20 (2,5 fois) et 82,2 % à 10 (1,25 fois). PixelMplus10 a 60,1 % à 15 pixels. Press Start 2P a 44,0 % à 12 pixels, 29,1 % à 20 et 21,7 % à 28.7 Sur les 40 rendus à 1,25, 1,5, 1,75, 2,5 et 3,5 fois la grille des huit polices dont la grille repose sur la ligne de base (les deux Misaki, k8x12 et k8x12S, PixelMplus10 Regular et Bold, Press Start 2P et Silkscreen), la part de pixels partiels va de 19,4 %, PixelMplus10 Bold à 35 pixels, à 82,2 %, Misaki à 10.7 Le bavage ne diminue pas régulièrement avec la taille : pour chacune des huit, 1,75 fois la grille bave plus que 1,5 fois (Misaki 65,7 % à 14 pixels contre 57,7 % à 12). À fraction égale, il diminue bien : à 1,5, 2,5 et 3,5 fois la grille, chacune des huit baisse à chaque cran (Misaki 57,7 %, 39,9 % et 30,4 %). Dans aucun rendu mesuré entre multiples entiers il n’atteint zéro ; selon ma lecture, la baisse à fraction fixe tient à ce qu’une erreur d’un demi-pixel au bord d’un glyphe pèse moins dans un glyphe plus grand.7

Désactiver l’anticrénelage (setShouldAntialias(false)) laisse chaque pixel plein ou vide à chaque taille, mais les formes sont rééchantillonnées de manière inégale. Les 191 pixels encrés de Misaki à 8 pixels deviennent 377 à 10 ; une mise à l’échelle uniforme de 1,25 dans chaque direction en donnerait environ 298.733 Certaines rangées et colonnes sont doublées et d’autres non, si bien que des traits d’un pixel de large sortent à un ou deux pixels.

Un graphique en courbes des pixels partiels selon la taille exprimée en multiple de la grille de chaque police, de 1 à 4, lissage des polices désactivé : Misaki, PixelMplus10 Regular et Press Start 2P tombent à 0 % à chaque multiple entier et remontent entre eux à des valeurs allant de 21,7 %, Press Start 2P à 3,5 fois, à 82,2 %, Misaki à 1,25 fois ; PixelMplus12 n'atteint jamais 0 %, de 66,6 % à 1 fois à 23,6 % à 4 ; k8x12 avec des glyphes placés exactement n'atteint jamais 0 %, de 62,3 % à 1 fois à 25,9 % à 4.

Pixels partiels selon la taille, Core Text sous macOS 27.0 avec le lissage des polices désactivé, pas iOS ; dessiné par figures/make_figures.py à partir de measure_coretext.out.

Une taille propre ne suffit pas : le fichier doit l’être aussi

Deux défaillances plus discrètes comptent davantage que les multiples dans le choix d’une police.

Une grille décalée de la ligne de base bave à chaque taille. Les contours de PixelMplus12 se trouvent 0,39 pixel sous la grille de la ligne de base, et elle se rend avec 66,6 % de pixels partiels à ses propres 12 pixels, 40,0 % à 24 et 29,8 % à 36. Déplacer la ligne de base du décalage, via la transformation du contexte avec le positionnement subpixel activé, ramène 12 pixels à 0,0 % mais laisse 14,3 % à 24 et 19,0 % à 36, alors même que le décalage a été mis à l’échelle avec la taille (0,78 pixel à 24, 1,17 à 36).7 Pourquoi le décalage mis à l’échelle ne nettoie pas les grandes tailles, la sortie ne le dit pas, et je ne l’ai pas tracé.

Une avance hors grille bave dès que les glyphes sont placés exactement. L’avance des kana de k8x12 est de 8,01 pixels, pas 8. Avec les réglages par défaut du contexte bitmap, qui placent chaque glyphe sur un pixel entier, elle se rend avec 0,0 % de pixels partiels à 12, 24 et 36 pixels. Avec le positionnement subpixel activé et la quantification désactivée, les mêmes tailles ont 62,3 %, 42,8 % et 32,6 % de pixels partiels.7 Selon ma lecture, chaque kana tombe alors un centième de pixel plus loin de la grille que le précédent.

Des points de contour égarés laissent un peu de traces. Les dix coordonnées hors grille de Silkscreen se traduisent par 4,5 % de pixels partiels à 8 pixels et 1,2 % à 32.7

Une police à points ne s’aligne jamais. DotGothic16 n’a pas de grille dont prendre des multiples ; la sonde la dessine donc à 16 points par em nominaux et à 1,25 à 4 fois cette valeur (section 4). Anticrénelage activé et lissage désactivé, elle a 82,3 % de pixels partiels à 16 pixels, 52,5 % à 32, 48,8 % à 48 et 37,3 % à 64, et des pixels partiels à chacune des neuf tailles de 16 à 64 pixels, multiples entiers de 16 compris ; ses points ne reposent sur aucune grille de pixels (section 4).719

Misaki et k8x12 portent des tables bitmap incorporées (EBDT et EBLC, lues avec CTFontCopyAvailableTables). Les résultats mesurés ci-dessus sont ce que Core Text a dessiné ; qu’il ait dessiné ces bitmaps ou les contours n’a pas été établi (section 11).7

Misaki, PixelMplus10 et Press Start 2P n’ont ni l’un ni l’autre de ces défauts discrets. La règle qui en découle porte autant sur le fichier de police que sur la taille : multiples entiers, une grille sur la ligne de base, et des avances en pixels entiers.

Dynamic Type et la grille

Les Human Interface Guidelines d’Apple donnent à iOS une taille de texte par défaut de 17 points et un minimum de 11, ainsi qu’une table pour chaque taille Dynamic Type. Body parcourt, de xSmall à AX5, 14, 15, 16, 17, 19, 21, 23, 28, 33, 40, 47 et 53 points ; Caption 1 de 11 à 43.6010

Sur un écran 3×, un pixel de police n’est un nombre entier de pixels de l’appareil que si la taille en points multipliée par 3, divisée par l’em en pixels de police, est un entier. Sur les douze tailles Body, un em de 12 pixels tombe juste à 3 d’entre elles (16, 28 et 40 points), un em de 8 pixels à 2 (16 et 40), et un em de 10 pixels à 1 (40). Sur les douze tailles de Caption 1, un em de 12 pixels tombe juste à 3 (12, 16 et 32).760

Une grille des douze tailles Body d'iOS de xSmall à AX5, de 14 à 53 points : des points pleins là où un em de 8 pixels tombe juste en 3× (16 et 40 points), un em de 10 pixels (40 seulement) et un em de 12 pixels (16, 28 et 40) ; en dessous, le multiple arrondi m du cahier des charges pour PixelMplus10, une moitié arrondie vers le haut, 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 et 16, et les tailles en points qui en découlent, de 13,33 à 53,33.

Quelles tailles Body tombent sur des pixels entiers de l’appareil en 3×, et l’arrondi du cahier des charges ; dessiné par figures/make_figures.py à partir de measure_coretext.out et de measure_kiradex_text.out.

Une police pixel ne peut donc pas simplement suivre Dynamic Type. Si elle se met à l’échelle en continu avec les tailles du système, elle sort de sa grille à chaque taille sauf les quelques tailles entières : 11 tailles Body sur 12 pour un em de 10 pixels, 9 sur 12 pour un em de 12 pixels.733 Hors de la grille, Core Text sur le Mac, anticrénelage activé, a fait baver chaque police mesurée ; que SwiftUI sur un iPhone fasse de même dépend de son rendu et de son placement, ce que tranchent les captures du cahier des charges. Elle doit prendre la taille que le système demande et l’arrondir à la grille, ce que la section 10 transforme en recette d’API et la section 12 en règle du cahier des charges.

7. La coupure des lignes japonaises

Une courte réplique prédéfinie ne revient jamais à la ligne. Un bloc de texte dans la boîte, si, et le japonais a des règles sur l’endroit où couper.

Les règles

Les Requirements for Japanese Text Layout du W3C (une Working Group Note du 11 août 2020) posent en 3.1.7 la règle de début de ligne : « In principle, no line should begin with closing brackets (cl-02), hyphens (cl-03), dividing punctuation marks (cl-04), middle dots (cl-05), full stops (cl-06), commas (cl-07), iteration marks (cl-09), a prolonged sound mark (cl-10), small kana (cl-11) or warichu closing brackets (cl-29) » (en principe, aucune ligne ne devrait commencer par un crochet fermant, un trait d’union, une ponctuation de séparation, un point médian, un point, une virgule, une marque d’itération, une marque d’allongement, un petit kana ou un crochet fermant de warichu). Elles consignent aussi la pratique plus souple : « Not a small number of books adopt a less strict set of rules which allow IDEOGRAPHIC ITERATION MARK “々” (one of the iteration marks (cl-09)), prolonged sound mark (cl-10) and small kana (cl-11) to start a line. » (Bon nombre de livres adoptent un jeu de règles moins strict qui permet à la marque d’itération « 々 », à la marque d’allongement et aux petits kana de commencer une ligne.)21

L’algorithme de coupure de lignes d’Unicode, UAX #14 (révision 57, pour Unicode 18.0.0, datée du 2026-09-01), place les petits hiragana et katakana et la marque d’allongement dans sa classe CJ, et dit : « Treating characters of class CJ as class NS will give CSS strict line breaking; treating them as class ID will give CSS normal breaking. » (Traiter les caractères de la classe CJ comme de la classe NS donne la coupure stricte de CSS ; les traiter comme de la classe ID donne la coupure normale de CSS.)61

Ce qu’a fait Core Text

measure_coretext.swift, exécuté sous macOS 27.0 (pas iOS), compose un paragraphe japonais de 131 caractères avec CTFramesetter à des largeurs de 8, 10, 12, 14, 16, 18 et 20 em, et compare le premier caractère de chaque ligne après la première à sa propre liste de caractères interdits en début de ligne : certains crochets fermants, la virgule et le point japonais, le point médian, les deux-points et le point-virgule, les points d’interrogation et d’exclamation, la marque d’allongement, les petits kana et 々.7 Cette liste n’est pas un sous-ensemble des classes que nomme la règle de JLREQ, seulement un recoupement avec elles. Sept de ses 40 caractères, ,.:;?! et ), sont les formes pleine chasse que le texte japonais emploie là où JLREQ liste les caractères ASCII ; les 33 autres sont ceux de JLREQ. Parmi les crochets fermants, elle contient 」』】〉》〕 et la ) pleine chasse, et omet ’ ” ] } » 〟 ⦆ 〙 〗 et la ) ASCII ; parmi les 40 petits kana, elle en contient 22, et omet ゕ, ゖ et ㇰ à ㇿ ; et elle omet les traits d’union (cl-03), dont ‐, 〜, ゠ et le tiret demi-cadratin, ‼ ⁇ ⁈ ⁉ de cl-04, et toutes les marques d’itération (cl-09) sauf 々, dont ヽ, ヾ, ゝ, ゞ et 〻.721 Les résultats ci-dessous valent pour la liste de la sonde, et le texte du paragraphe de test décide quels caractères arrivent jamais en début de ligne.

Sans balise de langue, aucune ligne n’a commencé par un caractère de la liste de la sonde, à aucune largeur, ni dans Hiragino Sans (la police japonaise du système, utilisée comme témoin) ni dans aucune des huit polices pixel : Misaki Gothic, Misaki Gothic 2nd, k8x12, k8x12S, PixelMplus10 Regular et Bold, PixelMplus12, et DotGothic16 à ses 16 points nominaux.7

Balisée avec kCTLanguageAttributeName réglé sur ja, la règle s’est assouplie. Dans Hiragino Sans, les deux Misaki, les deux PixelMplus10, PixelMplus12 et DotGothic16, une ligne a commencé par un petit kana (っ, ョ ou ァ) à 6 des 7 largeurs, toutes sauf 18 em.7 Dans k8x12 et k8x12S, une ligne a commencé par un petit kana (っ, ョ ou ょ) à 4 des 7 largeurs, et par la marque d’allongement ー à deux, 10 em (à côté de ョ) et 18 em, une cinquième largeur.7

Selon ma lecture de ces décomptes au regard des deux citations, la balise ja donne la règle moins stricte que décrit JLREQ, celle qu’UAX #14 appelle la coupure normale de CSS, et l’exécution non balisée se comporte strictement.2161 C’est l’inverse de ce que j’attendais, puisque la balise nomme la langue, et c’est pourquoi le cahier des charges ne balise pas le texte de la boîte sans capture.

Deux remarques sur la mesure elle-même, toutes deux issues de la lecture du script face à sa sortie. Les largeurs sont en em, la taille complète de la police ; pour k8x12, dont les kana font 8 pixels dans un em de 12 pixels, chaque largeur contient donc une fois et demie autant de kana que son nombre l’indique ; le paragraphe de k8x12 a pris 12 lignes à 8 em là où les autres en ont pris 17.7 Et DotGothic16, qui n’a pas de grille, a été composée au double de ses 16 points nominaux, 32 pixels par em ; à cette taille, elle a pris le même nombre de lignes que Hiragino Sans à chaque largeur, de 17 à 8 em à 7 à 20, et les mêmes petits kana ont commencé ses lignes une fois balisée.7

8. Les jeux en pixel art depuis : boîtes, bulles, japonais et chinois

Stardew Valley : deux formes de parole

Stardew Valley possède les deux formes que Kiradex a déjà. Sa boîte de dialogue passe à la page avec une commande, $b, que la page de modding du Stardew Valley Wiki consacrée aux dialogues (dernière modification le 20 mai 2026) décrit comme une commande qui « Indicates pauses in dialogue, where the player will need to click for the next part to load in a new dialogue box » (indique des pauses dans le dialogue, où le joueur devra cliquer pour que la partie suivante se charge dans une nouvelle boîte de dialogue), et des commandes de portrait qui « set the portrait shown in the dialogue box for the current line of dialogue » (définissent le portrait affiché dans la boîte de dialogue pour la réplique en cours).62 Et il a des bulles, Strings\SpeechBubbles, pour le « dialogue spoken by NPCs through speech bubbles when the player enters a given location » (dialogue prononcé par les PNJ dans des bulles quand le joueur entre dans un lieu donné).62 La boîte sert aux conversations, précédée d’un visage ; les bulles, c’est ce que dit une ville quand vous y entrez.

Ses langues non latines utilisent des polices bitmap. La page du wiki sur les langues personnalisées (dernière modification le 3 janvier 2026) demande à une nouvelle langue de régler UseLatinFont sur false et de fournir un fichier .fnt, sur le modèle de « the Chinese, Japanese, Korean, and Russian fonts in the Fonts folder » (les polices chinoise, japonaise, coréenne et russe du dossier Fonts). Son FontPixelZoom est « A factor by which to multiply the font size. The recommended baseline is 1.5 » (un facteur par lequel multiplier la taille de la police ; la base recommandée est 1,5), et FontApplyYOffset règle « Whether to shift the font up by four pixels » (si la police doit être remontée de quatre pixels), pour aligner les « languages with larger characters like Chinese and Japanese » (langues aux caractères plus grands, comme le chinois et le japonais).63 Un zoom de 1,5 n’est pas un multiple entier, mais la page le donne comme point de départ pour une langue qu’ajoute un moddeur, pas comme ce qu’utilisent les polices chinoise, japonaise et coréenne livrées avec le jeu, et le fait qu’un zoom rééchantillonne à l’écran dépend aussi de l’échelle du jeu en pixels de l’appareil ; cela ne dit donc rien de certain sur la façon dont Stardew dessine son propre japonais.63

Final Fantasy Pixel Remaster : la police, c’était le travail

La version console du Pixel Remaster a ajouté une option de police pixel, et le producteur, Naofumi Takuma, a raconté à Siliconera (Jenni Lada, 2 juin 2023) comment elle a été faite. Pour les langues occidentales, l’équipe a trouvé des données de police produites en interne et les a utilisées. Pour le japonais : « We didn’t have any alternative data saved on file in-house for the Japanese language font, so we decided to create a new one. » (Nous n’avions en interne aucune donnée de remplacement pour la police japonaise, nous avons donc décidé d’en créer une nouvelle.) Et : « While the Western language font required us to create only a few hundred characters, with the Japanese font, we had to create around 7,500 characters. » (Alors que la police des langues occidentales ne nous demandait de créer que quelques centaines de caractères, pour la police japonaise, nous avons dû en créer environ 7 500.)8

C’est la quatrième imprécision de l’article sur le monde. Il donnait le même chiffre, « about 7,500 Japanese characters » (environ 7 500 caractères japonais), et citait le compte rendu de ce travail par GoNintendo ; la source primaire est l’entretien du producteur lui-même avec Siliconera, qui dit « around 7,500 characters » (environ 7 500 caractères).238 Même chiffre, meilleure source.

Sept mille cinq cents, c’est à peu près la taille des 6 879 caractères de JIS X 0208, la cible de couverture habituelle ; cette comparaison est de moi.533 Un jeu dont chaque réplique est figée avant sa sortie peut en dessiner moins, comme l’a fait la ROM japonaise de Final Fantasy VI avec 1 248 lettres de dialogue (section 3), et chaque chaîne doit alors être vérifiée contre la police ; une police qui couvre le jeu entier n’a besoin de cette vérification pour aucune chaîne qui en est tirée.4 La leçon pour une petite app est celle que les polices libres de la section 4 enseignent par l’autre bout : pour un texte qui ne cessera de grandir, la typographie pixel japonaise n’est un problème résolu que si quelqu’un a déjà dessiné les sept mille caractères et que la licence vous permet de les livrer.

Ce que livrent les jeux en pixel art modernes

Sur Steam, le 5 octobre 2026, Stardew Valley liste 12 langues, dont le japonais, le chinois simplifié et le coréen ; Celeste 9, dont le japonais et le chinois simplifié ; Sea of Stars 11, dont le japonais, le coréen et le chinois simplifié comme traditionnel ; et Eastward 7, dont les deux chinois et le japonais.64 Les quatre livrent le japonais.

Comment Celeste, Sea of Stars et Eastward dessinent leur texte japonais et chinois, je ne l’ai trouvé dans aucune source de développeur ; cet article n’en dit donc rien au-delà des listes de langues (section 11).

Deux formes de parole dans Kiradex

Kiradex possède déjà les deux formes de Stardew : la boîte WorldHUD en bas de l’écran et les bulles de la place au-dessus des têtes.2625 Ce qui lui manque, c’est la typographie du canon à l’intérieur, et un moyen pour VoiceOver d’entendre l’une ou l’autre. Le commentaire de la bulle dans le code dit ce qu’elle visait : « A line said, as the old games drew it: a cream box with a dark border. » (Une réplique prononcée, comme la dessinaient les vieux jeux : une boîte crème à la bordure sombre.)25

9. Le métier, en règles chiffrées

Voici les règles qu’appuient les mesures, chacune avec sa provenance. Là où une règle transforme un fait mesuré en recommandation, la recommandation est la mienne, et la règle le signale par son verbe.

Élément Règle Source
Boîte La boîte occupe le tiers inférieur de l’écran et deux lignes. Placez-la en bas ; un jeu qui a besoin du bas peut la déplacer en haut. Le cadre d’Émeraude, 30 tuiles sur 6, toute la largeur et 30 % de la hauteur, autour d’une fenêtre de 27 tuiles sur 4 à deux lignes de 16 pixels ; Cristal 20 sur 6, 33 %, deux lignes de 18, et Rouge les mêmes 20 sur 6 ; la fenêtre de Final Fantasy VI à la rangée 1 ou 18
Glyphes latins 4 à 12 pixels de large dans une cellule de 8 à 16 pixels de haut. La cellule de 6 sur 16 d’Émeraude, à 5,25 pixels par caractère en texte réel ; Final Fantasy VI de 4 à 12 dans une cellule de 16 sur 11 ; Cristal en 8 sur 8 fixe
Glyphes japonais 8 à 12 pixels, environ le double d’une lettre latine de la même police. Misaki 8 sur 8 avec une encre de 7 sur 7 ; Émeraude 8 de large dans une ligne de 16 ; la planche de Rouge Feu et PixelMplus10 10 ; Final Fantasy VI et PixelMplus12 12 ; 8 contre 4, 10 contre 5, 12 contre 6 dans chaque police mesurée capable d’afficher le japonais
Longueur de ligne 18 à 41 caractères par ligne ; écrire jusqu’à la largeur plutôt que la remplir. Cristal 18 ; la fenêtre de 216 pixels d’Émeraude en contient 41 à 5,25 pixels, ses scénaristes se sont arrêtés à 38 pour 95 % des lignes ; la ligne de 220 pixels de Final Fantasy VI contient 18 caractères japonais de 12 pixels
Coupures Soit le scénariste coupe chaque ligne, soit le code revient à la ligne par mot ; jamais les deux. Les 24 100 lignes coupées à la main d’Émeraude, aucune au-delà de 208 pixels ; CalcTextWidth et NewLine de Final Fantasy VI
Vitesse Compter les caractères en temps : 60 par seconde au plafond (le réglage rapide d’Émeraude et de Cristal, et la lettre par image de Final Fantasy VI), 15 à 20 pour un réglage moyen, 7,5 à 12 pour le lent. Émeraude 7,5, 15 et 60, 60 en maintien ; Cristal et Rouge 12, 20 et 60 ; Final Fantasy VI 60
Marque de suite Faire bouger la marque sur une horloge lente : un pas toutes les 150 millisecondes, un cycle d’environ 0,6 seconde. Les 0, 1, 2, 1 pixels d’Émeraude, un pas toutes les 9 images, 36 images par cycle ; le ▼ de Cristal qui clignote en alternance avec la tuile de bordure
Cadres Les cadres sont des tuiles, pas des dessins : un coin, un bord et un centre, répétés. Laisser le joueur choisir parmi quelques-uns. Les 20 cadres de menu sélectionnables de 24 sur 24 d’Émeraude et sa bande de dialogue de 14 tuiles au bord inférieur en miroir ; les 8 cadres de Cristal de 6 tuiles d’un bit ; les 8 styles de Final Fantasy VI de 28 tuiles avec 7 couleurs modifiables
Texte net Une police pixel n’est nette qu’avec un nombre entier de pixels de l’appareil par pixel de police et, sur le Mac, seulement lissage des polices désactivé ; capturez un iPhone pour voir ce qui tient là-bas. Core Text sous macOS 27.0, lissage des polices désactivé : 0,0 % de pixels partiels aux multiples entiers d’une grille sur la ligne de base avec des avances en pixels entiers (Misaki, PixelMplus10, Press Start 2P), 19,4 % à 82,2 % entre eux ; avec le lissage par défaut du contexte, PixelMplus10 à ses propres 10 pixels, 178 pixels encrés partiels sur 272
Fichiers propres Vérifier la grille d’une police, son décalage par rapport à la ligne de base et ses avances avant de la choisir. Core Text sous macOS 27.0 : la grille de PixelMplus12 décalée de 0,39 pixel de la ligne de base, qui bave à chaque taille ; l’avance des kana de k8x12, 8,01 pixels, qui bave dès que les glyphes sont placés exactement ; les 10 points égarés de Silkscreen, 1,2 % à 4,5 %
Dynamic Type Respecter la taille de l’utilisateur en l’arrondissant à la grille, jamais par mise à l’échelle continue, et jamais sous 11 points. Un em de 10 pixels est entier à 1 taille Body sur 12 en 3×, un em de 12 pixels à 3, un em de 8 pixels à 2 ; le minimum de 11 points des HIG
Coupures japonaises Garder la règle stricte de début de ligne pour le texte de la boîte ; garder les répliques prédéfinies assez courtes pour ne jamais revenir à la ligne. JLREQ 3.1.7 ; Core Text sous macOS 27.0, sans balise, a gardé la règle stricte pour la liste partielle de début de ligne de la sonde à chaque largeur mesurée, balisé ja a laissé de petits kana commencer des lignes à 6 largeurs sur 7 dans la plupart des polices (4 sur 7 dans k8x12 et k8x12S)
Interlocuteurs Nommer l’interlocuteur dans la réplique, pas dans un champ séparé ni au-dessus de la tête. La boîte d’Émeraude n’a pas de champ de nom et 6,5 % de ses paragraphes commencent par un nom et deux-points
Couverture Couvrir le jeu standard, environ sept mille caractères, avant de pouvoir livrer un texte japonais que les scénaristes n’ont pas figé ; un script figé peut en livrer moins, vérifié chaîne par chaîne. Les 6 879 de JIS X 0208 ; les « around 7,500 » (environ 7 500) du Pixel Remaster ; les 1 248 lettres de dialogue de la ROM japonaise de Final Fantasy VI ; Misaki, k8x12 et DotGothic16 dessinent tout JIS X 0208, PixelMplus tout sauf trois points de code
Licences Ne livrer une police qu’avec le texte de sa licence en main et ses conditions lues. la licence en trois phrases de Misaki, k8x12 et PixelMplus ; la SIL Open Font License de DotGothic16, Press Start 2P et Silkscreen, avec le Reserved Font Name de Press Start 2P
Deux formes Garder les deux formes, une boîte et une bulle. Qui dit quoi est le choix d’un jeu ; celui de Kiradex, le mien, est la boîte pour ce que dit un lieu et la bulle pour ce que dit une personne. Stardew Valley a les deux : sa boîte pour les conversations, ses bulles pour ce que disent les PNJ quand le joueur entre dans un lieu

Chaque chiffre du tableau vient des sections 1 à 8 et y porte sa note.1467921862

10. La méthode Apple : typographie, tailles, coupures de ligne et VoiceOver

Kiradex World est une scène RealityKit utilisée comme moteur de rendu 2D, avec une caméra orthographique à un nombre entier de pixels de l’appareil par texel et SwiftUI par-dessus pour tout ce qu’une personne lit.23 L’article sur le monde a décidé que le texte des dialogues « lives in SwiftUI at UI scale rather than in the world » (vit dans SwiftUI à l’échelle de l’interface plutôt que dans le monde), et cette section garde cette décision et complète les API : ce qu’Apple demande à une typographie personnalisée, comment une police pixel intégrée entre dans l’app, comment Dynamic Type peut être respecté par arrondi, comment le japonais se coupe, et comment VoiceOver peut lire une scène qui n’est qu’une seule vue RealityKit. Chaque disponibilité ci-dessous vient des pages de documentation d’Apple, enregistrées le 5 octobre 2026 ; aucun de ces codes n’a été compilé, et chaque coût de cette section reste non mesuré sur un téléphone.65

Ce qu’Apple demande au texte

La page Typography des Human Interface Guidelines donne à iOS et iPadOS une taille de texte par défaut de 17 points et un minimum de 11, et demande aux apps de « Follow the recommended default and minimum text sizes for each platform » (respecter les tailles de texte par défaut et minimales recommandées pour chaque plateforme), pour les polices personnalisées comme pour celles du système.1060 Les polices système prennent en charge d’elles-mêmes Dynamic Type et des fonctions d’accessibilité comme le texte en gras ; pour le reste, la page dit : « If you use a custom font, make sure it implements the same behaviors. » (Si vous utilisez une police personnalisée, assurez-vous qu’elle reproduit les mêmes comportements.)10

La page Accessibility fixe l’ampleur de la tâche : « Ideally, give people the option to enlarge text by at least 200 percent (or 140 percent in watchOS apps). » (Idéalement, donnez aux gens la possibilité d’agrandir le texte d’au moins 200 %, ou 140 % dans les apps watchOS.)20 La page Games y ajoute la lisibilité : « To keep text comfortably legible on each device, ensure that it contrasts well with the background and uses at least the recommended minimum text size in each platform. » (Pour que le texte reste confortablement lisible sur chaque appareil, veillez à ce qu’il contraste bien avec l’arrière-plan et utilise au moins la taille de texte minimale recommandée sur chaque plateforme.)66

La plage de Body, de 14 à 53 points, va d’environ 82 % à environ 312 % de sa valeur par défaut de 17 ; les tailles arrondies de la boîte en 3×, de 13,33 à 53,33 points, vont de 80 % à 320 % de leur propre valeur par défaut de 16,67, au-delà des 200 % que demande la page Accessibility.601133

Mettre à l’échelle une police personnalisée

Apple propose trois façons de mettre une valeur à l’échelle avec Dynamic Type, et une pour l’en empêcher.

  • UIFontMetrics, « A utility object for obtaining custom fonts that scale to support Dynamic Type » (un objet utilitaire pour obtenir des polices personnalisées qui se mettent à l’échelle afin de prendre en charge Dynamic Type), disponible depuis iOS 11.0, avec scaledFont(for:), scaledFont(for:maximumPointSize:) et scaledValue(for:), qui « Scales an arbitrary layout value based on the current Dynamic Type settings » (met à l’échelle une valeur de mise en page arbitraire d’après les réglages Dynamic Type en cours).67
  • Font.custom(_:size:relativeTo:) dans SwiftUI crée une police personnalisée « that scales relative to the given textStyle » (qui se met à l’échelle par rapport au textStyle donné), depuis iOS 14.0.68
  • ScaledMetric, « A dynamic property that scales a numeric value » (une propriété dynamique qui met à l’échelle une valeur numérique), depuis iOS 14.0.69
  • Font.custom(_:fixedSize:) crée une police personnalisée « that does not scale with Dynamic Type » (qui ne se met pas à l’échelle avec Dynamic Type), depuis iOS 14.0.68

Une police pixel a besoin de la première ou de la troisième, et de la dernière, ensemble : lire la taille qu’utiliserait le système, l’arrondir à la grille de la police, et fixer la taille arrondie comme taille fixe pour que le système ne la remette pas à l’échelle. L’arrondi lui-même est de l’arithmétique, dans le cahier des charges de la section 12 : un pixel de police devient m pixels de l’appareil, m = max(m₀, ⌊size × displayScale / 10 + 0.5⌋) pour un em de 10 pixels, l’entier le plus proche avec une moitié arrondie vers le haut, où m₀, le plus petit m d’au moins 11 points, vaut 4 en 3× et 3 en 2×, et la taille en points vaut 10 × m / displayScale. En 3×, cela fait correspondre les douze tailles Body à des m de 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 et 16 (Small, 15 points, est la seule moitié exacte : 4,5 s’arrondit à 5) : 16,67 points à la taille par défaut, 53,33 à AX5.11

Pour limiter la taille au sein d’une vue, dynamicTypeSize(_:) (iOS 15.0) fixe ou borne la taille Dynamic Type, et sa page conseille : « When limiting the Dynamic Type size, consider if adding a large content view with accessibilityShowsLargeContentViewer() would be appropriate. » (Quand vous limitez la taille Dynamic Type, demandez-vous s’il serait approprié d’ajouter une vue de grand contenu avec accessibilityShowsLargeContentViewer().)70 Ce modificateur, iOS 15.0 lui aussi, porte son propre avertissement : « Don’t use the large content viewer as a replacement for proper Dynamic Type support. » (N’utilisez pas le visualiseur de grand contenu en remplacement d’une véritable prise en charge de Dynamic Type.)71 Le cahier des charges ne limite que les bulles, qui doivent rester sur une ligne, et jamais la boîte.

Intégrer la police

Une police est livrée dans le bundle de l’app et nommée dans UIAppFonts, « App-specific font files located in the bundle and that the system loads at runtime » (des fichiers de police propres à l’app, situés dans le bundle et que le système charge à l’exécution) (iOS 3.2), ou enregistrée dans le code avec CTFontManagerRegisterFontsForURL(_:_:_:) (iOS 4.1).7273

Dessiner le cadre et placer la ligne de base

Un cadre dessiné à partir de quelques tuiles est une image redimensionnée sans lissage. Image.interpolation(_:) de SwiftUI règle la qualité d’interpolation, depuis iOS 13.0, et Image.resizable(capInsets:resizingMode:), qui « Sets the mode by which SwiftUI resizes an image to fit its space » (définit la manière dont SwiftUI redimensionne une image pour qu’elle remplisse son espace), étire ou répète en mosaïque tout sauf les coins, depuis iOS 13.0 également.7475 Le cahier des charges dessine plutôt son cadre dans un Canvas, pour que chaque pixel source fasse exactement m sur m pixels de l’appareil ; selon ma lecture, resizable(capInsets:) avec .tile ferait aussi bien l’affaire seulement si sa mosaïque tombe sur des pixels entiers de l’appareil, ce que personne n’a mesuré.

Text.baselineOffset(_:), qui « Sets the vertical offset for the text relative to its baseline » (définit le décalage vertical du texte par rapport à sa ligne de base) (iOS 13.0), est l’outil pour une police dont la grille est décalée de la ligne de base, comme celle de PixelMplus12 ; la section 6 a mesuré qu’un décalage ne corrige une telle police qu’à une seule taille, ce qui est l’une des raisons pour lesquelles le cahier des charges ne l’utilise pas.767

La coupure des lignes japonaises dans SwiftUI et UIKit

typesettingLanguage(_:isEnabled:) (iOS 17.0) est destiné au Text qui « may contain text of a particular language which doesn’t match the device UI language. In that case it’s useful to specify a language so line height, line breaking and spacing will respect the script used for that language » (peut contenir du texte dans une langue qui ne correspond pas à la langue de l’interface de l’appareil ; il est alors utile de préciser une langue pour que la hauteur de ligne, la coupure des lignes et l’espacement respectent l’écriture de cette langue). Sa page ajoute : « this language does not affect text localization. » (cette langue n’affecte pas la localisation du texte.)77

NSParagraphStyle.LineBreakStrategy d’UIKit (iOS 9.0) a trois stratégies nommées. standard (iOS 14.0) « uses the same configuration of line-break strategies that it uses for standard UI labels » (utilise la même configuration de stratégies de coupure que pour les libellés d’interface standard) et « may be unsuitable for large amounts of text » (peut ne pas convenir à de grandes quantités de texte) ; pushOut (iOS 9.0) sert à éviter « an orphan word on the last line » (un mot orphelin sur la dernière ligne) ; hangulWordPriority (iOS 14.0) concerne le coréen.78

La section 7 a mesuré que Core Text sous macOS, balisé ja, laissait de petits kana commencer des lignes à 6 largeurs sur 7 dans la plupart des polices (4 sur 7 dans k8x12 et k8x12S), et non balisé, non.7 Ce que fait le Text de SwiftUI avec le japonais quand la langue de l’appareil est le japonais, et si typesettingLanguage règle le même attribut que la mesure, n’a pas été mesuré (section 11). Le cahier des charges garde donc le texte de la boîte non balisé, sauf si une capture montre que la règle stricte tient avec la balise.

VoiceOver au-dessus d’une scène

Les modificateurs d’accessibilité de SwiftUI décrivent une vue : accessibilityLabel(_:) la nomme, et accessibilityValue(_:) ajoute sa valeur, avec une règle dans sa documentation : « Use this method to describe the value represented by a view, but only if that’s different than the view’s label. » (Utilisez cette méthode pour décrire la valeur que représente une vue, mais seulement si elle diffère du libellé de la vue.) Les deux sont iOS 16.0 dans la forme LocalizedStringResource que listent les pages.79

La scène d’un jeu a besoin de plus qu’un libellé. accessibilityDirectTouch(_:options:) (iOS 17.0) crée une zone où « Direct touch areas passthrough touch events to the app rather than being handled through an assistive technology, such as VoiceOver » (les zones de toucher direct transmettent les événements tactiles à l’app au lieu qu’ils soient traités par une technologie d’assistance comme VoiceOver), une fois que l’utilisateur l’a activée.80 AccessibilityTraits.updatesFrequently (iOS 13.0) sert à un élément qui change : « Use this trait when you want an assistive technology to poll for changes when it needs updated information. » (Utilisez ce trait quand vous voulez qu’une technologie d’assistance interroge les changements lorsqu’elle a besoin d’informations à jour.)81 AccessibilityNotification.Announcement (iOS 17.0) publie une annonce vocale, avec un attribut accessibilitySpeechAnnouncementPriority qui règle sa place dans la file face aux autres paroles.82 Et accessibilityReduceMotion (iOS 13.0) signale le réglage « Réduire les animations » (Reduce Motion), qui, dit sa page, signifie que « UI should avoid large animations » (l’interface devrait éviter les grandes animations).83

RealityKit peut décrire les entités elles-mêmes. AccessibilityComponent (iOS 17.0) est « A component that stores accessibility information for an entity » (un composant qui stocke des informations d’accessibilité pour une entité), dont le nom est ce que « VoiceOver says aloud when it describes the entity » (VoiceOver prononce quand il décrit l’entité).84 L’article d’Apple « Improving the Accessibility of RealityKit Apps » dit de « set the properties on each visible Entity in your scene » (définir les propriétés de chaque Entity visible de votre scène) : les propriétés d’accessibilité propres à Entity, isAccessibilityElement, accessibilityLabel et accessibilityDescription, que l’article date d’iOS 14 et dont les pages sont désormais marquées comme obsolètes, toutes sauf isAccessibilityElement (la page liste aussi accessibilityLabelKey, un libellé sous forme de clé de chaîne localisée), une voie plus ancienne à côté du composant.85 RealityView, la vue SwiftUI qu’utilise la scène de Kiradex, est iOS 18.0.86 Qu’une RealityView munie d’une caméra orthographique virtuelle, telle que Kiradex la configure, expose ces entités à VoiceOver n’est pas documenté sur ces pages et n’a pas été testé (section 11).

Le tester peut désormais être automatisé. XCUIVoiceOverService « Provides programmatic control of VoiceOver for UI testing » (fournit un contrôle programmatique de VoiceOver pour les tests d’interface), disponible à partir d’iOS 27.0, et s’atteint par la propriété voiceOverService de XCUIDevice.87

Kiradex aujourd’hui

Lu dans le dépôt le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, jamais exécuté ni modifié par moi ; l’app était en cours de modification pendant que je la lisais (section 11) :11

  • Une police, celle du système. La famille de polices du thème est .systemDesign(.monospaced), et les vues du monde utilisent les rôles du kit : caption à 10 points d’appel dans quatre fichiers, label à 3, body à 3 et title à 3.2411
  • Un chemin de police personnalisée qui se met à l’échelle en continu. Kit941.FontFamily.custom(named:) du kit partagé se résout en Font.custom(_:size:relativeTo:) à 34, 22, 17, 13 et 12 points pour display, title, body, label et caption.88 Avec une police pixel, cela passerait par chaque taille Dynamic Type, ce que la section 6 compte comme hors grille à 9 tailles Body sur 12 pour un em de 12 pixels et à 11 sur 12 pour un em de 10 pixels.7
  • La bulle. Text(text).kit941Font(.caption, weight: .bold), encre sur WorldInk.paper (une surface graphique à deux couleurs fixes), avec la petite marge du kit sur les côtés et la très petite en hauteur, une bordure d’encre de 2 points et .fixedSize(), donc sur une ligne. Elle est placée un grand pas d’espacement au-dessus de rig.headPoint, affichée tant que la réplique a moins de 4 secondes, et libellée "Said: \(text)" dans une surcouche dont le test de toucher est désactivé. Cette surcouche se trouve dans la scène, sous les deux surcouches que la scène pose sur toute l’image : des lignes de balayage, une ligne noire à 16 % d’opacité tous les 3 points, et un vignettage.2511
  • La boîte. WorldHUD place le titre du lieu (caption, gras, en capitales) et le statut (caption, demi-gras) dans une boîte crème avec la même bordure de 2 points en bas de l’écran ; à moitié ouvert, sur le Duo, elle se tient du côté le plus large du pli. Elle ne reçoit pas de touchers (.allowsHitTesting(false), « Taps go through to the ground under it », les touchers traversent jusqu’au sol en dessous), et elle est posée sur toute la scène, au-dessus des lignes de balayage et du vignettage.26
  • Ce qui se dit. 23 répliques prédéfinies sur le serveur, de 3 à 40 caractères, une médiane de 16 ; la plus longue est « Anyone have a card from the first years? » (Quelqu’un a une carte des premières années ?). Les pseudos sont deux mots tirés de listes de 20 et 20 et quatre chiffres, 20 caractères au plus (« Glacier Shuffle 0000 »). Rien n’est dessiné au-dessus de la tête d’un collectionneur hormis la bulle ; le pseudo s’affiche dans le panneau qu’ouvre un toucher.288911 L’article de cette série sur la vie propre de la ville a compté les mêmes 23 répliques et une limite serveur d’une réplique toutes les 2,0 secondes par joueur.9091
  • Pas de police, une seule langue. Aucun fichier de police n’est intégré (le seul .ttf du dépôt est scripts/screenshots/MartianMono-latin.ttf, pour l’outil de captures d’écran) ; les knownRegions du projet sont Base et en, et il n’existe aucun fichier .xcstrings ni .lproj.1127
  • VoiceOver entend des coordonnées. La scène est un seul élément, .accessibilityLabel("The plaza") et .accessibilityValue(stepTrace), où stepTrace est le dernier pas sous la forme « x,y facing » (x,y orientation), conservé parce que « the walks read it, since nothing else the simulator logs reaches them » (les tests de marche le lisent, car rien d’autre de ce que journalise le simulateur ne leur parvient). Aucun AccessibilityComponent, accessibilityDirectTouch, AccessibilityNotification, typesettingLanguage, UIFontMetrics, ScaledMetric ni dynamicTypeSize n’apparaît où que ce soit dans le code source de l’app.2511
  • Des panneaux sans lettres. Le sign() de la forge est « A hanging sign on a bracket, 12 × 11 » (un panneau suspendu à une potence, 12 × 11) dont la face porte deux rangées de traits d’encre, pas des lettres ; la carte de la place a une tuile de panneau, S. Un doigt posé sur le sol fait aussitôt marcher le joueur jusqu’à la tuile en dessous et est suivi de tuile en tuile ; un toucher sur un collectionneur ouvre son panneau quand le doigt se lève, et un toucher sur le kiosque le tableau de la ville ; une tuile de panneau est un sol comme un autre, sans toucher propre.2992
  • L’échelle du monde. Selon la règle de PlazaRig.fit, la partie entière de la hauteur en pixels de la vue divisée par 320, un texel fait 8 pixels de l’appareil sur l’iPhone 18 Pro Max (440 points sur 956) et sur l’écran intérieur du Duo en portrait, 6 sur l’écran extérieur du Duo en portrait et sur l’intérieur en paysage, et 4 sur l’extérieur en paysage : 2,67, 2,00 ou 1,33 points.9311

Pourquoi le texte reste hors du monde

L’arithmétique de l’article sur le monde est juste sur ce point : « At k = 8 on a 3× display a five-texel font is 13.3 points and at k = 6 it is 10 points, under the HIG’s 11-point minimum » (à k = 8 sur un écran 3×, une police de cinq texels fait 13,3 points et à k = 6 elle en fait 10, sous le minimum de 11 points des HIG).23 Elle ne contraint que les polices de moins de 5,5 texels à k = 6 (11 points à 2 points par texel) ; un em de 8 texels à k = 6 fait 16 points.1133 La conclusion, du texte à l’échelle de l’interface dans SwiftUI, tient pour une autre raison : à la densité du monde, un em de 10 pixels fait 26,67 points sur un téléphone à 8 pixels de l’appareil par texel, ce qui laisse 29 caractères latins par ligne de la boîte sur le 18 Pro Max.11 Mesurée en tuiles, la boîte du canon est étroite. Une boîte de 16 tuiles matérielles de large, 128 pixels, contient 16 caractères d’une police de 8 sur 8, environ 24 caractères latins d’Émeraude à sa moyenne de 5,25 pixels (21 dans le pire cas de 6), 16 caractères japonais d’Émeraude de 8 pixels et 10 caractères japonais de Final Fantasy VI de 12 pixels. Seize tuiles de 16 texels de Kiradex font 256 texels, qui, à la densité propre du monde, contiennent 42 caractères latins et 21 kana de PixelMplus12, ou 64 et 32 de Misaki.11 Et une boîte mesurée dans les propres tuiles de Kiradex ne tient pas dans trois des cinq vues mesurées : 16 tuiles de 16 texels font 683 points à 8 pixels de l’appareil par texel, plus large que l’iPhone 18 Pro Max (440 points) et que l’écran intérieur du Duo en portrait (669), et 512 points à 6, plus large que l’écran extérieur du Duo en portrait (466). Elle ne tient que dans les deux vues en paysage : 512 points dans les 951 de l’écran intérieur, et 341 points à 4 pixels de l’appareil par texel dans les 678 de l’écran extérieur.11 Une boîte de Kiradex se dimensionne en points, pas en tuiles.

La même arithmétique à la taille du cahier des charges : PixelMplus10 à m = 5, 16,67 points, contient 47 caractères latins ou 23 japonais par ligne dans la boîte sur un 18 Pro Max en portrait, après la marge intérieure actuelle de 48 points, 16 points de marge et 8 de remplissage de chaque côté, comptés depuis la largeur totale, sans marges latérales de zone de sécurité (supposé, non mesuré). La boîte se trouve dans la zone de sécurité ; sur l’écran extérieur du Duo en portrait, le nombre dépend donc de sa marge latérale : 50 ou 25 sans marge, et 40 ou 20 avec la marge de fin de 84 points, le rail latéral, que cette série a mesurée sur l’écran fermé (section 11). Sous le cadre du cahier des charges (section 12, point 3), qui prend 8 pixels de police de chaque côté à la place du remplissage et arrondit la boîte vers le bas à un nombre entier de pixels de police, les mêmes lignes contiennent 45 ou 22 caractères sur le 18 Pro Max, et 48 ou 24, ou 38 ou 19 avec le rail, sur le Duo.119495

Coûts

Rien de tout cela n’a été mesuré sur un appareil. Intégrer une police de 7 253 glyphes ajoute son fichier à l’app ;7 dessiner du texte via SwiftUI avec une police personnalisée coûte ce que coûte n’importe quel Text ; et le cadre du cahier des charges est un Canvas qui se redessine quand sa taille change. Selon ma lecture, rien de cela n’est lourd à côté d’une scène RealityKit, mais c’est une lecture, et les captures du cahier des charges sont l’endroit où elle se vérifie.

11. Ce qui est mesuré et ce qui ne l’est pas

Tout ce qui est chiffré dans cet article vient de scripts qui se relancent sur des sources enregistrées, de pages enregistrées le 5 octobre 2026, ou de lectures datées du code source de Kiradex ; les notes disent lesquels. Cette section est la liste de ce que cela ne couvre pas.30

Mesuré à la source, relançable. measure_gen_text.py sur les quatre décompilations de pret : la table des polices d’Émeraude (dix entrées, sept polices) ; la vitesse du texte en images et en caractères par seconde ; le défilement ; la flèche de suite ; la fenêtre de 27 sur 4 et le cadre de 30 sur 6 ; les 20 cadres sélectionnables ; les largeurs des glyphes latins ; 24 100 lignes de script en caractères et en pixels ; 934 paragraphes sur 14 412 qui nomment un interlocuteur ; les planches de glyphes japonais ; les délais et la fenêtre de Rouge Feu ; les écrans, boîtes, délais, cadres et planches de police de Cristal et Rouge.1 measure_ff6.py sur le désassemblage ff6 : les styles et palettes de fenêtre, la cellule et les largeurs de la police de dialogue, la taille et les séries de largeurs de la police japonaise, la ligne de 220 pixels, la lettre par image, le titre de 100 images, le retour à la ligne par mot, l’écran de configuration et la table de dégradé de la fenêtre.4 measure_bdf.py sur les fichiers BDF de Misaki et de k8x12.6 measure_coretext.swift et measure_outline_coords.swift, compilés et exécutés sous macOS 27.0, sur dix fichiers TrueType et Hiragino Sans.719 measure_kiradex_text.py sur Kiradex tel qu’il se trouvait sur le disque au commit 334fbfe (relancé le 15 octobre sur l’arborescence en cours, ses lignes antérieures inchangées), et measure_figure_width.py sur sa planche de corps.1196 measure_hig.py, measure_steam_languages.py et measure_apple_docs.py sur les pages enregistrées.606465 measure_ios27_iphones.py sur la liste d’iPhone du runtime de simulateur d’iOS 27.0, et probe_line_start_glyphs.swift, compilé et exécuté sous macOS 27.0, sur les tables de classes de JLREQ et PixelMplus10.9798 measure_cell_masks.py sur les planches pret enregistrées : la référence de masques de cellules du cahier des charges.99 Cela fait treize scripts ; un quatorzième, dump_glyphs.swift, rangé avec les figures de cet article, a dessiné PixelMplus10 à sa propre taille avec le lissage des polices activé et désactivé.9

Non mesuré, ou plus fragile qu’il n’y paraît.

  • Core Text sous macOS tient lieu d’iOS. Chaque chiffre de rendu et de coupure de ligne des sections 6 et 7 vient du Core Text de macOS 27.0 dessinant dans un contexte bitmap, lissage des polices désactivé, pas de SwiftUI sur un iPhone. Les vérifications par capture du cahier des charges sont la mesure qui compte ; tant qu’elles n’ont pas tourné, ce que dessine un iPhone reste une prédiction.79
  • Les jeux de bitmaps. Misaki et k8x12 portent des tables EBDT et EBLC ; que Core Text ait dessiné ces bitmaps ou les contours à 8 et 12 pixels n’a pas été établi.7
  • Le placement propre à SwiftUI. Que SwiftUI place les origines de Text sur des pixels entiers de l’appareil, et quelle règle de coupure il applique au japonais quand la langue de l’appareil est le japonais, n’a pas été mesuré.
  • Les graisses. PixelMplus10 Bold est la seule graisse grasse mesurée ; le gras synthétique sur une police pixel ne l’a pas été.7
  • La lisibilité. Les grilles et la couverture sont mesurées ; que des kanji de 10 sur 10 se lisent bien pour un lecteur japonais à 16,67 points ne l’est pas, et demande un lecteur japonais sur un appareil.
  • Les ROM japonaises. pret construit les jeux anglais. La boîte, les vitesses et la police de l’Émeraude japonais lui-même n’ont pas été lues ; les planches japonaises de la section 1 sont celles que porte la ROM anglaise pour les noms. Les versions japonaises de Cristal et Rouge n’ont pas été lues.30
  • Les graphismes de Final Fantasy VI. Le désassemblage contient des plages et du code, pas de pixels ; la disposition des tuiles de la fenêtre et l’aspect du dégradé n’ont pas été vus. La plage de la grande police anglaise, 5 120 octets, n’est pas un nombre entier de lettres de 22 octets, inexpliqué. Que la table de dégradé soit consommée à raison d’une valeur par ligne de balayage vient de commentaires, pas d’un traçage.4
  • La période de clignotement de Cristal n’a pas été déduite ; ses deux états ont été lus dans le code.16
  • Chrono Trigger n’a pas été mesuré du tout. Aucun désassemblage public n’a été cloné, et la carte de la ROM sur Data Crystal a renvoyé HTTP 403.49
  • Celeste, Sea of Stars et Eastward. Seulement leurs listes de langues sur Steam ; la façon dont ils dessinent le texte japonais et chinois n’a été trouvée dans aucune source de développeur.64
  • Pixel Operator, m5x7 et m3x6 n’ont été ni téléchargées ni mesurées, et le dépôt de Pixel Operator a renvoyé HTTP 404 ; leurs licences sont telles que leurs pages les indiquent, celle de Pixel Operator d’après DaFont seul.565758
  • Les trois points de code JIS non mappés de PixelMplus n’ont pas été vérifiés à leurs points de code alternatifs.7
  • La taille de DotGothic16. Elle n’a pas de grille de pixels ; elle a donc été dessinée et composée à 16 points par em nominaux et à des multiples de cette valeur, mon choix d’après son nom et son pas de points ; aucune autre taille nominale n’a été essayée, et ses chiffres des sections 6 et 7 ne valent qu’à ces tailles.719
  • Le lissage des polices. Les rendus de la section 6 excluent tous le lissage des polices ; avec le réglage par défaut du contexte bitmap, une seule police à une seule taille a été dessinée, PixelMplus10 à 10 pixels, 178 pixels encrés partiels sur 272. Les autres polices et tailles n’ont pas été dessinées avec le lissage activé.9
  • L’app a changé pendant que je la lisais. Kiradex était en cours de modification le 5 octobre pendant la rédaction de cet article. Plus tôt ce jour-là, la gestion des touchers de la scène avait été réécrite et ses lignes déplacées de 30 à 144, et trois des fichiers Swift du monde n’étaient encore dans aucun commit ; quatorze minutes après ma lecture, la scène et le rig avaient de nouveau changé. L’article lit le commit 334fbfe avec les modifications non validées de ce jour telles qu’elles se présentaient quand je les ai lues : les fichiers de l’app à 12 h 28, heure du Pacifique (les collectionneurs du serveur et la carte de la ville à 12 h 33), les scripts qui la mesurent à 12 h 34 et 12 h 36. Les notes donnent les lignes de chaque fichier à cette lecture ; le cahier des charges nomme des symboles plutôt que des lignes, et j’ai vérifié à 12 h 42 que chaque symbole qu’il nomme était toujours là.11
  • Les iPhone sur lesquels l’app tourne. La liste de 33 iPhone, dont trois en 2×, est celle du runtime de simulateur d’iOS 27.0, lue dans Xcode sur mon Mac ; la page d’Apple des iPhone que prend en charge iOS 27 n’a pas été récupérée.97
  • Les marges de la zone de sécurité. L’article de cette série sur le simulateur d’iPhone Duo a mesuré l’écran extérieur fermé, large de 466 points, avec un contenu large de 382 points à l’intérieur d’une marge de fin de 84 points, le rail latéral, dans une sonde qui affichait la barre d’état, une barre d’outils et une barre d’onglets.95 Le monde de Kiradex masque la barre d’état et l’indicateur d’accueil et n’affiche aucune barre ; le code place l’interface du monde dans la zone de sécurité et dit que cela la tient à l’écart des caméras et du rail latéral, mais ni le code ni cette mesure ne disent quelle largeur a la marge quand la barre d’état est masquée ; les chiffres du Duo des sections 10 et 12 sont donc donnés des deux façons, sans marge latérale et avec les 84 points mesurés. Les marges latérales des autres iPhone en portrait n’ont pas été mesurées ; les chiffres du cahier des charges pour eux supposent qu’il n’y en a aucune.94
  • L’accessibilité RealityKit dans la configuration de Kiradex. AccessibilityComponent est documenté pour iOS 17.0 ; qu’une RealityView munie d’une caméra orthographique virtuelle expose des éléments d’entité à VoiceOver reste non vérifié.84
  • Les chiffres du cahier des charges. L’arrondi de m, la bulle un cran plus petite que la boîte et tenue sous un plafond que fixe chaque vue, la marge de 32 points, la file de la boîte et ses trois répliques, les 8 pixels de police du cadre à la place du remplissage de la boîte, les pas de marque de 150 millisecondes, quatre cadres et six cellules pour les annonces sont des propositions, pas des faits.

Là où cet article diffère de l’article sur le monde. Les sections 1, 2, 5 et 8 corrigent chacune une affirmation de celui-ci, mesure à l’appui ; son arithmétique des tailles est juste (section 10). Trois faits que cet article reprend des autres articles de la série ont été vérifiés dans l’app : les 23 répliques prédéfinies de la ville correspondent à lines.py ; la note de l’article sur les personnages selon laquelle la bulle prend encore sa place d’après la hauteur de la cellule correspond à PlazaRig.headPoint, qui ajoute la moitié de la hauteur d’image de la planche et 4 à la position du marcheur ; et le personnage sous la bulle est celui que décrit la mise à jour de cet article, haut de 30 texels dans une cellule de 32 sur 40, ce que confirment le manifeste de l’app, rig.py et la planche de corps. Cet article ne donne de largeur que pour son ancien personnage de 26 texels, environ 14 aux épaules ; la section 12 mesure la largeur du personnage actuel sur la planche.90100289396

Sources non atteintes. La carte de la ROM de Chrono Trigger sur Data Crystal (HTTP 403) ; le dépôt de Pixel Operator sur NotABug (HTTP 404) ; les fichiers de police de m5x7 et m3x6 (derrière un téléchargement à prix libre, non tenté) ; et une source de développeur sur le texte japonais et chinois de Celeste, Sea of Stars ou Eastward (introuvable).49565758

12. Le cahier des charges : ce que Kiradex construit et les vérifications à passer

L’app a évolué depuis. Au commit fc9d4876 du 15 octobre, le libellé et la valeur de la scène sont définis dans un modificateur de vue StageAccessibility (PlazaStage.swift, appliqué à la ligne 373, défini à partir de la ligne 520), toujours « The plaza » avec la trace de pas comme valeur, et l’élément est absent quand un lieu recouvre la scène. Les vérifications ci-dessous lisent la version du 5 octobre, et chaque « aujourd’hui » désigne cette version ; le point 8 vaut pour les deux.101

Synthèse de l’auteur. Ce qui suit est ma proposition, tirée des sections 1 à 11 et rédigée comme un différentiel par rapport à Kiradex tel qu’il était le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour dans sa copie de travail (section 11) : du texte système à chasse fixe dans chaque vue du monde, une bulle d’une ligne en caption gras, la boîte WorldHUD en bas, une app en anglais seulement avec des répliques prédéfinies en anglais sur le serveur, un sprite de panneau sans lettres, et une scène que VoiceOver lit comme des coordonnées.2211 La direction de l’article sur le monde tient : le texte est un élément graphique ; les dialogues vivent dans SwiftUI à l’échelle de l’interface, pas dans les texels du monde ; une boîte construite en tuiles ; deux lignes ; une machine à écrire ; une marque de suite.23 Ce qui change, c’est la police, pour la raison que donne la section 5, et l’arithmétique, parce que Dynamic Type doit être arrondi à la grille de la police.7 Rien ici ne dessine un glyphe, un cadre, une marque ou un mot d’une franchise ; tout est dessiné par la forge ou composé dans une police dont la licence est citée à la section 4. Les remarques juridiques sont mes lectures, pas un avis juridique. Rien de tout cela n’a été construit.

Chaque vérification ci-dessous peut être validée ou invalidée par un script, une ligne de journal d’un test ou une capture, sauf une vérification manuelle datée sur un appareil au point 8. Quand le résultat d’une vérification dépend de l’appareil, de la taille du texte, de la réplique qu’elle choisit ou de ce que la boîte affiche par ailleurs, elle les nomme. Le cahier des charges désigne le code de l’app par ses symboles, pas par ses numéros de ligne, parce que le code bougeait pendant que je le lisais ; les notes donnent les lignes telles que je les ai lues, avec le commit et l’heure. Quand la preuve derrière un point est une mesure sur mon Mac et que la vérification tourne sur un iPhone ou son simulateur, c’est la vérification qui tranche, pas le Mac.

1. Une police pour les deux langues : PixelMplus10

Quoi. Intégrer PixelMplus10-Regular.ttf et PixelMplus10-Bold.ttf (d’itouhiro, d’après les M+ BITMAP FONTS du M+ FONTS PROJECT ; licence : M+ FONT LICENSE, https://github.com/itouhiro/PixelMplus/blob/master/misc/mplus_bitmap_fonts/LICENSE_E) via UIAppFonts, avec LICENSE_E dans le bundle, et ajouter aux Réglages, qui n’en ont pas aujourd’hui, une section de remerciements qui nomme itouhiro et le M+ FONTS PROJECT.1872 L’utiliser pour chaque mot dessiné au-dessus du monde : les bulles, la boîte WorldHUD, le texte des panneaux, le tableau des quêtes et les plaques de présentation. Le reste de l’app, le Dex, le scanner et les Réglages, garde la police du système.

Pourquoi celle-ci. Deux polices mesurées réunissent les trois conditions que demandent les règles de la section 9 : le japonais, une grille propre et des avances en pixels entiers. Misaki Gothic et Misaki Gothic 2nd dessinent les 6 879 caractères de JIS X 0208 sur une grille de 8 pixels posée sur la ligne de base, avec 0 sur 848 et 0 sur 860 coordonnées de contour échantillonnées hors grille et des avances de 4 et 8. PixelMplus10 dessine 6 876 des 6 879, chaque kanji des deux niveaux, sur une grille de 10 pixels posée sur la ligne de base, avec 0 coordonnée de contour sur 1 032 échantillonnées hors grille pour la Regular et 0 sur 1 000 pour la Bold, et des avances de 5 pour le latin et 10 pour les kana.7 Le cahier des charges retient PixelMplus10, c’est mon choix : c’est la plus grande des deux, 10 pixels contre 8 pour Misaki, et les kanji de 8 sur 8 de Misaki sont ceux dont la lisibilité pour un lecteur japonais est la plus douteuse (section 11) ; et elle est fournie avec une Bold qui a été mesurée, si bien que le texte en gras n’a besoin d’aucune synthèse. Écartées, chacune avec la mesure qui l’écarte :

  • PixelMplus12 (M+ FONT LICENSE) : sa grille se trouve 0,39 pixel sous la ligne de base, et Core Text sous macOS 27.0 l’a fait baver à chaque taille.7
  • k8x12 (la licence en trois phrases de littlelimit.net, https://littlelimit.net/font.htm#license) : l’avance de ses kana est de 8,01 pixels, et ses lettres latines font 4 pixels de large.717
  • Misaki (la même licence, https://littlelimit.net/font.htm#license) : elle réunit les trois conditions, mais ce sont les plus petits kanji mesurés, 8 sur 8, et ceux dont la lisibilité pour un lecteur japonais est la plus douteuse (section 11).617
  • DotGothic16 (SIL Open Font License 1.1, https://github.com/google/fonts/blob/main/ofl/dotgothic16/OFL.txt) : ses points ne sont sur aucune grille, et Core Text sous macOS 27.0 l’a dessinée avec 37,3 % à 82,3 % de pixels partiels de 16 à 64 pixels.753
  • Press Start 2P (SIL Open Font License 1.1 avec un Reserved Font Name, https://github.com/google/fonts/blob/main/ofl/pressstart2p/OFL.txt) et Silkscreen (SIL Open Font License 1.1, https://github.com/google/fonts/blob/main/ofl/silkscreen/OFL.txt) : pas de japonais.75455
  • m5x7 (sa page : « Creative Commons Zero v1.0 Universal », https://managore.itch.io/m5x7) et m3x6 (sa page : « free to use with attribution » (libre d’utilisation avec mention), aucun champ de licence, https://managore.itch.io/m3x6) : non mesurées ; latines d’après les descriptions de leurs propres pages ; derrière un téléchargement à prix libre.5758
  • Pixel Operator (CC0 1.0 d’après les notes de son auteur sur DaFont, https://www.dafont.com/pixel-operator.font) : non mesurée ; 241 glyphes sur DaFont, bien trop peu pour le japonais.56

Le kit. Le kit partagé de mes apps fait passer chaque style de texte par .kit941Font(role), et son chemin de police personnalisée met une police à l’échelle en continu, ce qui sortirait cette police de sa grille à 11 tailles Body sur 12, la condition dans laquelle Core Text sur le Mac l’a fait baver.887 Le monde est déjà une surface graphique déclarée, WorldInk, avec ses deux couleurs propres.25 Ajouter un modificateur PixelText propre à Kiradex, déclaré comme exception de cette surface graphique avec un commentaire, qui prend un rôle du kit et applique l’arrondi du point 2 ; ne le proposer en amont comme cas de famille du kit qu’une fois livré.

Pas de graisses synthétiques. Le texte en gras utilise PixelMplus10-Bold ; pas de .weight(_:) sur la police pixel, parce que ce que Core Text fait d’une graisse qu’il doit synthétiser n’a pas été mesuré (section 11).

Vérifications. - Un test unitaire, puisque Core Text existe sous iOS, charge les deux fichiers intégrés et vérifie : 1 000 unités par em ; chaque glyphe qu’utilisent les chaînes du monde et les répliques prédéfinies, dans les deux langues, a une avance multiple de 100 unités ; les points de contour des glyphes d’échantillon se trouvent à moins de 4 unités de la grille de 100 unités (0,04 pixel de police, la tolérance qu’utilisait la sonde de la section 4). Une police qui échoue ne peut pas être livrée. - Un test qui vérifie que chaque chaîne que le monde peut afficher (répliques prédéfinies, clés du monde dans le String Catalog, textes des panneaux) correspond à des glyphes de la police, sans police de secours. - Un script de phase de build qui trouve LICENSE_E dans le bundle de l’app construite, et un test d’interface qui trouve le nom de la police, son auteur et sa licence dans la nouvelle section de remerciements des Réglages.

2. Les tailles : Dynamic Type, arrondi à la grille

Quoi. Un pixel de police est toujours un nombre entier m de pixels de l’appareil. Pour un rôle, lire la taille Dynamic Type qu’utiliserait le système (UIFontMetrics(forTextStyle:).scaledValue(for:), ou @ScaledMetric(relativeTo:) sur la taille de base du rôle), arrondir cette taille au centième de point, pour qu’une valeur mise à l’échelle un rien sous une taille de la table, 14,999… pour 15, s’arrondisse comme la taille de la table, puis poser m = max(m₀, ⌊size × displayScale / 10 + 0.5⌋), l’entier le plus proche avec une moitié arrondie vers le haut, où m₀ = ⌈11 × displayScale / 10⌉ est le plus petit m d’au moins 11 points, et pointSize = 10 × m / displayScale, et l’appliquer avec Font.custom(_:fixedSize:) pour que le système ne le remette pas à l’échelle.676968 Le titre et le texte de la boîte passent du rôle caption qu’ils utilisent aujourd’hui au rôle body, si bien qu’en 3× la boîte fait correspondre les douze tailles Body à m = 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 et 16 : 16,67 points à la taille par défaut et 53,33 à AX5.2611 Le plus petit, m₀ = 4 en 3×, fait 13,33 points, au-dessus du minimum de 11 points.1033 En 2×, que le simulateur d’iOS 27.0 liste pour trois iPhone (point 4), m₀ vaut 3, 15 points, et les douze tailles Body donnent 3, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9 et 11 : 15 points à la taille par défaut.9711 Les bulles utilisent un cran de moins, jamais sous m₀, et jamais au-dessus d’un plafond que fixe chaque vue (point 4).

Placement. Une police pixel n’est nette que si sa ligne de base tombe sur un pixel de l’appareil (section 6). Arrondir chaque origine calculée dans la surcouche du monde à la grille des pixels de l’appareil, (x × scale).rounded() / scale, y compris la .position de PlazaStage.bubbles, et donner aux vues de texte des largeurs en pixels entiers de l’appareil, et à la boîte une largeur et une hauteur en pixels entiers de police (point 3).

Pas de ligne. PixelMplus10 monte de 9 pixels de police et descend de 2 ; régler le pas de ligne à 12 pixels de police, une rangée vide entre les lignes, comme les glyphes japonais de 15 pixels et la cellule latine de 16 pixels d’Émeraude se placent dans une ligne de 16 pixels.71 À m = 5, cela fait 20 points par ligne.33

Vérifications. - Un test de fonction pure de l’arrondi pour chaque DynamicTypeSize aux échelles d’affichage 2 et 3 : le résultat est un nombre entier de pixels de l’appareil par pixel de police, jamais sous 11 points, et jamais plus petit pour une taille plus grande. Le test fournit directement à la fonction les tailles Body de la table des HIG, pas des valeurs lues dans scaledValue(for:), dont je n’ai pas vu la sortie exacte : en 3×, les douze donnent les douze valeurs ci-dessus, le 4,5 de Small s’arrondissant à 5, et en 2× les douze valeurs 2× ; chaque taille diminuée d’un milliardième de point donne le même m, grâce à l’arrondi au centième.11 - Des captures de la place avec une bulle et la boîte en Large, xxxLarge et AX3 sur le simulateur de l’iPhone 18 Pro Max, et en Large sur le simulateur de l’iPhone SE (3e génération) en 2×, avec la surcouche des bulles déplacée au-dessus des surcouches de lignes de balayage et de vignettage de la scène (point 4), et un script qui recadre le texte et compte les pixels partiels par rapport aux deux couleurs, encre et papier : 0 dans les boîtes des glyphes, dans la bulle et dans la boîte. Chaque capture a besoin du serveur de la place en marche (KIRADEX_PLAZA=1) et se prend dans les 4 secondes qui suivent la réplique la plus longue du jour dite par le joueur depuis le menu « Say », puisque chaque réplique revient de la salle102 et qu’une bulle ne s’affiche que tant que sa réplique a moins de 4 secondes.25103 Le même script, lancé sur la version actuelle, enregistre la référence qu’il remplace. Le 0,0 % de la section 6 est le Core Text d’un Mac avec le lissage des polices désactivé ; cette capture est la vérification qui décide si un iPhone dessine la même chose.

3. La boîte : un cadre dessiné par la forge, deux lignes, des pages

Quoi. La boîte de WorldHUD garde sa place : en bas, dans la zone de sécurité, et du côté le plus large du pli quand le Duo est à moitié ouvert.26 Son rectangle tracé de 2 points devient un cadre que dessine la forge : frame-paper.png, 24 sur 24 pixels de police, trois tuiles de 8 sur trois (un coin, un bord et un centre), dans les deux couleurs de WorldInk plus une nuance, original pour Kiradex. Une vue PixelFrame le dessine dans un Canvas, chaque pixel source étant un carré de m sur m pixels de l’appareil : les tuiles de coin aux quatre coins, et les tuiles de bord et de centre répétées vers l’extérieur depuis le coin supérieur gauche, la dernière tuile coupée là où elle rencontre le côté opposé, pour que les pixels du cadre soient les pixels du texte. Pour que chaque pixel source soit un carré entier, la largeur de la boîte est la largeur de la zone de sécurité, là où WorldHUD la dispose aujourd’hui, moins 16 points de chaque côté, arrondie vers le bas à un nombre entier de pixels de police, un multiple de m pixels de l’appareil, et sa hauteur est elle aussi un nombre entier de pixels de police : sur le 18 Pro Max en portrait à m = 5, sans marges latérales (supposé, non mesuré), 1 224 pixels de l’appareil, soit 244,8 pixels de police, arrondis vers le bas à 1 220, 244 pixels de police.1194 Le texte se trouve à l’intérieur du cadre, à 8 pixels de police du bord de la boîte de chaque côté, à la place des 8 points de remplissage actuels (mon choix), ce qui laisse là 228 pixels de police par ligne, 45 caractères latins ou 22 pleine chasse.11 Le titre du lieu occupe sa propre ligne en haut de chaque page ; en dessous, le texte affiche deux lignes à la fois, comme dans Émeraude et Cristal.1 Un texte de plus de deux lignes est découpé en pages, chaque page attendant sous la marque de suite (point 6) ; quand une page se termine sur une espace, l’espace est supprimée. Le box de WorldHUD (dans WorldHUD.swift) reçoit les pages à la place de son unique chaîne de statut.

La file. Mes règles, pour que ce que montre la boîte ne dépende jamais de la longueur du statut :

  • Les pages du statut appartiennent à la boîte. Quand le statut change (sur la place, quand quoi que ce soit y change : l’état de la connexion, le nombre de présents, le moment de la journée, la saison, les rubans gagnés), ses pages sont remplacées et la boîte affiche le nouveau statut depuis sa première page, sauf si une page relevant de la règle suivante est affichée.
  • Le texte d’un panneau (point 5) et une réplique dite à proximité aux grandes tailles (point 4) sont des pages placées avant le statut : affichées aussitôt, chacune après toute page de ce type déjà en attente, dans leur ordre d’arrivée, et, après la dernière, de nouveau le statut depuis sa première page. La boîte garde les pages d’au plus les trois répliques les plus récentes ; quand une quatrième arrive, la plus ancienne encore en attente qui n’est pas du joueur lui-même est supprimée (la plus ancienne du joueur seulement quand chaque réplique en attente est la sienne), si bien qu’un joueur qui dit une réplique ne la perd jamais. Un nouveau panneau remplace les pages d’un panneau encore en attente.
  • Le texte de statut garde le statut entier comme libellé d’accessibilité, quelle que soit la page affichée, comme aujourd’hui, pour que VoiceOver le lise en entier, et que les tests d’interface existants qui lisent plaza-status, room-status et hall-status (une locution d’ouverture, un nombre de présents, les premiers mots d’une pièce) lisent ce qu’ils lisent maintenant. La page affichée, pour la partie déjà révélée, est la valeur d’un élément masqué, plaza-page sur la place (<name>-page, nom de mon choix). Il contient les lignes de texte de la page sans le titre du lieu, et il n’existe que sous l’argument de lancement des tests de marche, -worldTrace (point 8) ; les vérifications ci-dessous le lisent, et chacune passe -worldTrace.103

Les cadres comme choix. Émeraude laisse le joueur choisir le cadre de ses menus parmi 20 (sa boîte de dialogue garde un seul cadre), et Final Fantasy VI sa fenêtre parmi 8 aux couleurs modifiables.14 Proposer quatre cadres dessinés par la forge dans les Réglages, tous gratuits, les trois premiers dès le départ et le quatrième à un niveau donné ; aucun cadre n’est jamais vendu. Qu’un cadre devienne un jour une récompense pour les soutiens est une décision que je n’ai pas prise.

Vérifications. Une vérification de la forge selon laquelle frame-paper.png et chaque variante font 24 sur 24, n’utilisent que des index de palette et ont des bords opposés identiques là où le dessin le prévoit ; sur le simulateur de l’iPhone 18 Pro Max, une capture de la boîte à m = 4 (xSmall) et m = 5 (la taille par défaut), dans laquelle un script trouve que la largeur et la hauteur de la boîte en pixels de l’appareil sont des multiples de m et compare son cadre, les 8 pixels de police extérieurs de chaque côté, pixel par pixel avec un cadre qu’il compose lui-même à partir du PNG à la taille de la boîte capturée selon la même règle (coins aux coins, tuiles de bord et de centre répétées depuis le coin supérieur gauche et coupées au côté opposé), chaque pixel source formant un carré de m sur m, en laissant de côté tout pixel que le PNG laisse transparent, là où le monde transparaît ; et un test d’interface sur le même simulateur à la taille par défaut, sans le serveur de la place, pour qu’aucune réplique ne soit dite, lancé avec -worldTrace et -worldBoxSample et un statut de trois lignes de 30 à 40 caractères jointes par des retours à la ligne, chacune dans les 45 caractères que contient une ligne à cet endroit, qui attend que plaza-page soit égal aux deux premières lignes, espaces mis à part, puis seulement alors, la première page entière, touche la boîte et trouve la troisième, avec une capture de la première page dans laquelle un script trouve la marque en bas à droite de la boîte.11 -worldBoxSample est un argument de test de mon invention, lu dans PlazaView comme l’est -plazaTell (son tellOnArrival) et transmis au WorldHUD comme statut à la place de celui de la place.92

4. Les bulles : une ligne, pas de nom, les deux langues

Quoi. Garder la forme de la bulle : crème, une bordure d’encre, au-dessus de la tête, moins de 4 secondes.25 La composer dans la police pixel au m de la bulle, avec les pixels de coin du cadre au lieu d’un rectangle tracé, dessinés à l’intérieur des 8 points de remplissage de chaque côté de la bulle, pour que, comme la bordure actuelle, ils n’ajoutent aucune largeur. Pas de nom dans la bulle ni au-dessus : l’interlocuteur est la tête qu’elle surplombe, et VoiceOver le nomme (point 8). Une réplique prédéfinie ne revient jamais à la ligne ; .fixedSize() reste.

Au-dessus des lignes de balayage. Aujourd’hui, les bulles sont une surcouche à l’intérieur de la scène, sous ses lignes de balayage, une ligne noire à 16 % d’opacité tous les 3 points (en 3×, une bande de 3 pixels de l’appareil tous les 9), et sous son vignettage, alors que la boîte est posée sur toute la scène.2526 Sous ces couches, le papier d’une bulle est assombri par bandes à travers ses glyphes, si bien que son texte n’est fait d’encre et de papier à aucune taille. Le cahier des charges déplace la surcouche des bulles au-dessus des surcouches de lignes de balayage et de vignettage, là où se trouve déjà la boîte, plutôt que de limiter la vérification du point 2 à la boîte : c’est mon choix, tiré de la règle de la section 6 selon laquelle une police pixel n’est nette qu’en pixels entiers de l’appareil faits d’encre et de papier, appliquée de la même façon aux deux formes de parole. Dans PlazaStage.swift, le .overlay des bulles, le dernier modificateur à l’intérieur du GeometryReader de la scène, passe après le .overlay du vignettage (le RadialGradient), avec un GeometryReader propre pour la taille de la vue.25

À l’écran. Une bulle est aujourd’hui centrée au-dessus de la tête de son interlocuteur, sans rien qui la maintienne à l’écran ; un interlocuteur près d’un bord en pousse donc une partie hors de l’écran.25 Contraindre le cadre de la bulle à la zone de sécurité moins 16 points de chaque côté, la marge que laisse la règle de largeur ci-dessous, en ne l’écartant du centre au-dessus de la tête que dans la mesure où la contrainte l’exige. La zone de sécurité, pas la vue : la scène ignore la zone de sécurité (.ignoresSafeArea()), si bien que sa propre géométrie passe sous tout ce que la zone de sécurité laisse libre, ce qui, sur le Duo, désigne les caméras et le rail latéral, comme le dit le commentaire de WorldHUD à propos de sa propre place dans la zone de sécurité.94 Rien ne transmet aujourd’hui ses marges à la scène. PlazaView les lit en dehors de la scène et les lui transmet, comme le fait déjà CardViewer pour sa carte (proxy.safeAreaInsets transmis à CardStage) ; la contrainte est une fonction pure, BubblePlacement.frame(head:size:view:insets:), appelée depuis PlazaStage.bubbles(of:in:), et les mêmes marges fixent le plafond de la bulle ci-dessous (les noms sont de moi). Le monde masque la barre d’état et l’indicateur d’accueil (.statusBarHidden() et .persistentSystemOverlays(.hidden) sur sa vue racine) ; que cela réduise les marges, le code ne le dit pas ; l’app les lit donc plutôt que de les supposer.94

La règle de largeur. Chaque réplique, dans les deux langues, tient dans une bulle d’une ligne sur chaque iPhone où l’app peut tourner quand la zone de sécurité n’a pas de marges latérales ; c’est ce que garantit la borne du serveur ci-dessous. Une marge peut laisser trop peu de place, même pour m₀. Pour la réplique la plus longue du jour, parmi toutes les largeurs que liste le simulateur d’iOS 27.0, c’est le cas de chaque largeur 3× jusqu’à 393 points et des deux largeurs 2× avec la marge de 84 points du Duo, ainsi que des téléphones 2× de 375 points avec 16 points de chaque côté. Une réplique à la borne du serveur échoue dans davantage de cas : une japonaise aussi sur les téléphones de 402 points avec la marge du Duo (286 points de place contre 296 à m₀), et une latine là aussi (302,67), sur le téléphone 3× de 414 points avec la marge (298) et sur les téléphones de 360 points avec 16 points de chaque côté (296) : sur les 36 cas formés par douze largeurs et trois marges, 7 pour la réplique actuelle, 8 pour une réplique japonaise à la borne et 10 pour une latine. Là, une telle réplique n’a pas de bulle et part dans la boîte, tandis que les répliques plus courtes gardent la leur (ci-dessous).11 L’app est construite pour iPhone seulement, à partir d’iOS 27.0, et le runtime de simulateur d’iOS 27.0 installé avec Xcode sur mon Mac liste 33 iPhone : le plus étroit en 3× fait 360 points de large (l’iPhone 12 mini et le 13 mini), et trois sont en 2×, les iPhone SE de 2e et 3e génération à 375 points et l’iPhone 11 à 414.97 C’est la liste des appareils que simule le SDK ; je n’ai pas récupéré la liste d’Apple elle-même des iPhone que prend en charge iOS 27. La place d’une bulle est la largeur de la zone de sécurité moins 32 points, les 16 de chaque côté que garde la contrainte. Sur les téléphones en portrait, sans marges latérales (supposé ; aucune n’a été mesurée), cela fait 408 sur l’iPhone 18 Pro Max, la plus étroite des cinq vues que cette série a mesurées, mais 328 sur un téléphone de 360 points et 343 sur les téléphones 2× de 375 points. Sur l’écran extérieur du Duo en portrait, c’est 434 sans marge latérale, et 350 si le monde garde la marge de fin de 84 points, le rail latéral, que cette série a mesurée sur l’écran fermé (section 11).11979533 La réplique la plus longue du jour, « Anyone have a card from the first years? », de 40 caractères, mesure en bulle 283 points de large à m = 4, 349 à 5, 416 à 6 et 549 à 8 en 3×, ses glyphes plus les 8 points de remplissage actuels de chaque côté ; la bordure de 2 points est dessinée à l’intérieur de la bulle et n’ajoute aucune largeur. En 2×, la même réplique fait 316 points à m = 3 et 416 à 4.1125 Le m de la bulle a donc un plafond sur chaque vue, le plus grand auquel la plus longue réplique prédéfinie, dans l’une ou l’autre langue, tient dans la place. Pour les répliques actuelles, toutes en anglais, il vaut 5 sur le 18 Pro Max (349 dans 408) et sur chaque iPhone 3× listé de 390 à 440 points, 4 sur les téléphones 3× de 360 et 375 points, et 3, le plus petit que permet le 2×, sur les téléphones 2× ; sur l’écran extérieur du Duo en portrait, il vaut 6 sans marge latérale (416 dans 434) et 5 avec le rail mesuré (349,33 dans 350). Une réplique japonaise à la borne du serveur ci-dessous, 21 caractères pleine chasse, 210 pixels de police d’avances, abaisserait celui du Duo à 5 et 4 et celui des téléphones de 390 et 393 points à 4 (sept des iPhone listés font 390 points de large et quatre 393), et laisserait les autres tels quels.1197 L’app calcule le plafond à partir de la largeur sûre que lui transmet PlazaView et des répliques qu’envoie le serveur, si bien qu’une zone de sécurité plus étroite ou une réplique plus longue, japonaise notamment, peut l’abaisser. Le plafond est une valeur unique pour toutes les répliques ; ce qui se passe là où il n’y en a pas se décide réplique par réplique, c’est mon choix : là où même m₀ ne loge pas la plus longue réplique, une réplique qui tient à m₀ garde sa bulle à m₀, et une réplique qui ne tient pas n’a pas de bulle et part dans la boîte, en pages qui lui sont propres, comme aux grandes tailles (ci-dessous). Garder la bulle de cette réplique à m₀ casserait la contrainte : sur une vue de 360 points avec une marge de fin de 84 points, la place est de 244 points, et la réplique la plus longue du jour fait 282,67 points à m₀.11 Le serveur, de son côté, borne chaque réplique à ce qui tient sur le téléphone le plus étroit de chaque échelle à la plus petite taille, sans marges latérales : au plus 218 pixels de police d’avances, 43 caractères latins ou 21 pleine chasse, le plus petit de (360 − 48) × 3 ÷ 4 = 234 en 3× et (375 − 48) × 2 ÷ 3 = 218 en 2×. Une réplique japonaise à cette borne fait 296 points en bulle à m = 4 en 3×, et 331 à m = 3 en 2× ; une latine fait 302,67 et 338,50 ; les deux tiennent dans les 350 du Duo avec le rail.1133 Les marges latérales des téléphones en portrait n’ont pas été mesurées ; les captures ci-dessous, qui vérifient que la bulle reste dans la zone de sécurité, sont l’endroit où une marge se révélerait.

Barres horizontales de la largeur de la plus longue réplique prédéfinie en bulle d'une ligne en PixelMplus10 en 3×, face à des lignes pointillées pour la place d'une bulle, la largeur de la zone de sécurité moins 32, les téléphones étant supposés sans marges latérales en portrait : 283 points à m 4, dans 328 points, la place d'un iPhone de 360 points ; 349 à m 5, au-delà de 328 mais dans 350, l'écran extérieur de l'iPhone Duo en portrait avec son rail de 84 points, et dans 408, celle de l'iPhone 18 Pro Max ; 416 à m 6, au-delà de 408 mais dans 434, l'écran extérieur du Duo sans marge latérale ; 549 à m 8, au-delà des quatre.

La plus longue réplique prédéfinie en bulle, selon la taille, en 3×, face à la place de chaque vue ; dessiné par figures/make_figures.py à partir de measure_kiradex_text.out.

Texte plus grand. Le m de la bulle suit celui de la boîte, un cran en dessous, jamais sous m₀ et jamais au-dessus du plafond de la vue : sur le 18 Pro Max, il atteint 5 quand la boîte est en xLarge (m = 6), et à partir de xxxLarge, où le m de la boîte vaut 7 ou plus, il reste à 5. Partout où le plafond maintient la bulle deux crans ou plus sous la boîte (sur le 18 Pro Max à partir de xxxLarge, sur un téléphone de 360 points à partir de xLarge), le lecteur reçoit aussi dans la boîte chaque réplique dite à six cellules ou moins du joueur, en pages qui lui sont propres à la taille de la boîte : le pseudo de l’interlocuteur, deux-points et la réplique, comme Émeraude nomme un interlocuteur dans la ligne (le point 3 la découpe en pages ; le point 8 l’annonce). PlazaView, qui détient les répliques de la salle et compose aujourd’hui le statut de la boîte, compose ces pages et les place avant le statut, selon la file du point 3 : affichées aussitôt, après les pages de réplique déjà en attente, trois répliques gardées au plus. Il place les pages d’une réplique dans la file de la boîte dans la même mise à jour qui ajoute sa bulle, si bien que la bulle d’une telle réplique à l’écran signifie que ses pages sont en file. Elles ne tiennent pas toujours sur une page. Un pseudo fait de 14 à 20 caractères, donc « handle: line » de 19 à 62, et sous le cadre du point 3, une ligne de la boîte sur le 18 Pro Max contient 31 caractères latins en xxxLarge, 17 en AX3 et 12 en AX5 : sur chaque pseudo que peut produire names.py et chaque réplique prédéfinie, une ou deux pages de xxxLarge à AX3 et jusqu’à trois en AX4 et AX5, et jusqu’à quatre sur les téléphones de 360 points et 2× aux plus grandes tailles et sur l’écran extérieur du Duo, avec le rail mesuré, en AX4 et AX5.1189 La boîte elle-même n’est jamais limitée ; elle revient à la ligne et se découpe en pages. La page de dynamicTypeSize(_:) suggère une vue de grand contenu pour une taille limitée ; le cahier des charges ne s’y fie pas, parce que la page de ce visualiseur met elle-même en garde contre son usage « as a replacement for proper Dynamic Type support » (en remplacement d’une véritable prise en charge de Dynamic Type), et qu’aucune des deux pages ne dit s’il s’ouvre depuis une surcouche dont le test de toucher est désactivé, comme l’est celle des bulles.707125 Mes notes de recherche avaient proposé d’arrêter la bulle à m = 8, mais la réplique la plus longue fait 549 points de large à 8 (les notes donnent 553, en comptant la bordure qui se trouve à l’intérieur de la bulle), plus large que le 18 Pro Max (440 points) et que l’écran extérieur du Duo en portrait (466), et les notes mesuraient la limite par rapport à l’écran extérieur du Duo alors que le 18 Pro Max, à 440, est le plus étroit ; le cahier des charges suit ici les largeurs.2211

Vérifications. - Le test de police de l’app (point 1) vérifie aussi qu’une table des avances de PixelMplus10 versionnée dans le dépôt, une entrée par caractère qu’utilisent les répliques prédéfinies dans l’une ou l’autre langue, correspond à la police intégrée. La suite de tests du serveur, qui n’a pas d’analyseur de polices (ses dépendances sont FastAPI, uvicorn, websockets, httpx et pytest), lit cette table, refuse une réplique contenant un caractère absent de la table, et refuse une réplique, dans l’une ou l’autre langue, dont les avances dépassent au total 218 pixels de police, la borne ci-dessus.10411 - Un test unitaire du plafond, fonction pure de la largeur sûre, de l’échelle d’affichage et des avances des répliques : en 3×, avec la réplique la plus longue du jour, il donne 5 pour chaque largeur listée de 390 à 440 points, 4 pour 360 et 375, 6 pour 466 et 5 pour 382 (l’écran extérieur du Duo, sans et avec le rail mesuré), et en 2× 3 pour 375 et 414 ; avec une réplique japonaise de 210 pixels de police parmi les répliques, 5 pour 466, 4 pour 382 et 4 pour 390 et 393 ; et aucun pour une place de 244 points (360 points moins la marge de 84 points et 32) en 3× avec la réplique la plus longue du jour ; et un test du compositeur de PlazaView qui, dans cette place, ne dessine pas de bulle pour la réplique la plus longue du jour et met ses pages dans la boîte, et dessine « Hi! » en bulle à m₀ (36 points de large, dans 244).11 - Un test unitaire de BubblePlacement place la tête d’un interlocuteur à chaque bord, chaque coin et au centre de vues de 360, 375, 440 et 466 points de large, chacune sans marges de zone de sécurité, avec 16 points sur les côtés de début et de fin, et avec la marge de fin de 84 points du Duo, et des bulles de plusieurs largeurs jusqu’à la largeur sûre de ce cas moins 32 (de 244 points, 360 moins 84 et 32, à 434), et trouve chaque bulle dans la zone de sécurité moins 16 points de chaque côté et, partout où le x de la tête se trouve dans cette bande, couvrant le x de la tête.11 - Sur les simulateurs de l’iPhone 18 Pro Max, de l’iPhone 13 mini et de l’iPhone SE (3e génération), et sur l’écran extérieur de l’iPhone Duo dans son simulateur, quatre captures chacun (anglais et japonais, chacun à la taille par défaut et en AX3), avec le serveur de la place en marche (KIRADEX_PLAZA=1) et l’app lancée avec -worldTrace, chacune avec la réplique la plus longue de cette langue dite par le joueur ; dans chacune, un script trouve la bordure d’encre de la bulle et vérifie qu’elle fait une ligne de texte de haut, n’est pas plus large que la largeur de la zone de sécurité moins 32 points, et se trouve dans la zone de sécurité moins 16 points de chaque côté. Le script prend les marges dans le test d’interface qui fait la capture, lequel les lit dans un élément masqué qu’active -worldTrace (point 8), où PlazaView écrit les marges qu’il a lues (ma conception) ; le script trouve aussi le m de la bulle d’après les pixels de ses glyphes et vérifie qu’il est le plus petit entre le m de la boîte moins un (jamais sous m₀) et le plafond pour cette largeur sûre, calculé à partir des répliques de la version dans les deux langues, si bien qu’il n’égale le plafond que là où le plafond le limite. À la taille par défaut, cela fait 4 sur les téléphones 3× et le Duo, quel que soit le plafond, et 3 sur le téléphone 2× ; en AX3, c’est le plafond, et sur le Duo ce sont les marges qui décident : avec les répliques anglaises actuelles, 6 sans marge latérale et 5 avec le rail, et avec une réplique japonaise à la borne du serveur, 5 et 4.119597 - Un test d’interface sur le simulateur de l’iPhone 18 Pro Max en AX3, où le m de la boîte vaut 12 et une ligne contient 17 caractères latins, a besoin du serveur de la place (KIRADEX_PLAZA=1) et d’aucun autre joueur, et se lance avec -worldTrace et la vitesse du texte réglée sur Instant (point 6) via la clé world.textSpeed que nomme le point 5, pour que chaque page s’affiche entière dès qu’elle s’ouvre et qu’un toucher sur la boîte tourne exactement une page. Il lit le pseudo du joueur dans l’onglet « Me », choisit « Hi! » dans le menu « Say » et attend, dans les 5 secondes qu’accorde le test de compagnie existant, la bulle Said: Hi!, pour que la réplique soit revenue de la salle et que, selon la règle du point 4, ses pages soient dans la file de la boîte, avant le statut, quelle que soit la longueur du statut. Une réplique des quatre collectionneurs de la ville, qui en disent chacun une toutes les 20 secondes au plus, peut se trouver devant ; la file garde au plus trois répliques et ne supprime jamais l’unique réplique d’un joueur (point 3), si bien qu’au plus deux répliques se trouvent devant, d’au plus deux pages chacune à cet endroit, et que les répliques dites plus tard attendent derrière. Le test lit plaza-page avant chaque toucher et ne touche la boîte que tant que la page n’est pas le pseudo, deux-points, une espace et « Hi! », à une seconde d’intervalle et quatre fois au plus, et passe si une lecture trouve cette page, la dernière lecture ayant lieu une seconde après le quatrième toucher. Il nomme cette réplique parce que « handle: Hi! » tient sur une page à cet endroit pour chaque pseudo que peut produire names.py, alors que la réplique la plus longue peut en prendre deux. Il journalise chaque page qu’il a lue.11103102

5. Panneaux et noms

Panneaux. Le graphisme des panneaux reste sans lettres, tel que le dessine kit.py, pour qu’un seul atlas serve les deux langues.29 Un toucher sur une tuile de panneau ouvre la boîte WorldHUD avec le texte du panneau, en pages placées avant le statut (point 3), au lieu d’y faire marcher le joueur comme aujourd’hui. La scène lit aujourd’hui les touchers par un DragGesture : touched(_:in:) fait aussitôt marcher le joueur jusqu’au sol sous un doigt, sauf au-dessus d’un collectionneur ou du kiosque, qui attendent que le doigt se lève, et lifted(_:in:) lit ce lever comme un toucher. Une tuile de panneau rejoint le collectionneur et le kiosque dans les deux : elle attend le lever, et lifted(_:in:) appelle pour elle une closure onSign comme il appelle onKiosk pour le kiosque (le nom est de moi).92 Les textes des panneaux vivent avec la carte : town.json reçoit signs: { "7,10": { "en": ..., "ja": ... } }, indexé par cellule, et les vérifications de la forge refusent une cellule de panneau qui n’a pas les deux langues.

Noms. Pas d’étiquettes de nom permanentes au-dessus des têtes. La boîte du canon nomme son interlocuteur dans la ligne, Émeraude dans 6,5 % de ses paragraphes.1 Les pseudos de Kiradex vont jusqu’à 20 caractères ; à la plus petite taille de la bulle, m = 4 en 3× et 3 en 2×, un pseudo de 20 caractères fait 133 points de large en 3× et 150 en 2× ; en 3×, il surplombe un personnage large de 16 texels face à la caméra et de 13 de profil (30 de haut, dans une cellule de 32 sur 40), soit 42,7 points, ou 34,7, sur le 18 Pro Max.119610033 Un pseudo s’affiche dans le panneau qu’ouvre un toucher, comme maintenant, et VoiceOver le lit (point 8).

Vérifications. Un test d’interface ne peut pas savoir où, à l’écran, la cellule du panneau est dessinée, puisque la caméra suit le joueur ; le test lance donc la place avec -worldTrace et -plazaTapCell 7,10, un argument de test de mon invention, lu dans le body de PlazaStage comme l’est -plazaFacingDemo (dans un .task), qui fait passer la cellule par le même traitement, extrait de lifted(_:in:) dans une méthode. Sur le simulateur de l’iPhone 18 Pro Max à la taille par défaut, sans le serveur de la place, pour qu’aucune réplique ne soit dite, le test passe aussi le -worldBoxSample du point 3 avec un statut d’une page, « Sample. », et la vitesse du texte réglée sur Instant (point 6) via sa clé de Réglages dans les arguments de lancement (la clé, world.textSpeed, est de moi, à côté du world.stick du joystick). Il attend jusqu’à 5 secondes que plaza-page passe de « Sample. » à la première page du panneau, puis la lit, touche la boîte et relit, au plus un toucher par mot du texte du panneau (par caractère, en japonais), plus un, jusqu’à ce que plaza-page affiche « Sample. », et trouve que les pages précédentes, jointes dans l’ordre, égalent le texte anglais du panneau, espaces mis à part, si bien qu’un texte de panneau assez long pour se découper en pages passe aussi bien qu’un texte court ; relancé avec le japonais comme langue de l’app, le texte japonais.92 La vérification de la forge refuse un town.json dont une cellule S manque de l’une ou l’autre langue.

6. Vitesse du texte et marque de suite

Quoi. Un affichage façon machine à écrire dont le décompte est du temps, pas des images : shown = floor(elapsed × cps), une fonction pure du temps écoulé, de la vitesse et de deux réglages, « Réduire les animations » (Reduce Motion) et VoiceOver actif, pour que 60 et 120 hertz affichent à la même vitesse. Les Réglages proposent Slow, 12 caractères par seconde, le lent de Cristal ; Normal, 30, la valeur par défaut, mon choix entre les réglages moyens du canon (15 pour Émeraude, 20 pour Cristal) et son réglage rapide de 60 ; et Instant. Un toucher pendant l’affichage termine la page ; un toucher sur une page terminée la tourne.1 Pour cela, la boîte reçoit les touchers tant qu’elle affiche ou qu’elle a une page après celle qui est montrée ; sur sa dernière page, affichée entière, elle laisse passer les touchers jusqu’au sol, comme elle le fait tout le temps aujourd’hui.26 La même vitesse dans les deux langues, comme Final Fantasy VI affiche les deux à une lettre par image.4 Avec « Réduire les animations » activé, ou VoiceOver actif, la fonction renvoie aussitôt la page entière ; c’est mon choix, d’après la phrase de la documentation selon laquelle, avec « Réduire les animations », « UI should avoid large animations » (l’interface devrait éviter les grandes animations).83

La marque. Une marque propre à Kiradex, dessinée par la forge, un chevron pointant vers le bas de 5 pixels de police sur 3, en bas à droite de la boîte, qui descend de 0, 1, 2 et 1 pixels de police (le sens est mon choix ; celui d’Émeraude, selon ma lecture, monte) toutes les 150 millisecondes, un cycle de 600 millisecondes : le pas de 9 images d’Émeraude à 60 hertz.133

Vérifications. Un test unitaire de la fonction d’affichage à 30 caractères par seconde : 0 caractère à t = 0 et 30 à 1,000 seconde, le temps écoulé étant calculé à partir d’un nombre de ticks, les ticks divisés par la fréquence, à 60 et à 120 ticks simulés par seconde, et non additionné à partir de deltas par tick (120 deltas de 1/120 additionnés en double précision donnent 0,9999999999999989, qui, multiplié par 30, donne 29,999999999999968 et s’arrondit par défaut à 29) ; la même fonction, avec « Réduire les animations » activé, et séparément avec VoiceOver actif, renvoie la page entière à t = 0 ; un test d’interface sur le simulateur de l’iPhone 18 Pro Max à la taille par défaut et en Slow, sans le serveur de la place, lancé avec -worldTrace et le statut -worldBoxSample du point 3, dont la première page compte deux lignes de 30 à 40 caractères, au moins 60 caractères et donc au moins 5 secondes d’affichage à 12 par seconde, qui vérifie qu’un toucher sur la boîte une seconde après le début de l’affichage fait de plaza-page la page entière en moins d’une demi-seconde, moment où l’affichage seul montrerait 18 caractères (12 au moment du toucher) ; un test unitaire du décalage de la marque en fonction du temps écoulé, 0, 1, 2, 1 et 0 pixels de police à 0, 150, 300, 450 et 600 millisecondes ; et un enregistrement d’écran de deux secondes de la marque sur le simulateur, dans lequel ses décalages, répétitions retirées, suivent 0, 1, 2, 1 dans un ordre cyclique.33

7. Le japonais

Quoi. Le japonais d’abord pour le texte du monde, le reste de l’app plus tard. server/app/lines.py garde l’ordre de ses index, comme le dit son commentaire, « add at the end only » (n’ajouter qu’à la fin), et chaque réplique devient une chaîne anglaise et une chaîne japonaise ; le serveur envoie les deux, et chaque client affiche la langue que lit son utilisateur, si bien qu’une réplique dite sur une place mixte se lit en deux langues à partir d’un seul index.28 C’est, selon ma lecture, le meilleur argument en faveur des répliques prédéfinies dans cette app. Les chaînes du monde, les titres et statuts de WorldHUD et les titres de quêtes, passent dans un String Catalog avec ja ; les textes des panneaux restent avec la carte (point 5). Chaque chaîne japonaise est écrite ou relue par une personne qui parle japonais ; aucune n’est livrée traduite automatiquement et non relue.

Coupure des lignes. Les répliques prédéfinies ne reviennent jamais à la ligne (point 4). Le texte de la boîte est écrit pour tenir en deux lignes par page à la taille par défaut, comme le script d’Émeraude a été coupé à la main.1 Là où il doit revenir à la ligne, garder la règle stricte : ne pas baliser le texte de la boîte avec une langue de composition ja sauf si une capture montre que la règle stricte tient avec, parce que Core Text sous macOS, balisé ja, laissait de petits kana commencer des lignes à 6 largeurs sur 7 dans la plupart des polices (4 sur 7 dans k8x12 et k8x12S).777 Si un réglage système par défaut applique malgré tout la règle assouplie, le correctif prévu est un U+2060 WORD JOINER avant le petit kana dans les quelques chaînes qui en ont besoin.

Vérifications. Un test qui vérifie que chaque réplique et chaque clé du monde du String Catalog existent dans les deux langues ; le test serveur du point 4, qui refuse une réplique prédéfinie japonaise au-delà de la borne de 218 pixels de police, 21 caractères pleine chasse ; et, sur le simulateur de l’iPhone 18 Pro Max à la taille par défaut avec le japonais comme langue de l’appareil, sans le serveur de la place et à la vitesse Instant, lancé avec -worldTrace, une capture de chaque page d’un paragraphe de test dans la boîte, transmis comme statut -worldBoxSample du point 3 et rédigé pour qu’aux 22 caractères pleine chasse que contient une ligne à cet endroit une coupure tombe avant un petit kana, dans laquelle aucune ligne après la première du paragraphe ne commence par un caractère de la liste de début de ligne de JLREQ ou par une forme de compatibilité de l’un d’eux.11 Cette liste est la liste complète, les 83 entrées des dix classes que nomme la règle de 3.1.7 (trois caractères figurent dans deux classes), versionnée dans la cible de test ; la sonde de la section 7 utilisait une liste partielle (section 7). Les logiciels de mise en page japonais emploient le plus souvent, à leur place, les formes de compatibilité qu’Unicode encode pour les allers-retours avec les normes plus anciennes, comme le note JLREQ à propos de la parenthèse ouvrante : « the compatibility counterpart U+FF08 FULLWIDTH LEFT PARENTHESIS is used in the Japanese context in most Japanese text layout software » (dans la plupart des logiciels de mise en page japonais, c’est l’équivalent de compatibilité U+FF08 FULLWIDTH LEFT PARENTHESIS qui est utilisé en contexte japonais).21 La liste ajoute donc chaque caractère du bloc Unicode Halfwidth and Fullwidth Forms dont la normalisation de compatibilité (NFKC) est l’une des 83, soit 24, dont ! (U+FF01), ? (U+FF1F), ) (U+FF09), :, ;, , et ., ainsi que les 。, 、, ー demi-chasse et les petits katakana à partir de ァ (U+FF67) ; et ~ (U+FF5E), le tilde pleine chasse, à côté du tiret ondulé 〜 de la même classe, mon ajout, puisque sa normalisation est le tilde ASCII, que la liste ne contient pas : 25 en tout.98 PixelMplus10 fait correspondre 54 des 83 entrées et 24 des 25 formes. Elle ne dessine pas les 29 autres entrées, dont 〜 (U+301C), ゠, le tiret demi-cadratin, ‼ et les petits katakana à partir de U+31F0, ni le ⦆ pleine chasse (U+FF60), et le test de couverture du point 1 tient chacun d’eux hors des chaînes du monde, si bien qu’une capture ne peut jamais montrer que les 75 caractères qu’elle dessine (les 54 entrées, dont trois, ), 〕 et ], figurent dans deux classes, soit 51 caractères, et les 24 formes).98 Un script compare la première cellule de glyphe de chaque ligne, pixel par pixel, avec les glyphes de la police pour ces 75 au m de la capture, dessinés à partir de la police intégrée avec CTFontGetGlyphsForCharacters, et ne trouve aucune correspondance. Les pixels ne peuvent distinguer un caractère listé des autres caractères du paragraphe que là où la police les dessine différemment. Dessinés à 10 pixels, deux des 75 ont un jumeau identique au pixel près dans la police : ‐ (U+2010), identique au trait d’union insécable U+2011, et les deux-points, identiques au deux-points lettre modificative U+A789 ; aucune des 24 formes n’en a. Le paragraphe de test n’utilise donc aucun des deux jumeaux ; ー (U+30FC) et le kanji 一 (U+4E00) diffèrent, 200 pixels encrés de l’appareil contre 250 à m = 5, si bien que 一 peut figurer n’importe où dans le paragraphe.9821 Le résultat de la section 7 est le Core Text d’un Mac ; cette capture décide de ce que fait SwiftUI sous iOS.

8. VoiceOver au-dessus de la scène

Aujourd’hui. VoiceOver entend « The plaza » puis la trace des tests de marche, par exemple « 12,4 down ».25 Cette valeur est destinée aux tests, pas aux personnes.

Quoi. 1. Le libellé de la scène reste « The plaza », ou le nom de la pièce, et sa valeur devient une phrase dont une personne peut se servir : où se tient le collectionneur, par point de repère, qui est à proximité, et ce qu’ils brandissent (par exemple, By the pond. Three collectors nearby. Holding up a card., soit près de l’étang, trois collectionneurs à proximité, brandit une carte), selon la règle d’accessibilityValue voulant qu’une valeur dise ce que le libellé ne dit pas.79 Tout ce qui passe aujourd’hui par stepTrace jusqu’à la valeur de la scène déménage, quel que soit ce qui l’écrit : quand j’ai lu PlazaStage.swift, la trace de pas de rig.onStep, le message d’échec de build de la closure de création de la RealityView et le rapport de la sonde d’air du .task qui lit -airProbe, et quelques minutes plus tard une quatrième écriture, venue d’une démo de marche ajoutée ce jour-là. Tout cela déménage dans un élément masqué qui n’existe que lorsque l’app est lancée avec -worldTrace (nom de mon choix), que passent les tests de marche, à côté de la page de la boîte (point 3) et des marges qu’a lues PlazaView (point 4), et les tests qui lisent la valeur de la scène, parmi lesquels testTheCollectorFacesTheWayItWalks et testTheChimneysSmoke, qui attend value CONTAINS 'smoke 18 ', lisent cet élément à la place.25103 2. accessibilityDirectTouch(_:options:) sur la scène, pour qu’un utilisateur de VoiceOver puisse toucher pour marcher après l’avoir activé (iOS 17.0).80 3. L’entité de chaque collectionneur reçoit un AccessibilityComponent avec le pseudo comme libellé et ce qu’il brandit comme valeur, pour que VoiceOver puisse le décrire (iOS 17.0).84 4. Une réplique dite à six cellules ou moins du joueur est publiée sous forme d’AccessibilityNotification.Announcement (par exemple, Glacier Shuffle 4471: Nice card!) avec une priorité basse, au plus une toutes les deux secondes, le rythme propre du serveur pour une réplique.8291 5. Une page de la boîte qui s’ouvre est annoncée en entier, sauf si elle répète une réplique que le sous-point 4 a déjà annoncée, comme le font les pages grand texte du point 4.

Vérifications. Un test d’interface qui vérifie que la valeur de la scène ne contient aucune coordonnée sans -worldTrace ; sur un simulateur iOS 27, un test avec XCUIVoiceOverService qui active VoiceOver, le déplace sur la scène, et vérifie que ce que renvoie currentSpeech() contient le libellé de la scène et aucune paire de nombres sous la forme x,y de la trace de pas ;87 une vérification manuelle sur un appareil avec VoiceOver activé, datée et avec le numéro de build, qu’une entité de collectionneur est atteignable et prononcée, parce que savoir si une RealityView munie d’une caméra virtuelle expose les éléments AccessibilityComponent n’est pas vérifié (section 11) ; et un test unitaire de la limitation des annonces, dans lequel deux répliques à moins de 2 secondes d’intervalle donnent une seule annonce.

9. Jamais, pour la typographie et les fenêtres

  1. Aucun glyphe, cadre, flèche ou graphisme de fenêtre issu de la ROM ou de la décompilation d’un jeu, ni dans l’app ni dans la forge. Les planches pret et ff6 sont mesurées ici et jamais copiées. Le cadre, la marque et le panneau de Kiradex sont dessinés par la forge ; sa typographie est une police sous licence.
  2. Aucun personnage ni ligature de la franchise. La charmap d’Émeraude porte des glyphes spéciaux pour des mots de la franchise ; rien de tel n’est dessiné, et aucune chaîne n’utilise les noms que la franchise donne aux choses.31
  3. Aucune police sans son fichier de licence en main : une police n’est livrée qu’avec le texte de sa licence cité à la section 4 ou 5 et enregistré à côté des preuves.
  4. Aucune police OFL modifiée sous son Reserved Font Name. Press Start 2P en déclare un ; si une police OFL est un jour modifiée, elle est renommée.54
  5. Aucun texte incrusté dans les graphismes du monde : panneaux, devantures et tableaux restent sans lettres, si bien qu’aucun atlas n’est jamais propre à une langue.

Vérification. Un script calcule deux empreintes SHA-256 de chaque image qu’écrit la forge et que contient le catalogue d’assets, l’une des octets du fichier et l’autre de ses pixels décodés, et les compare aux deux mêmes empreintes de chaque PNG mesuré ici (graphics/fonts et graphics/text_window de pret, les cadres de Cristal, et gfx/font/font.png de Cristal et de Rouge). La liste de ces empreintes, pas les images, est versionnée dans le dépôt sous scripts/forge/canon_digests.txt (un nom de mon choix, à côté du kit.py de la forge), pour que la vérification tourne sans le dossier de preuves, et toute correspondance fait échouer le build. Son propre test est un témoin négatif qui n’a besoin d’aucun fichier de jeu : il ajoute les empreintes d’une sortie de la forge à une copie de la liste et vérifie que le script échoue alors. Le même script liste pour revue, sans échouer, toute cellule de 8 sur 8 d’une sortie de la forge dont le masque d’un bit, ou son inverse, correspond à une cellule de ces planches. Les empreintes d’images entières ne permettent pas de reconstituer ces cellules ; la liste contient donc aussi l’empreinte SHA-256 de chaque masque de cellule distinct des planches, selon une règle (la mienne) appliquée de la même façon aux sorties de la forge : un pixel est actif là où sa couleur diffère de celle du pixel supérieur gauche de l’image ; les cellules vont par 8 sur 8 depuis le coin supérieur gauche, une cellule partielle en bordure étant écartée ; une cellule vide ou pleine est laissée de côté des deux côtés, puisque les planches en ont beaucoup (japanese_normal.png à lui seul a 430 cellules vides sur 1 024) ; et un masque correspond à ses 64 bits rangée par rangée, 8 octets. Sur les 50 planches, cela fait 7 746 cellules gardées et 1 484 empreintes distinctes.99 Une copie recolorée, remise à l’échelle ou redessinée passe les empreintes ; la règle 1 repose sur le fait que la forge dessine chaque asset à partir de code et sur cette revue, pas sur la seule vérification. Et la vérification des chaînes que proposent les cahiers des charges précédents de cette série est étendue aux abréviations de la franchise tirées de la charmap d’Émeraude.105

Hors de ce cahier des charges

  • Une police pixel pour toute l’app. Le Dex, le scanner et les Réglages gardent la police du système ; la typographie pixel est pour le monde.
  • Le chinois et le coréen. Les quatre jeux en pixel art modernes examinés livrent le chinois, et deux d’entre eux, Stardew Valley et Sea of Stars, le coréen, mais rien ici n’a mesuré de police pixel pour l’une ou l’autre langue.64
  • Misaki ou toute police de 8 points pour le texte de lecture. Ce sont les plus petits kanji mesurés ; la lisibilité à cette taille pour un lecteur japonais n’est pas mesurée.6
  • Des étiquettes de nom au-dessus des têtes et un champ de nom dans la boîte.1
  • Du japonais traduit automatiquement que personne n’a relu.
  • Un gras ou un italique synthétique sur la police pixel.
  • Des cadres à vendre. Chaque cadre est gratuit ; un cadre pour les soutiens attend une décision.
  • Du texte dessiné dans la scène RealityKit. La raison de l’article sur le monde et l’arithmétique de la section 10 le maintiennent toutes deux dans SwiftUI.2311

Points clés

Si vous dessinez les graphismes

  • Construisez les cadres avec des tuiles : un coin, un bord et un centre. Les menus d’Émeraude choisissent parmi 20 cadres de 24 pixels sur 24, trois tuiles sur trois ; sa boîte de dialogue se dessine à partir d’une bande de 14 tuiles au bord inférieur en miroir ; les cadres de Cristal comptent 6 tuiles d’un bit chacun.1
  • Donnez à la marque de suite une horloge lente. La flèche d’Émeraude se décale de 0, 1, 2 et 1 pixels depuis le repos, un pas toutes les 9 images, 0,6 seconde par cycle.1
  • Gardez les mots hors des graphismes du monde. Un panneau sans lettres sert toutes les langues à partir d’un seul atlas ; les mots vont dans la boîte.29
  • Dessinez le japonais entre 8 et 12 pixels et attendez-vous à ce qu’un kana fasse le double d’une lettre latine : 8 contre 4, 10 contre 5, 12 contre 6 dans chaque police pixel mesurée capable d’afficher le japonais.7

Si vous construisez le moteur

  • Choisissez une police pixel d’après son fichier, pas d’après son nom : vérifiez la grille, le décalage par rapport à la ligne de base et les avances. La grille de PixelMplus12, décalée de 0,39 pixel par rapport à la ligne de base, a bavé à chaque taille dans Core Text sous macOS ; l’avance des kana de k8x12, 8,01 pixels, a bavé dès que les glyphes étaient placés exactement.7
  • Ne dessinez qu’avec un nombre entier de pixels de l’appareil par pixel de police : 0,0 % de pixels partiels aux multiples entiers d’une grille posée sur la ligne de base avec des avances en pixels entiers (Misaki, PixelMplus10, Press Start 2P), 19,4 % à 82,2 % entre eux, dans le Core Text d’un Mac avec le lissage des polices désactivé. Avec le lissage par défaut du contexte, PixelMplus10 à sa propre taille avait 178 pixels encrés partiels sur 272 ; un multiple entier est donc nécessaire, pas suffisant ; capturez un iPhone avant de vous fier à l’un ou l’autre résultat là-bas.79
  • Respectez Dynamic Type en arrondissant à la grille. Un em de 10 pixels n’est entier qu’à 1 des 12 tailles Body en 3× ; arrondissez la taille mise à l’échelle à un nombre entier de pixels de l’appareil par pixel de police et fixez-la comme taille fixe.768
  • Comptez la vitesse du texte en temps, pas en images, pour que 60 et 120 hertz affichent de la même façon ; 60 caractères par seconde est le rythme le plus rapide dans chaque jeu mesuré, et 15 à 20 le réglage moyen là où un jeu en a un.14
  • Donnez à VoiceOver une phrase, pas une trace. accessibilityValue sert à ce que le libellé ne dit pas, AccessibilityComponent décrit une entité, et XCUIVoiceOverService peut tester ce que prononce VoiceOver à partir d’iOS 27.0.798487

Si vous concevez la boucle de jeu

  • Deux lignes à la fois, en bas. La boîte d’Émeraude occupe 30 % de l’écran et celle de Cristal 33 %, chacune sur deux lignes.1
  • Écrivez jusqu’à la largeur. Les scénaristes d’Émeraude ont tenu 95 % de 24 100 lignes à 38 caractères dans une fenêtre qui en contient 41, et aucune au-delà de la fenêtre.1
  • Nommez l’interlocuteur dans la ligne, pas au-dessus de sa tête ; la boîte d’Émeraude n’a pas de champ de nom.1
  • Prévoyez le japonais dès le départ : les quatre jeux en pixel art modernes examinés le livrent tous. Une police qui couvre JIS X 0208 dessine environ sept mille caractères ; un jeu au script figé peut en livrer moins, comme la ROM japonaise de Final Fantasy VI avec ses 1 248 lettres, et chaque nouvelle chaîne doit alors être vérifiée contre la police.64584

Questions fréquentes

Quelle taille doit faire une boîte de texte dans un jeu en pixel art ?

Dans les jeux Pokémon sur console portable, le tiers inférieur de l’écran et deux lignes. Le cadre de dialogue d’Émeraude fait 30 tuiles sur 6, 240 pixels sur 48, 30 % de la hauteur de l’écran de la Game Boy Advance, autour d’une fenêtre de texte de 27 tuiles sur 4 qui contient deux lignes d’une police de 16 pixels. La boîte de Cristal fait 20 tuiles sur 6, 33 % de l’écran de la Game Boy, deux lignes de 18 caractères, et celle de Rouge les mêmes 20 sur 6. Final Fantasy VI ouvre sa fenêtre en haut ou en bas de l’écran.12345

Quelle vitesse donner à un effet de texte machine à écrire ?

Le canon compte des images. Émeraude et Rouge Feu affichent un caractère toutes les 8, 4 ou 1 images, 7,5, 15 ou 60 par seconde à 60 hertz, et un par image tant que A ou B est maintenu après une pression pendant l’affichage ; Cristal et Rouge affichent 12, 20 ou 60 par seconde ; Final Fantasy VI affiche les dialogues de terrain à une lettre par image, 60 par seconde, sans réglage pour cela (sa vitesse des messages à six crans règle le temps d’attente d’un message de combat). Sur un téléphone moderne, comptez les caractères en temps plutôt qu’en images, pour que la vitesse ne change pas à 120 hertz. Chaque jeu mesuré plafonne à 60 par seconde, et les réglages moyens d’Émeraude et de Cristal sont de 15 et 20 ; le cahier des charges ci-dessous propose 30 par défaut, ce qui est mon choix entre ces réglages moyens et le rapide à 60, pas un réglage mesuré.1144

Quelles polices pixel prennent en charge le japonais ?

Parmi celles mesurées, Misaki (8 points sur 8), k8x12 (8 sur 12) et DotGothic16 dessinent les 6 879 caractères de JIS X 0208, et PixelMplus10 et PixelMplus12 tous sauf trois points de code. Misaki, k8x12 et PixelMplus sont sous une licence libre en trois phrases qui autorise l’usage, la copie et la distribution « either commercially or noncommercially » (à des fins commerciales ou non) ; DotGothic16 est sous SIL Open Font License 1.1. Mesurées avec Core Text sous macOS 27.0, Misaki et PixelMplus10 sont les deux qui ont une grille propre sur la ligne de base et des avances en pixels entiers, à 8 et 10 pixels ; la grille de PixelMplus12 se trouve 0,39 pixel sous la ligne de base, l’avance des kana de k8x12 est de 8,01 pixels, et les points de DotGothic16 ne reposent sur aucune grille de pixels.67171853

Pourquoi ma police pixel paraît-elle floue sur iPhone ?

Le plus souvent parce qu’un pixel de police n’est pas un nombre entier de pixels de l’appareil. Dans Core Text sous macOS 27.0 avec le lissage des polices désactivé, les polices pixel TrueType à grille propre sur la ligne de base et aux avances en pixels entiers (Misaki, PixelMplus10 et Press Start 2P) se sont dessinées sans aucun pixel partiel aux multiples entiers de leur grille et avec 19,4 % à 82,2 % des pixels encrés brouillés entre ces multiples. Avec le lissage par défaut du contexte de dessin, même un multiple entier est devenu flou : PixelMplus10 à ses propres 10 pixels est sortie avec 178 pixels encrés partiels sur 272.9 Deux défauts du fichier de police provoquent un flou à chaque taille : une grille décalée de la ligne de base, comme celle de PixelMplus12 de 0,39 pixel, et une avance qui n’est pas entière, comme celle des kana de k8x12, 8,01 pixels, une fois les glyphes placés exactement. La mesure a été faite sur un Mac ; sur l’iPhone, c’est une capture qui tranche.7

Une police pixel peut-elle prendre en charge Dynamic Type ?

Seulement par arrondi. Sur les douze tailles Body d’iOS, de 14 à 53 points, un em de 10 pixels ne tombe sur des pixels entiers de l’appareil en 3× qu’à 40 points ; une police pixel qui se met à l’échelle en continu avec Font.custom(_:size:relativeTo:) sort donc de sa grille aux onze autres ; dans le Core Text du Mac, hors de la grille voulait dire baveux, et c’est une capture sur l’iPhone qui décide de ce que SwiftUI y dessine. Lisez la taille mise à l’échelle avec UIFontMetrics ou ScaledMetric, arrondissez-la à un nombre entier de pixels de l’appareil par pixel de police, et fixez-la avec Font.custom(_:fixedSize:). Apple demande qu’une police personnalisée reproduise les comportements des polices système, Dynamic Type compris, et qu’une app permette idéalement d’agrandir son texte d’au moins 200 %.710206768

Core Text respecte-t-il les règles japonaises de coupure des lignes ?

Dans ma mesure sous macOS 27.0, oui, et strictement, quand le texte n’était pas balisé avec une langue : un paragraphe de 131 caractères s’est coupé à sept largeurs sans qu’aucune ligne ne commence par un caractère de la liste de début de ligne de la sonde, une liste partielle, surtout des caractères de JLREQ dont sept sous leur forme pleine chasse, dans Hiragino Sans et huit polices pixel. Balisé ja, il s’est assoupli vers la règle moins stricte que décrivent les exigences du W3C pour la mise en page du japonais, et un petit kana a commencé une ligne à 6 largeurs sur 7 dans la plupart des polices. Ce que fait SwiftUI sur un iPhone dont la langue est le japonais n’a pas été mesuré.721

Combien de caractères une police pixel japonaise doit-elle contenir ?

JIS X 0208, la cible de couverture habituelle, en compte 6 879 : 524 non-kanji, 2 965 kanji de niveau 1 et 3 390 kanji de niveau 2. Le producteur de Final Fantasy Pixel Remaster a dit à Siliconera que sa police pixel japonaise avait demandé « around 7,500 characters » (environ 7 500 caractères), contre « a few hundred » (quelques centaines) pour les langues occidentales. Un jeu au script figé peut en livrer moins : la ROM japonaise de Final Fantasy VI compte 1 248 lettres dans sa police de dialogue.584

Chaque chiffre de cet article vient d’un script qui se relance sur des sources enregistrées, d’une page enregistrée le 5 octobre 2026, ou d’une lecture datée du code source de Kiradex, et les notes disent lequel ; les chiffres de Core Text viennent de macOS 27.0, pas d’iOS. Le cahier des charges de la section 12 est ma proposition et présenté comme tel ; rien n’en a été construit.

À lire aussi sur ce site : Des mondes en pixel art sur iPhone est le premier article de cette série, avec la recette RealityKit et la direction pour le texte que mesure cet article ; Personnages en pixel art : des personnages et un éditeur sur iPhone est le collectionneur au-dessus duquel flottent les bulles ; Des structures en pixel art : maisons, halls et intérieurs sur iPhone présente la forge dont le panneau reste sans lettres ; Le mouvement en pixel art : marches, caméras et portes sur iPhone présente les horloges de 60 et 120 hertz sur lesquelles se cale l’affichage progressif ; Une ville en pixel art vivante quand personne n’est en ligne présente les répliques prédéfinies et leur rythme ; Des pièces en pixel art et Des routes en pixel art sont les cahiers des charges dont celui-ci étend les vérifications ; Honorer les anciens jeux Pokémon sans les emprunter explique pourquoi le canon est mesuré et jamais copié ; et Xcode 27.1 bêta : votre app dans le simulateur d’iPhone Duo présente le simulateur sur lequel tourne la capture des bulles.

Sources


  1. Mesure de l’auteur, 5 octobre 2026 : measure_gen_text.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_gen_text.out. Entrées tirées de pret pokeemerald à 731ad5b : src/text.c (sFontInfos, le cas RENDER_STATE_HANDLE_CHAR, sWindowVerticalScrollSpeeds, sDownArrowYCoords, les fonctions de glyphes japonais), src/menu.c (sTextSpeedFrameDelays, sStandardTextBox_WindowTemplates, WindowFunc_DrawDialogueFrame évalué pour la fenêtre standard), src/fonts.c et charmap.txt (largeurs des glyphes), graphics/text_window/*.png (tailles), include/text_window.h, chaque ligne de data/text/*.inc et de data/maps/*/scripts.inc découpée à \n, \l et \p et mesurée via la table des largeurs (504 fichiers, 24 100 lignes sans emplacement réservé, 3 000 avec, 0 ignorée), et graphics/fonts/japanese_*.png (cellules non vides). De pokefirered à 037335f : src/new_menu_helpers.c. De pokecrystal à 5beda23 : constants/hardware.inc, constants/text_constants.asm, home/print_text.asm, gfx/font.asm, les PNG des cadres et gfx/font/font.png. De pokered à d2704a6 : data/text_boxes.asm, constants/ram_constants.asm, gfx/font/font.png. Les caractères par seconde valent 60 divisé par le nombre d’images par caractère. La lecture de l’imprimeur d’Émeraude (le délai stocké moins un par AddTextPrinter, puis décompté, si bien qu’un caractère prend le délai en images), les conditions du délai de défilement automatique tirées de src/battle_message.c et src/scrcmd.c, et la lecture à deux lignes des constantes de Cristal sont vérifiées par assertion par le script, pas exécutées. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  2. pret, pokeemerald/src/menu.c au commit 731ad5b (sTextSpeedFrameDelays à la ligne 77 ; sStandardTextBox_WindowTemplates à la ligne 84 ; LoadMessageBoxGfx et LoadUserWindowBorderGfx aux lignes 212 et 213 ; WindowFunc_DrawDialogueFrame appelé à la ligne 218, ses remplissages utilisant BG_TILE_V_FLIP à partir de la ligne 378), lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/src/menu.c. ↩↩↩↩↩↩↩

  3. pret, pokecrystal/constants/text_constants.asm au commit 5beda23 (lignes 24 à 32 : BORDER_WIDTH EQU 2, TEXTBOX_WIDTH EQU SCREEN_WIDTH, TEXTBOX_HEIGHT EQU 6, TEXTBOX_Y EQU SCREEN_HEIGHT - TEXTBOX_HEIGHT, TEXTBOX_INNERY EQU TEXTBOX_Y + 2) et constants/hardware.inc (lignes 922 et 923, SCREEN_WIDTH 20 et SCREEN_HEIGHT 18), lu le 5 octobre 2026, https://github.com/pret/pokecrystal/blob/master/constants/text_constants.asm. ↩↩↩↩

  4. Mesure de l’auteur, 5 octobre 2026 : measure_ff6.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_ff6.out. Entrées tirées d’everything8215/ff6 à 8130132 : tools/rip_list_en.json et tools/rip_list_jp.json (plages d’octets des graphismes de fenêtre, des palettes et des polices), src/menu/config.asm (ConfigWindowNumPos, LoadWindowPal, ConfigColorBarTextList, ChangeConfigOption_02), src/menu/menu_sram.asm, src/btlgfx/gfx_cmd.asm (MsgSpeedTbl, mess_wait), chaque fichier sous src/ qui lit $1d4d, src/field/text.asm (LoadLetterGfx, les bornes x de la ligne, CalcTextWidth, NewLine, UpdateDlgTextOneLine, le titre de carte), src/gfx/font_gfx.asm (FontWidth), src/field/reset.asm (la boucle de terrain), src/field/hdma.asm (DlgWindowFixedColorTbl) et tools/char_table/ (kanji.json, kana.json). La lettre par image et la valeur du dégradé par ligne de balayage sont des lectures du code, vérifiées par assertion, pas exécutées. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  5. Consortium Unicode, JIS0208.TXT, la table de correspondance de JIS X 0208 vers Unicode, version 2.0, classée sous OBSOLETE/EASTASIA, récupérée et enregistrée le 5 octobre 2026, https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT. Compté par measure_coretext.swift et measure_bdf.py : 6 879 caractères ; 524 non-kanji, dont 169 kana aux rangées 4 et 5 ; 2 965 kanji de niveau 1 aux rangées 16 à 47 ; 3 390 kanji de niveau 2 aux rangées 48 à 84. ↩↩↩↩↩↩↩↩

  6. Mesure de l’auteur, 5 octobre 2026 : measure_bdf.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_bdf.out. Entrées : les fichiers BDF de Misaki Gothic, Misaki Gothic 2nd et Misaki Mincho et de k8x12, k8x12L et k8x12S (versions du 2021-05-05) et le JIS0208.TXT enregistré ; il donne pour chaque police la taille en pixels, la boîte englobante, les jambages supérieur et inférieur, le nombre de caractères, les largeurs d’avance, les glyphes pleine chasse dont l’encre remplit la cellule dans une direction, la même chose pour les kanji (U+4E00 à U+9FFF), et les caractères de JIS X 0208 dessinés. ↩↩↩↩↩↩↩↩↩↩↩

  7. Mesure de l’auteur, 5 octobre 2026 : measure_coretext.swift dans le dossier de preuves de l’auteur pour cet article, compilé avec swiftc -O et exécuté sous macOS 27.0 (build 26A428), sortie measure_coretext.out, pas sous iOS. Entrées : Misaki Gothic, Misaki Gothic 2nd, k8x12 et k8x12S (les versions TTF du 2021-05-05), PixelMplus10 Regular et Bold et PixelMplus12 Regular (PixelMplus à d89b95f), DotGothic16, Press Start 2P et Silkscreen (google/fonts à 9710da1), Hiragino Sans W3 comme témoin système, et le JIS0208.TXT enregistré. Il donne pour chaque police le nombre de glyphes et les tables bitmap (CTFontCopyAvailableTables) ; sa grille de conception (le plus petit nombre de pixels par em, de 4 à 40, pour lequel 95 % des points de contour des glyphes d’échantillon se trouvent à moins de 0,04 pixel, chaque axe pouvant avoir un décalage constant) ; ses avances latines et kana ; sa couverture de l’ASCII et de JIS X 0208 ; la part de pixels partiels parmi les pixels encrés (8 à 247 sur 255) quand une ligne d’exemple est dessinée dans un bitmap en niveaux de gris sur 8 bits à 1 à 4 fois la grille, anticrénelage activé, désactivé, et activé avec le positionnement subpixel activé et la quantification désactivée, lissage des polices non autorisé ; pour PixelMplus12, la même chose avec la ligne de base déplacée du décalage ; les tailles Body et Caption 1 des Human Interface Guidelines auxquelles un pixel de police est entier en 3× ; et le premier caractère de chaque ligne d’un paragraphe japonais de 131 caractères composé par CTFramesetter de 8 à 20 em, non balisé et avec kCTLanguageAttributeName “ja”, comparé à une liste de caractères interdits en début de ligne. DotGothic16 n’a pas de grille de conception ; elle est donc dessinée à 16 points par em nominaux (62,5 unités par point) et à 1,25 à 4 fois cette valeur, et composée à 32 pixels par em, une taille choisie d’après son nom et le pas de points de measure_outline_coords.out, que la sortie affiche. Chaque rendu interdit le lissage des polices (setAllowsFontSmoothing(false)). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  8. Jenni Lada, « Interview: Preparing Final Fantasy Pixel Remaster for the Switch and PS4 », Siliconera, 2 juin 2023, le producteur Naofumi Takuma sur la nouvelle option de police (« We didn’t have any alternative data saved on file in-house for the Japanese language font, so we decided to create a new one » (nous n’avions en interne aucune donnée de remplacement pour la police japonaise, nous avons donc décidé d’en créer une nouvelle) ; « around 7,500 characters » (environ 7 500 caractères) contre « a few hundred » (quelques centaines)), consulté le 5 octobre 2026, https://www.siliconera.com/interview-preparing-final-fantasy-pixel-remaster-for-the-switch-and-ps4/. ↩↩↩↩↩↩

  9. Mesure de l’auteur, 5 octobre 2026 : figures/dump_glyphs.swift dans le dossier de brouillon de cet article, compilé avec swiftc -O et exécuté sous macOS 27.0 (26A428), sortie figures/glyphs.txt : PixelMplus10 Regular à 10 pixels dessinée par Core Text, « Nice card! » avec 0 pixel encré partiel sur 94 quand le lissage des polices n’est pas autorisé (comme le dessine measure_coretext.swift) et 178 sur 272 avec le réglage par défaut du contexte bitmap ; la ligne d’exemple japonaise 0 sur 164 et 287 sur 451. ↩↩↩↩↩↩↩↩↩↩

  10. Apple, Human Interface Guidelines, « Typography » (les tailles de texte par défaut et minimale, 17 et 11 points sous iOS et iPadOS ; « Follow the recommended default and minimum text sizes for each platform » (respectez les tailles de texte par défaut et minimales recommandées pour chaque plateforme) ; « If you use a custom font, make sure it implements the same behaviors » (si vous utilisez une police personnalisée, assurez-vous qu’elle reproduit les mêmes comportements) ; les tables Dynamic Type d’iOS), consulté le 5 octobre 2026, https://developer.apple.com/design/human-interface-guidelines/typography. ↩↩↩↩↩↩↩

  11. Mesure de l’auteur, 5 octobre 2026 : measure_kiradex_text.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_kiradex_text.out, exécuté à 12 h 34, heure du Pacifique, sur Kiradex au commit 334fbfe avec les modifications non validées de ce jour, une copie de travail en cours de modification ce jour-là ; le script lit les fichiers en entier, par symbole, pas par ligne. Entrées, lues uniquement dans le dépôt Kiradex tel qu’il se trouvait sur le disque : server/app/lines.py (23 répliques, de 3 à 40 caractères, médiane 16), server/app/names.py (des pseudos de deux mots tirés de listes de 20 et de quatre chiffres, 20 caractères au plus), chaque appel à .kit941Font dans Kiradex/World/*.swift (caption 10, label 3, body 3, title 3, dans quatre fichiers), la famille de polices du thème, les modificateurs de la bulle, les modificateurs d’accessibilité de la scène, une recherche dans le code source de l’app de AccessibilityComponent, accessibilityDirectTouch, updatesFrequently, AccessibilityNotification, typesettingLanguage, UIFontMetrics, ScaledMetric et dynamicTypeSize (0 chacun), knownRegions (Base, en) et le nombre de fichiers .xcstrings et .lproj (0) ; l’échelle du monde selon la règle de PlazaRig.fit pour les tailles d’appareil que cette série a mesurées à partir des profils de simulateur de Xcode 27 (iPhone 18 Pro Max 440 points sur 956, iPhone Duo extérieur 466 sur 678, intérieur 669 sur 951, tous en 3×) ; et, tirés de measure_coretext.out, l’em et les avances de chaque police candidate, d’où découlent les caractères par ligne de la boîte après une marge intérieure de 48 points, les largeurs de bulle de la plus longue réplique prédéfinie à m = 4, 5, 6 et 8 (283, 349, 416 et 549 points : ses avances à cette taille plus les 8 points de remplissage de chaque côté de la bulle, la bordure étant dessinée à l’intérieur de la bulle), et les tailles Body arrondies à m pour un em de 10 pixels, une moitié arrondie vers le haut. Ajouté plus tard le 5 octobre, avec le script et la sortie antérieurs conservés à côté : l’arrondi en 2× avec le plus petit m d’au moins 11 points (4 en 3×, 3 en 2×) ; la boîte sous le cadre du cahier des charges (la largeur moins 32 points arrondie vers le bas à un nombre entier de pixels de police, 8 pixels de police de cadre de chaque côté ; de 1 224 à 1 220 pixels de l’appareil sur le 18 Pro Max à m = 5, 45 caractères latins ou 22 pleine chasse par ligne) ; les pages de « handle: line » pour les 400 paires de mots de names.py et les 23 répliques aux douze tailles Body sur cinq vues (deux lignes par page, coupures gloutonnes aux espaces, chaque caractère ASCII imprimable large de 5 pixels de police d’après probe_line_start_glyphs.out) ; le plafond de la bulle sur chaque iPhone de measure_ios27_iphones.out ; la borne du serveur de 218 pixels de police ; et la somme de 120 additions de 1/120. Ajouté plus tard le même jour, avec le script et la sortie antérieurs conservés à côté et chaque ligne antérieure de la sortie inchangée : la place et le plafond sur la largeur sûre, sans marges latérales sur les téléphones et, sur l’écran extérieur du Duo en portrait, à la fois sans marge et avec la marge de fin de 84 points qu’a mesurée l’article de cette série sur le Duo, extraite de cet article (largeur sûre 382, place 350, plafond 5 à 349,33 points ; la boîte à la taille par défaut, 38 caractères latins ou 19 pleine chasse sous le cadre, 40 ou 20 avec la marge intérieure actuelle) ; les pages de « handle: line » sur cette largeur sûre ; les largeurs pour le test de placement ; la borne du serveur dans cette place (296 et 302,67 points à m = 4, dans 350) ; et, tirés de server/app/npcs.py, les quatre collectionneurs de la ville, leurs pseudos, les pages de leurs répliques en AX3 sur le 18 Pro Max (2 au plus) et la borne de touchers du test du point 4, (3 − 1) répliques × 2 pages, un toucher par page en Instant : 4. Le 15 octobre, cette dernière ligne a été recalculée pour le test tel qu’il se présente désormais, à partir des mêmes entrées dans la mesure où la sortie le montre (le fichier de la scène tiré de la copie du 5 octobre lue à 12 h 28 ; lines.py, names.py et npcs.py inchangés depuis les 2 et 3 octobre ; les autres entrées lues dans l’arborescence en cours au commit fc9d4876, qui a affiché les mêmes lignes), avec le script et la sortie antérieurs conservés à côté ; toutes les autres lignes de la sortie s’affichent comme avant. Ajouté plus tard le 15 octobre, avec le script et la sortie antérieurs conservés à côté et chaque ligne antérieure inchangée : les cas du test de placement où la plus longue réplique du jour ne tient à aucun m autorisé (360 et 375 points en 3× et 375 en 2× avec la marge de 84 points, et 375 en 2× avec 16 de chaque côté : dans le premier, une place de 244 points contre 282,67 points à m₀) ; le plafond avec une réplique japonaise à la borne du serveur, 21 × 10 = 210 pixels de police (5 sur le 18 Pro Max, 5 et 4 sur le Duo sans et avec le rail, 4 à 360 points en 3×, 3 en 2×) ; et le m de bulle que les captures devraient trouver, le plus petit entre le m de la boîte moins un, jamais sous m₀, et le plafond sur les deux langues (4 à la taille par défaut sur les vues 3×, 3 sur les 2× ; en AX3, le plafond). Ajouté le même soir, de la même façon : chaque largeur que liste measure_ios27_iphones.out, sans marges latérales, avec 16 points de chaque côté et avec la marge de fin de 84 points, avec le plafond anglais actuel, le plafond avec une réplique japonaise à la borne et les deux (sans marges : 4 à 360 et 375, 4 à 390 et 393 avec la réplique japonaise, 5 de 402 à 440, 3 en 2×) ; les cas où la plus longue réplique du jour ne tient à aucun m autorisé (avec la marge de 84 points à 360, 375, 390 et 393 points en 3× et à 375 et 414 en 2× ; avec 16 de chaque côté à 375 en 2×) ; et « Hi! » à m₀, 36 points en 3× et 38,50 en 2×, qui tient dans chaque cas. Ajouté ensuite : la même table avec une réplique latine à la borne, 43 × 5 = 215 pixels de police, à côté de la réplique actuelle et de la japonaise, et les cas où chacune ne tient à aucun m autorisé : 7, 8 et 10 sur 36. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  12. pret, pokered/data/text_boxes.asm au commit d2704a6, ligne 10 (db MESSAGE_BOX, 0, 12, 19, 17), lu le 5 octobre 2026, https://github.com/pret/pokered/blob/master/data/text_boxes.asm. ↩↩

  13. pret, pokeemerald/src/text.c au commit 731ad5b (sFontInfos à partir de la ligne 119 ; sDownArrowYCoords { 0, 1, 2, 1 } à la ligne 75 et sWindowVerticalScrollSpeeds à la ligne 76 ; AddTextPrinter qui pose textSpeed = speed à la ligne 281 et --sTempTextPrinter.textSpeed; à la ligne 296 ; le downArrowDelay = 8 de la flèche à la ligne 832 ; dans le cas RENDER_STATE_HANDLE_CHAR, if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp) à la ligne 944, le décompte if (textPrinter->delayCounter && textPrinter->textSpeed) à la ligne 947, canABSpeedUpPrint et une nouvelle pression sur A ou B qui active hasPrintBeenSpedUp aux lignes 950 à 953, et if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll) qui règle le compteur sur 3, sinon sur textSpeed, aux lignes 958 à 961 ; EXT_CTRL_CODE_JPN à la ligne 1094 ; DecompressGlyph_Normal à partir de la ligne 1853 et GetGlyphWidth_Normal à partir de la ligne 1887), lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/src/text.c. ↩↩↩↩↩↩↩↩↩↩↩

  14. pret, pokefirered/src/text.c au commit 037335f, lignes 639 à 647 (dans le cas RENDER_STATE_HANDLE_CHAR, if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp) qui ramène le compteur à 0, et gTextFlags.canABSpeedUpPrint && JOY_NEW(A_BUTTON | B_BUTTON) qui active hasPrintBeenSpedUp pendant le décompte d’un délai), lu le 5 octobre 2026, https://github.com/pret/pokefirered/blob/master/src/text.c. ↩↩↩

  15. pret, pokecrystal/home/print_text.asm au commit 5beda23 (ligne 4, « The text speed setting in wOptions is actually a frame count » (le réglage de vitesse du texte dans wOptions est en réalité un nombre d’images), et PrintLetterDelay qui attend une image par lettre tant que A ou B est maintenu) et pokered/constants/ram_constants.asm à d2704a6 (lignes 38 à 40 : TEXT_DELAY_FAST 1, TEXT_DELAY_MEDIUM 3, TEXT_DELAY_SLOW 5), lu le 5 octobre 2026, https://github.com/pret/pokecrystal/blob/master/home/print_text.asm. ↩↩

  16. pret, pokecrystal/home/joypad.asm au commit 5beda23 (WaitPressAorB_BlinkCursor à la ligne 342 ; BlinkCursor à la ligne 442, qui alterne la tuile en hlcoord 18, 17 entre '▼' et '─'), lu le 5 octobre 2026, https://github.com/pret/pokecrystal/blob/master/home/joypad.asm. La période du clignotement n’a pas été déduite. ↩↩↩

  17. Num Kadoma, littlelimit.net, « font », la section sur la licence de Misaki, k8x12 et des autres polices de l’auteur (« These fonts are free software. Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially. » (Ces polices sont des logiciels libres. Permission illimitée est accordée de les utiliser, de les copier et de les distribuer, avec ou sans modification, à des fins commerciales ou non.)), consulté le 5 octobre 2026, https://littlelimit.net/font.htm#license. ↩↩↩↩↩

  18. itouhiro, PixelMplus, README.md (TrueType en contours seulement, « 埋め込みビットマップは なし » (pas de bitmaps incorporés) ; « JIS第1・第2水準のすべての漢字を収録 » (contient tous les kanji des niveaux JIS 1 et 2) et ISO-8859-1 ; PixelMplus12 et PixelMplus10 ; construites à partir des M+ BITMAP FONTS ; licence M+ FONT LICENSE) et misc/mplus_bitmap_fonts/LICENSE_E (les trois phrases de la M+ FONT LICENSE), au commit d89b95f, lu le 5 octobre 2026, https://github.com/itouhiro/PixelMplus/blob/master/misc/mplus_bitmap_fonts/LICENSE_E. ↩↩↩↩↩

  19. Mesure de l’auteur, 5 octobre 2026 : measure_outline_coords.swift dans le dossier de preuves de l’auteur pour cet article, compilé et exécuté sous macOS 27.0, sortie measure_outline_coords.out : les coordonnées de contour x et y brutes de H, A, o et あ dans PixelMplus12 Regular et DotGothic16 et de H, A et o dans Silkscreen, en unités de 1 000 par em. ↩↩↩↩↩↩↩

  20. Apple, Human Interface Guidelines, « Accessibility » (« Ideally, give people the option to enlarge text by at least 200 percent (or 140 percent in watchOS apps). » (Idéalement, donnez aux gens la possibilité d’agrandir le texte d’au moins 200 %, ou 140 % dans les apps watchOS.)), consulté le 5 octobre 2026, https://developer.apple.com/design/human-interface-guidelines/accessibility. ↩↩↩

  21. W3C, « Requirements for Japanese Text Layout », Working Group Note, 11 août 2020, section 3.1.7 (la règle d’interdiction en début de ligne et la remarque sur le jeu de règles moins strict), consulté le 5 octobre 2026, https://www.w3.org/TR/jlreq/. ↩↩↩↩↩↩↩↩

  22. Dossier de recherche de l’auteur pour cet article, 5 octobre 2026 (privé) : les mesures ci-dessus, les études de cas et un premier brouillon du cahier des charges pour Kiradex. ↩↩↩

  23. Blake Crosley, « Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew », blakecrosley.com, 3 octobre 2026 : règle 10 (« Text is art » (le texte est un élément graphique), le « use font size 16, 32, 48, etc » (utilisez une taille de police de 16, 32, 48, etc.) de la page de m5x7, et la phrase sur le cadre de fenêtre de texte de 24 sur 24 d’Émeraude), l’arithmétique des tailles du texte à k = 8 et k = 6, le paragraphe sur le Pixel Remaster qui cite GoNintendo pour environ 7 500 caractères japonais, et le plan pour le texte (« m5x7 at integer multiples in a 24-by-24 nine-slice box, two lines, typewriter reveal, a two-frame advance arrow » (m5x7 en multiples entiers dans une boîte nine-slice de 24 sur 24, deux lignes, affichage façon machine à écrire, une flèche de suite à deux images)), lu le 5 octobre 2026, https://blakecrosley.com/blog/pixel-art-world-on-iphone. Cet article est cité ici et n’est pas modifié. ↩↩↩↩↩↩↩↩↩↩

  24. Code source de Kiradex, Kiradex/KiradexTheme.swift, ligne 12 (let fontFamily: Kit941.FontFamily = .systemDesign(.monospaced)), lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé). ↩↩

  25. Code source de Kiradex, Kiradex/World/PlazaStage.swift, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) : lignes 146, 155 et 189 (stepTrace réglé sur un échec de build dans la closure de création de la RealityView, sur chaque pas dans rig.onStep et, sous -airProbe, sur le rapport de l’air dans un .task ; une lecture du même fichier à 12 h 42 en montrait un quatrième, stepTrace = "lap done" sous un nouveau -plazaLapDemo), lignes 58 à 61 (stepTrace, « The player’s last step, “x,y facing”, as the stage’s accessibility value: the walks read it, since nothing else the simulator logs reaches them. » (le dernier pas du joueur, « x,y orientation », comme valeur d’accessibilité de la scène : les tests de marche le lisent, car rien d’autre de ce que journalise le simulateur ne leur parvient.)), lignes 313 à 315 (.accessibilityElement(), .accessibilityLabel("The plaza"), .accessibilityValue(stepTrace)), lignes 289 à 293 (les bulles, le dernier .overlay dans le GeometryReader de la scène des lignes 121 à 294), lignes 295 à 309 (les deux surcouches de la scène par-dessus : des lignes de balayage, .black.opacity(0.16) d’un point de haut tous les 3 points, et un vignettage RadialGradient), lignes 402 à 418 (bubbles(of:in:) : une réplique affichée tant que timeline.date.timeIntervalSince(said.at) < 4, placée au point de la vue de la tête moins Kit941.Spacing.lg sans contrainte, test de toucher désactivé), et lignes 444 à 459 (la vue Bubble, .padding(.horizontal, Kit941.Spacing.sm) puis .overlay(Rectangle().strokeBorder(...)), son commentaire « A line said, as the old games drew it: a cream box with a dark border. » (Une réplique prononcée, comme la dessinaient les vieux jeux : une boîte crème à la bordure sombre.), caption gras, WorldInk.paper, une bordure d’encre de Kit941.Spacing.xxs, .fixedSize(), .accessibilityLabel("Said: \(text)")). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  26. Code source de Kiradex, Kiradex/World/WorldHUD.swift, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) ; le fichier était daté du 4 octobre et n’a pas changé pendant la lecture : lignes 31 à 38 (WorldInk, « The world’s own colours, as the old games drew a text box: cream paper » (les couleurs propres du monde, comme les vieux jeux dessinaient une boîte de texte : du papier crème), et sa couleur de papier), lignes 40 à 44 (la ligne du lieu « in a text box at the bottom. Half open, the box keeps to the wider side of the fold. » (dans une boîte de texte en bas. À moitié ouvert, la boîte se tient du côté le plus large du pli.)), lignes 61 à 79 (l’interface dans un GeometryReader, en retrait de .padding(Kit941.Spacing.md), la boîte sur toute cette largeur ou, à moitié ouvert, sur la largeur du volet le plus large moins Kit941.Spacing.md de chaque côté), et lignes 84 à 101 (la boîte : titre en caption gras, en capitales ; statut en caption demi-gras, identifiant <name>-status, plaza-status sur la place ; fond papier ; une bordure d’encre de Kit941.Spacing.xxs ; .allowsHitTesting(false) sous le commentaire « Taps go through to the ground under it. » (Les touchers traversent jusqu’au sol en dessous.)) ; et Kiradex/World/PlazaStage.swift lignes 581 et 582, où PlazaView pose le WorldHUD sur toute la scène avec words comme statut, et lignes 725 à 736 (words : la locution du moment de la journée, le nombre de présents et un conseil, les rubans gagnés et la locution de la saison). Les espacements du kit, Sources/Kit941/Tokens/Spacing.swift dans 941Kit : xxs 2, xs 4, sm 8, md 16, lg 24 points sur iPhone. ↩↩↩↩↩↩↩

  27. Code source de Kiradex, Kiradex.xcodeproj/project.pbxproj, lignes 1138 à 1141 (knownRegions = (Base, en)), lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) ; le fichier avait gagné des lignes à 12 h 42. ↩↩

  28. Code source de Kiradex, server/app/lines.py, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) : la docstring « The preset lines: everything anyone can say in the plaza. » (Les répliques prédéfinies : tout ce que chacun peut dire sur la place.), les 23 entrées de LINES (à partir de la ligne 5), et le commentaire de la ligne 20, avant l’index 14, « Indices are fixed; add at the end only, the app shows them by number. » (Les index sont fixes ; n’ajouter qu’à la fin, l’app les affiche par numéro.) ↩↩↩↩

  29. Code source de Kiradex, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) : scripts/forge/kit.py, lignes 529 et 530 (def sign():, « A hanging sign on a bracket, 12 × 11. » (un panneau suspendu à une potence, 12 × 11.)), Kiradex/World/TileMap.swift, ligne 52 (la lettre de carte "S": .sign), et Kiradex/Resources/World/town.json (une cellule S, en x 7, rangée 10, lue avec le module json de Python à 12 h 33). ↩↩↩↩

  30. Méthode. Décompilations de pret, clones superficiels lus à pokeemerald 731ad5b (1er octobre 2026), pokefirered 037335f (26 septembre 2026), pokecrystal 5beda23 (29 septembre 2026) et pokered d2704a6 (22 septembre 2026), https://github.com/pret. Final Fantasy VI d’après le désassemblage everything8215/ff6 à 813013276c952fdd27edcf7b8b86f17291542cbf (14 septembre 2026), qui contient du code et des listes d’extraction, pas les graphismes de la ROM, https://github.com/everything8215/ff6. PixelMplus cloné superficiellement à d89b95f04eeb95bf98bb09cb9234a2f99d186df5 (2 juin 2013) ; Misaki et k8x12, les versions du 2021-05-05 en TTF et BDF, téléchargées sur le site de l’auteur ; Press Start 2P, Silkscreen et DotGothic16 (fichiers de police, OFL.txt et METADATA.pb) tirés de google/fonts à 9710da1 (30 septembre 2026). Pages web récupérées et enregistrées le 5 octobre 2026. Treize scripts dans le dossier de preuves de l’auteur pour cet article, dix en Python et trois en Swift, et un script Swift de plus rangé avec les figures de cet article, dump_glyphs.swift, produisent chaque chiffre mesuré, chacun avec sa sortie enregistrée à côté. pret construit les jeux anglais ; aucune ROM japonaise n’a été lue. ↩↩↩↩↩↩

  31. pret, pokeemerald/src/fonts.c (gFontNormalLatinGlyphWidths à partir de la ligne 148) et pokeemerald/charmap.txt (lignes 50 à 52, glyphes spéciaux pour des mots de la franchise) au commit 731ad5b, lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/src/fonts.c. ↩↩

  32. Martin Korth, GBATEK, « LCD Dimensions and Timings » (« Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz » (228 lignes au total, 16,743 ms, 280 896 cycles, environ 59,737 Hz) ; 160 lignes visibles ; 240 points par ligne visible), consulté le 5 octobre 2026, https://problemkaputt.de/gbatek-lcd-dimensions-and-timings.htm. ↩

  33. Arithmétique de l’auteur sur les chiffres mesurés : 216 ÷ 5,25 = 41 caractères ; 59,737 ÷ 1 = 59,7 caractères par seconde ; 191 × 1,25² = 298 pixels ; les −33 unités de PixelMplus12 ÷ 83,33 unités par pixel = −0,396 pixel (la sonde donne −0,39, le centre de sa classe) ; 7 500 contre 6 879 ; 11 sur 12 et 9 sur 12 tailles Body hors grille, par complément de 1 et 3 tailles entières ; 14 ÷ 17 = 82 % et 53 ÷ 17 = 312 % ; 13,33 ÷ 16,67 = 80 % et 53,33 ÷ 16,67 = 320 % ; 11 points ÷ 2 points par texel = 5,5 texels ; 4 × 10 ÷ 3 = 13,33 points ; 12 pixels de police × 5 ÷ 3 = 20 points ; 440 − 32 = 408, 360 − 32 = 328 et 375 − 32 = 343 points ; 40 × 5 × 3 ÷ 2 + 16 = 316 points en 2× ; ⌈11 × 3 ÷ 10⌉ = 4 et ⌈11 × 2 ÷ 10⌉ = 3, et 10 × 3 ÷ 2 = 15 points ; (360 − 48) × 3 ÷ 4 = 234 et (375 − 48) × 2 ÷ 3 = 218 pixels de police, et 218 ÷ 5 = 43 caractères latins et 218 ÷ 10 = 21 pleine chasse ; 21 × 10 × 4 ÷ 3 + 16 = 296 et 21 × 10 × 3 ÷ 2 + 16 = 331 points ; (440 − 32) × 3 = 1 224 pixels de l’appareil, ÷ 5 = 244,8, donc 244 pixels de police (1 220 pixels de l’appareil), moins 2 × 8 = 228, ÷ 5 = 45 caractères latins et ÷ 10 = 22 pleine chasse ; 15 × 3 ÷ 10 = 4,5, arrondi à 5 ; 120 additions de 1/120 en double précision = 0,9999999999999989, × 30 = 29,999999999999968, arrondi par défaut à 29 ; 1,5 seconde × 12 = 18 caractères ; 20 × 5 × 4 ÷ 3 = 133 points et 20 × 5 × 3 ÷ 2 = 150 points en 2× ; 466 − 84 = 382 et 382 − 32 = 350 points, 466 − 32 = 434 ; 40 × 5 × 5 ÷ 3 + 16 = 349,33 points ; (382 − 32) × 3 ÷ 5 = 210 pixels de police, moins 2 × 8 = 194, ÷ 5 = 38 caractères latins et ÷ 10 = 19 pleine chasse ; (382 − 48) ÷ (5 × 5 ÷ 3) = 40,08 et ÷ (10 × 5 ÷ 3) = 20,04 ; 43 × 5 × 4 ÷ 3 + 16 = 302,67 points ; 360 − 84 − 32 = 244 points ; (3 − 1) × 2 × 1 = 4 touchers ; 16 texels × 2,67 points = 42,7 points et 13 × 2,67 = 34,7 ; 9 images ÷ 60 = 150 millisecondes, quatre pas 600 millisecondes ; 3 points × 3 = 9 pixels de l’appareil entre les lignes de balayage, chacune de 1 point × 3 = 3 pixels de l’appareil de haut. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  34. pret, pokeemerald/src/battle_message.c au commit 731ad5b (lignes 3002 et 3003 : défilement automatique activé quand gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED) ; ligne 3010, speed = 1 pour la fenêtre de message dans les combats en link ; ligne 3012, speed = sRecordedBattleTextSpeeds[GetTextSpeedInRecordedBattle()] d’un combat enregistré, la table {8, 4, 1, 0} à la ligne 1957) et pokeemerald/src/scrcmd.c (ScrCmd_messageautoscroll à partir de la ligne 1286, qui active autoScroll et forceMidTextSpeed aux lignes 1292 et 1293), lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/src/battle_message.c. ↩↩↩

  35. pret, pokeemerald/charmap.txt au commit 731ad5b, lignes 1087 à 1089 ('\l' = FA @ scroll up window text, '\p' = FB @ new paragraph, '\n' = FE @ new line), et include/constants/characters.h, lignes 175 et 176 (CHAR_PROMPT_SCROLL, « waits for button press and scrolls dialog » (attend une pression sur un bouton et fait défiler le dialogue) ; CHAR_PROMPT_CLEAR, « waits for button press and clears dialog » (attend une pression sur un bouton et efface le dialogue)), avec src/text.c lignes 968 à 971 (CHAR_NEWLINE qui descend currentY d’une ligne) et 1102 à 1109 (les deux invites qui entrent dans RENDER_STATE_CLEAR et RENDER_STATE_SCROLL_START), lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/charmap.txt. ↩

  36. pret, pokeemerald/src/text_window.c (message_box.png à la ligne 53 ; LoadMessageBoxGfx à la ligne 93 ; LoadUserWindowBorderGfx à la ligne 110), pokeemerald/include/text_window.h (#define WINDOW_FRAMES_COUNT 20 à la ligne 4), pokeemerald/src/option_menu.c (l’option FRAME) et pokeemerald/graphics/text_window/ au commit 731ad5b, lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/src/text_window.c. ↩↩↩

  37. pret, pokeemerald/data/text/ et le scripts.inc de chaque carte au commit 731ad5b, dont data/maps/LittlerootTown/scripts.inc, ligne 854 (.string "MOM: {PLAYER}, we're here, honey!\p"), lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/data/maps/LittlerootTown/scripts.inc. ↩↩

  38. pret, pokeemerald/src/international_string_util.c, src/contest.c et src/battle_tower.c (les codes EXT_CTRL_CODE_JPN et EXT_CTRL_CODE_ENG dans les noms) et graphics/fonts/ (japanese_normal.png, japanese_small.png, japanese_bold.png, japanese_frlg_male.png) au commit 731ad5b, lu le 5 octobre 2026, https://github.com/pret/pokeemerald/blob/master/src/international_string_util.c. ↩↩

  39. pret, pokefirered/src/new_menu_helpers.c au commit 037335f (sStandardTextBox_WindowTemplates à partir de la ligne 34, .width = 26 à la ligne 40, et les délais de vitesse du texte) et pokefirered/src/text_printer.c (AddTextPrinter, textSpeed = speed à la ligne 78 et --sTempTextPrinter.textSpeed; à la ligne 93) au commit 037335f, lu le 5 octobre 2026, https://github.com/pret/pokefirered/blob/master/src/new_menu_helpers.c. ↩

  40. Pan Docs, « Rendering » (« One frame: 70224 dots @ 59.7 fps » (une image : 70 224 points à 59,7 fps) ; une image « takes ~16.74 ms instead of ~16.67 » (dure environ 16,74 ms au lieu d’environ 16,67)), gbdev.io, consulté le 5 octobre 2026, https://gbdev.io/pandocs/Rendering.html. ↩

  41. pret, pokecrystal/gfx/font.asm au commit 5beda23 (les cadres FRAME_1 à FRAME_8, TEXTBOX_FRAME_TILES de 6 tuiles d’un bit chacun, un neuvième cadre inutilisé) et gfx/font/font.png, lu le 5 octobre 2026, https://github.com/pret/pokecrystal/blob/master/gfx/font.asm. ↩

  42. everything8215, ff6, tools/rip_list_en.json et tools/rip_list_jp.json au commit 8130132 (assets/gfx/window/window_%s.4bpp de 0xED0000 à 0xED1BFF ; window_%s.pal de 0xED1C00 à 0xED1CFF), lu le 5 octobre 2026, https://github.com/everything8215/ff6/blob/master/tools/rip_list_en.json. ↩

  43. everything8215, ff6, src/menu/config.asm (ConfigWindowNumPos, LoadWindowPal qui lit à $1d57, la couleur de police à $1d55, ConfigColorBarTextList, ChangeConfigOption_02) et src/menu/menu_sram.asm (ligne 102, « message speed = 3, battle speed = 3 » (vitesse des messages = 3, vitesse de combat = 3)) au commit 8130132, lu le 5 octobre 2026, https://github.com/everything8215/ff6/blob/master/src/menu/config.asm. ↩↩↩

  44. everything8215, ff6, au commit 8130132 : src/btlgfx/gfx_cmd.asm lignes 1101 à 1103 (MsgSpeedTbl, « wait duration for each battle message speed » (durée d’attente pour chaque vitesse des messages de combat), $20,$30,$40,$50,$60,$70,$80,$90) et lignes 1151 à 1165 (mess_wait, qui lit les bits 4 à 6 de $1d4d comme index et appelle WaitFrame autant de fois) ; src/menu/config.asm ligne 1499 (« battle speed, battle message speed, magic order, and window pattern » (vitesse de combat, vitesse des messages de combat, ordre des magies et motif de fenêtre)) et ChangeConfigOption_02 à partir de la ligne 933 ; src/menu/menu_text.en.inc ligne 346 (les réglages dessinés de 1 à 6). Fichiers dont le code touche $1d4d : src/battle/init.asm (le réglage des commandes, le mode de combat et la vitesse de combat), src/btlgfx/gfx_cmd.asm, src/menu/config.asm, src/menu/field_menu.asm et src/menu/menu_sram.asm ; aucun sous src/field/. Lu le 5 octobre 2026, https://github.com/everything8215/ff6/blob/master/src/btlgfx/gfx_cmd.asm. ↩

  45. everything8215, ff6, src/field/hdma.asm (DlgWindowFixedColorTbl) et src/field/event.asm (ligne 1591, « dialog window y position (1 or 18) » (position y de la fenêtre de dialogue, 1 ou 18)) au commit 8130132, lu le 5 octobre 2026, https://github.com/everything8215/ff6/blob/master/src/field/hdma.asm. ↩↩↩

  46. everything8215, ff6, src/field/text.asm (LoadLetterGfx, la ligne de dialogue de x 4 à $e0, CalcTextWidth, NewLine, UpdateDlgTextOneLine, le titre de carte centré affiché pendant 100 images) et src/gfx/font_gfx.asm (FontWidth) au commit 8130132, lu le 5 octobre 2026, https://github.com/everything8215/ff6/blob/master/src/field/text.asm. ↩↩↩↩↩

  47. everything8215, ff6, tools/rip_list_jp.json (large_font.jp.1bpp de 0xC494C0 à 0xC4FFFF) et tools/char_table/ au commit 8130132, lu le 5 octobre 2026, https://github.com/everything8215/ff6/tree/master/tools/char_table. ↩

  48. everything8215, ff6, src/field/reset.asm (la boucle de terrain : attendre le retour vertical, appeler UpdateDlgText une fois par passage) au commit 8130132, lu le 5 octobre 2026, https://github.com/everything8215/ff6/blob/master/src/field/reset.asm. Le désassemblage construit la ROM anglaise et la ROM japonaise à partir d’une seule source. ↩↩

  49. Data Crystal, « Chrono Trigger/ROM map », datacrystal.tcrf.net, consulté le 5 octobre 2026 : HTTP 403, non lu, https://datacrystal.tcrf.net/wiki/Chrono_Trigger/ROM_map. ↩↩↩

  50. Kiradex, docs/TestFlight.md, étape 5 (lignes 192 à 194 : dans le Dex, choisir le japonais affiche les séries et les cartes avec leurs noms japonais et l’anglais en dessous), lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé). ↩

  51. Num Kadoma, littlelimit.net, « 8×8ドット日本語フォント「美咲フォント」 » (la cellule de 8 sur 8 de la présentation, les niveaux JIS 1 et 2 et l’encre de 7 sur 7 ; les descriptions de Misaki Mincho et de Misaki Gothic 2nd ; la note sur la licence M+ FONTS ; le récit de sa création pour le pilote d’affichage japonais du PC-E500), consulté le 5 octobre 2026, https://littlelimit.net/misaki.htm. ↩↩↩↩

  52. Num Kadoma, littlelimit.net, « 8×12ドット日本語フォント「k8x12」 » (la cellule de 8 sur 12, les niveaux JIS 1 et 2, les variantes L et S, la note de la variante S sur les graphismes de 8 sur 8 des anciennes consoles), consulté le 5 octobre 2026, https://littlelimit.net/k8x12.htm. ↩

  53. Google Fonts, ofl/dotgothic16/OFL.txt (SIL Open Font License 1.1, Copyright 2020 The DotGothic16 Project Authors, sans Reserved Font Name) et METADATA.pb (designer Fontworks Inc. ; sous-ensembles cyrillic, japanese, latin, latin-ext ; ajoutée le 2020-12-15) à 9710da1, lu le 5 octobre 2026, https://github.com/google/fonts/blob/main/ofl/dotgothic16/OFL.txt. ↩↩↩↩

  54. Google Fonts, ofl/pressstart2p/OFL.txt (SIL Open Font License 1.1, « with Reserved Font Name “Press Start 2P” » (avec le Reserved Font Name « Press Start 2P »)) et METADATA.pb (designer CodeMan38) à 9710da1, lu le 5 octobre 2026, https://github.com/google/fonts/blob/main/ofl/pressstart2p/OFL.txt. ↩↩↩

  55. Google Fonts, ofl/silkscreen/OFL.txt (SIL Open Font License 1.1, sans Reserved Font Name) et METADATA.pb (designer Jason Kottke) à 9710da1, lu le 5 octobre 2026, https://github.com/google/fonts/blob/main/ofl/silkscreen/OFL.txt. ↩↩

  56. DaFont, « Pixel Operator Font » (de Jayvee Enaguas ; « Bitmap (16 px) » ; 15 fichiers de police ; 241 glyphes ; les notes de l’auteur, « Changed licence to Creative Commons Zero (CC0) 1.0 » (licence changée pour Creative Commons Zero (CC0) 1.0) et « licensed under a Creative Commons Zero (CC0) 1.0 » (sous licence Creative Commons Zero (CC0) 1.0)), consulté le 5 octobre 2026, https://www.dafont.com/pixel-operator.font. Le dépôt que nomment les notes, https://notabug.org/HarvettFox96/ttf-pixeloperator, a renvoyé HTTP 404 le 5 octobre 2026. Source unique. ↩↩↩↩

  57. Daniel Linssen, « m5x7 », itch.io (« use font size 16, 32, 48, etc » (utilisez une taille de police de 16, 32, 48, etc.) ; « free to use but attribution appreciated » (libre d’utilisation, mention appréciée) ; « Asset license Creative Commons Zero v1.0 Universal » (licence de l’asset Creative Commons Zero v1.0 Universal) ; largeur moyenne 5, jambage supérieur 7, jambage inférieur 2, hauteur d’x 5 ; ses caractères décrits comme « uppercase and lowercase letters, including many accented letters, numbers and most common symbols » (des lettres majuscules et minuscules, dont de nombreuses lettres accentuées, des chiffres et la plupart des symboles courants) ; un téléchargement à prix libre), consulté le 5 octobre 2026, https://managore.itch.io/m5x7. Le fichier de police n’a pas été téléchargé. ↩↩↩↩↩↩

  58. Daniel Linssen, « m3x6 », itch.io (« use font size 16, 32, 48, etc » (utilisez une taille de police de 16, 32, 48, etc.) ; « free to use with attribution » (libre d’utilisation avec mention) ; aucun champ de licence d’asset ; un téléchargement à prix libre), consulté le 5 octobre 2026, https://managore.itch.io/m3x6. Le fichier de police n’a pas été téléchargé. ↩↩↩↩↩

  59. Apple Developer Documentation, « Core Text » (« Create text layouts, optimize font handling, and access font metrics and glyph data » (créer des mises en page de texte, optimiser la gestion des polices et accéder aux métriques des polices et aux données des glyphes) ; iOS 3.2, macOS 10.8), consulté le 5 octobre 2026, https://developer.apple.com/documentation/coretext. ↩

  60. Mesure de l’auteur, 5 octobre 2026 : measure_hig.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_hig.out : les tables de la page Typography enregistrée, les tailles par défaut et minimales et chaque table Dynamic Type d’iOS de xSmall à AX5 (Body 14, 15, 16, 17, 19, 21, 23, 28, 33, 40, 47 et 53 points ; Caption 1 11, 11, 11, 12, 14, 16, 18, 22, 26, 32, 37 et 43). ↩↩↩↩↩

  61. Consortium Unicode, « Unicode Standard Annex #14: Unicode Line Breaking Algorithm », révision 57, Unicode 18.0.0, 2026-09-01 (la classe CJ et « Treating characters of class CJ as class NS will give CSS strict line breaking; treating them as class ID will give CSS normal breaking. » (Traiter les caractères de la classe CJ comme de la classe NS donne la coupure stricte de CSS ; les traiter comme de la classe ID donne la coupure normale de CSS.)), consulté le 5 octobre 2026, https://www.unicode.org/reports/tr14/. ↩↩

  62. Stardew Valley Wiki, « Modding:Dialogue », dernière modification le 20 mai 2026 ($b ; commandes de portrait ; Strings\SpeechBubbles), consulté le 5 octobre 2026, https://stardewvalleywiki.com/Modding:Dialogue. ↩↩↩

  63. Stardew Valley Wiki, « Modding:Custom languages », dernière modification le 3 janvier 2026 (UseLatinFont, le fichier .fnt, FontPixelZoom avec une base recommandée de 1,5, FontApplyYOffset), consulté le 5 octobre 2026, https://stardewvalleywiki.com/Modding:Custom_languages. ↩↩

  64. Mesure de l’auteur, 5 octobre 2026 : measure_steam_languages.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_steam_languages.out, qui lit les tables de langues des pages Steam enregistrées de Stardew Valley (https://store.steampowered.com/app/413150/), Celeste (https://store.steampowered.com/app/504230/), Sea of Stars (https://store.steampowered.com/app/1244090/) et Eastward (https://store.steampowered.com/app/977880/), consultées le 5 octobre 2026. ↩↩↩↩↩

  65. Mesure de l’auteur, 5 octobre 2026 : measure_apple_docs.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_apple_docs.out : le titre, la déclaration, le résumé, les plateformes et les premiers paragraphes de chaque page de documentation Apple enregistrée citée à la section 10. ↩↩

  66. Apple, Human Interface Guidelines, « Designing for games » (« To keep text comfortably legible on each device, ensure that it contrasts well with the background and uses at least the recommended minimum text size in each platform. » (Pour que le texte reste confortablement lisible sur chaque appareil, veillez à ce qu’il contraste bien avec l’arrière-plan et utilise au moins la taille de texte minimale recommandée sur chaque plateforme.)), consulté le 5 octobre 2026, https://developer.apple.com/design/human-interface-guidelines/designing-for-games. ↩

  67. Apple Developer Documentation, « UIFontMetrics » (« A utility object for obtaining custom fonts that scale to support Dynamic Type » (un objet utilitaire pour obtenir des polices personnalisées qui se mettent à l’échelle afin de prendre en charge Dynamic Type) ; iOS 11.0), « scaledFont(for:) », « scaledFont(for:maximumPointSize:) » et « scaledValue(for:) » (« Scales an arbitrary layout value based on the current Dynamic Type settings » (met à l’échelle une valeur de mise en page arbitraire d’après les réglages Dynamic Type en cours) ; iOS 11.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/uikit/uifontmetrics. ↩↩↩

  68. Apple Developer Documentation, « custom(:size:relativeTo:) » (une police personnalisée « that scales relative to the given textStyle » (qui se met à l’échelle par rapport au textStyle donné) ; iOS 14.0), https://developer.apple.com/documentation/swiftui/font/custom(:size:relativeto:), et « custom(:fixedSize:) » (une police personnalisée « that does not scale with Dynamic Type » (qui ne se met pas à l’échelle avec Dynamic Type) ; iOS 14.0), https://developer.apple.com/documentation/swiftui/font/custom(:fixedsize:), consultés le 5 octobre 2026. ↩↩↩↩↩

  69. Apple Developer Documentation, « ScaledMetric » (« A dynamic property that scales a numeric value » (une propriété dynamique qui met à l’échelle une valeur numérique) ; iOS 14.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/scaledmetric. ↩↩

  70. Apple Developer Documentation, « dynamicTypeSize(:) » (iOS 15.0 ; « When limiting the Dynamic Type size, consider if adding a large content view with accessibilityShowsLargeContentViewer() would be appropriate. » (Quand vous limitez la taille Dynamic Type, demandez-vous s’il serait approprié d’ajouter une vue de grand contenu avec accessibilityShowsLargeContentViewer().)), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/view/dynamictypesize(:). ↩↩

  71. Apple Developer Documentation, « accessibilityShowsLargeContentViewer() » (iOS 15.0 ; « Don’t use the large content viewer as a replacement for proper Dynamic Type support. » (N’utilisez pas le visualiseur de grand contenu en remplacement d’une véritable prise en charge de Dynamic Type.)), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/view/accessibilityshowslargecontentviewer(). ↩↩

  72. Apple Developer Documentation, « UIAppFonts » (« App-specific font files located in the bundle and that the system loads at runtime » (des fichiers de police propres à l’app, situés dans le bundle et que le système charge à l’exécution) ; iOS 3.2), consulté le 5 octobre 2026, https://developer.apple.com/documentation/bundleresources/information-property-list/uiappfonts. ↩↩

  73. Apple Developer Documentation, « CTFontManagerRegisterFontsForURL(:::) » (iOS 4.1), consulté le 5 octobre 2026, https://developer.apple.com/documentation/coretext/ctfontmanagerregisterfontsforurl(:::). ↩

  74. Apple Developer Documentation, « interpolation(:) » (Image ; iOS 13.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/image/interpolation(:). ↩

  75. Apple Developer Documentation, « resizable(capInsets:resizingMode:) » (« Sets the mode by which SwiftUI resizes an image to fit its space » (définit la manière dont SwiftUI redimensionne une image pour qu’elle remplisse son espace) ; iOS 13.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/image/resizable(capinsets:resizingmode:). ↩

  76. Apple Developer Documentation, « baselineOffset(:) » (Text ; « Sets the vertical offset for the text relative to its baseline » (définit le décalage vertical du texte par rapport à sa ligne de base) ; iOS 13.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/text/baselineoffset(:). ↩

  77. Apple Developer Documentation, « typesettingLanguage(:isEnabled:) » (iOS 17.0 ; le passage sur le texte dans une langue qui ne correspond pas à la langue de l’interface de l’appareil ; « this language does not affect text localization. » (cette langue n’affecte pas la localisation du texte.)), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/view/typesettinglanguage(:isenabled:). ↩↩

  78. Apple Developer Documentation, « NSParagraphStyle.LineBreakStrategy » (iOS 9.0) et ses propriétés standard (iOS 14.0), pushOut (iOS 9.0) et hangulWordPriority (iOS 14.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/uikit/nsparagraphstyle/linebreakstrategy-swift.struct. ↩

  79. Apple Developer Documentation, « accessibilityValue(:) » (« Use this method to describe the value represented by a view, but only if that’s different than the view’s label. » (Utilisez cette méthode pour décrire la valeur que représente une vue, mais seulement si elle diffère du libellé de la vue.) ; iOS 16.0 dans la forme LocalizedStringResource), https://developer.apple.com/documentation/swiftui/view/accessibilityvalue(:), et « accessibilityLabel(:) » (iOS 16.0), https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(:), consultés le 5 octobre 2026. ↩↩↩

  80. Apple Developer Documentation, « accessibilityDirectTouch(:options:) » (iOS 17.0 ; « Direct touch areas passthrough touch events to the app rather than being handled through an assistive technology, such as VoiceOver. » (Les zones de toucher direct transmettent les événements tactiles à l’app au lieu qu’ils soient traités par une technologie d’assistance comme VoiceOver.) ; la zone doit d’abord être activée), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/view/accessibilitydirecttouch(:options:). ↩↩

  81. Apple Developer Documentation, « updatesFrequently » (AccessibilityTraits ; iOS 13.0 ; « Use this trait when you want an assistive technology to poll for changes when it needs updated information. » (Utilisez ce trait quand vous voulez qu’une technologie d’assistance interroge les changements lorsqu’elle a besoin d’informations à jour.)), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/accessibilitytraits/updatesfrequently. ↩

  82. Apple Developer Documentation, « AccessibilityNotification.Announcement » (iOS 17.0 ; l’attribut accessibilitySpeechAnnouncementPriority), consulté le 5 octobre 2026, https://developer.apple.com/documentation/accessibility/accessibilitynotification/announcement. ↩↩

  83. Apple Developer Documentation, « accessibilityReduceMotion » (iOS 13.0 ; « If this property’s value is true, UI should avoid large animations » (si la valeur de cette propriété est true, l’interface devrait éviter les grandes animations)), consulté le 5 octobre 2026, https://developer.apple.com/documentation/swiftui/environmentvalues/accessibilityreducemotion. ↩↩

  84. Apple Developer Documentation, « AccessibilityComponent » (RealityKit ; « A component that stores accessibility information for an entity » (un composant qui stocke des informations d’accessibilité pour une entité) ; le nom que « VoiceOver says aloud when it describes the entity » (VoiceOver prononce quand il décrit l’entité) ; iOS 17.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/realitykit/accessibilitycomponent. ↩↩↩↩

  85. Apple Developer Documentation, « Improving the Accessibility of RealityKit Apps » (« set the properties on each visible Entity in your scene » (définir les propriétés de chaque Entity visible de votre scène)), consulté le 5 octobre 2026, https://developer.apple.com/documentation/realitykit/improving-the-accessibility-of-realitykit-apps. ↩

  86. Apple Developer Documentation, « RealityView » (iOS 18.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/realitykit/realityview. ↩

  87. Apple Developer Documentation, « XCUIVoiceOverService » (« Provides programmatic control of VoiceOver for UI testing » (fournit un contrôle programmatique de VoiceOver pour les tests d’interface) ; iOS 27.0), consulté le 5 octobre 2026, https://developer.apple.com/documentation/xcuiautomation/xcuivoiceoverservice. ↩↩↩

  88. Code source de 941Kit, Sources/Kit941/Tokens/Typography/FontFamily.swift au commit 50bc657 (28 septembre 2026), lignes 21 à 63 (case custom(named: String), rendu via Font.custom(_:size:relativeTo:) à des tailles intrinsèques de 34, 22, 17, 13 et 12 points pour display, title, body, label et caption), lu le 5 octobre 2026 ; dépôt privé. ↩↩

  89. Code source de Kiradex, server/app/names.py (lignes 7 à 14, FIRSTS et SECONDS, 20 mots chacune, et le motif de pseudo à deux mots capitalisés et quatre chiffres), lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé). ↩↩

  90. Blake Crosley, « A Pixel Town Alive When Nobody Is Online: NPCs and Ghosts », blakecrosley.com, 5 octobre 2026 (les 23 répliques prédéfinies, 14 pour les joueurs et 9 pour les collectionneurs propres à la ville, et une réplique toutes les 2,0 secondes par joueur), https://blakecrosley.com/blog/pixel-art-town-alive-when-nobody-is-online. ↩↩

  91. Kiradex, docs/WORLD.md, section 5, « The stack », lignes 524 et 525 (limites de débit : « eight moves a second, a line every two seconds » (huit déplacements par seconde, une réplique toutes les deux secondes)), lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé). ↩↩

  92. Code source de Kiradex, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) : Kiradex/World/PlazaStage.swift, lignes 199 à 203 (le .gesture(DragGesture(minimumDistance: 0)) de la scène, qui appelle touched à chaque changement et lifted à la fin), lignes 329 à 359 (touched(_:in:) : sans le joystick, le sol sous le doigt est rejoint aussitôt et suivi de tuile en tuile, tandis qu’un toucher sur un collectionneur ou sur les tuiles du kiosque rend la main et attend le lever), lignes 364 à 380 (lifted(_:in:) : un toucher qui n’a ni fait marcher ni dirigé est un toucher simple ; sur un autre collectionneur, il règle tapped, sur les tuiles du kiosque il appelle onKiosk, et sinon il exécute rig.walk(to: tile)), lignes 31 et 32 (onKiosk, « The kiosk tapped: the town’s board. » (Le kiosque touché : le tableau de la ville.)), ligne 82 (stickKey, "world.stick"), l’argument de lancement de test -plazaFacingDemo (le .task des lignes 255 à 270), et le tellOnArrival de PlazaView, qui lit -plazaTell (lignes 515 à 522) ; KiradexUITests/KiradexUITests.swift, lignes 384 et 387, où le test de marche touche la scène à des fractions de son cadre. ↩↩↩↩

  93. Code source de Kiradex, Kiradex/World/PlazaRig.swift, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) : fit(viewSize:displayScale:tilesTall:fitsWhole:) à la ligne 432 (pixelScale, la partie entière de la hauteur en pixels de la vue divisée par tilesTall × TileMap.tileSize, 20 × 16 = 320, ligne 439 ; TileMap.tileSize vaut 16 à la ligne 60 de TileMap.swift) et headPoint(of:) à la ligne 479 (la position du marcheur plus la moitié de la hauteur d’image de la planche, plus 4, lignes 482 et 483). Le fichier avait de nouveau changé à 12 h 42 ; les deux fonctions étaient alors inchangées. ↩↩

  94. Code source de Kiradex, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) : Kiradex/World/PlazaStage.swift ligne 311 (.ignoresSafeArea() sur la scène, dont le GeometryReader, lignes 121 à 294, contient la surcouche des bulles), et aucune lecture des marges de la zone de sécurité nulle part dans le fichier ; Kiradex/World/WorldHUD.swift lignes 26 et 27 (.statusBarHidden() et .persistentSystemOverlays(.hidden) sur WorldView, la racine du monde) et lignes 40 à 42 (l’interface est « over its stage and inside the safe area, which on the Duo keeps it clear of the cameras and the side rail » (au-dessus de sa scène et dans la zone de sécurité, ce qui, sur le Duo, la tient à l’écart des caméras et du rail latéral)) ; et Kiradex/Views/Card/CardViewer.swift lignes 19 à 24, où un GeometryReader extérieur à la scène de la carte transmet proxy.safeAreaInsets à CardStage, qui ignore la zone de sécurité, si bien que « the card rests inside the safe area, clear of the Duo’s outer camera and side rail » (la carte repose dans la zone de sécurité, à l’écart de la caméra extérieure et du rail latéral du Duo), avec les deux mêmes modificateurs aux lignes 56 et 57. Une recherche de safeAreaInsets dans Kiradex/ n’a trouvé que la ligne de CardViewer. ↩↩↩↩↩

  95. Blake Crosley, « Xcode 27.1 Beta: Your App in the iPhone Duo Simulator », blakecrosley.com, 21 septembre 2026, mis à jour le 2 octobre 2026 (exécuter une app sur le simulateur d’iPhone Duo, écrans extérieur et intérieur ; fermé, lié au SDK 27.1, une fenêtre de 466 points sur 678 dont le contenu, dans une pile de navigation, mesurait 382 points sur 562, avec des marges de zone de sécurité de 82 en haut, 34 en bas et 84 du côté de fin, le rail, dans une sonde avec une barre d’outils et une barre d’onglets sur ce bord et la barre d’état affichée), https://blakecrosley.com/blog/xcode-27-1-beta-iphone-duo-simulator. ↩↩↩↩

  96. Mesure de l’auteur, 5 octobre 2026 : measure_figure_width.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_figure_width.out, relancé à 12 h 36, heure du Pacifique, sur Kiradex au commit 334fbfe avec les modifications non validées de ce jour, sortie inchangée. Entrées, lues uniquement dans Kiradex : Kiradex/Resources/World/characters.json ("cell": [32, 40], les images et les orientations), scripts/forge/rig.py (ligne 53, CELL_W, CELL_H = 32, 40) et la planche de corps fig-body.png (1 376 pixels sur 160, 43 images sur 4 orientations). Dans l’image debout idle_a, le personnage fait 30 texels de haut dans chaque orientation, 16 de large à sa rangée la plus large face vers le bas ou vers le haut, et 13 face à gauche ou à droite. ↩↩↩

  97. Mesure de l’auteur, 5 octobre 2026 : measure_ios27_iphones.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_ios27_iphones.out, relancé à 12 h 36, heure du Pacifique, sortie inchangée. Il lit xcrun simctl list runtimes -j pour le runtime de simulateur d’iOS 27.0 (24A434) installé avec Xcode 27.0 sur le Mac de l’auteur, et le capabilities.plist de chaque type d’appareil iPhone (main-screen-width, main-screen-height, main-screen-scale) : 33 iPhone, de l’iPhone 12 mini et 13 mini (1 080 pixels sur 2 340 en 3×, 360 points sur 780) aux iPhone 16, 17 et 18 Pro Max (440 points sur 956), dont trois en 2×, l’iPhone SE (2e et 3e génération, 375 points sur 667) et l’iPhone 11 (414 sur 896). C’est la liste d’appareils du simulateur, pas la page de compatibilité d’Apple, qui n’a pas été récupérée. Également tirés du fichier de projet de Kiradex au commit 334fbfe, en lecture seule : le TARGETED_DEVICE_FAMILY = 1 (iPhone) de la cible de l’app dans ses deux configurations (lignes 1559 et 1705 à la lecture de 12 h 28) et IPHONEOS_DEPLOYMENT_TARGET = 27.0 dans les deux configurations du projet (lignes 1608 et 1670), que la cible de l’app ne redéfinit pas. ↩↩↩↩↩↩↩

  98. Mesure de l’auteur, 5 octobre 2026 : probe_line_start_glyphs.swift dans le dossier de preuves de l’auteur pour cet article, compilé avec swiftc -O et exécuté sous macOS 27.0 (build 26A428), pas iOS, sortie probe_line_start_glyphs.out. Il lit les dix classes de début de ligne de JLREQ 3.1.7 dans les tables en annexe de la spécification enregistrée (83 entrées : cl-02 16, cl-03 4, cl-04 6, cl-05 3, cl-06 2, cl-07 2, cl-09 6, cl-10 1, cl-11 40, cl-29 3), demande à Core Text lesquelles PixelMplus10 Regular fait correspondre (CTFontGetGlyphsForCharacters, sans police de secours) : 54, avec 〜 U+301C, ゠ U+30A0 et le tiret demi-cadratin de cl-03, ⦆, 〙 et 〗 de cl-02, ‼ ⁇ ⁈ ⁉ de cl-04, 〻 de cl-09 et 18 petits kana de cl-11 (ゕ, ゖ et U+31F0 à U+31FF) non mappés. Il dessine les 7 223 points de code que la police fait correspondre à 10 pixels, anticrénelage et lissage des polices désactivés, et regroupe les cellules identiques au pixel près : parmi les 54, ‐ U+2010 correspond à U+2011 et : U+003A à U+A789, et aucun autre n’a de jumeau. À 50 pixels (m = 5), anticrénelage activé et lissage désactivé, ー U+30FC dessine 200 pixels encrés de l’appareil avec un crochet à son extrémité gauche et 一 U+4E00 250, non identiques, tous deux avec 0 pixel partiel. Chaque caractère ASCII imprimable avance de 5 pixels de police. Étendu plus tard le 5 octobre, avec le script et la sortie antérieurs conservés à côté et chaque ligne antérieure de la sortie inchangée : les 24 caractères du bloc Halfwidth and Fullwidth Forms, U+FF01 à U+FFEF, dont la normalisation de compatibilité (NFKC) est l’un des caractères listés (! ) , . : ; ? ] } ⦆ 。 」 、 ・ ァ ィ ゥ ェ ォ ャ ュ ョ ッ ー), et ~ U+FF5E, ajouté par l’auteur : la police en fait correspondre 24 sur 25, tous sauf ⦆ U+FF60, les pleine chasse larges de 10 pixels et les demi-chasse de 5, et aucun des 24 n’a de jumeau identique au pixel près parmi les points de code de la police ; l’ensemble de comparaison compte donc 75 caractères : les 54 entrées contiennent 51 caractères, puisque ), 〕 et ] figurent à la fois dans cl-02 et cl-29, et les 24 formes portent le total à 75 (la dernière ligne de la sonde elle-même, 54 + 24 = 78, compte des entrées). ↩↩↩↩

  99. Mesure de l’auteur, 15 octobre 2026 : measure_cell_masks.py dans le dossier de preuves de l’auteur pour cet article, sortie measure_cell_masks.out et liste d’empreintes canon_cell_masks.txt, exécuté sur les fichiers pret enregistrés pour cet article (pokeemerald graphics/fonts, 17 planches, et graphics/text_window, 21 ; pokecrystal gfx/frames, 10 ; le gfx/font/font.png de pokecrystal et de pokered). Chaque planche est décodée en RGBA ; un pixel est actif là où il diffère du pixel supérieur gauche de la planche ; les cellules font 8 sur 8 depuis le coin supérieur gauche ; les cellules vides et pleines sont écartées ; l’empreinte d’un masque est le SHA-256 de ses 8 octets de rangée, le pixel le plus à gauche étant le bit de poids fort. japanese_normal.png, 128 pixels sur 512 : 1 024 cellules, 430 vides, 594 gardées, 478 distinctes. Les 50 planches : 7 746 cellules gardées, 1 484 empreintes distinctes. ↩↩

  100. Blake Crosley, « Pixel-Art People: Characters and a Creator on iPhone », blakecrosley.com, 3 octobre 2026, lu le 5 octobre 2026 : pour le personnage des builds 21 à 33, « The cell is 32 by 32; the figure is 26 tall to the crown of the head, 27 with most hair (28 with buns, spikes or the puff), and about 14 wide at the shoulders » (la cellule fait 32 sur 32 ; le personnage mesure 26 jusqu’au sommet du crâne, 27 avec la plupart des coiffures (28 avec chignons, pics ou la houppe), et environ 14 de large aux épaules) ; dans sa mise à jour pour le build 34, « The cell is 32 by 40 now, with eight rows of headroom and two under the feet, and the figure 30 pixels » (la cellule fait désormais 32 sur 40, avec huit rangées de marge au-dessus de la tête et deux sous les pieds, et le personnage 30 pixels) ; et les ancres de tête et de main du manifeste pour la bulle et la carte 3D, « though both still take their place from the cell’s height for now » (même si toutes deux prennent encore leur place d’après la hauteur de la cellule pour l’instant), https://blakecrosley.com/blog/pixel-art-characters-on-iphone. ↩↩

  101. Lecture par l’auteur de la copie de travail de Kiradex au commit fc9d4876 (refs/heads/main), 15 octobre 2026 à 22 h 29, heure du Pacifique : Kiradex/World/PlazaStage.swift (modifié à 17 h 13 ce jour-là), ligne 373 (.modifier(StageAccessibility(covered: covered, trace: stepTrace))), ligne 520 (private struct StageAccessibility: ViewModifier), lignes 511 à 513 (le commentaire : la scène est un élément d’accessibilité, « The plaza » avec le dernier pas du joueur comme valeur, ou aucun tant qu’un lieu est posé par-dessus), ligne 525 (content.accessibilityRepresentation {), ligne 526 (if !covered {), ligne 533 (.accessibilityLabel("The plaza")) et ligne 534 (.accessibilityValue(trace)). Les autres lectures de Kiradex de l’article datent du 5 octobre et sont datées. ↩

  102. Code source de Kiradex, server/app/npcs.py, lu à 12 h 33, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe (le fichier daté du 3 octobre ; dépôt privé) : les quatre collectionneurs de la ville de DEFINITIONS (lignes 36 à 58), Moss Keeper 1999, Prism Scout 2024, Comet Pocket 3030 et Dawn Sprout 4040, dont les répliques sont des index de LINES ; chacun dit sa première réplique au bout de 8 à 20 secondes (ligne 77) puis une toutes les 20 à 40 secondes (ligne 108). server/app/main.py ligne 231 diffuse la réplique d’un joueur à toute la salle, joueur compris (le except_id de broadcast n’est pas défini ; rooms.py ligne 182), lu à 12 h 33. ↩↩

  103. Code source de Kiradex, KiradexUITests/KiradexUITests.swift, lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé) ; le fichier était daté de 10 h 32 et n’a pas changé pendant la lecture : ligne 228 (le me-handle de l’onglet « Me » contient le pseudo, deux mots et un nombre), testTheCollectorFacesTheWayItWalks à partir de la ligne 432 (lit la valeur de la scène pour chaque orientation), testThePlazaKeepsTime à partir de la ligne 502 (lit plaza-status et vérifie son préfixe, ligne 509), testTheChimneysSmoke à partir de la ligne 523 (lancé avec -airProbe, attend value CONTAINS 'smoke 18 ' sur la scène, ligne 531), testVisitingARoomIsAVisit (lignes 469 à 472) et testYourRoomHasYourBinders (lignes 604 à 607), qui attendent les premiers mots de room-status, testTheShowHallHasEveryonesCards (lignes 490 à 494), qui attend hall-status, testTheTownHasCollectors (lignes 672 à 676), qui attend sur plaza-status un nombre de cinq « here » ou plus, et testThePlazaHasCompany à partir de la ligne 689 (ignoré sauf avec KIRADEX_PLAZA=1, le serveur, et d’après son message d’ignorance il lui faut aussi un autre joueur, lignes 690 et 691 ; attend le nombre sur plaza-status, choisit « Hi! » dans le menu « Say » et attend 5 secondes la bulle Said: Hi!, ligne 714). ↩↩↩↩

  104. Code source de Kiradex, server/requirements.txt (fastapi, uvicorn[standard], websockets, httpx et pytest, aucune bibliothèque de polices) et server/tests/ (test_rooms.py), lu à 12 h 28, heure du Pacifique, le 5 octobre 2026, au commit 334fbfe avec les modifications non validées de ce jour, dans une copie de travail en cours de modification ce jour-là (dépôt privé). ↩

  105. Blake Crosley, « Pixel-Art Routes: Map Connections and Districts on iPhone », blakecrosley.com, 5 octobre 2026, section 7, vérification 13 (une liste d’exclusion dans le dépôt, avec son test, que doivent franchir chaque nom de zone, chaque panneau et chaque chaîne), https://blakecrosley.com/blog/pixel-art-world-beyond-the-plaza. ↩

Articles connexes

Personnes en pixel art : personnages et créateur sur iPhone

Comment les meilleurs jeux en pixel art construisent et habillent leurs personnages, mesures à l'appui, et comment nous …

106 min de lecture

Des structures en pixel art : maisons, halls et intérieurs sur iPhone

Comment Pokémon, les RPG SNES et Stardew bâtissent maisons, intérieurs et étages, mesuré dans le code, et comment la vil…

101 min de lecture

Ce que vérifie l'App Review, autant qu'on le sache : octobre 2026

L'App Review d'Apple en octobre 2026, d'après les sources publiques : qui examine, combien de temps cela prend, ce qui f…

65 min de lecture