Interfaces em pixel art: caixas de texto, fontes e japonês
A caixa de diálogo de Pokémon Emerald é uma moldura de 30 por 6 tiles, a largura inteira de 240 pixels da tela e 30% da sua altura, em volta de uma janela de texto de 27 por 4 tiles que comporta duas linhas de uma fonte de 16 pixels; a de Crystal tem 20 por 6 tiles, duas linhas de 18 caracteres.123 Emerald imprime um caractere a cada 8, 4 ou 1 quadros conforme o ajuste de velocidade, 7,5, 15 ou 60 por segundo a 60 hertz; o ajuste rápido de Crystal e Final Fantasy VI também imprimem um por quadro.14 Os roteiristas de Emerald quebraram cada linha à mão: em 24.100 linhas de texto de script, a mais larga tem 208 pixels contra uma janela de 216, e nenhuma transborda.1 Uma fonte pixelada japonesa que cobre o conjunto padrão desenha cerca de sete mil caracteres: o JIS X 0208 tem 6.879, Misaki desenha todos numa célula de 8 por 8, PixelMplus10 todos menos três pontos de código numa grade de 10 pixels, e o produtor de Final Fantasy Pixel Remaster estimou a nova fonte japonesa da equipe em “around 7,500 characters” (cerca de 7.500 caracteres).5678 No meu Mac, com a suavização de fontes desligada, o Core Text desenhou as fontes pixeladas TrueType cuja grade fica sobre a linha de base e cujos avanços são de pixels inteiros (Misaki, PixelMplus10 e Press Start 2P) sem nenhum pixel parcial nos múltiplos inteiros da grade, e com 19,4% a 82,2% dos pixels com tinta borrados entre eles; PixelMplus12, cuja grade fica deslocada da linha de base, Silkscreen, com pontos de contorno soltos, e DotGothic16, sem grade, saíram com pixels parciais em todos os tamanhos medidos, e k8x12 em todos os tamanhos assim que seus glifos foram posicionados com exatidão; com a suavização padrão do contexto de desenho, até PixelMplus10 nos seus próprios 10 pixels saiu com 178 de 272 pixels com tinta parciais.79 A 3×, um em de 10 pixels cai em pixels de dispositivo inteiros em apenas um dos doze tamanhos de Body do iOS, 40 pontos.710 Hoje o Kiradex World compõe cada palavra na fonte monoespaçada do sistema, fala só inglês e entrega ao VoiceOver as coordenadas dos testes de caminhada.11 Então medi as caixas, as fontes e as velocidades do cânone nas descompilações, medi cinco fontes pixeladas japonesas e duas latinas no Core Text e escrevi um briefing: uma única fonte licenciada para os dois idiomas, Dynamic Type arredondado à grade da fonte, uma moldura desenhada pela forja, duas linhas e páginas, e um palco que o VoiceOver consegue descrever.
Resumo
- A caixa é o terço inferior e duas linhas. A moldura de diálogo de Emerald tem 30 por 6 tiles (240 por 48 pixels, 30% da altura da tela) em volta de uma janela de 27 por 4 tiles com duas linhas de 16 pixels; a caixa de Crystal tem 20 por 6 tiles, 33% da altura, duas linhas de 18 caracteres, e a de Red é a mesma de 20 por 6. A caixa de Emerald não tem campo de nome, e 934 dos seus 14.412 parágrafos de script (6,5%) dizem quem fala dentro do próprio texto.12312
- Velocidade é uma contagem de quadros. Emerald e FireRed levam 8, 4 ou 1 quadros por caractere (7,5, 15 ou 60 por segundo a 60 hertz), e um quadro enquanto A ou B fica pressionado depois de apertado durante a impressão; Crystal e Red esperam 5, 3 ou 1 quadro (12, 20 ou 60); Final Fantasy VI desenha o diálogo de campo a uma letra por quadro, sem ajuste para isso (a velocidade de mensagens em seis níveis é quanto tempo espera uma mensagem de batalha). A seta de avanço de Emerald se desloca 0, 1, 2 e 1 pixels a partir do repouso, um passo a cada 9 quadros, num ciclo de 0,6 segundo; o ▼ de Crystal pisca alternando com o tile da borda.1131415164
- Cinco fontes gratuitas já desenham o japonês de que um jogo precisa. Misaki desenha todos os 6.879 caracteres do JIS X 0208 em 8 por 8, com cada kanji dentro de 7 por 7; k8x12, em 8 por 12; PixelMplus12 e PixelMplus10 desenham todos menos três pontos de código em 12 e 10 pixels; DotGothic16 desenha todos, mas sem grade de pixels. A licença de três frases de Misaki, k8x12 e PixelMplus concede uso, cópia e distribuição “either commercially or noncommercially” (comercialmente ou não).5671718
- Uma fonte pixelada só é nítida com pixels de dispositivo inteiros por pixel de fonte, e só se o arquivo estiver limpo. Medido com o Core Text no macOS 27.0, não num iPhone, com a suavização de fontes desligada: 0,0% de pixels parciais nos múltiplos inteiros de uma grade sobre a linha de base e com avanços de pixels inteiros (Misaki, PixelMplus10, Press Start 2P), de 19,4% a 82,2% entre eles.7 Com a suavização padrão do contexto de desenho, PixelMplus10 nos seus próprios 10 pixels saiu com 178 de 272 pixels com tinta parciais, então o resultado limpo nos múltiplos inteiros só vale com a suavização desligada.9 A grade de PixelMplus12 fica 0,39 pixel abaixo da linha de base e, com a suavização desligada, borra em todos os tamanhos; o avanço de kana de k8x12, de 8,01 pixels, borra assim que os glifos são posicionados com exatidão.719
- Dynamic Type precisa ser arredondado, não seguido. Dos doze tamanhos de Body do iOS, de 14 a 53 pontos, um em de 10 pixels é inteiro a 3× só em 40 pontos, um em de 8 pixels em 16 e 40, e um em de 12 pixels em 16, 28 e 40. A Apple pede que uma fonte personalizada implemente os comportamentos que as fontes do sistema têm, entre eles o Dynamic Type, e, para o texto de um app, que de preferência as pessoas possam ampliá-lo em pelo menos 200 por cento.71020
- A quebra de linha japonesa é estrita sem marcação e frouxa com marcação. No Core Text do macOS, um parágrafo de 131 caracteres quebrou sem que nenhuma linha começasse com um caractere da lista de início de linha da sonda (uma lista parcial, na maior parte caracteres do JLREQ, com sete nas formas de largura total) em nenhuma das larguras medidas, sem marcação; marcado com
ja, um kana pequeno abriu uma linha em 6 de 7 larguras em Hiragino Sans, Misaki, PixelMplus e DotGothic16, a regra menos estrita que os requisitos de composição japonesa do W3C descrevem.721 - O que isso dá ao Kiradex. Um briefing, não um build: PixelMplus10 para cada palavra desenhada sobre o mundo (das duas fontes medidas com japonês, grade limpa e avanços de pixels inteiros, Misaki e PixelMplus10, a maior), tamanhos arredondados a pixels de dispositivo inteiros por pixel de fonte (16,67 pontos no tamanho padrão nos iPhones de 3×, 15 nos de 2×), uma moldura própria desenhada pela forja, duas linhas e páginas, uma revelação de 30 caracteres por segundo com uma marca que avança em passos, balões de uma linha dimensionados para caber no iPhone mais estreito que o simulador do iOS 27.0 lista quando as laterais não têm margens, uma linha que não cabe indo para a caixa, linhas em japonês revisadas por um falante de japonês e um palco que o VoiceOver descreve com palavras em vez de coordenadas, cada item com uma verificação que um script, uma linha de log ou uma captura podem aprovar ou reprovar, com exceção de uma verificação manual do VoiceOver.2211
1. Emerald e FireRed: uma moldura de lado a lado, duas linhas, escritas à mão
Esta série começou pelo mundo e vem trabalhando de fora para dentro. Mundos em pixel art no iPhone definiu o sistema de tiles, a receita de RealityKit e uma direção para o texto numa regra, “Text is art” (texto é arte), e numa frase de plano: “m5x7 at integer multiples in a 24-by-24 nine-slice box, two lines, typewriter reveal, a two-frame advance arrow” (m5x7 em múltiplos inteiros numa caixa nine-slice de 24 por 24, duas linhas, revelação de máquina de escrever, uma seta de avanço de dois quadros).23 Os posts seguintes construíram o colecionador, os prédios, o movimento, a vida própria da cidade, os cômodos e as estradas. O texto ficou naquela frase. Este post mede de onde vêm os números dela, encontra quatro pontos em que o post do mundo está errado ou impreciso e acrescenta o que ele não tinha: japonês em matrizes de 8, 10 e 12 pontos, o que o Core Text realmente faz com uma fonte pixelada TrueType, como o Dynamic Type e uma grade de pixels podem chegar a um acordo, a quebra de linha japonesa e o VoiceOver sobre um palco de RealityKit.
O texto do Kiradex World hoje é simples. Cada palavra é SwiftUI na fonte monoespaçada do sistema definida pelo tema. Os balões sobre a cabeça dos colecionadores são uma linha do papel caption do kit em negrito, sobre papel creme com uma borda de tinta de 2 pontos, exibida enquanto a linha tem menos de 4 segundos. A linha do lugar fica numa caixa de texto creme na parte de baixo da tela. O app é só em inglês, as linhas predefinidas ficam no servidor em inglês, a placa da forja é uma tábua de 12 por 11 com rabiscos de tinta e nenhuma letra, e o VoiceOver lê o palco como um único elemento cujo valor é o rastro de passos dos testes de caminhada.11242526272829 A seção 10 tem os detalhes; o briefing da seção 12 está escrito como um diff em relação a isso.
Cada número de Pokémon abaixo foi medido em clones rasos das descompilações do pret, em pokeemerald 731ad5b (1º de outubro de 2026), pokefirered 037335f, pokecrystal 5beda23 e pokered d2704a6, por um único script em Python, measure_gen_text.py, cuja saída está salva ao lado; os números de Final Fantasy VI vêm de um segundo script sobre o disassembly everything8215/ff6. Os dois rodam de novo em segundos, e as notas indicam o arquivo-fonte por trás de cada número.3014
As fontes
Emerald declara suas fontes numa única tabela, sFontInfos em src/text.c, cada uma com largura e altura máximas de letra em pixels. A fonte pequena tem 5 por 12; a normal, que a caixa de diálogo usa, 6 por 16; a curta, 6 por 14, declarada quatro vezes (ela mesma e três cópias); a estreita, 5 por 16; a pequena estreita, 5 por 8; a negrito, 8 por 8; e a de braile, 8 por 16 com 8 pixels de entrelinha. Todas as outras têm entrelinha 0: a célula de 16 pixels é a linha.113
A fonte latina normal é proporcional. Indexadas pelo charmap.txt do jogo em gFontNormalLatinGlyphWidths de src/fonts.c, as minúsculas de a a z têm em média 5,77 pixels de largura, de 4 a 6; as maiúsculas de A a Z e os dígitos de 0 a 9 têm todos 6; o espaço e o ponto final, 3, e o ponto de exclamação, 4.131 Em texto corrido, isso dá uma média de 5,25 pixels por caractere, medida sobre cada linha do script do jogo analisado mais abaixo.1
Velocidade é uma contagem de quadros
Em Emerald, a velocidade do texto é um atraso em quadros. As três velocidades da tela de Opções passam por sTextSpeedFrameDelays em src/menu.c e viram atrasos de 8 (lenta), 4 (média) e 1 (rápida). AddTextPrinter, em src/text.c, guarda o atraso menos um como a velocidade da impressora de texto; a impressora põe o contador nessa velocidade guardada quando imprime um caractere e o decrementa de um em um a cada quadro, e o próximo caractere sai no quadro seguinte àquele em que o contador chega a 0. Assim, um caractere leva o próprio atraso: 8, 4 e 1 quadros. A 60 hertz, isso dá 7,5, 15 e 60 caracteres por segundo.1213 Essa contagem é a minha leitura da máquina de estados da impressora (AddTextPrinter e o caso RENDER_STATE_HANDLE_CHAR em src/text.c), que o script afirma em vez de executar.1 O Game Boy Advance não roda a exatos 60 hertz: o GBATEK dá o quadro dele como “Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz” (228 linhas no total, 16,743 ms, 280.896 ciclos, cerca de 59,737 Hz), 160 dessas linhas visíveis, cada uma com 240 pontos de largura, então o ajuste rápido dá cerca de 59,7 caracteres por segundo no hardware.3233
Segurar um botão acelera os ajustes mais lentos. Um novo aperto de A ou B enquanto o atraso de um caractere está sendo contado, numa mensagem que permita isso (gTextFlags.canABSpeedUpPrint), ativa hasPrintBeenSpedUp. A partir daí, a impressora verifica JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp e, quando a condição vale, baixa o contador para 0: um caractere por quadro, 60 por segundo, a mesma cadência do ajuste rápido.131
O texto com rolagem automática tem seu próprio atraso. Quando gTextFlags.autoScroll está ligado, a impressora põe o contador em 3 em vez da velocidade guardada, 4 quadros por caractere, exceto em batalhas gravadas: a condição é if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll).131 As mensagens de batalha ligam a rolagem automática em batalhas por cabo e em batalhas gravadas, e o comando de script messageautoscroll a liga e força a velocidade média.34 Assim, o atraso de 3 pode valer para outras janelas de batalha em batalhas por cabo e para mensagens de script com rolagem automática, e nunca em batalhas gravadas, cujas mensagens saem na velocidade que a impressora recebeu: nas mensagens de batalha, a velocidade de texto salva com a gravação, exceto numa batalha por cabo gravada, cuja janela de mensagens recebe 1, como a de uma batalha por cabo.341 Na minha leitura, o contador só corre enquanto a velocidade guardada da impressora é maior que 0, e a janela de mensagens de uma batalha por cabo recebe o atraso de 1, o que deixa o texto a um caractere por quadro com a rolagem automática ligada.1334
Os três códigos de quebra fazem três coisas diferentes. \n leva a impressora para a segunda linha; \l espera sob a seta de avanço por um aperto e então rola a caixa uma linha para cima; \p espera do mesmo jeito e então limpa a caixa e recomeça do topo.1335 Numa rolagem, a linha de 16 pixels sobe 1, 2 ou 4 pixels por quadro conforme o ajuste de velocidade (sWindowVerticalScrollSpeeds), então uma rolagem leva 16, 8 ou 4 quadros.113
A velocidade do texto em cada jogo medido e os dois ajustes propostos pelo briefing; desenhado por figures/make_figures.py a partir das saídas das medições.
A seta
Quando uma página está cheia, a caixa espera sob uma pequena seta. Emerald copia uma célula de seta de 8 por 16 com deslocamentos verticais de { 0, 1, 2, 1 } pixels (sDownArrowYCoords) e a redesenha quando o atraso dela, reiniciado em 8, se esgota. Isso dá um passo a cada 9 quadros e um ciclo de 36 quadros, 0,60 segundo a 60 hertz.113 A seta não pisca; ela balança, por três posições e de volta.
A caixa e sua moldura
A caixa de texto de campo de Emerald é definida por sStandardTextBox_WindowTemplates em src/menu.c. A janela de texto fica na coluna de tiles 2, linha 15, e tem 27 por 4 tiles: 216 por 32 pixels, duas linhas da fonte normal de 16 pixels. A moldura, desenhada por WindowFunc_DrawDialogueFrame e avaliada pelo script para essa janela, vai da coluna 0 à 29 e da linha 14 à 19: 30 por 6 tiles, 240 por 48 pixels, a largura inteira da tela e 30% dos seus 160 pixels de altura.12
Essa moldura não é um nine-slice. LoadMessageBoxGfx a carrega de message_box.png, uma tira de 56 por 16 pixels, 14 tiles, e a moldura é desenhada a partir de oito posições de tile, com a linha de baixo sendo a de cima espelhada verticalmente com BG_TILE_V_FLIP.1362 O script mede a extensão da moldura e o tamanho do PNG; o espelhamento é lido nas chamadas de preenchimento.1
As molduras escolhidas pelo jogador são para as outras janelas: os menus e as caixas de sim ou não. WINDOW_FRAMES_COUNT vale 20, e as vinte molduras são PNGs de 24 por 24 pixels, três por três tiles de 8, carregados por LoadUserWindowBorderGfx a partir do tipo de moldura salvo pelo item FRAME da tela de Opções.136
Isso corrige o post do mundo. A regra 10 dele diz “Pokémon Emerald’s text-window frame is a 24-by-24 image, three by three hardware tiles, which is a nine-slice” (a moldura da janela de texto de Pokémon Emerald é uma imagem de 24 por 24, três por três tiles de hardware, ou seja, um nine-slice).23 As vinte imagens de 24 por 24 em graphics/text_window/ são as molduras selecionáveis das janelas de menu. A caixa de diálogo, a caixa de texto que os jogadores lembram, é desenhada a partir do message_box.png de 56 por 16 pixels como uma moldura de 30 por 6 tiles com a borda de baixo espelhada, não como um nine-slice de uma imagem de 24 por 24.136 A ideia do nine-slice continua de pé, e os menus a usam; a atribuição à caixa de diálogo, não.
As duas caixas de diálogo como grades de tiles, sem arte dos jogos; desenhado por figures/make_figures.py, que confere cada extensão contra measure_gen_text.out.
O texto foi escrito para a caixa
Para ver quão cheia a caixa fica, o script leu cada linha do texto de script de Emerald: 504 arquivos, os 36 de data/text/*.inc e os scripts.inc dos 468 mapas, 24.100 linhas sem marcador de substituição e 3.000 com um, divididas nos três códigos de quebra do jogo, \n, \l e \p. A linha mediana tem 29 caracteres e 150 pixels; o percentil 95, 38 caracteres e 196 pixels; a mais longa, 53 caracteres, e a mais larga, 208 pixels. Nenhuma passa dos 216 pixels da janela, e o caractere médio tem 5,25 pixels de largura.137
A 5,25 pixels por caractere, a janela comporta 41; os roteiristas pararam em 38 em 95% das linhas.331 Cada quebra é um código posto à mão. O jogo nunca quebra uma linha sozinho; ele confia em quem escreve.
A caixa tem uma janela e nenhum campo de nome. Quando os roteiristas queriam indicar quem fala, digitavam o nome e dois-pontos dentro da linha, como no “MOM: {PLAYER}, we’re here, honey!” (MÃE: {PLAYER}, chegamos, querido!) de Littleroot Town. Dos 14.412 parágrafos, 934, ou 6,5%, começam com um nome em maiúsculas e dois-pontos.137 Que não exista um campo de nome separado é a minha leitura do modelo de janela, que tem espaço para as duas linhas de texto e nada mais.2
Japonês no jogo em inglês
O Emerald em inglês ainda carrega glifos japoneses, para os nomes de criaturas e treinadores que vêm de cartuchos japoneses. A impressora troca de conjunto de glifos com o código de controle EXT_CTRL_CODE_JPN e volta com EXT_CTRL_CODE_ENG, que esses nomes carregam (international_string_util.c, contest.c e battle_tower.c os inserem). Nesse modo, a fonte normal tem 8 pixels de largura para cada caractere e 15 de altura (DecompressGlyph_Normal, e GetGlyphWidth_Normal retorna 8).11338
As folhas a partir das quais a ROM em inglês é montada: japanese_normal.png tem 128 por 512 pixels em células de 8 por 16, com 302 das suas 512 células desenhadas; japanese_small.png, do mesmo tamanho, 307 desenhadas; japanese_bold.png, 128 por 256, 246 de 256 desenhadas; e o japanese_frlg_male.png de FireRed, também na árvore de Emerald, 256 por 512 pixels em células de 16 por 16, 269 de 512 desenhadas, com uma tabela de larguras de 512 entradas de 0 a 10 pixels, 10 em 408 delas.138 O pret monta os jogos em inglês, então estas são as folhas para nomes numa ROM em inglês; não foram lidas a caixa, as velocidades nem a fonte próprias de nenhuma ROM japonesa (seção 11).30
Então, nos próprios termos do cânone, um caractere japonês numa linha de 16 pixels tem 8 pixels de largura em Emerald e quase sempre 10 na folha de FireRed, contra uma letra latina de 4 a 6.1
FireRed
FireRed mantém os atrasos de Emerald, 8, 4 e 1, guardados menos um pelo seu próprio AddTextPrinter em src/text_printer.c como os de Emerald, e uma janela de texto padrão de 26 por 4 tiles na coluna 2 (src/new_menu_helpers.c), um tile mais estreita que a de Emerald.139 A impressora dele também tem o recurso de segurar de Emerald: if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp) baixa o contador para 0, e um novo aperto de A ou B enquanto o atraso de um caractere está sendo contado, numa mensagem que permita isso, ativa hasPrintBeenSpedUp.14
2. Crystal e Red: dezoito caracteres e uma marca que pisca
A caixa do Game Boy é a ancestral da caixa do Game Boy Advance numa resolução menor. A tela de Crystal tem 20 por 18 tiles (SCREEN_WIDTH 20, SCREEN_HEIGHT 18), 160 por 144 pixels. A caixa de texto é TEXTBOX_WIDTH = SCREEN_WIDTH por TEXTBOX_HEIGHT 6: a largura inteira e um terço da altura, 33%. Com BORDER_WIDTH 2, o interior tem 18 por 4 tiles, e a fonte de 8 por 8 dá 18 caracteres por linha. O texto começa duas linhas para dentro da caixa, TEXTBOX_INNERY = TEXTBOX_Y + 2, e as linhas ficam a duas fileiras uma da outra, então a caixa mostra duas linhas com uma fileira em branco entre elas; o layout de duas linhas é a minha leitura dessas constantes.13 A caixa de mensagens de Red é db MESSAGE_BOX, 0, 12, 19, 17: os mesmos 20 por 6 tiles embaixo.112
Os dois jogos também contam a velocidade em quadros. Os atrasos são TEXT_DELAY_FAST 1, médio 3 e lento 5 quadros por letra: 60, 20 e 12 letras por segundo a 60 hertz. O PrintLetterDelay de Crystal diz isso sem rodeios: “The text speed setting in wOptions is actually a frame count” (o ajuste de velocidade do texto em wOptions é na verdade uma contagem de quadros). Enquanto A ou B fica pressionado, ele espera um quadro por letra.115 Então o ajuste rápido de Crystal bate com o de Emerald, 60 por segundo, e segurar A ou B em Emerald dá a mesma cadência; os ajustes médio e lento de Crystal, 20 e 12, são mais rápidos que os 15 e 7,5 de Emerald.1 O Pan Docs dá o quadro do Game Boy como “One frame: 70224 dots @ 59.7 fps” (um quadro: 70.224 pontos a 59,7 fps) e observa que um quadro “takes ~16.74 ms instead of ~16.67” (leva cerca de 16,74 ms em vez de cerca de 16,67).40
Crystal deixa o jogador escolher entre oito molduras, de FRAME_1 a FRAME_8, cada uma com TEXTBOX_FRAME_TILES, 6 tiles de 1 bit, guardadas como PNGs de 24 por 16 pixels; uma nona moldura está na ROM sem uso. A folha da fonte, gfx/font/font.png, tem 128 por 64 pixels, 128 células de 8 por 8, e a de Red tem o mesmo tamanho.141
A marca pisca
A marca de avanço de Crystal faz parte da moldura. WaitPressAorB_BlinkCursor espera A ou B enquanto BlinkCursor alterna o tile em (18, 17), na própria linha de baixo da moldura, entre '▼' e o tile de borda '─'.16 Os dois estados são lidos no código; o período do piscar não foi derivado (seção 11).
Essa é a segunda imprecisão do post do mundo. O plano de texto dele pede “a two-frame advance arrow” (uma seta de avanço de dois quadros).23 Uma marca de dois estados é o piscar de Crystal. A seta de Emerald, aquela que o resto do plano segue (a linha de 16 pixels, a caixa embaixo), não pisca: ela passa por três posições, 0, 1, 2 e 1 pixels a partir do repouso, num ciclo de 36 quadros (o código desloca as linhas de origem do bitmap, o que, na minha leitura, ergue a cunha na tela; não executei isso).113 O briefing da seção 12 segue os passos de Emerald, não a direção deles.
3. Final Fantasy VI: uma janela que se repinta, uma fonte que quebra sozinha
Final Fantasy VI trata a janela como coisa do jogador. Os números dele aqui vêm do disassembly everything8215/ff6 em 8130132 (14 de setembro de 2026), que contém código e listas de extração em vez dos gráficos da ROM, então os tamanhos vêm de intervalos de bytes e do código que os lê.304
Oito janelas, sete cores cada
As listas de extração em inglês e em japonês dão os gráficos de janela como 0xED0000 a 0xED1BFF, 7.168 bytes: oito estilos de 896 bytes, cada um com 28 tiles de 8 por 8 a 4 bits por pixel. As paletas vêm em seguida, de 0xED1C00 a 0xED1CFF, oito paletas de 16 cores.442 A tela de configuração lista 8 padrões de janela (ConfigWindowNumPos).443
A cor da janela é o jogador quem define. LoadWindowPal lê as cores do padrão na memória de save em $1d57, 14 bytes por padrão, ou seja, sete cores de dois bytes, e a tela de configuração as edita em barras de R, G e B (ConfigColorBarTextList); mais uma cor, a da fonte, fica em $1d55.443 O passo é lido no código, e os três canais, na lista de barras.4
A velocidade de mensagens da tela de configuração tem seis ajustes, de 1 a 6 (ChangeConfigOption_02 para em $05), e um save novo começa com “message speed = 3, battle speed = 3” (velocidade de mensagens = 3, velocidade de batalha = 3), como diz o comentário em menu_sram.asm.443 É a velocidade das mensagens de batalha, como config.asm a chama num comentário, “battle speed, battle message speed, magic order, and window pattern” (velocidade de batalha, velocidade das mensagens de batalha, ordem da magia e padrão de janela). Fora da tela de configuração, o único código que lê os bits dela no byte de configuração, $1d4d, é mess_wait em src/btlgfx/gfx_cmd.asm, que procura o ajuste em MsgSpeedTbl, “wait duration for each battle message speed” (duração da espera para cada velocidade de mensagem de batalha), e espera esse número de quadros: 32, 48, 64, 80, 96 ou 112 para os ajustes de 1 a 6. Nenhum arquivo do código de campo lê $1d4d, então o diálogo de campo não tem ajuste de velocidade; ele sai na cadência única descrita abaixo.444
Uma janela sombreada sem um tile
A janela de diálogo é sombreada ao longo da altura pela matemática de cores do hardware. DlgWindowFixedColorTbl, em src/field/hdma.asm, são 200 bytes de $E0 a $E7, uma cor fixa nos três canais com intensidades de 0 a 7, e a primeira sequência são 72 valores descendo a partir de 7, depois até 0 e depois de volta até 7.445 A janela abre perto do topo ou da base da tela, “dialog window y position (1 or 18)” (posição y da janela de diálogo, 1 ou 18) no comentário de event.asm.45 Que a tabela é consumida a um valor por linha de varredura vem dos comentários, não foi rastreado, e a aparência do degradê não foi vista, já que o disassembly não tem pixels (seção 11).
A fonte
A fonte de diálogo tem 1 bit por pixel, 22 bytes por letra: 11 fileiras de 16 bits, uma célula de 16 pixels de largura e 11 de altura. A sombra não é desenhada: LoadLetterGfx a cria deslocando cada fileira um pixel (lsr).446 As larguras em inglês, de FontWidth, são proporcionais: as maiúsculas têm em média 8,04 pixels (de 5 a 12), as minúsculas 7,73 (de 4 a 12), os dígitos 7,80 (de 5 a 9), e o espaço, 5.446
A fonte de diálogo da ROM japonesa, de 0xC494C0 a 0xC4FFFF na lista de extração dela, ocupa 27.456 bytes: 1.248 letras de 22 bytes. A tabela de larguras tem 1.280 entradas, em sequências de 1.056 a 12 pixels, 180 a 8 e 44 a 12, e as tabelas de caracteres do disassembly listam 1.010 kanji e 173 kana.447 O intervalo da fonte grande em inglês, 5.120 bytes, não é um número inteiro de letras de 22 bytes (232,7), o que não consigo explicar a partir do disassembly (seção 11).4
Uma linha, uma quebra automática, uma letra por quadro
Uma linha de diálogo vai do x 4 até um x máximo de $e0, 224: 220 pixels, 18 letras japonesas de 12 pixels.446 O texto em inglês quebra por palavra no código. CalcTextWidth soma a largura da próxima palavra, e NewLine é chamada quando ela passaria do máximo, então o script em inglês não precisa de quebras postas à mão; o script de medição afirma as chamadas, e a leitura é de UpdateDlgTextOneLine.446
O texto sai a uma letra por quadro. O loop de campo espera o vertical blank e chama UpdateDlgText uma vez por passagem, e UpdateDlgTextOneLine desenha uma letra e retorna: 60 letras por segundo a 60 hertz.448 Uma passagem por quadro é a minha leitura do loop. Como uma única fonte gera as duas ROMs, os dois idiomas compartilham esse loop, o que, na minha leitura, significa as mesmas letras por segundo em inglês e em japonês.48
O título de um mapa é centralizado, ($e0 - width) / 2, e exibido por 100 quadros, 1,67 segundo.446
Ao lado de Emerald, Final Fantasy VI mostra o outro jeito de construir a mesma caixa: Emerald escreve cada quebra à mão e deixa o jogador escolher a moldura dos menus, nunca a da caixa de diálogo; Final Fantasy VI quebra no código, deixa o jogador repintar a janela e a sombreia no hardware. A caixa de campo de Emerald fica embaixo, a de Final Fantasy VI em cima ou embaixo, e os dois imprimem numa contagem fixa de quadros.
Chrono Trigger
Eu pretendia pôr Chrono Trigger ao lado de Final Fantasy VI e não consegui. Nenhum disassembly público foi clonado, e o mapa da ROM do jogo no Data Crystal retornou HTTP 403 ao ser buscado, então nada neste post sobre caixas de texto vem dele (seção 11).49
4. Japonês em matrizes de 8, 10 e 12 pontos
O Kiradex é um app para colecionadores de cartas, e a Dex já mostra os nomes japoneses das cartas com o inglês embaixo, nas fontes do sistema.50 O texto do mundo também vai precisar de japonês, e uma fonte pixelada que não consegue desenhá-lo não é candidata. A pergunta é quão pequeno um kanji pode ser e quem já desenhou sete mil deles.
O tamanho do trabalho
O JIS X 0208, o conjunto de caracteres contra o qual se mede a cobertura das fontes pixeladas japonesas, tem 6.879 caracteres segundo a tabela de mapeamento do Unicode Consortium: 524 que não são kanji, 169 deles os kana das linhas 4 e 5; 2.965 kanji de nível 1 nas linhas 16 a 47; e 3.390 kanji de nível 2 nas linhas 48 a 84.576 A tabela é a versão 2.0, e o consórcio a arquiva entre os mapeamentos obsoletos; ainda é a lista contra a qual é mais fácil contar a cobertura de uma fonte.5
Misaki: 8 por 8
Misaki (美咲フォント), de Num Kadoma, é a menor. A página dela a chama de “8×8 ドットの日本語ビットマップフォント” (uma fonte bitmap japonesa de 8 por 8 pontos) com suporte a “JIS 第一・第二水準” (os níveis 1 e 2 do JIS), e diz “一部の記号を除いた全ての文字は 7×7 ドットの範囲に収まっている” (todos os caracteres, exceto alguns símbolos, cabem dentro de 7 por 7 pontos), de modo que, continua a página, caracteres postos lado a lado ainda mantêm o espaçamento entre linhas e entre letras.51 Segundo o histórico da página, ela foi feita para um driver de tela japonês dos computadores de bolso PC-E500.51
Medida nos arquivos BDF da versão 2021-05-05, Misaki Gothic tem 7.171 caracteres: 168 de meia largura, com 4 pixels, e 7.003 de largura total, com 8 pixels, numa célula de 8 por 8 com ascendente de 6 e descendente de 2. Dos seus 6.356 glifos de U+4E00 a U+9FFF (um intervalo de código, daí uma contagem diferente dos 6.355 kanji dos dois níveis do JIS), nenhum preenche a célula de 8 pontos em nenhuma direção, enquanto 53 glifos de largura total, símbolos, preenchem. Ela desenha todos os 6.879 caracteres do JIS X 0208.6 Misaki Mincho, que a página descreve como Misaki Gothic com os caracteres que não são kanji redesenhados em estilo Mincho, mede o mesmo em todas as contagens. Misaki Gothic 2nd, que segundo a página usa os 3 por 7 pixels completos para caracteres de meia largura e 7 por 7 para kana de largura total, tem 7.170 caracteres (167 de meia largura), ascendente de 7 e descendente de 1, e mede o mesmo no resto.651
k8x12: 8 por 12
k8x12, do mesmo autor, mantém a largura de 8 pontos e acrescenta altura: “8×12 ドットの日本語ビットマップフォント” (uma fonte bitmap japonesa de 8 por 12 pontos), níveis 1 e 2 do JIS. Ela tem duas variantes: k8x12L, cujos kana são desenhados altos, e k8x12S, cujos caracteres que não são kanji têm 8 pontos de altura, feita, diz a página, pensando nos limites dos consoles antigos: “8×8 ドット単位でグラフィックを扱うような、昔のゲーム機等の制限を意識しています” (pensada nos limites de máquinas de jogo antigas que tratam os gráficos em unidades de 8 por 8 pontos).52
Medida no BDF, k8x12 é uma célula de 8 por 12 com ascendente de 10 e descendente de 2: 7.170 caracteres, 168 com 4 pixels e 7.002 com 8. Nenhum kanji preenche a célula; 53 glifos de largura total preenchem em k8x12 e k8x12L, e 45 em k8x12S. As três desenham todos os 6.879 caracteres do JIS X 0208.6
PixelMplus: 10 e 12, como contornos
PixelMplus, de itouhiro (2013), é a outra abordagem: contornos TrueType com cara de fonte bitmap, sem bitmaps embutidos (“埋め込みビットマップは なし”, sem bitmaps embutidos), com todos os kanji dos níveis 1 e 2 do JIS (“JIS第1・第2水準のすべての漢字を収録”, inclui todos os kanji dos níveis 1 e 2 do JIS) mais ISO-8859-1, num peso regular e num negrito e em dois tamanhos, PixelMplus12 e PixelMplus10, contornados a partir das M+ BITMAP FONTS.18 Medida com o Core Text, cada uma tem 7.253 glifos e desenha 6.876 dos 6.879 do JIS X 0208. Os três que ela não mapeia são U+301C 〜, U+2016 ‖ e U+2212 −.7 São caracteres cujo mapeamento Unicode difere entre o JIS0208.TXT do consórcio e as tabelas de outros fabricantes, então, na minha leitura, as fontes provavelmente trazem esses glifos nos outros pontos de código; isso não foi verificado (seção 11).5
DotGothic16: uma fonte de pontos sem grade
DotGothic16, da Fontworks, está no Google Fonts sob a SIL Open Font License 1.1 sem Reserved Font Name, listada com os subconjuntos cyrillic, japanese, latin e latin-ext, e adicionada em 2020-12-15.53 Ela tem 9.362 glifos e desenha todos os 6.879 caracteres do JIS X 0208.7 Mas é desenhada como pontos separados, não como pixels. No A dela, as bordas x do contorno vêm em pares, 91 e 102, 153 e 163, 214 e 225, 276 e 287, 338 e 349, 400 e 410 unidades de 1.000: pontos a cerca de 62 unidades uns dos outros, com vãos de 10 ou 11 unidades entre eles. Nenhuma grade de pixels inteiros se encaixa nisso, e a sonda não encontrou nenhuma de 4 a 40 pixels por em.197 Um passo de cerca de 62 unidades fica perto de 16 pontos por em, o número do nome da fonte, então a sonda a desenha a 16 pontos por em nominais e em múltiplos disso; a escolha de tamanho é minha, não uma grade que a fonte declara. O Core Text no macOS 27.0 a desenhou com 82,3% de pixels parciais a 16 pixels e 37,3% a 64, e parcial em todos os tamanhos medidos (seção 6).7
O que as fontes gratuitas medem
A grade de projeto na tabela é o menor número de pixels por em em que 95% dos pontos de contorno de uma amostra de glifos ficam a menos de 0,04 pixel, com um deslocamento constante permitido em cada eixo; o script que a encontra, measure_coretext.swift, foi compilado com swiftc -O e executado no macOS 27.0 (build 26A428), então estes são números do Core Text de um Mac, não de um iPhone.7
| Fonte | Glifos | Tabelas de bitmap | Grade de projeto (pixels por em) | Avanço latino / kana (pixels) | JIS X 0208 desenhados |
|---|---|---|---|---|---|
| Misaki Gothic | 7.174 | EBDT, EBLC | 8, na linha de base | 4 / 8 | 6.879 |
| k8x12 | 7.173 | EBDT, EBLC | 12, na linha de base | 4 / 8,01 | 6.879 |
| PixelMplus10 Regular e Bold | 7.253 cada | nenhuma | 10, na linha de base | 5 / 10 | 6.876 |
| PixelMplus12 | 7.253 | nenhuma | 12, 0,39 pixel abaixo da linha de base | 6 / 12 | 6.876 |
| DotGothic16 | 9.362 | nenhuma | nenhuma (desenhada a 16 pontos nominais) | 8 / 16 pontos nominais | 6.879 |
Por essa medida, PixelMplus10 Regular tem 0 de 1.032 coordenadas de contorno amostradas fora da sua grade de 10 pixels, e a Bold, 0 de 1.000; Misaki Gothic, 0 de 848; k8x12, 0 de 988; PixelMplus12, 0 de 1.192 depois de permitido o seu deslocamento de 0,39 pixel, e DotGothic16 tem todas as 1.544 fora.7 O deslocamento de PixelMplus12 aparece nas coordenadas brutas: o H dela vai de y −33 a 717 unidades de 1.000, numa grade de 83,3 unidades por pixel, então a linha de base cai 0,396 pixel para dentro de uma fileira da grade; a estimativa da sonda, 0,39, vem de distribuir as frações das coordenadas em faixas de um cinquenta avos de pixel, e é o valor pelo qual os renderizados abaixo foram deslocados.19733
Em todas as fontes com japonês medidas, um caractere japonês tem o dobro de um latino: 8 contra 4 em Misaki e k8x12, 10 contra 5 e 12 contra 6 em PixelMplus.7 O cânone fica na mesma faixa: o japonês de Emerald tem 8 pixels de largura numa linha de 16, a folha de FireRed quase sempre 10, o de Final Fantasy VI, 12.14
As licenças
Tudo isso é gratuito nos termos mais simples. A licença de Misaki e k8x12, na página de fontes do autor, são três frases: “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.” (estas fontes são software livre; é concedida permissão ilimitada para usá-las, copiá-las e distribuí-las, com ou sem modificações, comercialmente ou não; elas são fornecidas no estado em que se encontram, sem garantia). O leia-me de cada pacote traz os mesmos termos em palavras um pouco diferentes, e a página de Misaki indica que a licença usada é a licença das M+ FONTS.1751 PixelMplus está sob a M+ FONT LICENSE, cujo texto em inglês, LICENSE_E, são as mesmas três frases.18 DotGothic16 está sob a SIL Open Font License 1.1, cujas condições ficam no seu próprio OFL.txt.53 Na minha leitura, a licença de três frases não pede nada em troca; dar crédito aos autores continua sendo o certo, e o briefing faz isso (seção 12). São leituras, não aconselhamento jurídico.
O que a legibilidade custa
Um kana em Misaki tem 8 pixels de fonte de largura; em PixelMplus12, doze. Na densidade do próprio mundo no iPhone 18 Pro Max, onde um texel são 8 pixels de dispositivo, uma linha da caixa desenhada em Misaki comporta 36 caracteres latinos ou 18 japoneses.11 A linha japonesa carrega mais por caractere, então a caixa não precisa comportar tantos caracteres para dizer a mesma coisa; isso é minha leitura, não uma medição.
5. Fontes pixeladas latinas, e as que não consegui medir
Duas fontes pixeladas latinas do Google Fonts foram medidas junto com as japonesas.30
Press Start 2P, de CodeMan38, está sob a SIL Open Font License 1.1 “with Reserved Font Name “Press Start 2P”” (com o nome de fonte reservado “Press Start 2P”).54 Ela tem 658 glifos numa grade de 8 pixels sobre a linha de base, cada caractere latino com 8 pixels de largura, e todos os 95 caracteres ASCII imprimíveis. Não tem kana nem kanji: do JIS X 0208, desenha 150 caracteres que não são kanji (o grego, o cirílico e os símbolos dela) e nenhum dos 6.355 kanji.7
Silkscreen, de Jason Kottke, está sob a SIL Open Font License 1.1 sem Reserved Font Name.55 Ela tem 228 glifos numa grade de 8 pixels, com 10 de 588 coordenadas de contorno amostradas fora dela (o A e o o dela têm valores y de 495 e 130 unidades numa grade de 125 unidades), e o A tem 6 pixels de largura. Do JIS X 0208, desenha 21 caracteres que não são kanji.719
Outras três fontes muito recomendadas para jogos pixelados não foram medidas, e digo isso em vez de descrevê-las de memória.
Pixel Operator, de Jayvee Enaguas: as notas do autor no DaFont dizem “Changed licence to Creative Commons Zero (CC0) 1.0” (licença alterada para Creative Commons Zero, CC0, 1.0) e que a fonte está “licensed under a Creative Commons Zero (CC0) 1.0” (licenciada sob Creative Commons Zero, CC0, 1.0). O DaFont a lista como “Bitmap (16 px)”, em 15 arquivos de fonte, com 241 glifos. O repositório do próprio autor, que as notas indicam no NotABug, retornou HTTP 404, e a fonte não foi baixada nem medida. Tudo aqui vem dessa única página.56
m5x7, de Daniel Linssen: a página dela no itch.io diz “use font size 16, 32, 48, etc” (use tamanhos de fonte de 16, 32, 48 etc.) e “free to use but attribution appreciated” (de uso livre, mas a atribuição é bem-vinda), informa “Asset license Creative Commons Zero v1.0 Universal” (licença do recurso: Creative Commons Zero v1.0 Universal) e dá largura média de 5, ascendente de 7, descendente de 2 e altura-x de 5. Descreve seus caracteres como “uppercase and lowercase letters, including many accented letters, numbers and most common symbols” (maiúsculas e minúsculas, incluindo muitas letras acentuadas, números e os símbolos mais comuns).57
m3x6, do mesmo autor: “use font size 16, 32, 48, etc” e “free to use with attribution” (de uso livre com atribuição); a página não mostra nenhum campo de licença do recurso.58 Nenhum dos dois arquivos foi baixado, porque ambos ficam atrás de um download de pague quanto quiser, então nenhum foi medido.5758
Essa é a terceira imprecisão do post do mundo. O plano de texto dele indica m5x7 como a fonte.23 Pela descrição da própria página, m5x7 é uma fonte latina, e nada na página diz que ela tem kana ou kanji; um app que vai mostrar japonês não pode compor o texto do seu mundo nela.57 O briefing troca a fonte por PixelMplus10, uma das duas fontes medidas com japonês, grade limpa e avanços de pixels inteiros (a outra é Misaki), e a maior das duas (seções 6 e 12). A própria m5x7 continua sem medição.
6. O que o Core Text faz com uma fonte pixelada
O post do mundo afirmava que “Bitmap fonts render at integer multiples only” (fontes bitmap só renderizam em múltiplos inteiros).23 É a regra que todo guia de pixel art repete, e eu queria vê-la, com números, no próprio motor de texto da Apple.
A medição
O Core Text é a camada da Apple para “Create text layouts, optimize font handling, and access font metrics and glyph data” (criar layouts de texto, otimizar o tratamento de fontes e acessar métricas de fonte e dados de glifos), disponível desde o iOS 3.2 e o macOS 10.8.59 measure_coretext.swift carrega cada arquivo TrueType candidato, desenha uma linha de amostra (“Nice card! Which set? 0123” para as fontes latinas, “カードを見せて! Nice card!” para as japonesas) num bitmap cinza de 8 bits em tamanhos que são múltiplos inteiros e fracionários da grade de cada fonte, e conta os pixels com tinta que são parciais: entre 8 e 247 de 255, a mais de 3% de cheio ou de vazio. Ele desenha cada tamanho de três jeitos: com o antialiasing padrão do Core Text, com o antialiasing desligado, e com o posicionamento de subpixel ligado e a quantização desligada, para que os glifos caiam exatamente onde seus avanços os colocam.7
Dois limites importam antes de qualquer número. A execução é no macOS 27.0, build 26A428, no meu Mac, não no iOS, e desenha com o Core Text num contexto de bitmap, não com o Text do SwiftUI; o que um iPhone desenha é uma previsão até que as verificações por captura do briefing rodem em um (seção 11).7 E o script desenha com a suavização de fontes desligada (setAllowsFontSmoothing(false)). Quando desenhei a figura abaixo com o padrão do contexto de bitmap, 178 dos 272 pixels com tinta de “Nice card!” em PixelMplus10 a 10 pixels saíram parciais, contra 0 de 94 com a suavização desligada.9 A suavização de fontes é um ajuste do macOS; se algo parecido se aplica no iOS, quem decide são as verificações por captura.
Múltiplos inteiros saem limpos
Nos múltiplos inteiros da grade, com o antialiasing ligado (o padrão do contexto) e a suavização de fontes desligada, Misaki a 8, 16, 24 e 32 pixels, PixelMplus10 Regular e Bold a 10, 20, 30 e 40, e Press Start 2P a 8, 16, 24 e 32 renderizam com 0,0% de pixels parciais.7 Cada pixel é tinta ou papel. Isso depende do ajuste de suavização: com a suavização padrão do contexto de bitmap, PixelMplus10 nos seus próprios 10 pixels desenhou parciais 178 dos 272 pixels com tinta de “Nice card!”, então, no Mac, um múltiplo inteiro sozinho não deixou o texto nítido.9
PixelMplus10 Regular desenhada pelo Core Text no macOS 27.0 a um pixel de dispositivo por pixel de fonte, mostrada com o antialiasing desligado. Com o antialiasing ligado e a suavização de fontes desligada, 0 dos 94 pixels com tinta de “Nice card!” são parciais; com a suavização padrão do contexto de bitmap, 178 de 272 são. A fonte está sob a M+ FONT LICENSE, que permite cópia e distribuição; desenhado por figures/dump_glyphs.swift e figures/make_figures.py.
Entre eles, borram
Entre os múltiplos inteiros, as mesmas fontes borram, com a suavização desligada como em toda a sonda. Misaki tem 57,7% de pixels parciais a 12 pixels (1,5 vez a grade), 39,9% a 20 (2,5 vezes) e 82,2% a 10 (1,25 vez). PixelMplus10 tem 60,1% a 15 pixels. Press Start 2P, 44,0% a 12 pixels, 29,1% a 20 e 21,7% a 28.7 Nos 40 renderizados a 1,25, 1,5, 1,75, 2,5 e 3,5 vezes a grade das oito fontes cuja grade fica sobre a linha de base (as duas Misaki, k8x12 e k8x12S, PixelMplus10 Regular e Bold, Press Start 2P e Silkscreen), a parcela de parciais vai de 19,4%, PixelMplus10 Bold a 35 pixels, a 82,2%, Misaki a 10.7 O borrão não cai de forma contínua com o tamanho: em cada uma das oito, 1,75 vez a grade borra mais do que 1,5 vez (Misaki, 65,7% a 14 pixels contra 57,7% a 12). Na mesma fração, ele cai: a 1,5, 2,5 e 3,5 vezes a grade, cada uma das oito fica mais baixa a cada passo (Misaki, 57,7%, 39,9% e 30,4%). Em nenhum renderizado medido entre múltiplos inteiros ele chega a zero; na minha leitura, a queda numa fração fixa se deve a que um erro de meio pixel na borda de um glifo é uma parte menor de um glifo maior.7
Desligar o antialiasing (setShouldAntialias(false)) deixa cada pixel cheio ou vazio em qualquer tamanho, mas as formas são reamostradas de modo desigual. Os 191 pixels com tinta de Misaki a 8 pixels viram 377 a 10; uma escala uniforme de 1,25 em cada direção daria cerca de 298.733 Algumas fileiras e colunas são duplicadas e outras não, então traços que tinham um pixel de largura saem com um ou dois.
Pixels parciais por tamanho, Core Text no macOS 27.0 com a suavização de fontes desligada, não o iOS; desenhado por figures/make_figures.py a partir de measure_coretext.out.
Um tamanho limpo não basta: o arquivo precisa estar limpo
Duas falhas mais discretas importam mais na escolha de uma fonte do que os múltiplos.
Uma grade fora da linha de base borra em todos os tamanhos. Os contornos de PixelMplus12 ficam 0,39 pixel abaixo da grade da linha de base, e ela renderiza com 66,6% de parciais nos seus próprios 12 pixels, 40,0% a 24 e 29,8% a 36. Mover a linha de base pelo deslocamento, na transformação do contexto com o posicionamento de subpixel ligado, leva 12 pixels a 0,0%, mas deixa 14,3% a 24 e 19,0% a 36, embora o deslocamento tenha sido escalado com o tamanho (0,78 pixel a 24, 1,17 a 36).7 Por que o deslocamento escalado não limpa os tamanhos maiores, a saída não diz, e eu não rastreei.
Um avanço fora da grade borra assim que os glifos são posicionados com exatidão. O avanço de kana de k8x12 é de 8,01 pixels, não 8. Com os ajustes padrão do contexto de bitmap, que põem cada glifo num pixel inteiro, ela renderiza com 0,0% de parciais a 12, 24 e 36 pixels. Com o posicionamento de subpixel ligado e a quantização desligada, os mesmos tamanhos dão 62,3%, 42,8% e 32,6% de parciais.7 Na minha leitura, cada kana cai então um centésimo de pixel mais longe da grade do que o anterior.
Pontos de contorno soltos deixam um pouco. As dez coordenadas fora da grade de Silkscreen aparecem como 4,5% de parciais a 8 pixels e 1,2% a 32.7
Uma fonte de pontos nunca se alinha. DotGothic16 não tem uma grade da qual tirar múltiplos, então a sonda a desenha a 16 pontos por em nominais e a 1,25 a 4 vezes isso (seção 4). Com o antialiasing ligado e a suavização desligada, ela tem 82,3% de parciais a 16 pixels, 52,5% a 32, 48,8% a 48 e 37,3% a 64, e é parcial em cada um dos nove tamanhos de 16 a 64 pixels, incluindo os múltiplos inteiros de 16; os pontos dela não ficam em nenhuma grade de pixels (seção 4).719
Misaki e k8x12 trazem tabelas de bitmap embutidas (EBDT e EBLC, lidas com CTFontCopyAvailableTables). Os resultados medidos acima são o que o Core Text desenhou; se ele desenhou esses bitmaps ou os contornos, não foi determinado (seção 11).7
Misaki, PixelMplus10 e Press Start 2P não têm nenhuma das duas falhas discretas. A regra que daí decorre diz respeito ao arquivo da fonte tanto quanto ao tamanho: múltiplos inteiros, uma grade sobre a linha de base e avanços de pixels inteiros.
Dynamic Type e a grade
As Human Interface Guidelines da Apple dão ao iOS um tamanho de texto padrão de 17 pontos e um mínimo de 11, e uma tabela para cada tamanho de Dynamic Type. Body percorre, de xSmall a AX5, 14, 15, 16, 17, 19, 21, 23, 28, 33, 40, 47 e 53 pontos; Caption 1, de 11 a 43.6010
Numa tela de 3×, um pixel de fonte é um número inteiro de pixels de dispositivo só quando o tamanho em pontos vezes 3, dividido pelo em em pixels de fonte, dá um número inteiro. Dos doze tamanhos de Body, um em de 12 pixels cai inteiro em 3 deles (16, 28 e 40 pontos), um em de 8 pixels em 2 (16 e 40) e um em de 10 pixels em 1 (40). Dos doze tamanhos de Caption 1, um em de 12 pixels cai inteiro em 3 (12, 16 e 32).760
Quais tamanhos de Body caem em pixels de dispositivo inteiros a 3×, e o arredondamento do briefing; desenhado por figures/make_figures.py a partir de measure_coretext.out e measure_kiradex_text.out.
Então uma fonte pixelada não pode simplesmente seguir o Dynamic Type. Se escalar continuamente com os tamanhos do sistema, ela fica fora da grade em todos os tamanhos menos os poucos inteiros: 11 de 12 tamanhos de Body para um em de 10 pixels, 9 de 12 para um de 12.733 Fora da grade, o Core Text no Mac, com o antialiasing ligado, borrou todas as fontes medidas; se o SwiftUI num iPhone faz o mesmo depende da renderização e do posicionamento dele, e isso as capturas do briefing decidem. A fonte precisa pegar o tamanho que o sistema pede e arredondá-lo à grade, algo que a seção 10 transforma numa receita de API e a seção 12 na regra do briefing.
7. Quebra de linha japonesa
Uma linha predefinida curta nunca quebra. Uma caixa de texto quebra, e o japonês tem regras sobre onde.
As regras
Os Requirements for Japanese Text Layout do W3C (uma Working Group Note de 11 de agosto de 2020) definem no item 3.1.7 a regra de início de linha: “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)” (em princípio, nenhuma linha deve começar com colchetes de fechamento, hífens, sinais de pontuação divisórios, pontos médios, pontos finais, vírgulas, marcas de iteração, a marca de som prolongado, kana pequenos ou colchetes de fechamento de warichu). O documento também registra a prática mais frouxa: “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.” (não poucos livros adotam um conjunto de regras menos estrito, que permite começar uma linha com a marca de iteração ideográfica 々, a marca de som prolongado e kana pequenos).21
O algoritmo de quebra de linha do Unicode, o UAX #14 (revisão 57, para o Unicode 18.0.0, datado de 2026-09-01), põe os hiragana e katakana pequenos e a marca de som prolongado na classe CJ e diz: “Treating characters of class CJ as class NS will give CSS strict line breaking; treating them as class ID will give CSS normal breaking.” (tratar os caracteres da classe CJ como classe NS dá a quebra de linha estrita do CSS; tratá-los como classe ID dá a quebra normal do CSS).61
O que o Core Text fez
measure_coretext.swift, executado no macOS 27.0 (não no iOS), compõe um parágrafo japonês de 131 caracteres com CTFramesetter nas larguras de 8, 10, 12, 14, 16, 18 e 20 em, e confere o primeiro caractere de cada linha depois da primeira contra a sua própria lista de caracteres proibidos no início de linha: alguns dos colchetes de fechamento, a vírgula e o ponto final japoneses, o ponto médio, os dois-pontos e o ponto e vírgula, os pontos de interrogação e exclamação, a marca de som prolongado, os kana pequenos e 々.7 Essa lista não é um subconjunto das classes que a regra do JLREQ nomeia, apenas se sobrepõe a elas. Sete dos seus 40 caracteres, ,.:;?! e ), são as formas de largura total que o texto japonês usa onde o JLREQ lista as ASCII; os outros 33 são do próprio JLREQ. Dos colchetes de fechamento, ela tem 」』】〉》〕 e o ) de largura total, deixando de fora ’ ” ] } » 〟 ⦆ 〙 〗 e o ) ASCII; dos 40 kana pequenos, tem 22, deixando de fora ゕ, ゖ e de ㇰ a ㇿ; e deixa de fora os hífens (cl-03), entre eles ‐, 〜, ゠ e a meia-risca, ‼ ⁇ ⁈ ⁉ de cl-04, e todas as marcas de iteração (cl-09) menos 々, entre elas ヽ, ヾ, ゝ, ゞ e 〻.721 Os resultados abaixo valem para a lista da sonda, e é o texto do parágrafo de teste que decide quais caracteres chegam a um início de linha.
Sem marcação, nenhuma linha começou com um caractere da lista da sonda em nenhuma largura, nem em Hiragino Sans (a fonte japonesa do sistema, usada como controle) nem em nenhuma das oito fontes pixeladas: Misaki Gothic, Misaki Gothic 2nd, k8x12, k8x12S, PixelMplus10 Regular e Bold, PixelMplus12 e DotGothic16 nos seus 16 pontos nominais.7
Com kCTLanguageAttributeName definido como ja, a regra afrouxou. Em Hiragino Sans, nas duas Misaki, nas duas PixelMplus10, em PixelMplus12 e em DotGothic16, uma linha começou com um kana pequeno (っ, ョ ou ァ) em 6 das 7 larguras, todas menos 18 em.7 Em k8x12 e k8x12S, uma linha começou com um kana pequeno (っ, ョ ou ょ) em 4 das 7 larguras, e com a marca de som prolongado ー em duas, 10 em (ao lado de ョ) e 18 em, uma quinta largura.7
Na minha leitura dessas contagens diante das duas citações, a marcação ja dá a regra menos estrita que o JLREQ descreve, a que o UAX #14 chama de quebra normal do CSS, e a execução sem marcação se comporta de forma estrita.2161 É o contrário do que eu esperava, já que a marcação nomeia o idioma, e é por isso que o briefing não marca o texto da caixa sem uma captura.
Duas notas sobre a própria medição, ambas de ler o script contra a saída dele. As larguras estão em em, o tamanho cheio da fonte, então, para k8x12, cujos kana têm 8 pixels num em de 12, cada largura comporta uma vez e meia os kana que o número indica; o parágrafo de k8x12 ocupou 12 linhas a 8 em, onde os outros ocuparam 17.7 E DotGothic16, que não tem grade, foi composta no dobro dos seus 16 pontos nominais, 32 pixels por em; nesse tamanho, ela ocupou o mesmo número de linhas que Hiragino Sans em cada largura, de 17 a 8 em a 7 a 20, e os mesmos kana pequenos abriram as linhas dela com a marcação.7
8. Os jogos pixelados que vieram depois: caixas, balões, japonês e chinês
Stardew Valley: duas formas de fala
Stardew Valley tem as duas formas que o Kiradex já tem. A caixa de diálogo dele passa de página com um comando, $b, que a página de modding sobre diálogo da Stardew Valley Wiki (editada pela última vez em 20 de maio de 2026) descreve como um que “Indicates pauses in dialogue, where the player will need to click for the next part to load in a new dialogue box” (indica pausas no diálogo, em que o jogador precisa clicar para a próxima parte carregar numa nova caixa de diálogo), e comandos de retrato que “set the portrait shown in the dialogue box for the current line of dialogue” (definem o retrato mostrado na caixa de diálogo para a linha atual).62 E tem balões de fala, Strings\SpeechBubbles, para o “dialogue spoken by NPCs through speech bubbles when the player enters a given location” (diálogo falado pelos NPCs em balões quando o jogador entra num determinado lugar).62 A caixa é para conversas, com um rosto à frente; os balões são o que uma cidade diz quando você entra.
Os idiomas não latinos dele usam fontes bitmap. A página da wiki sobre idiomas personalizados (editada pela última vez em 3 de janeiro de 2026) manda um idioma novo definir UseLatinFont como false e fornecer um arquivo .fnt, tomando como modelo “the Chinese, Japanese, Korean, and Russian fonts in the Fonts folder” (as fontes chinesa, japonesa, coreana e russa da pasta Fonts). O FontPixelZoom é “A factor by which to multiply the font size. The recommended baseline is 1.5” (um fator pelo qual multiplicar o tamanho da fonte; a base recomendada é 1.5), e FontApplyYOffset define “Whether to shift the font up by four pixels” (se a fonte deve subir quatro pixels), para alinhar “languages with larger characters like Chinese and Japanese” (idiomas com caracteres maiores, como chinês e japonês).63 Um zoom de 1,5 não é um múltiplo inteiro, mas a página o dá como ponto de partida para um idioma que um modder acrescenta, não como o que as fontes chinesa, japonesa e coreana que vêm com o jogo usam, e se um zoom reamostra na tela depende também da escala do jogo em relação aos pixels de dispositivo; então isso não diz nada certo sobre como o Stardew desenha o próprio japonês.63
Final Fantasy Pixel Remaster: o trabalho foi a fonte
A versão para consoles do Pixel Remaster acrescentou uma opção de fonte pixelada, e o produtor, Naofumi Takuma, contou à Siliconera (Jenni Lada, 2 de junho de 2023) como ela foi feita. Para os idiomas ocidentais, a equipe encontrou dados de fonte produzidos internamente e os usou. Para o japonês: “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.” (não tínhamos nenhum dado alternativo salvo internamente para a fonte do idioma japonês, então decidimos criar uma nova). E: “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.” (enquanto a fonte dos idiomas ocidentais exigiu criar só algumas centenas de caracteres, na fonte japonesa tivemos de criar cerca de 7.500).8
Essa é a quarta imprecisão do post do mundo. Ele dava o mesmo número, “about 7,500 Japanese characters” (cerca de 7.500 caracteres japoneses), e citava a reportagem do GoNintendo sobre o trabalho; a fonte primária é a própria entrevista do produtor à Siliconera, que diz “around 7,500 characters”.238 Mesmo número, fonte melhor.
Sete mil e quinhentos é mais ou menos o tamanho dos 6.879 caracteres do JIS X 0208, a meta de cobertura habitual; a comparação é minha.533 Um jogo cujas linhas ficam todas fixadas antes do lançamento pode desenhar menos, como fez a ROM japonesa de Final Fantasy VI com 1.248 letras de diálogo (seção 3), e aí cada string precisa ser conferida contra a fonte; uma fonte que cobre o conjunto inteiro não precisa dessa conferência para nenhuma string tirada dele.4 A lição para um app pequeno é a mesma que as fontes gratuitas da seção 4 ensinam pelo outro lado: para um texto que vai continuar crescendo, a tipografia pixelada japonesa só é um problema resolvido se alguém já desenhou os sete mil caracteres e a licença deixa você distribuí-los.
O que os jogos pixelados modernos trazem
Na Steam, em 5 de outubro de 2026, Stardew Valley lista 12 idiomas, entre eles japonês, chinês simplificado e coreano; Celeste, 9, entre eles japonês e chinês simplificado; Sea of Stars, 11, entre eles japonês, coreano e chinês simplificado e tradicional; e Eastward, 7, entre eles os dois chineses e o japonês.64 Todos os quatro trazem japonês.
Como Celeste, Sea of Stars e Eastward desenham o texto em japonês e em chinês, não encontrei em nenhuma fonte dos desenvolvedores, então este post não diz nada sobre isso além das listas de idiomas (seção 11).
Duas formas de fala no Kiradex
O Kiradex já tem as duas formas do Stardew: a caixa do WorldHUD na parte de baixo da tela e os balões da praça sobre as cabeças.2625 O que falta é a tipografia do cânone dentro deles, e um jeito de o VoiceOver ouvir qualquer um dos dois. O próprio comentário do balão no código diz o que ele buscava: “A line said, as the old games drew it: a cream box with a dark border.” (uma fala, como os jogos antigos a desenhavam: uma caixa creme com uma borda escura).25
9. O ofício, como regras com números
Estas são as regras que as medições sustentam, cada uma com a sua origem. Onde uma regra transforma um fato medido numa recomendação, a recomendação é minha, e a regra deixa isso claro pelo verbo.
| Elemento | Regra | Fonte |
|---|---|---|
| Caixa | A caixa é o terço inferior da tela e duas linhas. Ponha-a embaixo; deixe que um jogo que precise da parte de baixo a mova para cima. | A moldura de Emerald, de 30 por 6 tiles, a largura inteira e 30% da altura, em volta de uma janela de 27 por 4 tiles com duas linhas de 16 pixels; a de Crystal, de 20 por 6, 33%, duas linhas de 18, e a de Red, a mesma de 20 por 6; a janela de Final Fantasy VI na linha 1 ou 18 |
| Glifos latinos | De 4 a 12 pixels de largura numa célula de 8 a 16 pixels de altura. | A célula de 6 por 16 de Emerald, a 5,25 pixels por caractere em texto real; os 4 a 12 de Final Fantasy VI numa célula de 16 por 11; os 8 por 8 fixos de Crystal |
| Glifos japoneses | De 8 a 12 pixels, cerca do dobro de uma letra latina na mesma fonte. | Misaki, 8 por 8 com tinta de 7 por 7; Emerald, 8 de largura numa linha de 16; a folha de FireRed e PixelMplus10, 10; Final Fantasy VI e PixelMplus12, 12; 8 contra 4, 10 contra 5 e 12 contra 6 em todas as fontes com japonês medidas |
| Comprimento da linha | De 18 a 41 caracteres por linha; escreva na medida da largura em vez de preenchê-la. | Crystal, 18; a janela de 216 pixels de Emerald comporta 41 a 5,25 pixels, e os roteiristas pararam em 38 em 95% das linhas; a linha de 220 pixels de Final Fantasy VI comporta 18 caracteres japoneses de 12 pixels |
| Quebras | Ou quem escreve quebra cada linha, ou o código quebra por palavra; nunca as duas coisas. | As 24.100 linhas quebradas à mão de Emerald, nenhuma acima de 208 pixels; CalcTextWidth e NewLine de Final Fantasy VI |
| Velocidade | Conte os caracteres em tempo: 60 por segundo como teto (o ajuste rápido de Emerald e de Crystal, e a letra por quadro de Final Fantasy VI), de 15 a 20 para um ajuste médio, de 7,5 a 12 para o lento. | Emerald, 7,5, 15 e 60, e 60 com o botão pressionado; Crystal e Red, 12, 20 e 60; Final Fantasy VI, 60 |
| Marca de avanço | Mova a marca num relógio lento: um passo a cada 150 milissegundos, um ciclo de cerca de 0,6 segundo. | Os 0, 1, 2 e 1 pixels de Emerald, um passo a cada 9 quadros, 36 quadros por ciclo; o ▼ de Crystal piscando contra o tile da borda |
| Molduras | Molduras são tiles, não desenhos: um canto, uma borda e um centro, repetidos. Deixe o jogador escolher entre algumas. | As 20 molduras de menu selecionáveis de 24 por 24 de Emerald e a tira de diálogo de 14 tiles com a borda de baixo espelhada; as 8 molduras de Crystal de 6 tiles de um bit; os 8 estilos de 28 tiles de Final Fantasy VI com 7 cores editáveis |
| Tipografia nítida | Uma fonte pixelada só é nítida com pixels de dispositivo inteiros por pixel de fonte e, no Mac, só com a suavização de fontes desligada; capture um iPhone para ver o que vale lá. | Core Text no macOS 27.0 com a suavização de fontes desligada: 0,0% de pixels parciais nos múltiplos inteiros de uma grade sobre a linha de base com avanços de pixels inteiros (Misaki, PixelMplus10, Press Start 2P), de 19,4% a 82,2% entre eles; com a suavização padrão do contexto, PixelMplus10 nos seus próprios 10 pixels, 178 de 272 pixels com tinta parciais |
| Arquivos limpos | Confira a grade de uma fonte, o deslocamento dela em relação à linha de base e os avanços antes de escolhê-la. | Core Text no macOS 27.0: a grade de PixelMplus12, 0,39 pixel fora da linha de base, borrada em todos os tamanhos; o avanço de kana de 8,01 pixels de k8x12, borrado assim que os glifos são posicionados com exatidão; os 10 pontos soltos de Silkscreen, de 1,2% a 4,5% |
| Dynamic Type | Respeite o tamanho do usuário arredondando-o à grade, nunca escalando continuamente, e nunca abaixo de 11 pontos. | Um em de 10 pixels é inteiro em 1 dos 12 tamanhos de Body a 3×, um de 12 pixels em 3 e um de 8 pixels em 2; o mínimo de 11 pontos das HIG |
| Quebras em japonês | Mantenha a regra estrita de início de linha para o texto da caixa; mantenha as linhas predefinidas curtas o bastante para nunca quebrarem. | JLREQ 3.1.7; o Core Text no macOS 27.0, sem marcação, manteve a regra estrita para a lista parcial de início de linha da sonda em todas as larguras medidas, e com a marcação ja deixou kana pequenos começarem linhas em 6 de 7 larguras na maioria das fontes (4 de 7 em k8x12 e k8x12S) |
| Quem fala | Diga quem fala dentro da linha, não num campo separado nem sobre a cabeça. | A caixa de Emerald não tem campo de nome, e 6,5% dos seus parágrafos começam com um nome e dois-pontos |
| Cobertura | Cubra o conjunto padrão, cerca de sete mil caracteres, antes que possa sair texto japonês que os roteiristas não fixaram; um script fixo pode sair com menos, conferido string por string. | Os 6.879 do JIS X 0208; os “around 7,500” do Pixel Remaster; as 1.248 letras de diálogo da ROM japonesa de Final Fantasy VI; Misaki, k8x12 e DotGothic16 desenham todo o JIS X 0208, PixelMplus todo menos três pontos de código |
| Licenças | Só distribua uma fonte com o texto da licença em mãos e os termos lidos. | A licença de três frases de Misaki, k8x12 e PixelMplus; a SIL Open Font License de DotGothic16, Press Start 2P e Silkscreen, com o Reserved Font Name de Press Start 2P |
| Duas formas | Mantenha as duas formas, uma caixa e um balão. O que cada uma diz é escolha de cada jogo; a do Kiradex, minha, é a caixa para o que um lugar diz e o balão para o que uma pessoa diz. | Stardew Valley tem as duas: a caixa para conversas, os balões para o que os NPCs dizem quando o jogador entra num lugar |
Cada número da tabela vem das seções 1 a 8 e tem lá a sua nota.1467921862
10. Do jeito da Apple: tipografia, tamanhos, quebras de linha e VoiceOver
O Kiradex World é uma cena de RealityKit usada como renderizador 2D, com uma câmera ortográfica a um número inteiro de pixels de dispositivo por texel e SwiftUI por cima para tudo o que uma pessoa lê.23 O post do mundo decidiu que o texto de diálogo “lives in SwiftUI at UI scale rather than in the world” (vive no SwiftUI em escala de interface, e não no mundo), e esta seção mantém essa decisão e preenche as APIs: o que a Apple pede da tipografia personalizada, como entra uma fonte pixelada incluída no app, como o Dynamic Type pode ser respeitado por arredondamento, como o japonês quebra e como o VoiceOver pode ler um palco que é uma única view de RealityKit. Cada disponibilidade abaixo vem das páginas de documentação da Apple, salvas em 5 de outubro de 2026; nada do código foi compilado, e nenhum custo desta seção foi medido num telefone.65
O que a Apple pede do texto
A página Typography das Human Interface Guidelines dá ao iOS e ao iPadOS um tamanho de texto padrão de 17 pontos e um mínimo de 11, e pede que os apps “Follow the recommended default and minimum text sizes for each platform” (sigam os tamanhos de texto padrão e mínimo recomendados para cada plataforma), tanto para fontes personalizadas quanto para as do sistema.1060 As fontes do sistema dão suporte por conta própria ao Dynamic Type e a recursos de acessibilidade como Texto em Negrito; para o resto, a página diz: “If you use a custom font, make sure it implements the same behaviors.” (se você usar uma fonte personalizada, garanta que ela implemente os mesmos comportamentos).10
A página Accessibility define o tamanho do trabalho: “Ideally, give people the option to enlarge text by at least 200 percent (or 140 percent in watchOS apps).” (de preferência, dê às pessoas a opção de ampliar o texto em pelo menos 200 por cento, ou 140 por cento em apps de watchOS).20 A página sobre jogos acrescenta a legibilidade: “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.” (para manter o texto confortavelmente legível em cada dispositivo, garanta que ele contraste bem com o fundo e use pelo menos o tamanho mínimo de texto recomendado em cada plataforma).66
A faixa de Body, de 14 a 53 pontos, vai de cerca de 82% a cerca de 312% do padrão de 17; os tamanhos arredondados da caixa a 3×, de 13,33 a 53,33 pontos, vão de 80% a 320% do seu próprio padrão de 16,67, além dos 200% que a página Accessibility pede.601133
Escalar uma fonte personalizada
A Apple tem três jeitos de escalar um valor com o Dynamic Type, e um para impedir isso.
UIFontMetrics, “A utility object for obtaining custom fonts that scale to support Dynamic Type” (um objeto utilitário para obter fontes personalizadas que escalam para dar suporte ao Dynamic Type), disponível desde o iOS 11.0, comscaledFont(for:),scaledFont(for:maximumPointSize:)escaledValue(for:), que “Scales an arbitrary layout value based on the current Dynamic Type settings” (escala um valor de layout arbitrário com base nos ajustes atuais do Dynamic Type).67Font.custom(_:size:relativeTo:), no SwiftUI, cria uma fonte personalizada “that scales relative to the giventextStyle” (que escala em relação aotextStyleinformado), desde o iOS 14.0.68ScaledMetric, “A dynamic property that scales a numeric value” (uma propriedade dinâmica que escala um valor numérico), desde o iOS 14.0.69Font.custom(_:fixedSize:)cria uma fonte personalizada “that does not scale with Dynamic Type” (que não escala com o Dynamic Type), desde o iOS 14.0.68
Uma fonte pixelada precisa da primeira ou da terceira junto com a última: ler o tamanho que o sistema usaria, arredondá-lo à grade da fonte e aplicar o tamanho arredondado como tamanho fixo, para que o sistema não o escale de novo. O arredondamento em si é aritmética, e está no briefing da seção 12: um pixel de fonte vira m pixels de dispositivo, m = max(m₀, ⌊size × displayScale / 10 + 0.5⌋) para um em de 10 pixels, o inteiro mais próximo com a metade arredondando para cima, onde m₀, o menor m com pelo menos 11 pontos, vale 4 a 3× e 3 a 2×, e o tamanho em pontos é 10 × m / displayScale. A 3×, isso leva os doze tamanhos de Body a valores de m de 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 e 16 (Small, de 15 pontos, é a única metade exata: 4,5 arredonda para 5): 16,67 pontos no tamanho padrão e 53,33 em AX5.11
Para limitar o tamanho dentro de uma view, dynamicTypeSize(_:) (iOS 15.0) define ou limita o tamanho do Dynamic Type, e a página dele aconselha: “When limiting the Dynamic Type size, consider if adding a large content view with accessibilityShowsLargeContentViewer() would be appropriate.” (ao limitar o tamanho do Dynamic Type, considere se seria apropriado acrescentar uma visualização de conteúdo grande com accessibilityShowsLargeContentViewer()).70 Esse modificador, também do iOS 15.0, traz o seu próprio aviso: “Don’t use the large content viewer as a replacement for proper Dynamic Type support.” (não use o visualizador de conteúdo grande como substituto de um suporte adequado ao Dynamic Type).71 O briefing limita só os balões, que precisam continuar com uma linha, e nunca a caixa.
Incluir a fonte
Uma fonte vai no bundle do app e é nomeada em UIAppFonts, “App-specific font files located in the bundle and that the system loads at runtime” (arquivos de fonte específicos do app, localizados no bundle e que o sistema carrega em tempo de execução) (iOS 3.2), ou registrada em código com CTFontManagerRegisterFontsForURL(_:_:_:) (iOS 4.1).7273
Desenhar a moldura e posicionar a linha de base
Uma moldura desenhada a partir de poucos tiles é uma imagem redimensionada sem suavização. Image.interpolation(_:), do SwiftUI, define a qualidade da interpolação desde o iOS 13.0, e Image.resizable(capInsets:resizingMode:), que “Sets the mode by which SwiftUI resizes an image to fit its space” (define o modo pelo qual o SwiftUI redimensiona uma imagem para caber no espaço dela), estica ou repete em mosaico tudo menos os cantos, também desde o iOS 13.0.7475 O briefing desenha a moldura num Canvas, para que cada pixel de origem tenha exatamente m por m pixels de dispositivo; na minha leitura, resizable(capInsets:) com .tile serviria igualmente só se o mosaico dele caísse em pixels de dispositivo inteiros, o que ninguém mediu.
Text.baselineOffset(_:), que “Sets the vertical offset for the text relative to its baseline” (define o deslocamento vertical do texto em relação à linha de base) (iOS 13.0), é a ferramenta para uma fonte cuja grade fica fora da linha de base, como a de PixelMplus12; a seção 6 mediu que um deslocamento conserta uma fonte assim num único tamanho, que é um dos motivos de o briefing não usá-la.767
Quebra de linha japonesa no SwiftUI e no UIKit
typesettingLanguage(_:isEnabled:) (iOS 17.0) serve para um Text que “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” (pode conter texto de um idioma específico que não coincide com o idioma da interface do dispositivo; nesse caso, é útil especificar um idioma para que a altura de linha, a quebra de linha e o espaçamento respeitem a escrita desse idioma). A página dele acrescenta: “this language does not affect text localization.” (esse idioma não afeta a localização do texto).77
O NSParagraphStyle.LineBreakStrategy do UIKit (iOS 9.0) tem três estratégias nomeadas. standard (iOS 14.0) “uses the same configuration of line-break strategies that it uses for standard UI labels” (usa a mesma configuração de estratégias de quebra de linha que usa para os rótulos padrão da interface) e “may be unsuitable for large amounts of text” (pode não ser adequada para grandes quantidades de texto); pushOut (iOS 9.0) existe para evitar “an orphan word on the last line” (uma palavra órfã na última linha); hangulWordPriority (iOS 14.0) é para o coreano.78
A seção 7 mediu que o Core Text no macOS, marcado com ja, deixou kana pequenos começarem linhas em 6 de 7 larguras na maioria das fontes (4 de 7 em k8x12 e k8x12S), e sem marcação não.7 O que o Text do SwiftUI faz com o japonês quando o idioma do dispositivo é japonês, e se typesettingLanguage define o mesmo atributo que a medição definiu, não foi medido (seção 11). Por isso o briefing mantém o texto da caixa sem marcação, a menos que uma captura mostre a regra estrita valendo com a marcação.
VoiceOver sobre um palco
Os modificadores de acessibilidade do SwiftUI descrevem uma view: accessibilityLabel(_:) a nomeia, e accessibilityValue(_:) acrescenta o valor dela, com uma regra na documentação: “Use this method to describe the value represented by a view, but only if that’s different than the view’s label.” (use este método para descrever o valor representado por uma view, mas só se ele for diferente do rótulo da view). Os dois são do iOS 16.0 na forma com LocalizedStringResource que as páginas listam.79
O palco de um jogo precisa de mais do que um rótulo. accessibilityDirectTouch(_:options:) (iOS 17.0) cria uma área em que “Direct touch areas passthrough touch events to the app rather than being handled through an assistive technology, such as VoiceOver” (áreas de toque direto repassam os eventos de toque ao app em vez de serem tratadas por uma tecnologia assistiva, como o VoiceOver), depois que o usuário a ativa.80 AccessibilityTraits.updatesFrequently (iOS 13.0) é para um elemento que muda: “Use this trait when you want an assistive technology to poll for changes when it needs updated information.” (use este traço quando quiser que uma tecnologia assistiva consulte as mudanças quando precisar de informação atualizada).81 AccessibilityNotification.Announcement (iOS 17.0) publica um anúncio falado, com um atributo accessibilitySpeechAnnouncementPriority que define como ele entra na fila em relação a outras falas.82 E accessibilityReduceMotion (iOS 13.0) informa o ajuste Reduzir Movimento (Reduce Motion), que, segundo a página dele, significa que a “UI should avoid large animations” (interface deve evitar animações grandes).83
O RealityKit consegue descrever as próprias entidades. AccessibilityComponent (iOS 17.0) é “A component that stores accessibility information for an entity” (um componente que guarda informações de acessibilidade de uma entidade), cujo nome o “VoiceOver says aloud when it describes the entity” (VoiceOver diz em voz alta ao descrever a entidade).84 O artigo da Apple “Improving the Accessibility of RealityKit Apps” manda “set the properties on each visible Entity in your scene” (definir as propriedades em cada Entity visível da cena): as propriedades de acessibilidade da própria Entity, isAccessibilityElement, accessibilityLabel e accessibilityDescription, que o artigo data do iOS 14 e cujas páginas agora estão marcadas como obsoletas, todas menos isAccessibilityElement (a página também lista accessibilityLabelKey, um rótulo como chave de string localizada), um caminho mais antigo ao lado do componente.85 RealityView, a view de SwiftUI que o palco do Kiradex usa, é do iOS 18.0.86 Se uma RealityView com uma câmera ortográfica virtual, como o Kiradex a configura, expõe essas entidades ao VoiceOver, não está documentado nessas páginas e não foi testado (seção 11).
Testar isso já pode ser automatizado. XCUIVoiceOverService “Provides programmatic control of VoiceOver for UI testing” (oferece controle programático do VoiceOver para testes de interface), disponível desde o iOS 27.0, e é acessado pela propriedade voiceOverService de XCUIDevice.87
O Kiradex hoje
Lido no repositório em 5 de outubro de 2026, no commit 334fbfe com as edições não commitadas daquele dia, sem que eu o executasse ou alterasse; o app estava sendo editado enquanto eu o lia (seção 11):11
- Uma fonte só, a do sistema. A família de fontes do tema é
.systemDesign(.monospaced), e as views do mundo usam os papéis do kit: caption em 10 chamadas distribuídas em quatro arquivos, label em 3, body em 3 e title em 3.2411 - Um caminho de fonte personalizada que escala continuamente. O
Kit941.FontFamily.custom(named:)do kit compartilhado resolve paraFont.custom(_:size:relativeTo:)em 34, 22, 17, 13 e 12 pontos para display, title, body, label e caption.88 Com uma fonte pixelada, isso escalaria por todos os tamanhos de Dynamic Type, o que a seção 6 conta como fora da grade em 9 de 12 tamanhos de Body para um em de 12 pixels e em 11 de 12 para um em de 10.7 - O balão.
Text(text).kit941Font(.caption, weight: .bold), tinta sobreWorldInk.paper(uma superfície de arte com duas cores fixas), com o espaçamento pequeno do kit como preenchimento na horizontal e o extrapequeno na vertical, com uma borda de tinta de 2 pontos e.fixedSize(), então uma linha. Ele fica um passo de espaçamento grande acima derig.headPoint, aparece enquanto a linha tem menos de 4 segundos e tem o rótulo"Said: \(text)"dentro de uma sobreposição com o hit testing desligado. Essa sobreposição fica dentro do palco, sob as duas sobreposições que o palco põe sobre a cena inteira: scanlines, uma linha preta com 16% de opacidade a cada 3 pontos, e uma vinheta.2511 - A caixa.
WorldHUDpõe o título do lugar (caption, negrito, em maiúsculas) e o status dele (caption, seminegrito) numa caixa creme com a mesma borda de 2 pontos na parte de baixo da tela; com o Duo meio aberto, ela fica do lado mais largo da dobra. Ela não recebe toques (.allowsHitTesting(false), “Taps go through to the ground under it”, os toques passam para o chão embaixo dela) e fica sobreposta ao palco inteiro, acima das scanlines e da vinheta.26 - O que se diz. 23 linhas predefinidas no servidor, de 3 a 40 caracteres, com mediana de 16; a mais longa é “Anyone have a card from the first years?” (alguém tem uma carta dos primeiros anos?). Os apelidos são duas palavras de listas de 20 e 20 e quatro dígitos, com no máximo 20 caracteres (“Glacier Shuffle 0000”). Nada é desenhado sobre a cabeça de um colecionador além do balão; o apelido aparece no painel que um toque abre.288911 O post desta série sobre a vida própria da cidade contou as mesmas 23 linhas e um limite do servidor de uma linha a cada 2,0 segundos por jogador.9091
- Nenhuma fonte, um idioma. Nenhum arquivo de fonte é incluído (o único
.ttfdo repositório éscripts/screenshots/MartianMono-latin.ttf, para a ferramenta de capturas de tela); asknownRegionsdo projeto sãoBaseeen, e não há nenhum arquivo.xcstringsou.lproj.1127 - O VoiceOver ouve coordenadas. O palco é um elemento só,
.accessibilityLabel("The plaza")e.accessibilityValue(stepTrace), em questepTraceé o último passo como “x,y facing” (x,y direção), mantido porque “the walks read it, since nothing else the simulator logs reaches them” (os testes de caminhada o leem, já que nada mais do que o simulador registra chega até eles). NenhumAccessibilityComponent,accessibilityDirectTouch,AccessibilityNotification,typesettingLanguage,UIFontMetrics,ScaledMetricoudynamicTypeSizeaparece em nenhum lugar do código do app.2511 - Placas sem letras. O
sign()da forja é “A hanging sign on a bracket, 12 × 11” (uma placa pendurada num suporte, 12 × 11) cuja face são duas fileiras de marcas de tinta, não letras; o mapa da praça tem um tile de placa,S. Um dedo no chão leva o jogador na hora até o tile embaixo dele e é seguido de tile em tile; um toque num colecionador abre o painel dele quando o dedo sobe, e um no quiosque abre o quadro da cidade; um tile de placa é chão como qualquer outro, sem toque próprio.2992 - A escala do mundo. Pela regra de
PlazaRig.fit, a parte inteira da altura em pixels da view dividida por 320, um texel são 8 pixels de dispositivo no iPhone 18 Pro Max (440 por 956 pontos) e na tela interna do Duo em retrato, 6 na tela externa do Duo em retrato e na interna em paisagem, e 4 na externa em paisagem: 2,67, 2,00 ou 1,33 pontos.9311
Por que o texto fica fora do mundo
A aritmética do post do mundo sobre isso está certa: “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” (com k = 8 numa tela de 3×, uma fonte de cinco texels tem 13,3 pontos e com k = 6 tem 10 pontos, abaixo do mínimo de 11 pontos das HIG).23 Isso só restringe fontes abaixo de 5,5 texels com k = 6 (11 pontos a 2 pontos por texel); um em de 8 texels com k = 6 tem 16 pontos.1133 A conclusão, texto em escala de interface no SwiftUI, se sustenta por outro motivo: na densidade do mundo, um em de 10 pixels tem 26,67 pontos num telefone a 8 pixels de dispositivo por texel, o que deixa 29 caracteres latinos por linha da caixa no 18 Pro Max.11 Medida em tiles, a caixa do cânone é estreita. Uma caixa de 16 tiles de hardware de largura, 128 pixels, comporta 16 caracteres de uma fonte de 8 por 8, cerca de 24 dos latinos de Emerald com a média de 5,25 pixels (21 no pior caso, de 6), 16 dos japoneses de 8 pixels de Emerald e 10 dos japoneses de 12 pixels de Final Fantasy VI. Dezesseis dos tiles de 16 texels do Kiradex são 256 texels, que na densidade do próprio mundo comportam 42 caracteres latinos e 21 kana de PixelMplus12, ou 64 e 32 de Misaki.11 E uma caixa medida nos próprios tiles do Kiradex não cabe em três das cinco views medidas: 16 tiles de 16 texels são 683 pontos a 8 pixels de dispositivo por texel, mais largos que o iPhone 18 Pro Max (440 pontos) e que a tela interna do Duo em retrato (669), e 512 pontos a 6, mais largos que a tela externa do Duo em retrato (466). Ela só cabe nas duas views em paisagem: 512 pontos nos 951 da tela interna, e 341 pontos a 4 pixels de dispositivo por texel nos 678 da externa.11 Uma caixa do Kiradex é dimensionada em pontos, não em tiles.
A mesma aritmética no tamanho do briefing: PixelMplus10 com m = 5, 16,67 pontos, comporta 47 caracteres latinos ou 23 japoneses por linha na caixa de um 18 Pro Max em retrato, depois da margem atual de 48 pontos, 16 pontos de margem e 8 de preenchimento de cada lado, contados a partir da largura inteira, sem margens laterais (suposto, não medido). A caixa fica dentro da área segura, então, na tela externa do Duo em retrato, a contagem depende da margem lateral: 50 ou 25 sem margem, e 40 ou 20 com a margem final de 84 pontos, o trilho lateral, que esta série mediu na tela fechada (seção 11). Com a moldura do briefing (seção 12, item 3), que ocupa 8 pixels de fonte de cada lado no lugar do preenchimento e arredonda a caixa para baixo a pixels de fonte inteiros, as mesmas linhas comportam 45 ou 22 no 18 Pro Max, e 48 ou 24, ou 38 ou 19 com o trilho, no Duo.119495
Custos
Nada disso foi medido num dispositivo. Incluir uma fonte de 7.253 glifos acrescenta o arquivo dela ao app;7 desenhar texto pelo SwiftUI com uma fonte personalizada custa o que qualquer Text custa; e a moldura do briefing é um Canvas que se redesenha quando o tamanho muda. Na minha leitura, nada disso é grande perto de uma cena de RealityKit, mas é uma leitura, e as capturas do briefing são onde ela é conferida.
11. O que está medido e o que não está
Tudo o que tem número neste post vem de scripts que rodam de novo sobre fontes salvas, de páginas salvas em 5 de outubro de 2026 ou de leituras datadas do código do Kiradex; as notas dizem qual. Esta seção é a lista do que isso não cobre.30
Medido no código, reexecutável. measure_gen_text.py sobre as quatro descompilações do pret: a tabela de fontes de Emerald (dez entradas, sete fontes); a velocidade do texto em quadros e caracteres por segundo; a rolagem; a seta de avanço; a janela de 27 por 4 e a moldura de 30 por 6; as 20 molduras selecionáveis; as larguras dos glifos latinos; 24.100 linhas de script em caracteres e pixels; 934 de 14.412 parágrafos que dizem quem fala; as folhas de glifos japoneses; os atrasos e a janela de FireRed; as telas, caixas, atrasos, molduras e folhas de fonte de Crystal e Red.1 measure_ff6.py sobre o disassembly de ff6: estilos e paletas de janela, a célula e as larguras da fonte de diálogo, o tamanho e as sequências de largura da fonte japonesa, a linha de 220 pixels, uma letra por quadro, o título de 100 quadros, a quebra por palavra, a tela de configuração e a tabela do degradê da janela.4 measure_bdf.py sobre os arquivos BDF de Misaki e k8x12.6 measure_coretext.swift e measure_outline_coords.swift, compilados e executados no macOS 27.0, sobre dez arquivos TrueType e Hiragino Sans.719 measure_kiradex_text.py sobre o Kiradex como estava em disco no commit 334fbfe (reexecutado em 15 de outubro contra a árvore atual, com as linhas anteriores inalteradas), e measure_figure_width.py sobre a folha de corpo dele.1196 measure_hig.py, measure_steam_languages.py e measure_apple_docs.py sobre as páginas salvas.606465 measure_ios27_iphones.py sobre a lista de iPhones do runtime do simulador do iOS 27.0, e probe_line_start_glyphs.swift, compilado e executado no macOS 27.0, sobre as tabelas de classes do JLREQ e PixelMplus10.9798 measure_cell_masks.py sobre as folhas do pret salvas: a referência de máscaras de célula do briefing.99 São treze scripts; um décimo quarto, dump_glyphs.swift, guardado com as figuras deste post, desenhou PixelMplus10 no próprio tamanho com a suavização de fontes ligada e desligada.9
Não medido, ou mais fraco do que parece.
- O Core Text do macOS faz as vezes do iOS. Cada número de renderização e de quebra de linha das seções 6 e 7 vem do Core Text do macOS 27.0 desenhando num contexto de bitmap, com a suavização de fontes desligada, não do SwiftUI num iPhone. As verificações por captura do briefing são a medição que conta; até que rodem, o que um iPhone desenha é uma previsão.79
- Bitmaps embutidos. Misaki e k8x12 trazem tabelas
EBDTeEBLC; se o Core Text desenhou esses bitmaps ou os contornos a 8 e 12 pixels, não foi determinado.7 - O posicionamento do próprio SwiftUI. Se o SwiftUI posiciona as origens de
Textem pixels de dispositivo inteiros, e qual regra de quebra de linha ele aplica ao japonês quando o idioma do dispositivo é japonês, não foi medido. - Pesos. PixelMplus10 Bold é o único negrito medido; negrito sintético em qualquer fonte pixelada não foi.7
- Legibilidade. Grades e cobertura estão medidas; se kanji de 10 por 10 são bem legíveis para um leitor japonês a 16,67 pontos não está, e isso precisa de um leitor japonês com um dispositivo.
- ROMs japonesas. O pret monta os jogos em inglês. A caixa, as velocidades e a fonte próprias do Emerald japonês não foram lidas; as folhas japonesas da seção 1 são as que a ROM em inglês carrega para nomes. Crystal e Red em japonês não foram lidos.30
- Os gráficos de Final Fantasy VI. O disassembly tem intervalos e código, não pixels; o layout dos tiles da janela e a aparência do degradê não foram vistos. O intervalo da fonte grande em inglês, 5.120 bytes, não é um número inteiro de letras de 22 bytes, sem explicação. Que a tabela do degradê é consumida a um valor por linha de varredura vem dos comentários, não foi rastreado.4
- O período do piscar de Crystal não foi derivado; os dois estados foram lidos no código.16
- Chrono Trigger não foi medido de forma alguma. Nenhum disassembly público foi clonado, e o mapa da ROM do Data Crystal retornou HTTP 403.49
- Celeste, Sea of Stars e Eastward. Só as listas de idiomas na Steam; como eles desenham texto em japonês e em chinês não foi encontrado em nenhuma fonte dos desenvolvedores.64
- Pixel Operator, m5x7 e m3x6 não foram baixadas nem medidas, e o repositório do próprio Pixel Operator retornou HTTP 404; as licenças delas são como as páginas as descrevem, a de Pixel Operator só pelo DaFont.565758
- Os três pontos de código do JIS que PixelMplus não mapeia não foram conferidos nos pontos de código alternativos.7
- O tamanho de DotGothic16. Ela não tem grade de pixels, então foi desenhada e composta a 16 pontos por em nominais e em múltiplos disso, escolha minha a partir do nome e do passo dos pontos; nenhum outro tamanho nominal foi testado, e os números dela nas seções 6 e 7 valem só para esses tamanhos.719
- Suavização de fontes. Todos os renderizados da seção 6 impedem a suavização de fontes; com o padrão do contexto de bitmap, só uma fonte num tamanho foi desenhada, PixelMplus10 a 10 pixels, com 178 de 272 pixels com tinta parciais. As outras fontes e tamanhos não foram desenhados com a suavização ligada.9
- O app mudou enquanto eu o lia. O Kiradex estava sendo editado em 5 de outubro enquanto este post era escrito. Mais cedo naquele dia, o tratamento de toques do palco foi reescrito e as linhas dele se moveram de 30 a 144, e três dos arquivos Swift do mundo ainda não estavam em nenhum commit; catorze minutos depois da minha leitura, o palco e o rig tinham mudado de novo. O post lê o commit
334fbfecom as edições não commitadas daquele dia como estavam quando as li: os arquivos do app às 12:28, horário do Pacífico (os colecionadores do servidor e o mapa da cidade às 12:33), os scripts que o medem às 12:34 e 12:36. As notas dão as linhas de cada arquivo nessa leitura; o briefing nomeia símbolos em vez de linhas, e às 12:42 conferi que cada símbolo que ele nomeia ainda estava lá.11 - Os iPhones em que o app roda. A lista de 33 iPhones, três deles de 2×, é a do runtime do simulador do iOS 27.0, lida do Xcode no meu Mac; a página da Apple com os iPhones compatíveis com o iOS 27 não foi buscada.97
- Margens da área segura. O post desta série sobre o simulador do iPhone Duo mediu a tela externa fechada, com 466 pontos de largura, com o conteúdo de 382 pontos de largura dentro de uma margem final de 84 pontos, o trilho lateral, numa sonda que mostrava a barra de status, uma barra de ferramentas e uma barra de abas.95 O mundo do Kiradex esconde a barra de status e o indicador de início e não mostra barras; o código põe os controles do mundo dentro da área segura e diz que isso os mantém longe das câmeras e do trilho lateral, mas nem o código nem aquela medição dizem qual é a largura da margem com a barra de status escondida, então os números do Duo nas seções 10 e 12 aparecem das duas formas, sem margem lateral e com os 84 pontos medidos. As margens laterais dos outros iPhones em retrato não foram medidas; os números do briefing para eles supõem nenhuma.94
- Acessibilidade do RealityKit na configuração do Kiradex.
AccessibilityComponentestá documentado para o iOS 17.0; se umaRealityViewcom uma câmera ortográfica virtual expõe elementos de entidade ao VoiceOver não foi verificado.84 - Os números do briefing. O arredondamento de
m, o balão um passo menor que a caixa e mantido abaixo de um teto definido por cada view, a margem de 32 pontos, a fila da caixa e suas três linhas, os 8 pixels de fonte da moldura no lugar do preenchimento da caixa, os passos de 150 milissegundos da marca, quatro molduras e seis células para os anúncios são propostas, não fatos.
Onde este post diverge do post do mundo. As seções 1, 2, 5 e 8 corrigem, cada uma, uma afirmação dele, com a medição; a aritmética de tamanhos dele está certa (seção 10). Três fatos que este post toma dos outros posts da série foram conferidos contra o app: as 23 linhas predefinidas da cidade batem com lines.py; a nota do post dos personagens de que o balão ainda tira a posição da altura da célula bate com PlazaRig.headPoint, que soma metade da altura do quadro da folha e 4 à posição de quem caminha; e a figura sob o balão é a que a atualização daquele post descreve, com 30 texels de altura numa célula de 32 por 40, o que o manifesto do app, rig.py e a folha de corpo confirmam. Aquele post só dá uma largura para a figura anterior, de 26 texels, cerca de 14 nos ombros; a seção 12 mede a largura da figura atual na folha.90100289396
Fontes não alcançadas. O mapa da ROM de Chrono Trigger no Data Crystal (HTTP 403); o repositório do Pixel Operator no NotABug (HTTP 404); os arquivos de fonte de m5x7 e m3x6 (atrás de um download de pague quanto quiser, não tentado); e uma fonte dos desenvolvedores sobre o texto em japonês e em chinês de Celeste, Sea of Stars ou Eastward (não encontrada).49565758
12. O briefing: o que o Kiradex constrói e as verificações que precisa passar
O app andou desde então. No commit fc9d4876, de 15 de outubro, o rótulo e o valor do palco são definidos num modificador de view StageAccessibility (PlazaStage.swift, aplicado na linha 373, definido a partir da linha 520), ainda “The plaza” com o rastro de passos como valor, e o elemento some enquanto um lugar cobre o palco. As verificações abaixo leem o build de 5 de outubro, e cada “hoje” se refere a esse build; o item 8 vale para os dois.101
Síntese do autor. O que segue é a minha proposta, derivada das seções 1 a 11 e escrita como um diff em relação ao Kiradex como ele estava em 5 de outubro de 2026, no commit 334fbfe com as edições não commitadas daquele dia na árvore de trabalho (seção 11): texto monoespaçado do sistema em cada view do mundo, um balão de uma linha em caption negrito, a caixa do WorldHUD embaixo, um app só em inglês com linhas predefinidas em inglês no servidor, um sprite de placa sem letras e um palco que o VoiceOver lê como coordenadas.2211 A direção do post do mundo continua de pé: texto é arte; o diálogo vive no SwiftUI em escala de interface, não nos texels do mundo; uma caixa construída com tiles; duas linhas; uma máquina de escrever; uma marca de avanço.23 O que muda é a fonte, pelo motivo que a seção 5 dá, e a aritmética, porque o Dynamic Type precisa ser arredondado à grade da fonte.7 Nada aqui desenha um glifo, uma moldura, uma marca ou uma palavra de uma franquia; tudo é desenhado pela forja ou composto numa fonte cuja licença é citada na seção 4. As notas jurídicas são leituras minhas, não aconselhamento jurídico. Nada disso foi construído.
Cada verificação abaixo é uma que um script, uma linha de log de um teste ou uma captura podem aprovar ou reprovar, com exceção de uma verificação manual datada num dispositivo, no item 8. Onde o resultado de uma verificação depende do dispositivo, do tamanho do texto, da linha escolhida ou do que mais a caixa está mostrando, ela os nomeia. O briefing nomeia o código do app pelos símbolos, não pelos números de linha, porque o código se moveu enquanto eu o lia; as notas dão as linhas como eu as li, com o commit e a hora. Onde a evidência por trás de um item é uma medição no meu Mac e a verificação roda num iPhone ou no simulador dele, quem decide é a verificação, não o Mac.
1. Uma fonte para os dois idiomas: PixelMplus10
O quê. Incluir PixelMplus10-Regular.ttf e PixelMplus10-Bold.ttf (de itouhiro, a partir das M+ BITMAP FONTS do M+ FONTS PROJECT; licença: M+ FONT LICENSE, https://github.com/itouhiro/PixelMplus/blob/master/misc/mplus_bitmap_fonts/LICENSE_E) via UIAppFonts, com LICENSE_E no bundle, e acrescentar aos Ajustes uma seção de agradecimentos, que hoje não existe, nomeando itouhiro e o M+ FONTS PROJECT.1872 Usá-la para cada palavra desenhada sobre o mundo: os balões, a caixa do WorldHUD, o texto das placas, o quadro de missões e as plaquinhas da exposição. O resto do app, a Dex, o scanner e os Ajustes, mantém a fonte do sistema.
Por que esta. Duas fontes medidas têm as três coisas que as regras da seção 9 pedem: japonês, uma grade limpa e avanços de pixels inteiros. Misaki Gothic e Misaki Gothic 2nd desenham todos os 6.879 caracteres do JIS X 0208 numa grade de 8 pixels sobre a linha de base, com 0 de 848 e 0 de 860 coordenadas de contorno amostradas fora dela e avanços de 4 e 8. PixelMplus10 desenha 6.876 dos 6.879, todos os kanji dos dois níveis, numa grade de 10 pixels sobre a linha de base, com 0 de 1.032 coordenadas de contorno amostradas fora dela na Regular e 0 de 1.000 na Bold, e avanços de 5 para o latim e 10 para os kana.7 O briefing escolhe PixelMplus10, escolha minha: é a maior das duas, 10 pixels contra os 8 de Misaki, e os kanji de 8 por 8 de Misaki são aqueles cuja legibilidade para um leitor japonês está mais em dúvida (seção 11); e ela vem com uma Bold que foi medida, então o texto em negrito não precisa de síntese. Recusadas, cada uma com a medição que a recusa:
- PixelMplus12 (M+ FONT LICENSE): a grade dela fica 0,39 pixel abaixo da linha de base, e o Core Text no macOS 27.0 a borrou em todos os tamanhos.7
- k8x12 (a licença de três frases em littlelimit.net, https://littlelimit.net/font.htm#license): o avanço de kana dela é de 8,01 pixels, e as letras latinas têm 4 pixels de largura.717
- Misaki (a mesma licença, https://littlelimit.net/font.htm#license): tem as três coisas, mas os kanji dela são os menores medidos, 8 por 8, e aqueles cuja legibilidade para um leitor japonês está mais em dúvida (seção 11).617
- DotGothic16 (SIL Open Font License 1.1, https://github.com/google/fonts/blob/main/ofl/dotgothic16/OFL.txt): os pontos dela não ficam em nenhuma grade, e o Core Text no macOS 27.0 a desenhou com 37,3% a 82,3% de parciais de 16 a 64 pixels.753
- Press Start 2P (SIL Open Font License 1.1 com um Reserved Font Name, https://github.com/google/fonts/blob/main/ofl/pressstart2p/OFL.txt) e Silkscreen (SIL Open Font License 1.1, https://github.com/google/fonts/blob/main/ofl/silkscreen/OFL.txt): sem japonês.75455
- m5x7 (a página dela: “Creative Commons Zero v1.0 Universal”, https://managore.itch.io/m5x7) e m3x6 (a página dela: “free to use with attribution”, de uso livre com atribuição, sem campo de licença, https://managore.itch.io/m3x6): não medidas; latinas pelas descrições das próprias páginas; atrás de um download de pague quanto quiser.5758
- Pixel Operator (CC0 1.0 pelas notas do autor no DaFont, https://www.dafont.com/pixel-operator.font): não medida; 241 glifos no DaFont, poucos demais para o japonês.56
O kit. O kit compartilhado dos meus apps encaminha cada estilo de texto por .kit941Font(role), e o caminho de fonte personalizada dele escala uma fonte continuamente, o que deixaria esta fonte fora da grade em 11 de 12 tamanhos de Body, a condição em que o Core Text no Mac a borrou.887 O mundo já é uma superfície de arte declarada, WorldInk, com as suas próprias duas cores.25 Acrescentar um modificador PixelText local do Kiradex, declarado com um comentário como a exceção dessa superfície de arte, que recebe um papel do kit e aplica o arredondamento do item 2; propô-lo para o kit como um caso de família de fontes só depois que ele for lançado.
Sem pesos sintéticos. O texto em negrito usa PixelMplus10-Bold; nada de .weight(_:) na fonte pixelada, porque o que o Core Text faz com um peso que precisa sintetizar não foi medido (seção 11).
Verificações.
- Um teste unitário, já que o Core Text existe no iOS, carrega os dois arquivos incluídos e afirma: 1.000 unidades por em; cada glifo que as strings do mundo e as linhas predefinidas usam, nos dois idiomas, tem um avanço múltiplo de 100 unidades; os pontos de contorno dos glifos de amostra ficam a até 4 unidades da grade de 100 unidades (0,04 de pixel de fonte, a tolerância que a sonda da seção 4 usou). Uma fonte que falhe não pode ser lançada.
- Um teste de que cada string que o mundo pode mostrar (linhas predefinidas, as chaves do mundo no String Catalog, os textos das placas) tem glifos na fonte, sem fonte de fallback.
- Um script de fase de build que encontra LICENSE_E no bundle do app compilado, e um teste de interface que encontra o nome da fonte, o autor e a licença na nova seção de agradecimentos dos Ajustes.
2. Tamanhos: Dynamic Type, arredondado à grade
O quê. Um pixel de fonte é sempre um número inteiro m de pixels de dispositivo. Para um papel, ler o tamanho de Dynamic Type que o sistema usaria (UIFontMetrics(forTextStyle:).scaledValue(for:), ou @ScaledMetric(relativeTo:) sobre o tamanho base do papel), arredondar esse tamanho ao centésimo de ponto, para que um valor escalado um fio abaixo de um tamanho da tabela, 14,999… no lugar de 15, arredonde como o tamanho da tabela, e então definir m = max(m₀, ⌊size × displayScale / 10 + 0.5⌋), o inteiro mais próximo com a metade arredondando para cima, onde m₀ = ⌈11 × displayScale / 10⌉ é o menor m com pelo menos 11 pontos, e pointSize = 10 × m / displayScale, e aplicá-lo com Font.custom(_:fixedSize:) para que o sistema não o escale de novo.676968 O título e o texto da caixa passam do papel caption que usam hoje para o papel body, para que a 3× a caixa leve os doze tamanhos de Body a m = 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 e 16: 16,67 pontos no tamanho padrão e 53,33 em AX5.2611 O menor, m₀ = 4 a 3×, são 13,33 pontos, acima do mínimo de 11.1033 A 2×, que o simulador do iOS 27.0 lista para três iPhones (item 4), m₀ vale 3, 15 pontos, e os doze tamanhos de Body dão 3, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9 e 11: 15 pontos no tamanho padrão.9711 Os balões usam um passo a menos, nunca abaixo de m₀ e nunca acima de um teto que cada view define (item 4).
Posicionamento. Uma fonte pixelada só é nítida se a linha de base dela cai num pixel de dispositivo (seção 6). Arredondar cada origem calculada na sobreposição do mundo à grade de pixels de dispositivo, (x × scale).rounded() / scale, incluindo a .position de PlazaStage.bubbles, e dar às views de texto larguras em pixels de dispositivo inteiros, e à caixa largura e altura em pixels de fonte inteiros (item 3).
Passo de linha. PixelMplus10 tem 9 pixels de fonte para cima e 2 para baixo; definir o passo de linha em 12 pixels de fonte, uma fileira em branco entre as linhas, como os glifos japoneses de 15 pixels e a célula latina de 16 de Emerald cabem numa linha de 16.71 Com m = 5, isso dá 20 pontos por linha.33
Verificações.
- Um teste de função pura do arredondamento para cada DynamicTypeSize nas escalas de tela 2 e 3: o resultado é um número inteiro de pixels de dispositivo por pixel de fonte, nunca abaixo de 11 pontos e nunca menor para um tamanho maior. O teste alimenta a função diretamente com os tamanhos de Body da tabela das HIG, não com valores lidos de scaledValue(for:), cuja saída exata eu não vi: a 3×, os doze dão os doze valores acima, com o 4,5 de Small arredondando para 5, e a 2× os doze valores de 2×; cada tamanho menos um bilionésimo de ponto dá o mesmo m, graças ao arredondamento ao centésimo.11
- Capturas da praça com um balão e a caixa em Large, xxxLarge e AX3 no simulador do iPhone 18 Pro Max, e em Large no simulador do iPhone SE (3ª geração) a 2×, com a sobreposição dos balões movida para cima das sobreposições de scanlines e vinheta do palco (item 4), e um script que recorta o texto e conta os pixels parciais em relação às duas cores, tinta e papel: 0 dentro das caixas dos glifos, no balão e na caixa. Cada captura precisa do servidor da praça rodando (KIRADEX_PLAZA=1) e é feita até 4 segundos depois que o jogador diz a linha mais longa de hoje pelo menu Say, porque toda linha volta da sala102 e um balão só aparece enquanto a linha dele tem menos de 4 segundos.25103 O mesmo script, rodado no build atual, registra a linha de base que ele substitui. O 0,0% da seção 6 é o Core Text de um Mac com a suavização de fontes desligada; esta captura é a verificação que decide se um iPhone desenha igual.
3. A caixa: uma moldura desenhada pela forja, duas linhas, páginas
O quê. A caixa do WorldHUD mantém o seu lugar: embaixo, dentro da área segura, e do lado mais largo da dobra quando o Duo está meio aberto.26 O retângulo traçado de 2 pontos vira uma moldura desenhada pela forja: frame-paper.png, de 24 por 24 pixels de fonte, três por três tiles de 8 (um canto, uma borda e um centro), nas duas cores de WorldInk mais um tom, original do Kiradex. Uma view PixelFrame a desenha num Canvas, cada pixel de origem um quadrado de m por m pixels de dispositivo: os tiles de canto nos quatro cantos, e os de borda e centro repetidos para fora a partir do canto superior esquerdo, com o último tile cortado onde encontra o lado oposto, para que os pixels da moldura sejam os pixels do texto. Para que cada pixel de origem seja um quadrado inteiro, a largura da caixa é a largura da área segura, onde o WorldHUD a dispõe hoje, menos 16 pontos de cada lado, arredondada para baixo a pixels de fonte inteiros, um múltiplo de m pixels de dispositivo, e a altura também é em pixels de fonte inteiros: no 18 Pro Max em retrato com m = 5, sem margens laterais (suposto, não medido), 1.224 pixels de dispositivo, que são 244,8 pixels de fonte, arredondados para baixo a 1.220, 244 pixels de fonte.1194 O texto fica dentro da moldura, a 8 pixels de fonte da borda da caixa de cada lado, no lugar dos 8 pontos de preenchimento de hoje (escolha minha), o que deixa ali 228 pixels de fonte por linha, 45 caracteres latinos ou 22 de largura total.11 O título do lugar fica numa linha própria no topo de cada página; abaixo dele, o texto mostra duas linhas por vez, como em Emerald e Crystal.1 Texto com mais de duas linhas passa de página, e cada página espera sob a marca de avanço (item 6); onde uma página termina num espaço, o espaço é descartado. A box de WorldHUD (em WorldHUD.swift) recebe as páginas no lugar da sua única string de status.
A fila. Regras minhas, para que o que a caixa mostra nunca dependa do tamanho do status:
- As páginas do status são as próprias páginas da caixa. Quando o status muda (na praça, quando qualquer coisa nele muda: o estado da conexão, a contagem de quem está aqui, a hora do dia, a estação, as fitas ganhas), as páginas dele são substituídas e a caixa mostra o novo status a partir da primeira página, a menos que uma página da regra seguinte esteja sendo mostrada.
- O texto de uma placa (item 5) e uma linha dita por perto em tamanhos grandes (item 4) são páginas à frente do status: mostradas na hora, cada uma depois de qualquer página desse tipo que já esteja esperando, na ordem em que chegaram, e, depois da última delas, de novo o status a partir da primeira página. A caixa guarda as páginas de no máximo as três linhas mais recentes; quando chega uma quarta, é descartada a mais antiga ainda em espera que não seja do próprio jogador (a mais antiga das do jogador só quando todas as linhas em espera são dele), então um jogador que diz uma linha nunca a perde. Uma placa nova substitui as páginas de uma placa que ainda estejam esperando.
- O texto do status mantém o status inteiro como rótulo de acessibilidade, seja qual for a página mostrada, como hoje, para que o VoiceOver o leia inteiro, e os testes de interface existentes que leem
plaza-status,room-statusehall-status(uma frase de abertura, uma contagem de quem está aqui, as primeiras palavras de uma sala) leiam o que leem agora. A página mostrada, até onde já foi revelada, é o valor de um elemento oculto,plaza-pagena praça (<name>-page, nome meu). Ele contém as linhas de texto da página sem o título do lugar, e só existe com o argumento de inicialização dos testes de caminhada,-worldTrace(item 8); as verificações abaixo o leem, e cada uma passa-worldTrace.103
Molduras como escolha. Emerald deixa o jogador escolher a moldura dos menus entre 20 (a caixa de diálogo mantém uma moldura só), e Final Fantasy VI a da janela entre 8 com cores editáveis.14 Oferecer nos Ajustes quatro molduras desenhadas pela forja, todas gratuitas, as três primeiras desde o início e a quarta ao atingir um nível; nenhuma moldura jamais é vendida. Se alguma moldura algum dia vai ser recompensa para apoiadores é uma decisão que não tomei.
Verificações. Uma verificação da forja de que frame-paper.png e cada alternativa têm 24 por 24, usam só índices da paleta e têm bordas opostas idênticas onde o design manda; no simulador do iPhone 18 Pro Max, uma captura da caixa com m = 4 (xSmall) e m = 5 (o tamanho padrão), em que um script encontra a largura e a altura da caixa em pixels de dispositivo como múltiplos de m e compara a moldura dela, os 8 pixels de fonte externos de cada lado, pixel a pixel com uma moldura que ele mesmo compõe a partir do PNG no tamanho da caixa capturada pela mesma regra (cantos nos cantos, tiles de borda e centro repetidos a partir do canto superior esquerdo e cortados no lado oposto), cada pixel de origem um quadrado de m por m, deixando de fora qualquer pixel que o PNG deixe transparente, onde o mundo aparece; e um teste de interface no mesmo simulador no tamanho padrão, sem o servidor da praça, para que nenhuma linha seja dita, iniciado com -worldTrace e -worldBoxSample e um status de três linhas de 30 a 40 caracteres unidas por quebras de linha, cada uma dentro dos 45 caracteres que uma linha comporta ali, que espera plaza-page ficar igual às duas primeiras linhas, desconsiderando espaços em branco, e só então, com a primeira página inteira, toca a caixa e encontra a terceira, com uma captura da primeira página em que um script encontra a marca no canto inferior direito da caixa.11 -worldBoxSample é um argumento de teste com nome meu, lido em PlazaView como -plazaTell é (o tellOnArrival dele) e entregue ao WorldHUD como status no lugar do status da própria praça.92
4. Balões: uma linha, sem nome, os dois idiomas
O quê. Manter a forma do balão: creme, uma borda de tinta, sobre a cabeça, menos de 4 segundos.25 Compô-lo na fonte pixelada no m do balão, com os pixels de canto da moldura no lugar de um retângulo traçado, desenhados dentro dos 8 pontos de preenchimento de cada lado do balão, para que, como a borda de hoje, não acrescentem largura. Nenhum nome dentro ou acima do balão: quem fala é a cabeça sob ele, e o VoiceOver diz o nome (item 8). Uma linha predefinida nunca quebra; .fixedSize() fica.
Acima das scanlines. Hoje os balões são uma sobreposição dentro do palco, sob as scanlines dele, uma linha preta com 16% de opacidade a cada 3 pontos (a 3×, uma faixa de 3 pixels de dispositivo a cada 9), e sob a vinheta, enquanto a caixa fica sobreposta ao palco inteiro.2526 Sob essas camadas, o papel de um balão escurece em faixas que atravessam os glifos, então o texto dele não é tinta e papel em nenhum tamanho. O briefing move a sobreposição dos balões para cima das sobreposições de scanlines e vinheta, onde a caixa já está, em vez de restringir à caixa a verificação do item 2: escolha minha, a partir da regra da seção 6 de que uma fonte pixelada só é nítida como pixels de dispositivo inteiros de tinta e papel, aplicada igualmente às duas formas de fala. Em PlazaStage.swift, o .overlay dos balões, o último modificador dentro do GeometryReader do palco, passa para depois do .overlay da vinheta (o RadialGradient), com um GeometryReader próprio para o tamanho da view.25
Na tela. Hoje um balão fica centralizado sobre a cabeça de quem fala, sem nada que o mantenha na tela, então quem fala perto de uma borda empurra parte dele para fora.25 Limitar o frame do balão à área segura menos 16 pontos de cada lado, a margem que a regra de largura abaixo deixa, afastando-o do centro sobre a cabeça só o quanto o limite exigir. A área segura, não a view: o palco ignora a área segura (.ignoresSafeArea()), então a geometria dele se estende sob tudo o que a área segura mantém livre, que no Duo são as câmeras e o trilho lateral, como diz o comentário do WorldHUD sobre o próprio lugar dele dentro da área segura.94 Hoje nada passa as margens para o palco. PlazaView as lê fora do palco e as repassa, como CardViewer já faz para a carta dele (proxy.safeAreaInsets para CardStage); o limite é uma função pura, BubblePlacement.frame(head:size:view:insets:), chamada a partir de PlazaStage.bubbles(of:in:), e as mesmas margens definem o teto do balão descrito abaixo (os nomes são meus). O mundo esconde a barra de status e o indicador de início (.statusBarHidden() e .persistentSystemOverlays(.hidden) na view raiz); se isso estreita as margens não está no código, então o app as lê em vez de supô-las.94
A regra de largura. Toda linha, nos dois idiomas, cabe como balão de uma linha em todo iPhone em que o app pode rodar quando a área segura não tem margens laterais; é isso que o limite do servidor descrito abaixo garante. Uma margem pode deixar espaço pequeno demais até para m₀. Para a linha mais longa de hoje, de todas as larguras que o simulador do iOS 27.0 lista, isso acontece em cada largura de 3× até 393 pontos e nas duas larguras de 2× com a margem de 84 pontos do Duo, e nos telefones de 2× de 375 pontos com 16 pontos de cada lado. Uma linha no limite do servidor falha em mais casos: uma japonesa também nos telefones de 402 pontos com a margem do Duo (286 pontos de espaço contra 296 com m₀), e uma latina ali (302,67), no telefone de 3× de 414 pontos com a margem (298) e nos telefones de 360 pontos com 16 pontos de cada lado (296): dos 36 casos de doze larguras e três margens, 7 para a linha de hoje, 8 para uma linha japonesa no limite e 10 para uma latina. Ali essa linha não ganha balão e vai para a caixa, enquanto as linhas mais curtas mantêm o seu (abaixo).11 O app é feito só para iPhone, a partir do iOS 27.0, e o runtime do simulador do iOS 27.0 instalado com o Xcode no meu Mac lista 33 iPhones: o mais estreito a 3× tem 360 pontos de largura (o iPhone 12 mini e o 13 mini), e três são de 2×, o iPhone SE de 2ª e 3ª geração, com 375 pontos, e o iPhone 11, com 414.97 Essa é a lista do SDK dos dispositivos que ele simula; não busquei a lista da própria Apple dos iPhones compatíveis com o iOS 27. O espaço para um balão é a largura da área segura menos 32 pontos, os 16 de cada lado que o limite mantém. Nos telefones em retrato, sem margens laterais (suposto; nenhuma foi medida), isso dá 408 no iPhone 18 Pro Max, a mais estreita das cinco views que esta série mediu, mas 328 num telefone de 360 pontos e 343 nos telefones de 2× de 375 pontos. Na tela externa do Duo em retrato, são 434 sem margem lateral, e 350 se o mundo mantiver a margem final de 84 pontos, o trilho lateral, que esta série mediu na tela fechada (seção 11).11979533 A linha mais longa de hoje, “Anyone have a card from the first years?”, com 40 caracteres, mede 283 pontos de largura como balão com m = 4, 349 com 5, 416 com 6 e 549 com 8 a 3×, os glifos mais os 8 pontos de preenchimento de cada lado de hoje; a borda de 2 pontos é desenhada dentro do balão e não acrescenta largura. A 2×, a mesma linha mede 316 pontos com m = 3 e 416 com 4.1125 Então o m do balão tem um teto em cada view, o maior com que a linha predefinida mais longa, em qualquer dos dois idiomas, cabe no espaço. Para as linhas de hoje, todas em inglês, é 5 no 18 Pro Max (349 em 408) e em cada iPhone de 3× listado de 390 a 440 pontos, 4 nos telefones de 3× de 360 e 375 pontos, e 3, o menor que o 2× permite, nos telefones de 2×; na tela externa do Duo em retrato, é 6 sem margem lateral (416 em 434) e 5 com o trilho medido (349,33 em 350). Uma linha japonesa no limite do servidor descrito abaixo, 21 caracteres de largura total, 210 pixels de fonte de avanços, baixaria o do Duo para 5 e 4 e o dos telefones de 390 e 393 pontos para 4 (sete dos iPhones listados têm 390 pontos de largura e quatro, 393), e deixaria os outros como estão.1197 O app calcula o teto a partir da largura segura que PlazaView repassa e das linhas que o servidor envia, então uma área segura mais estreita ou uma linha mais longa, japonesa entre elas, pode baixá-lo. O teto é um único valor para todas as linhas; o que acontece onde não há teto é decidido linha a linha, escolha minha: onde nem m₀ comporta a linha mais longa, uma linha que cabe com m₀ mantém o balão com m₀, e uma que não cabe fica sem balão e vai para a caixa, como páginas próprias, como em tamanhos grandes (abaixo). Manter com m₀ o balão dessa linha quebraria o limite: numa view de 360 pontos com uma margem final de 84 pontos, o espaço é de 244 pontos, e a linha mais longa de hoje com m₀ mede 282,67.11 O servidor, por sua vez, limita cada linha ao que cabe no telefone mais estreito de cada escala no menor tamanho, sem margens laterais: no máximo 218 pixels de fonte de avanços, 43 caracteres latinos ou 21 de largura total, o menor entre (360 − 48) × 3 ÷ 4 = 234 a 3× e (375 − 48) × 2 ÷ 3 = 218 a 2×. Uma linha japonesa nesse limite mede 296 pontos como balão com m = 4 a 3×, e 331 com m = 3 a 2×; uma latina, 302,67 e 338,50; as duas cabem nos 350 do Duo com o trilho.1133 As margens laterais dos telefones em retrato não foram medidas; as capturas abaixo, que afirmam o balão dentro da área segura, são onde alguma apareceria.
A linha predefinida mais longa como balão, por tamanho, a 3×, contra o espaço de cada view; desenhado por figures/make_figures.py a partir de measure_kiradex_text.out.
Texto maior. O m do balão acompanha o da caixa, um passo a menos, nunca abaixo de m₀ e nunca acima do teto da view: no 18 Pro Max ele chega a 5 quando a caixa está em xLarge (m = 6), e de xxxLarge para cima, onde o m da caixa é 7 ou mais, fica em 5. Onde quer que o teto segure o balão dois ou mais passos abaixo da caixa (no 18 Pro Max de xxxLarge para cima, num telefone de 360 pontos de xLarge para cima), quem lê recebe também na caixa cada linha dita a até seis células do jogador, como páginas próprias no tamanho da caixa: o apelido de quem fala, dois-pontos e a linha, como Emerald diz quem fala dentro da linha (o item 3 a pagina; o item 8 a anuncia). PlazaView, que guarda as linhas da sala e hoje compõe o status da caixa, compõe essas páginas e as põe à frente do status, pela fila do item 3: mostradas na hora, depois das páginas de qualquer linha que já estejam esperando, com no máximo três linhas guardadas. Ele põe as páginas de uma linha na fila da caixa na mesma atualização que acrescenta o balão dela, então o balão de uma linha dessas na tela significa que as páginas dela estão na fila. Nem sempre é uma página só. Um apelido tem de 14 a 20 caracteres, então “apelido: linha” vai de 19 a 62, e com a moldura do item 3 uma linha da caixa no 18 Pro Max comporta 31 caracteres latinos em xxxLarge, 17 em AX3 e 12 em AX5: considerando todo apelido que names.py pode gerar e toda linha predefinida, uma ou duas páginas de xxxLarge a AX3 e até três em AX4 e AX5, e até quatro nos telefones de 360 pontos e de 2× nos maiores tamanhos e na tela externa do Duo, com o trilho medido, em AX4 e AX5.1189 A caixa em si nunca é limitada; ela quebra linhas e passa de página. A página de dynamicTypeSize(_:) sugere uma visualização de conteúdo grande para um tamanho limitado; o briefing não depende dela, porque a própria página desse visualizador adverte contra usá-lo “as a replacement for proper Dynamic Type support” (como substituto de um suporte adequado ao Dynamic Type), e nenhuma das duas páginas diz se ele abre a partir de uma sobreposição com o hit testing desligado, como a dos balões.707125 Minhas notas de pesquisa tinham proposto parar o balão em m = 8, mas a linha mais longa com 8 mede 549 pontos de largura (as notas dão 553, contando a borda que fica dentro do balão), mais larga que o iPhone 18 Pro Max (440 pontos) e que a tela externa do Duo em retrato (466), e as notas mediram o limite contra a tela externa do Duo quando o 18 Pro Max, com 440, é o mais estreito; o briefing aqui segue as larguras.2211
Verificações.
- O teste de fonte do app (item 1) também afirma que uma tabela dos avanços de PixelMplus10 guardada no repositório, com uma entrada para cada caractere que as linhas predefinidas usam em qualquer dos dois idiomas, bate com a fonte incluída. A suíte de testes do servidor, que não tem parser de fontes (as dependências dele são FastAPI, uvicorn, websockets, httpx e pytest), lê essa tabela, recusa uma linha que contenha um caractere ausente da tabela e recusa uma linha, em qualquer dos dois idiomas, cujos avanços somem mais de 218 pixels de fonte, o limite acima.10411
- Um teste unitário do teto, uma função pura da largura segura, da escala de tela e dos avanços das linhas: a 3× ele dá, com a linha mais longa de hoje, 5 para cada largura listada de 390 a 440 pontos, 4 para 360 e 375, 6 para 466 e 5 para 382 (a tela externa do Duo, sem e com o trilho medido), e a 2× 3 para 375 e 414; com uma linha japonesa de 210 pixels de fonte entre as linhas, 5 para 466, 4 para 382 e 4 para 390 e 393; e nenhum para um espaço de 244 pontos (360 pontos menos a margem de 84 e 32) a 3× com a linha mais longa de hoje; e um teste do compositor em PlazaView que, nesse espaço, não desenha balão para a linha mais longa de hoje e põe as páginas dela na caixa, e desenha “Hi!” como balão com m₀ (36 pontos de largura, em 244).11
- Um teste unitário de BubblePlacement põe a cabeça de quem fala em cada borda, em cada canto e no centro de views de 360, 375, 440 e 466 pontos de largura, cada uma sem margens de área segura, com 16 pontos nos lados inicial e final, e com a margem final de 84 pontos do Duo, e balões de várias larguras até a largura segura daquele caso menos 32 (de 244 pontos, 360 menos 84 e 32, até 434), e encontra cada balão dentro da área segura menos 16 pontos de cada lado e, sempre que o x da cabeça estiver dentro dessa faixa, abrangendo o x da cabeça.11
- Nos simuladores do iPhone 18 Pro Max, do iPhone 13 mini e do iPhone SE (3ª geração), e na tela externa do iPhone Duo no simulador dele, quatro capturas em cada um (inglês e japonês, cada um no tamanho padrão e em AX3), com o servidor da praça rodando (KIRADEX_PLAZA=1) e o app iniciado com -worldTrace, cada uma com a linha mais longa daquele idioma dita pelo jogador; em cada uma, um script encontra a borda de tinta do balão e afirma que ele tem a altura de uma linha de texto, não é mais largo que a largura da área segura menos 32 pontos e fica dentro da área segura menos 16 pontos de cada lado. O script pega as margens do teste de interface que faz a captura, que as lê de um elemento oculto que -worldTrace ativa (item 8), onde PlazaView escreve as margens que leu (proposta minha); o script também encontra o m do balão a partir dos pixels dos glifos e afirma que ele é o menor entre o m da caixa menos um (nunca abaixo de m₀) e o teto para aquela largura segura, calculado a partir das linhas do build nos dois idiomas, de modo que ele só é igual ao teto onde o teto o limita. No tamanho padrão, isso dá 4 nos telefones de 3× e no Duo, seja qual for o teto, e 3 no telefone de 2×; em AX3 é o teto, e no Duo as margens decidem: com as linhas em inglês de hoje, 6 sem margem lateral e 5 com o trilho, e com uma linha japonesa no limite do servidor, 5 e 4.119597
- Um teste de interface no simulador do iPhone 18 Pro Max em AX3, onde o m da caixa é 12 e uma linha comporta 17 caracteres latinos, precisa do servidor da praça (KIRADEX_PLAZA=1) e de nenhum outro jogador, e é iniciado com -worldTrace e a velocidade do texto em Instantânea (item 6) pela chave world.textSpeed que o item 5 nomeia, para que cada página apareça inteira no momento em que abre e um toque na caixa passe exatamente uma página. Ele lê o apelido do jogador na aba Me, escolhe “Hi!” no menu Say e espera, dentro dos 5 segundos que o teste de companhia existente permite, pelo balão Said: Hi!, de modo que a linha voltou da sala e, pela regra do item 4, as páginas dela estão na fila da caixa, à frente do status, seja qual for o tamanho do status. Uma linha dos quatro colecionadores da cidade, que dizem cada um no máximo uma a cada 20 segundos, pode estar à frente dela; a fila guarda no máximo três linhas e nunca descarta a única linha de um jogador (item 3), então no máximo duas linhas ficam à frente, de no máximo duas páginas cada ali, e as linhas ditas depois esperam atrás. O teste lê plaza-page antes de cada toque e só toca a caixa enquanto a página não for o apelido, dois-pontos, um espaço e “Hi!”, com um segundo entre toques e no máximo quatro vezes, e passa se uma leitura encontrar essa página, sendo a última leitura um segundo depois do quarto toque. Ele usa essa linha porque “apelido: Hi!” é uma página só ali para todo apelido que names.py pode gerar, enquanto a linha mais longa pode ocupar duas. Ele registra cada página que leu.11103102
5. Placas e nomes
Placas. A arte da placa continua sem letras, como kit.py a desenha, para que um único atlas sirva aos dois idiomas.29 Um toque num tile de placa abre a caixa do WorldHUD com o texto da placa, como páginas à frente do status (item 3), em vez de levar o jogador até lá como acontece hoje. Hoje o palco lê os toques por um DragGesture: touched(_:in:) leva o jogador na hora até o chão sob o dedo, exceto sobre um colecionador ou o quiosque, que esperam o dedo subir, e lifted(_:in:) lê essa subida como um toque. Um tile de placa se junta ao colecionador e ao quiosque nos dois: espera o dedo subir, e lifted(_:in:) chama para ele uma closure onSign como chama onKiosk para o quiosque (o nome é meu).92 Os textos das placas ficam com o mapa: town.json ganha signs: { "7,10": { "en": ..., "ja": ... } }, com a célula como chave, e as verificações da forja recusam uma célula de placa sem os dois idiomas.
Nomes. Nada de rótulos de nome permanentes sobre as cabeças. A caixa do cânone diz quem fala dentro da linha, Emerald em 6,5% dos parágrafos.1 Os apelidos do Kiradex chegam a 20 caracteres; no menor tamanho do balão, m = 4 a 3× e 3 a 2×, um apelido de 20 caracteres mede 133 pontos de largura a 3× e 150 a 2×; a 3×, isso fica sobre uma figura de 16 texels de largura de frente para a câmera e 13 de perfil (30 de altura, numa célula de 32 por 40), que são 42,7 pontos, ou 34,7, no 18 Pro Max.119610033 O apelido aparece no painel que um toque abre, como agora, e o VoiceOver o lê (item 8).
Verificações. Um teste de interface não tem como saber onde na tela a célula da placa é desenhada, já que a câmera segue o jogador, então o teste inicia a praça com -worldTrace e -plazaTapCell 7,10, um argumento de teste com nome meu, lido no corpo de PlazaStage como -plazaFacingDemo é (num .task), que envia a célula pelo mesmo tratamento, extraído de lifted(_:in:) para um único método. No simulador do iPhone 18 Pro Max no tamanho padrão, sem o servidor da praça, para que nenhuma linha seja dita, o teste também passa o -worldBoxSample do item 3 com um status de uma página, “Sample.”, e a velocidade do texto em Instantânea (item 6) pela chave dela nos Ajustes, nos argumentos de inicialização (a chave, world.textSpeed, é minha, ao lado da world.stick do direcional). Ele espera até 5 segundos que plaza-page mude de “Sample.” para a primeira página da placa, então a lê, toca a caixa e lê de novo, no máximo um toque para cada palavra do texto da placa (cada caractere, em japonês), mais um, até plaza-page mostrar “Sample.”, e confere que as páginas anteriores, unidas em ordem, são iguais ao texto em inglês da placa, desconsiderando espaços em branco, para que um texto de placa longo o bastante para paginar passe tão bem quanto um curto; reiniciado com o japonês como idioma do app, o texto em japonês.92 A verificação da forja recusa um town.json com uma célula S sem algum dos dois idiomas.
6. Velocidade do texto e a marca de avanço
O quê. Uma revelação de máquina de escrever cuja contagem é tempo, não quadros: shown = floor(elapsed × cps), uma função pura do tempo decorrido, da velocidade e de dois ajustes, Reduzir Movimento e VoiceOver em execução, para que 60 e 120 hertz revelem na mesma velocidade. Os Ajustes oferecem Lenta, 12 caracteres por segundo, a lenta de Crystal; Normal, 30, o padrão, escolha minha entre os ajustes médios do cânone (os 15 de Emerald, os 20 de Crystal) e o ajuste rápido dele, de 60; e Instantânea. Um toque durante a revelação completa a página; um toque numa página completa a vira.1 Para isso, a caixa recebe toques enquanto está revelando ou tem uma página depois da que está à mostra; na última página, mostrada inteira, ela deixa os toques passarem para o chão, como faz o tempo todo hoje.26 A mesma velocidade nos dois idiomas, como Final Fantasy VI imprime os dois a uma letra por quadro.4 Com Reduzir Movimento ligado, ou com o VoiceOver em execução, a função devolve a página inteira de uma vez; isso é escolha minha, a partir da frase da documentação de que, com Reduzir Movimento, a “UI should avoid large animations”.83
A marca. Uma marca própria do Kiradex desenhada pela forja, uma cunha para baixo de 5 por 3 pixels de fonte, no canto inferior direito da caixa, descendo 0, 1, 2 e 1 pixels de fonte (a direção é escolha minha; a de Emerald, na minha leitura, sobe) a cada 150 milissegundos, num ciclo de 600 milissegundos: o passo de 9 quadros de Emerald a 60 hertz.133
Verificações. Um teste unitário da função de revelação a 30 caracteres por segundo: 0 caracteres em t = 0 e 30 em 1,000 segundo, com o tempo decorrido calculado a partir de uma contagem de ticks, os ticks divididos pela frequência, a 60 e a 120 ticks simulados por segundo, não somado a partir de deltas por tick (120 deltas de 1/120 somados em precisão dupla dão 0,9999999999999989, que, vezes 30, dá 29,999999999999968 e arredonda para baixo para 29); a mesma função com Reduzir Movimento ligado, e separadamente com o VoiceOver em execução, devolve a página inteira em t = 0; um teste de interface no simulador do iPhone 18 Pro Max no tamanho padrão e em Lenta, sem o servidor da praça, iniciado com -worldTrace e o status -worldBoxSample do item 3, cuja primeira página são duas linhas de 30 a 40 caracteres, pelo menos 60 caracteres e portanto pelo menos 5 segundos de revelação a 12 por segundo, que confere que um toque na caixa um segundo depois do início da revelação faz plaza-page virar a página inteira em meio segundo, quando a revelação sozinha mostraria 18 caracteres (12 no momento do toque); um teste unitário do deslocamento da marca em função do tempo decorrido, 0, 1, 2, 1 e 0 pixels de fonte a 0, 150, 300, 450 e 600 milissegundos; e uma gravação de tela de dois segundos da marca no simulador em que os deslocamentos dela, sem repetições, seguem 0, 1, 2, 1 em ordem cíclica.33
7. Japonês
O quê. Japonês primeiro para o texto do mundo, o resto do app depois. server/app/lines.py mantém a ordem dos índices, como diz o comentário dele, “add at the end only” (acrescentar só no final), e cada linha vira uma string em inglês e uma em japonês; o servidor envia as duas, e cada cliente mostra o idioma que o seu usuário lê, então uma linha dita numa praça mista é lida em dois idiomas a partir de um único índice.28 Esse é, na minha leitura, o melhor argumento a favor das linhas predefinidas neste app. As strings do mundo, os títulos e status do WorldHUD e os títulos das missões, vão para um String Catalog com ja; os textos das placas ficam com o mapa (item 5). Cada string em japonês é escrita ou revisada por um falante de japonês; nenhuma é lançada com tradução automática sem revisão.
Quebra de linha. As linhas predefinidas nunca quebram (item 4). O texto da caixa é escrito para caber em duas linhas por página no tamanho padrão, como o script de Emerald foi quebrado à mão.1 Onde precisar quebrar, manter a regra estrita: não marcar o texto da caixa com um idioma de composição ja, a menos que uma captura mostre a regra estrita valendo com ele, porque o Core Text no macOS, marcado com ja, deixou kana pequenos começarem linhas em 6 de 7 larguras na maioria das fontes (4 de 7 em k8x12 e k8x12S).777 Se um padrão do sistema aplicar a regra frouxa mesmo assim, a correção escrita é um U+2060 WORD JOINER antes do kana pequeno nas poucas strings que precisarem.
Verificações. Um teste de que cada linha e cada chave do mundo no String Catalog tem os dois idiomas; o teste do servidor do item 4, que recusa uma linha predefinida em japonês acima do limite de 218 pixels de fonte, 21 caracteres de largura total; e, no simulador do iPhone 18 Pro Max no tamanho padrão com o idioma do dispositivo em japonês, sem o servidor da praça e na velocidade Instantânea, iniciado com -worldTrace, uma captura de cada página de um parágrafo de teste na caixa, passado como o status -worldBoxSample do item 3 e escrito de modo que, com os 22 caracteres de largura total por linha que cabem ali, uma quebra cairia antes de um kana pequeno, em que nenhuma linha depois da primeira do parágrafo começa com um caractere da lista de início de linha do JLREQ ou com uma forma de compatibilidade de um deles.11 Essa lista é a completa, as 83 entradas das dez classes que a regra do 3.1.7 nomeia (três caracteres estão em duas classes), guardada no target de testes; a sonda da seção 7 usou uma lista parcial (seção 7). O software de composição japonês costuma usar, no lugar deles, as formas de compatibilidade que o Unicode codifica para a conversão de ida e volta com padrões mais antigos, como o JLREQ observa sobre o parêntese de abertura: “the compatibility counterpart U+FF08 FULLWIDTH LEFT PARENTHESIS is used in the Japanese context in most Japanese text layout software” (a contraparte de compatibilidade U+FF08 FULLWIDTH LEFT PARENTHESIS é usada no contexto japonês na maioria dos softwares de composição de texto japonês).21 Então a lista acrescenta cada caractere do bloco Halfwidth and Fullwidth Forms do Unicode cuja normalização de compatibilidade (NFKC) é uma das 83, 24 deles, entre eles ! (U+FF01), ? (U+FF1F), ) (U+FF09), :, ;, , e ., e os de meia largura 。, 、, ー e os katakana pequenos a partir de ァ (U+FF67); e ~ (U+FF5E), o til de largura total, ao lado do traço ondulado 〜 da mesma classe, acréscimo meu, já que a normalização dele é o til ASCII, que a lista não contém: 25 ao todo.98 PixelMplus10 mapeia 54 das 83 entradas e 24 das 25 formas. Ela não desenha as outras 29 entradas, entre elas 〜 (U+301C), ゠, a meia-risca, ‼ e os katakana pequenos a partir de U+31F0, nem o ⦆ de largura total (U+FF60), e o teste de cobertura do item 1 mantém cada um deles fora das strings do mundo, então uma captura só pode mostrar os 75 caracteres que ela desenha (as 54 entradas, três das quais, ), 〕 e ], estão listadas em duas classes, o que dá 51 caracteres, e as 24 formas).98 Um script compara a primeira célula de glifo de cada linha, pixel a pixel, com os glifos da fonte para esses 75 no m da captura, desenhados a partir da fonte incluída com CTFontGetGlyphsForCharacters, e não encontra nenhuma correspondência. Os pixels só conseguem distinguir um caractere da lista dos outros do parágrafo onde a fonte os desenha de forma diferente. Desenhados a 10 pixels, dois dos 75 têm um gêmeo idêntico pixel a pixel na fonte: ‐ (U+2010), idêntico ao hífen inseparável U+2011, e os dois-pontos, idênticos à letra modificadora dois-pontos U+A789; nenhuma das 24 formas tem um. Então o parágrafo de teste não usa nenhum dos dois gêmeos; ー (U+30FC) e o kanji 一 (U+4E00) diferem, 200 pixels de dispositivo com tinta contra 250 com m = 5, então 一 pode aparecer em qualquer ponto dele.9821 O resultado da seção 7 é o Core Text de um Mac; esta captura decide o que o SwiftUI faz no iOS.
8. VoiceOver sobre o palco
Hoje. O VoiceOver ouve “The plaza” e depois o rastro dos testes de caminhada, como “12,4 down” (12,4 para baixo).25 Esse valor é para testes, não para pessoas.
O quê.
1. O rótulo do palco continua “The plaza”, ou o nome da sala, e o valor dele vira uma frase que uma pessoa pode usar: onde o colecionador está, por ponto de referência, quem está perto e o que a pessoa ergue (por exemplo, Perto do lago. Três colecionadores por perto. Erguendo uma carta.), pela regra de accessibilityValue de que um valor diz o que o rótulo não diz.79 Tudo o que hoje vai por stepTrace até o valor do palco é transferido, seja quem for que o escreva: quando li PlazaStage.swift, o rastro de passos de rig.onStep, a mensagem de falha de build da closure make da RealityView e o relatório da sonda do ar do .task que lê -airProbe, e minutos depois uma quarta escrita, de uma demo de caminhada acrescentada naquele dia. Isso vai para um elemento oculto que só existe quando o app é iniciado com -worldTrace (nome meu), que os testes de caminhada passam, ao lado da página da caixa (item 3) e das margens que PlazaView leu (item 4), e os testes que leem o valor do palco, entre eles testTheCollectorFacesTheWayItWalks e testTheChimneysSmoke, que espera value CONTAINS 'smoke 18 ', passam a ler esse elemento.25103
2. accessibilityDirectTouch(_:options:) no palco, para que um usuário de VoiceOver possa tocar para caminhar depois de ativá-lo (iOS 17.0).80
3. A entidade de cada colecionador ganha um AccessibilityComponent com o apelido como rótulo e o que ele segura como valor, para que o VoiceOver possa descrevê-lo (iOS 17.0).84
4. Uma linha dita a até seis células do jogador é publicada como AccessibilityNotification.Announcement (por exemplo, Glacier Shuffle 4471: Nice card!) com prioridade baixa, no máximo uma a cada dois segundos, a própria frequência do servidor para uma linha.8291
5. Uma página da caixa que abre é anunciada inteira, a menos que repita uma linha que o ponto 4 já anunciou, como fazem as páginas de texto grande do item 4.
Verificações. Um teste de interface de que o valor do palco não contém coordenadas sem -worldTrace; num simulador do iOS 27, um teste com XCUIVoiceOverService que ativa o VoiceOver, leva-o ao palco e afirma que o que currentSpeech() retorna contém o rótulo do palco e nenhum par de números no formato x,y do rastro de passos;87 uma verificação manual num dispositivo com o VoiceOver ligado, datada e com o número do build, de que uma entidade de colecionador é alcançável e falada, porque não está verificado se uma RealityView com uma câmera virtual expõe elementos de AccessibilityComponent (seção 11); e um teste unitário do limitador de anúncios, em que duas linhas em menos de 2 segundos geram um único anúncio.
9. Nunca, para tipografia e janelas
- Nenhum glifo, moldura, seta ou gráfico de janela da ROM ou da descompilação de qualquer jogo no app ou na forja. As folhas do pret e do ff6 são medidas aqui e nunca copiadas. A moldura, a marca e a placa do Kiradex são desenhadas pela forja; a tipografia dele é uma fonte licenciada.
- Nenhum personagem ou ligadura da franquia. O charmap de Emerald traz glifos especiais para palavras da franquia; nada parecido é desenhado, e nenhuma string usa os nomes que a franquia dá às coisas.31
- Nenhuma fonte sem o arquivo de licença em mãos: uma fonte só é lançada com o texto da licença citado na seção 4 ou 5 e salvo ao lado da evidência.
- Nenhuma fonte OFL modificada sob o seu Reserved Font Name. Press Start 2P declara um; se alguma fonte OFL for modificada algum dia, ela é renomeada.54
- Nenhum texto embutido na arte do mundo: placas, fachadas de lojas e quadros continuam sem letras, para que nenhum atlas seja jamais por idioma.
Verificação. Um script calcula dois resumos SHA-256 de cada imagem que a forja escreve e que o catálogo de assets contém, um dos bytes do arquivo e outro dos pixels decodificados, e os compara com os mesmos dois resumos de cada PNG medido aqui (graphics/fonts e graphics/text_window do pret, as molduras de Crystal e os gfx/font/font.png de Crystal e Red). A lista desses resumos, não as imagens, vai para o repositório como scripts/forge/canon_digests.txt (um nome escolhido por mim, ao lado do kit.py da forja), para que a verificação rode sem a pasta de evidências, e qualquer correspondência faz o build falhar. O teste dela própria é um controle negativo que não precisa de nenhum arquivo de jogo: acrescenta os resumos de uma saída da forja a uma cópia da lista e afirma que então o script falha. O mesmo script lista para revisão, sem falhar, qualquer célula de 8 por 8 de uma saída da forja cuja máscara de um bit, ou a inversa dela, coincida com uma célula daquelas folhas. Resumos de imagem inteira não conseguem reconstruir essas células, então a lista também guarda o resumo SHA-256 de cada máscara de célula distinta das folhas, sob uma única regra (minha), aplicada do mesmo jeito às saídas da forja: um pixel é ligado onde a cor dele difere do pixel superior esquerdo da imagem; as células vão de 8 em 8 a partir do canto superior esquerdo, descartando uma célula parcial numa borda; uma célula vazia ou sólida fica de fora dos dois lados, já que as folhas têm muitas (só japanese_normal.png tem 430 células vazias de 1.024); e uma máscara são os 64 bits dela fileira a fileira, 8 bytes. Nas 50 folhas, isso dá 7.746 células mantidas e 1.484 resumos distintos.99 Uma cópia recolorida, reescalada ou redesenhada passa pelos resumos; a regra 1 se apoia na forja desenhar cada asset a partir de código e nessa revisão, não só na verificação. E a verificação de strings que os briefings anteriores desta série propõem é ampliada com as abreviações da franquia presentes no charmap de Emerald.105
Fora deste briefing
- Uma fonte pixelada para o app inteiro. A Dex, o scanner e os Ajustes mantêm a fonte do sistema; a tipografia pixelada é para o mundo.
- Chinês e coreano. Os quatro jogos pixelados modernos verificados trazem chinês, e dois deles, Stardew Valley e Sea of Stars, trazem coreano, mas nada aqui mediu uma fonte pixelada para nenhum dos dois.64
- Misaki ou qualquer fonte de 8 pontos para leitura de texto. Os kanji dela são os menores medidos; a legibilidade nesse tamanho para um leitor japonês não foi medida.6
- Rótulos de nome sobre as cabeças e um campo de nome na caixa.1
- Japonês com tradução automática que ninguém revisou.
- Negrito ou itálico sintéticos na fonte pixelada.
- Molduras à venda. Toda moldura é gratuita; uma moldura para apoiadores aguarda uma decisão.
- Texto desenhado na cena de RealityKit. O motivo do post do mundo e a aritmética da seção 10 o mantêm no SwiftUI.2311
Principais conclusões
Se você desenha a arte
- Construa molduras com tiles: um canto, uma borda e um centro. Os menus de Emerald escolhem entre 20 molduras de 24 por 24 pixels, três por três tiles; a caixa de diálogo dele é desenhada a partir de uma tira de 14 tiles com a borda de baixo espelhada; as molduras de Crystal têm 6 tiles de um bit cada.1
- Dê à marca de avanço um relógio lento. A seta de Emerald se desloca 0, 1, 2 e 1 pixels a partir do repouso, um passo a cada 9 quadros, 0,6 segundo por ciclo.1
- Mantenha as palavras fora da arte do mundo. Uma placa sem letras serve a todos os idiomas a partir de um único atlas; as palavras vão na caixa.29
- Desenhe o japonês entre 8 e 12 pixels e conte com um kana tendo o dobro de uma letra latina: 8 contra 4, 10 contra 5 e 12 contra 6 em todas as fontes pixeladas com japonês medidas.7
Se você constrói o motor
- Escolha uma fonte pixelada pelo arquivo, não pelo nome: confira a grade, o deslocamento em relação à linha de base e os avanços. A grade de PixelMplus12, 0,39 pixel fora da linha de base, borrou em todos os tamanhos no Core Text do macOS; o avanço de kana de 8,01 pixels de k8x12 borrou assim que os glifos foram posicionados com exatidão.7
- Desenhe só com pixels de dispositivo inteiros por pixel de fonte: 0,0% de pixels parciais nos múltiplos inteiros de uma grade sobre a linha de base com avanços de pixels inteiros (Misaki, PixelMplus10, Press Start 2P), de 19,4% a 82,2% entre eles, no Core Text de um Mac com a suavização de fontes desligada. Com a suavização padrão do contexto, PixelMplus10 no próprio tamanho saiu com 178 de 272 pixels com tinta parciais, então um múltiplo inteiro é necessário, não suficiente; capture um iPhone antes de confiar em qualquer um dos dois resultados lá.79
- Respeite o Dynamic Type arredondando à grade. Um em de 10 pixels é inteiro em 1 dos 12 tamanhos de Body a 3×; arredonde o tamanho escalado a pixels de dispositivo inteiros por pixel de fonte e aplique-o como tamanho fixo.768
- Conte a velocidade do texto em tempo, não em quadros, para que 60 e 120 hertz revelem igual; 60 caracteres por segundo é a cadência mais rápida em todos os jogos medidos, e de 15 a 20 o ajuste médio onde um jogo tem um.14
- Dê ao VoiceOver uma frase, não um rastro.
accessibilityValueé para o que o rótulo não diz,AccessibilityComponentdescreve uma entidade, eXCUIVoiceOverServiceconsegue testar o que o VoiceOver fala a partir do iOS 27.0.798487
Se você projeta o loop
- Duas linhas por vez, embaixo. A caixa de Emerald ocupa 30% da tela e a de Crystal 33%, cada uma com duas linhas.1
- Escreva na medida da largura. Os roteiristas de Emerald mantiveram 95% de 24.100 linhas em 38 caracteres numa janela que comporta 41, e nenhuma passou da janela.1
- Diga quem fala dentro da linha, não sobre a cabeça; a caixa de Emerald não tem campo de nome.1
- Planeje o japonês desde o início: quatro de quatro jogos pixelados modernos verificados o trazem. Uma fonte que cobre o JIS X 0208 desenha cerca de sete mil caracteres; um jogo com um script fixo pode sair com menos, como fez a ROM japonesa de Final Fantasy VI com 1.248 letras, e aí cada string nova precisa ser conferida contra a fonte.64584
Perguntas frequentes
Qual deve ser o tamanho de uma caixa de texto num jogo de pixel art?
Nos Pokémon de portátil, o terço inferior da tela e duas linhas. A moldura de diálogo de Emerald tem 30 por 6 tiles, 240 por 48 pixels, 30% da altura da tela do Game Boy Advance, em volta de uma janela de texto de 27 por 4 tiles que comporta duas linhas de uma fonte de 16 pixels. A caixa de Crystal tem 20 por 6 tiles, 33% da tela do Game Boy, duas linhas de 18 caracteres, e a de Red é a mesma de 20 por 6. Final Fantasy VI abre a janela em cima ou embaixo da tela.12345
Qual deve ser a velocidade de um efeito de texto de máquina de escrever?
O cânone conta quadros. Emerald e FireRed imprimem um caractere a cada 8, 4 ou 1 quadros, 7,5, 15 ou 60 por segundo a 60 hertz, e um por quadro enquanto A ou B fica pressionado depois de um aperto durante a impressão; Crystal e Red imprimem a 12, 20 ou 60 por segundo; Final Fantasy VI imprime o diálogo de campo a uma letra por quadro, 60 por segundo, sem ajuste para isso (a velocidade de mensagens em seis níveis define quanto tempo espera uma mensagem de batalha). Num telefone moderno, conte os caracteres em tempo e não em quadros, para que a velocidade não mude a 120 hertz. Todos os jogos medidos chegam no máximo a 60 por segundo, e os ajustes médios de Emerald e Crystal são 15 e 20; o briefing abaixo propõe 30 como padrão, o que é escolha minha entre esses ajustes médios e o rápido de 60, não um ajuste medido.1144
Quais fontes pixeladas têm suporte a japonês?
Das medidas, Misaki (8 por 8 pontos), k8x12 (8 por 12) e DotGothic16 desenham todos os 6.879 caracteres do JIS X 0208, e PixelMplus10 e PixelMplus12, todos menos três pontos de código. Misaki, k8x12 e PixelMplus estão sob uma licença gratuita de três frases que permite uso, cópia e distribuição “either commercially or noncommercially” (comercialmente ou não); DotGothic16 está sob a SIL Open Font License 1.1. Medidas com o Core Text no macOS 27.0, Misaki e PixelMplus10 são as duas com uma grade limpa sobre a linha de base e avanços de pixels inteiros, a 8 e 10 pixels; a grade de PixelMplus12 fica 0,39 pixel abaixo da linha de base, o avanço de kana de k8x12 é de 8,01 pixels, e os pontos de DotGothic16 não ficam em grade de pixels nenhuma.67171853
Por que minha fonte pixelada fica borrada no iPhone?
Normalmente porque um pixel de fonte não é um número inteiro de pixels de dispositivo. No Core Text do macOS 27.0 com a suavização de fontes desligada, as fontes pixeladas TrueType com uma grade limpa sobre a linha de base e avanços de pixels inteiros (Misaki, PixelMplus10 e Press Start 2P) foram desenhadas sem pixels parciais nos múltiplos inteiros da grade e com 19,4% a 82,2% dos pixels com tinta borrados entre eles. Com a suavização padrão do contexto de desenho, até um múltiplo inteiro borrou: PixelMplus10 nos seus próprios 10 pixels saiu com 178 de 272 pixels com tinta parciais.9 Duas falhas no arquivo da fonte causam borrão em todos os tamanhos: uma grade fora da linha de base, como a de PixelMplus12, em 0,39 pixel, e um avanço que não é inteiro, como o avanço de kana de 8,01 pixels de k8x12 assim que os glifos são posicionados com exatidão. A medição foi feita num Mac; uma captura no iPhone é a verificação que resolve a questão lá.7
Uma fonte pixelada pode ter suporte a Dynamic Type?
Só por arredondamento. Dos doze tamanhos de Body do iOS, de 14 a 53 pontos, um em de 10 pixels cai em pixels de dispositivo inteiros a 3× só em 40 pontos, então uma fonte pixelada que escala continuamente com Font.custom(_:size:relativeTo:) fica fora da grade nos outros onze; no Core Text do Mac, fora da grade significou borrado, e uma captura no iPhone decide o que o SwiftUI desenha lá. Leia o tamanho escalado com UIFontMetrics ou ScaledMetric, arredonde-o a um número inteiro de pixels de dispositivo por pixel de fonte e aplique-o com Font.custom(_:fixedSize:). A Apple pede que uma fonte personalizada implemente os comportamentos que as fontes do sistema têm, entre eles o Dynamic Type, e que um app de preferência deixe as pessoas ampliarem o texto em pelo menos 200 por cento.710206768
O Core Text segue as regras japonesas de quebra de linha?
Na minha medição no macOS 27.0, sim, de forma estrita, quando o texto não estava marcado com um idioma: um parágrafo de 131 caracteres quebrou em sete larguras sem que nenhuma linha começasse com um caractere da lista de início de linha da sonda (uma lista parcial, na maior parte caracteres do JLREQ, com sete nas formas de largura total), em Hiragino Sans e em oito fontes pixeladas. Marcado com ja, ele afrouxou para a regra mais frouxa que os requisitos de composição japonesa do W3C descrevem, e um kana pequeno começou uma linha em 6 de 7 larguras na maioria das fontes. O que o SwiftUI faz num iPhone com o idioma do dispositivo em japonês não foi medido.721
De quantos caracteres uma fonte pixelada japonesa precisa?
O JIS X 0208, a meta de cobertura habitual, tem 6.879: 524 que não são kanji, 2.965 kanji de nível 1 e 3.390 de nível 2. O produtor de Final Fantasy Pixel Remaster contou à Siliconera que a fonte pixelada japonesa dele levou “around 7,500 characters” (cerca de 7.500 caracteres), contra “a few hundred” (algumas centenas) para os idiomas ocidentais. Um jogo com um script fixo pode sair com menos: a ROM japonesa de Final Fantasy VI tem 1.248 letras na fonte de diálogo.584
Cada número deste post vem de um script que roda de novo sobre fontes salvas, de uma página salva em 5 de outubro de 2026 ou de uma leitura datada do código do Kiradex, e as notas dizem qual; os números do Core Text são do macOS 27.0, não do iOS. O briefing da seção 12 é a minha proposta e está identificado como tal; nada dele foi construído.
Relacionados neste site: Mundos em pixel art no iPhone é o primeiro post desta série, com a receita de RealityKit e a direção para o texto que este post mede; Pessoas em pixel art: personagens e um criador no iPhone é o colecionador sobre o qual os balões ficam; Estruturas em pixel art: casas, salões e interiores no iPhone tem a forja cuja placa continua sem letras; Movimento em pixel art: caminhadas, câmeras e portas no iPhone tem os relógios de 60 e 120 hertz contra os quais a revelação é cronometrada; Uma cidade pixelada viva quando ninguém está online tem as linhas predefinidas e a frequência delas; Cômodos em pixel art e Rotas em pixel art são os briefings cujas verificações este amplia; Como homenagear os velhos jogos de Pokémon sem tomar nada emprestado explica por que o cânone é medido e nunca copiado; e Xcode 27.1 beta: seu app no simulador do iPhone Duo trata do simulador em que roda a captura do balão.
Fontes
-
Medição do autor, 5 de outubro de 2026:
measure_gen_text.pyna pasta de evidências do autor para este post, saídameasure_gen_text.out. Entradas do pokeemerald do pret em731ad5b:src/text.c(sFontInfos, o casoRENDER_STATE_HANDLE_CHAR,sWindowVerticalScrollSpeeds,sDownArrowYCoords, as funções de glifos japoneses),src/menu.c(sTextSpeedFrameDelays,sStandardTextBox_WindowTemplates,WindowFunc_DrawDialogueFrameavaliada para a janela padrão),src/fonts.cecharmap.txt(larguras de glifo),graphics/text_window/*.png(tamanhos),include/text_window.h, cada linha dedata/text/*.incedata/maps/*/scripts.incdividida em\n,\le\pe medida pela tabela de larguras (504 arquivos, 24.100 linhas sem marcador de substituição, 3.000 com, 0 ignoradas), egraphics/fonts/japanese_*.png(células não vazias). Do pokefirered em037335f:src/new_menu_helpers.c. Do pokecrystal em5beda23:constants/hardware.inc,constants/text_constants.asm,home/print_text.asm,gfx/font.asm, os PNGs das molduras egfx/font/font.png. Do pokered emd2704a6:data/text_boxes.asm,constants/ram_constants.asm,gfx/font/font.png. Caracteres por segundo são 60 divididos pelos quadros por caractere. A leitura da impressora de Emerald (o atraso guardado menos um porAddTextPrintere depois decrementado, de modo que um caractere leva o atraso em quadros), as condições do atraso da rolagem automática emsrc/battle_message.cesrc/scrcmd.ce a leitura em duas linhas das constantes de Crystal são afirmadas pelo script, não executadas. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/menu.cno commit731ad5b(sTextSpeedFrameDelaysna linha 77;sStandardTextBox_WindowTemplatesna linha 84;LoadMessageBoxGfxeLoadUserWindowBorderGfxnas linhas 212 e 213;WindowFunc_DrawDialogueFramechamada na linha 218, com os preenchimentos usandoBG_TILE_V_FLIPa partir da linha 378), lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/src/menu.c. ↩↩↩↩↩↩↩ -
pret,
pokecrystal/constants/text_constants.asmno commit5beda23(linhas 24 a 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) econstants/hardware.inc(linhas 922 e 923,SCREEN_WIDTH20 eSCREEN_HEIGHT18), lido em 5 de outubro de 2026, https://github.com/pret/pokecrystal/blob/master/constants/text_constants.asm. ↩↩↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_ff6.pyna pasta de evidências do autor para este post, saídameasure_ff6.out. Entradas do everything8215/ff6 em8130132:tools/rip_list_en.jsonetools/rip_list_jp.json(intervalos de bytes dos gráficos de janela, das paletas e das fontes),src/menu/config.asm(ConfigWindowNumPos,LoadWindowPal,ConfigColorBarTextList,ChangeConfigOption_02),src/menu/menu_sram.asm,src/btlgfx/gfx_cmd.asm(MsgSpeedTbl,mess_wait), cada arquivo emsrc/que lê$1d4d,src/field/text.asm(LoadLetterGfx, os limites x da linha,CalcTextWidth,NewLine,UpdateDlgTextOneLine, o título do mapa),src/gfx/font_gfx.asm(FontWidth),src/field/reset.asm(o loop de campo),src/field/hdma.asm(DlgWindowFixedColorTbl) etools/char_table/(kanji.json,kana.json). Uma letra por quadro e um valor do degradê por linha de varredura são leituras do código, afirmadas, não executadas. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Unicode Consortium,
JIS0208.TXT, a tabela de mapeamento do JIS X 0208 para o Unicode, versão 2.0, arquivada em OBSOLETE/EASTASIA, baixada e salva em 5 de outubro de 2026, https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT. Contada pormeasure_coretext.swiftemeasure_bdf.py: 6.879 caracteres; 524 que não são kanji, 169 deles kana nas linhas 4 e 5; 2.965 kanji de nível 1 nas linhas 16 a 47; 3.390 kanji de nível 2 nas linhas 48 a 84. ↩↩↩↩↩↩↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_bdf.pyna pasta de evidências do autor para este post, saídameasure_bdf.out. Entradas: os arquivos BDF de Misaki Gothic, Misaki Gothic 2nd e Misaki Mincho e de k8x12, k8x12L e k8x12S (versões 2021-05-05) e oJIS0208.TXTsalvo; ele informa o tamanho em pixels de cada fonte, a caixa delimitadora, o ascendente e o descendente, a contagem de caracteres, as larguras de avanço, os glifos de largura total cuja tinta preenche a célula em alguma direção, o mesmo para os kanji (U+4E00 a U+9FFF), e os caracteres do JIS X 0208 desenhados. ↩↩↩↩↩↩↩↩↩↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_coretext.swiftna pasta de evidências do autor para este post, compilado comswiftc -Oe executado no macOS 27.0 (build 26A428), saídameasure_coretext.out, não no iOS. Entradas: Misaki Gothic, Misaki Gothic 2nd, k8x12 e k8x12S (as versões TTF 2021-05-05), PixelMplus10 Regular e Bold e PixelMplus12 Regular (PixelMplus emd89b95f), DotGothic16, Press Start 2P e Silkscreen (google/fonts em9710da1), Hiragino Sans W3 como controle do sistema, e oJIS0208.TXTsalvo. Ele informa a contagem de glifos e as tabelas de bitmap de cada fonte (CTFontCopyAvailableTables); a grade de projeto (o menor número de pixels por em, de 4 a 40, em que 95% dos pontos de contorno dos glifos de amostra ficam a menos de 0,04 pixel, com um deslocamento constante permitido em cada eixo); os avanços latino e kana; a cobertura de ASCII e do JIS X 0208; a parcela de pixels com tinta parciais (8 a 247 de 255) quando uma linha de amostra é desenhada num bitmap cinza de 8 bits a 1 a 4 vezes a grade, com o antialiasing ligado, desligado, e ligado com o posicionamento de subpixel ligado e a quantização desligada, sem permitir a suavização de fontes; para PixelMplus12, o mesmo com a linha de base deslocada pelo deslocamento; os tamanhos de Body e Caption 1 das Human Interface Guidelines em que um pixel de fonte é inteiro a 3×; e o primeiro caractere de cada linha de um parágrafo japonês de 131 caracteres composto porCTFramesettera 8 a 20 em, sem marcação e comkCTLanguageAttributeName“ja”, contra uma lista de caracteres proibidos no início de linha. DotGothic16 não tem grade de projeto, então é desenhada a 16 pontos por em nominais (62,5 unidades por ponto) e a 1,25 a 4 vezes isso, e composta a 32 pixels por em, um tamanho escolhido a partir do nome e do passo dos pontos emmeasure_outline_coords.out, que a saída imprime. Todo renderizado impede a suavização de fontes (setAllowsFontSmoothing(false)). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Jenni Lada, “Interview: Preparing Final Fantasy Pixel Remaster for the Switch and PS4”, Siliconera, 2 de junho de 2023, o produtor Naofumi Takuma sobre a nova opção de fonte (“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”; “around 7,500 characters” contra “a few hundred”), acessado em 5 de outubro de 2026, https://www.siliconera.com/interview-preparing-final-fantasy-pixel-remaster-for-the-switch-and-ps4/. ↩↩↩↩↩↩
-
Medição do autor, 5 de outubro de 2026:
figures/dump_glyphs.swiftna pasta de rascunho deste post, compilado comswiftc -Oe executado no macOS 27.0 (26A428), saídafigures/glyphs.txt: PixelMplus10 Regular a 10 pixels desenhada pelo Core Text, “Nice card!” com 0 de 94 pixels com tinta parciais quando a suavização de fontes não é permitida (comomeasure_coretext.swiftdesenha) e 178 de 272 com o padrão do contexto de bitmap; a linha de amostra japonesa, 0 de 164 e 287 de 451. ↩↩↩↩↩↩↩↩↩↩ -
Apple, Human Interface Guidelines, “Typography” (os tamanhos de texto padrão e mínimo, 17 e 11 pontos no iOS e no iPadOS; “Follow the recommended default and minimum text sizes for each platform”; “If you use a custom font, make sure it implements the same behaviors”; as tabelas de Dynamic Type do iOS), acessado em 5 de outubro de 2026, https://developer.apple.com/design/human-interface-guidelines/typography. ↩↩↩↩↩↩↩
-
Medição do autor, 5 de outubro de 2026:
measure_kiradex_text.pyna pasta de evidências do autor para este post, saídameasure_kiradex_text.out, executado às 12:34, horário do Pacífico, sobre o Kiradex no commit334fbfecom as edições não commitadas daquele dia, numa árvore de trabalho que estava sendo editada naquele dia; o script lê os arquivos inteiros, por símbolo, não por linha. Entradas, só lidas do repositório do Kiradex como estava em disco:server/app/lines.py(23 linhas, de 3 a 40 caracteres, mediana 16),server/app/names.py(apelidos de duas palavras de listas de 20 e quatro dígitos, com no máximo 20 caracteres), cada chamada a.kit941FontemKiradex/World/*.swift(caption 10, label 3, body 3, title 3, em quatro arquivos), a família de fontes do tema, os modificadores do balão, os modificadores de acessibilidade do palco, uma busca no código do app porAccessibilityComponent,accessibilityDirectTouch,updatesFrequently,AccessibilityNotification,typesettingLanguage,UIFontMetrics,ScaledMetricedynamicTypeSize(0 cada),knownRegions(Base, en) e a contagem de arquivos.xcstringse.lproj(0); a escala do mundo pela regra dePlazaRig.fitpara os tamanhos de dispositivo que esta série mediu nos perfis do simulador do Xcode 27 (iPhone 18 Pro Max, 440 por 956 pontos; iPhone Duo externa, 466 por 678; interna, 669 por 951; todos a 3×); e, demeasure_coretext.out, o em e os avanços de cada fonte candidata, que dão os caracteres por linha da caixa depois de uma margem de 48 pontos, as larguras do balão para a linha predefinida mais longa comm= 4, 5, 6 e 8 (283, 349, 416 e 549 pontos: os avanços dela nesse tamanho mais os 8 pontos de preenchimento de cada lado do balão, já que a borda é desenhada dentro do balão), e os tamanhos de Body arredondados ampara um em de 10 pixels, com a metade arredondando para cima. Acrescentado mais tarde em 5 de outubro, com o script e a saída anteriores guardados ao lado: o arredondamento a 2× com o menormde pelo menos 11 pontos (4 a 3×, 3 a 2×); a caixa sob a moldura do briefing (a largura menos 32 pontos arredondada para baixo a pixels de fonte inteiros, 8 pixels de fonte de moldura de cada lado; de 1.224 pixels de dispositivo para 1.220 no 18 Pro Max comm= 5, 45 caracteres latinos ou 22 de largura total por linha); as páginas de “apelido: linha” para todos os 400 pares de palavras denames.pye todas as 23 linhas nos doze tamanhos de Body em cinco views (duas linhas por página, quebras gulosas nos espaços, cada caractere ASCII imprimível com 5 pixels de fonte de largura segundoprobe_line_start_glyphs.out); o teto do balão em cada iPhone demeasure_ios27_iphones.out; o limite do servidor de 218 pixels de fonte; e a soma de 120 adições de 1/120. Acrescentado mais tarde naquele dia, com o script e a saída anteriores guardados ao lado e cada linha anterior da saída inalterada: o espaço e o teto pela largura segura, sem margens laterais nos telefones e, na tela externa do Duo em retrato, tanto sem margem quanto com a margem final de 84 pontos que o post da série sobre o Duo mediu, extraída daquele post (largura segura 382, espaço 350, teto 5 com 349,33 pontos; a caixa no tamanho padrão, 38 caracteres latinos ou 19 de largura total sob a moldura, 40 ou 20 depois da margem de hoje); as páginas de “apelido: linha” nessa largura segura; as larguras para o teste de posicionamento; o limite do servidor nesse espaço (296 e 302,67 pontos comm= 4, em 350); e, deserver/app/npcs.py, os quatro colecionadores da cidade, os apelidos deles, as páginas das linhas deles em AX3 no 18 Pro Max (no máximo 2) e o limite de toques para o teste do item 4, (3 − 1) linhas × 2 páginas, um toque por página em Instantânea: 4. Em 15 de outubro essa última linha foi recalculada para o teste como ele está agora, a partir das mesmas entradas até onde a saída mostra (o arquivo do palco da cópia de 5 de outubro lida às 12:28;lines.py,names.pyenpcs.pyinalterados desde 2 e 3 de outubro; as outras entradas lidas da árvore atual no commitfc9d4876, que imprimiu as mesmas linhas), com o script e a saída anteriores guardados ao lado; todas as outras linhas da saída saem como antes. Acrescentado mais tarde em 15 de outubro, com o script e a saída anteriores guardados ao lado e cada linha anterior inalterada: os casos do teste de posicionamento em que a linha mais longa de hoje não cabe com nenhummpermitido (360 e 375 pontos a 3× e 375 a 2× com a margem de 84 pontos, e 375 a 2× com 16 de cada lado: no primeiro, um espaço de 244 pontos contra 282,67 pontos comm₀); o teto com uma linha japonesa no limite do servidor, 21 × 10 = 210 pixels de fonte (5 no 18 Pro Max, 5 e 4 no Duo sem e com o trilho, 4 a 360 pontos a 3×, 3 a 2×); e omdo balão que as capturas devem encontrar, o menor entre omda caixa menos um, nunca abaixo dem₀, e o teto sobre os dois idiomas (4 no tamanho padrão nas views de 3×, 3 na de 2×; em AX3, o teto). Acrescentado na mesma noite, do mesmo jeito: cada largura quemeasure_ios27_iphones.outlista, sem margens laterais, com 16 pontos de cada lado e com a margem final de 84 pontos, com o teto em inglês de hoje, o teto com uma linha japonesa no limite e os dois (sem margens: 4 a 360 e 375, 4 a 390 e 393 com a linha japonesa, 5 de 402 a 440, 3 a 2×); onde a linha mais longa de hoje não cabe com nenhummpermitido (com a margem de 84 pontos a 360, 375, 390 e 393 pontos a 3× e a 375 e 414 a 2×; com 16 de cada lado a 375 a 2×); e “Hi!” comm₀, 36 pontos a 3× e 38,50 a 2×, que cabe em todos os casos. Acrescentado depois disso: a mesma tabela com uma linha latina no limite, 43 × 5 = 215 pixels de fonte, ao lado da linha de hoje e da japonesa, e os casos em que cada uma não cabe com nenhummpermitido: 7, 8 e 10 dos 36. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokered/data/text_boxes.asmno commitd2704a6, linha 10 (db MESSAGE_BOX, 0, 12, 19, 17), lido em 5 de outubro de 2026, https://github.com/pret/pokered/blob/master/data/text_boxes.asm. ↩↩ -
pret,
pokeemerald/src/text.cno commit731ad5b(sFontInfosa partir da linha 119;sDownArrowYCoords{ 0, 1, 2, 1 }na linha 75 esWindowVerticalScrollSpeedsna linha 76;AddTextPrinterdefinindotextSpeed = speedna linha 281 e--sTempTextPrinter.textSpeed;na linha 296; odownArrowDelay = 8da seta na linha 832; no casoRENDER_STATE_HANDLE_CHAR,if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp)na linha 944, a contagem regressivaif (textPrinter->delayCounter && textPrinter->textSpeed)na linha 947,canABSpeedUpPrinte um novo aperto de A ou B ativandohasPrintBeenSpedUpnas linhas 950 a 953, eif (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll)pondo o contador em 3, ou então emtextSpeed, nas linhas 958 a 961;EXT_CTRL_CODE_JPNna linha 1094;DecompressGlyph_Normala partir da linha 1853 eGetGlyphWidth_Normala partir da linha 1887), lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/src/text.c. ↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokefirered/src/text.cno commit037335f, linhas 639 a 647 (no casoRENDER_STATE_HANDLE_CHAR,if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp)pondo o contador em 0, egTextFlags.canABSpeedUpPrint && JOY_NEW(A_BUTTON | B_BUTTON)ativandohasPrintBeenSpedUpenquanto um atraso é contado), lido em 5 de outubro de 2026, https://github.com/pret/pokefirered/blob/master/src/text.c. ↩↩↩ -
pret,
pokecrystal/home/print_text.asmno commit5beda23(linha 4, “The text speed setting in wOptions is actually a frame count”, ePrintLetterDelayesperando um quadro por letra enquanto A ou B fica pressionado) epokered/constants/ram_constants.asmemd2704a6(linhas 38 a 40:TEXT_DELAY_FAST1,TEXT_DELAY_MEDIUM3,TEXT_DELAY_SLOW5), lido em 5 de outubro de 2026, https://github.com/pret/pokecrystal/blob/master/home/print_text.asm. ↩↩ -
pret,
pokecrystal/home/joypad.asmno commit5beda23(WaitPressAorB_BlinkCursorna linha 342;BlinkCursorna linha 442, alternando o tile emhlcoord 18, 17entre'▼'e'─'), lido em 5 de outubro de 2026, https://github.com/pret/pokecrystal/blob/master/home/joypad.asm. O período do piscar não foi derivado. ↩↩↩ -
Num Kadoma, littlelimit.net, “font”, a seção de licença de Misaki, k8x12 e das outras fontes do autor (“These fonts are free software. Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.”), acessado em 5 de outubro de 2026, https://littlelimit.net/font.htm#license. ↩↩↩↩↩
-
itouhiro, PixelMplus,
README.md(TrueType só de contornos, “埋め込みビットマップは なし”; “JIS第1・第2水準のすべての漢字を収録” e ISO-8859-1; PixelMplus12 e PixelMplus10; feitas a partir das M+ BITMAP FONTS; licença M+ FONT LICENSE) emisc/mplus_bitmap_fonts/LICENSE_E(as três frases da M+ FONT LICENSE), no commitd89b95f, lido em 5 de outubro de 2026, https://github.com/itouhiro/PixelMplus/blob/master/misc/mplus_bitmap_fonts/LICENSE_E. ↩↩↩↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_outline_coords.swiftna pasta de evidências do autor para este post, compilado e executado no macOS 27.0, saídameasure_outline_coords.out: as coordenadas brutas x e y de contorno deH,A,oeあem PixelMplus12 Regular e DotGothic16 e deH,Aeoem Silkscreen, em unidades de 1.000 por em. ↩↩↩↩↩↩↩ -
Apple, Human Interface Guidelines, “Accessibility” (“Ideally, give people the option to enlarge text by at least 200 percent (or 140 percent in watchOS apps).”), acessado em 5 de outubro de 2026, https://developer.apple.com/design/human-interface-guidelines/accessibility. ↩↩↩
-
W3C, “Requirements for Japanese Text Layout”, Working Group Note, 11 de agosto de 2020, seção 3.1.7 (a regra de proibição no início de linha e a nota sobre o conjunto de regras menos estrito), acessado em 5 de outubro de 2026, https://www.w3.org/TR/jlreq/. ↩↩↩↩↩↩↩↩
-
Dossiê de pesquisa do autor para este post, 5 de outubro de 2026 (privado): as medições acima, os estudos de caso e um primeiro rascunho do briefing para o Kiradex. ↩↩↩
-
Blake Crosley, “Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew”, blakecrosley.com, 3 de outubro de 2026: a regra 10 (“Text is art”, o “use font size 16, 32, 48, etc” da página de m5x7 e a frase sobre a moldura de janela de texto de 24 por 24 de Emerald), a aritmética de tamanho do texto com k = 8 e k = 6, o parágrafo sobre o Pixel Remaster citando o GoNintendo para cerca de 7.500 caracteres japoneses, e o plano de texto (“m5x7 at integer multiples in a 24-by-24 nine-slice box, two lines, typewriter reveal, a two-frame advance arrow”), lido em 5 de outubro de 2026, https://blakecrosley.com/blog/pixel-art-world-on-iphone. Aquele post é citado aqui e não é alterado. ↩↩↩↩↩↩↩↩↩↩
-
Código do Kiradex,
Kiradex/KiradexTheme.swift, linha 12 (let fontFamily: Kit941.FontFamily = .systemDesign(.monospaced)), lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado). ↩↩ -
Código do Kiradex,
Kiradex/World/PlazaStage.swift, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado): linhas 146, 155 e 189 (stepTracedefinido como uma falha de build na closure make daRealityView, como cada passo emrig.onStepe, com-airProbe, como o relatório do ar num.task; uma leitura do mesmo arquivo às 12:42 mostrou uma quarta,stepTrace = "lap done", com um novo-plazaLapDemo), linhas 58 a 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.”), linhas 313 a 315 (.accessibilityElement(),.accessibilityLabel("The plaza"),.accessibilityValue(stepTrace)), linhas 289 a 293 (os balões, o último.overlaydentro doGeometryReaderdo palco das linhas 121 a 294), linhas 295 a 309 (as duas sobreposições do palco por cima: scanlines,.black.opacity(0.16)com um ponto de altura a cada 3 pontos, e uma vinhetaRadialGradient), linhas 402 a 418 (bubbles(of:in:): uma linha mostrada enquantotimeline.date.timeIntervalSince(said.at) < 4, posicionada no ponto da cabeça na view menosKit941.Spacing.lg, sem limite nenhum, com o hit testing desligado), e linhas 444 a 459 (a viewBubble,.padding(.horizontal, Kit941.Spacing.sm)e depois.overlay(Rectangle().strokeBorder(...)), o comentário dela “A line said, as the old games drew it: a cream box with a dark border.”, caption negrito,WorldInk.paper, uma borda de tinta deKit941.Spacing.xxs,.fixedSize(),.accessibilityLabel("Said: \(text)")). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Código do Kiradex,
Kiradex/World/WorldHUD.swift, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado); o arquivo tinha data de 4 de outubro e não mudou durante a leitura: linhas 31 a 38 (WorldInk, “The world’s own colours, as the old games drew a text box: cream paper”, e a cor de papel dele), linhas 40 a 44 (a linha do lugar “in a text box at the bottom. Half open, the box keeps to the wider side of the fold.”), linhas 61 a 79 (os controles numGeometryReader, com uma margem de.padding(Kit941.Spacing.md), a caixa ao longo dessa largura ou, meio aberto, na largura do painel mais largo menosKit941.Spacing.mdde cada lado), e linhas 84 a 101 (a caixa: título em caption negrito, em maiúsculas; status em caption seminegrito, identificador<name>-status,plaza-statusna praça; fundo de papel; uma borda de tinta deKit941.Spacing.xxs;.allowsHitTesting(false)sob o comentário “Taps go through to the ground under it.”); eKiradex/World/PlazaStage.swift, linhas 581 e 582, ondePlazaViewpõe oWorldHUDsobre o palco inteiro comwordscomo status, e linhas 725 a 736 (words: a frase da hora do dia, a contagem de quem está aqui e uma linha de conselho, as fitas ganhas e a frase da estação). O espaçamento do kit,Sources/Kit941/Tokens/Spacing.swiftno 941Kit:xxs2,xs4,sm8,md16,lg24 pontos no iPhone. ↩↩↩↩↩↩↩ -
Código do Kiradex,
Kiradex.xcodeproj/project.pbxproj, linhas 1138 a 1141 (knownRegions = (Base, en)), lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado); o arquivo tinha ganhado linhas às 12:42. ↩↩ -
Código do Kiradex,
server/app/lines.py, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado): a docstring, “The preset lines: everything anyone can say in the plaza.”, as 23 entradas deLINES(a partir da linha 5), e o comentário na linha 20, antes do índice 14, “Indices are fixed; add at the end only, the app shows them by number.” ↩↩↩↩ -
Código do Kiradex, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit
334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado):scripts/forge/kit.py, linhas 529 e 530 (def sign():, “A hanging sign on a bracket, 12 × 11.”),Kiradex/World/TileMap.swift, linha 52 (a letra de mapa"S": .sign), eKiradex/Resources/World/town.json(uma célulaS, no x 7, linha 10, lida com o módulojsondo Python às 12:33). ↩↩↩↩ -
Método. Descompilações do pret, clones rasos lidos em pokeemerald
731ad5b(1º de outubro de 2026), pokefirered037335f(26 de setembro de 2026), pokecrystal5beda23(29 de setembro de 2026) e pokeredd2704a6(22 de setembro de 2026), https://github.com/pret. Final Fantasy VI, do disassembly everything8215/ff6 em813013276c952fdd27edcf7b8b86f17291542cbf(14 de setembro de 2026), que contém código e listas de extração, não os gráficos da ROM, https://github.com/everything8215/ff6. PixelMplus, em clone raso emd89b95f04eeb95bf98bb09cb9234a2f99d186df5(2 de junho de 2013); Misaki e k8x12, as versões 2021-05-05 em TTF e BDF, baixadas do site do autor; Press Start 2P, Silkscreen e DotGothic16 (arquivos de fonte,OFL.txteMETADATA.pb) degoogle/fontsem9710da1(30 de setembro de 2026). Páginas web baixadas e salvas em 5 de outubro de 2026. Treze scripts na pasta de evidências do autor para este post, dez em Python e três em Swift, e mais um script em Swift junto com as figuras deste post,dump_glyphs.swift, produzem cada número medido, cada um com a saída salva ao lado. O pret monta os jogos em inglês; nenhuma ROM japonesa foi lida. ↩↩↩↩↩↩ -
pret,
pokeemerald/src/fonts.c(gFontNormalLatinGlyphWidthsa partir da linha 148) epokeemerald/charmap.txt(linhas 50 a 52, glifos especiais para palavras da franquia) no commit731ad5b, lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/src/fonts.c. ↩↩ -
Martin Korth, GBATEK, “LCD Dimensions and Timings” (“Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz”; 160 linhas visíveis; 240 pontos por linha visível), acessado em 5 de outubro de 2026, https://problemkaputt.de/gbatek-lcd-dimensions-and-timings.htm. ↩
-
Aritmética do autor sobre os números medidos: 216 ÷ 5,25 = 41 caracteres; 59,737 ÷ 1 = 59,7 caracteres por segundo; 191 × 1,25² = 298 pixels; −33 unidades de PixelMplus12 ÷ 83,33 unidades por pixel = −0,396 pixel (a sonda informa −0,39, o centro da faixa dela); 7.500 contra 6.879; 11 de 12 e 9 de 12 tamanhos de Body fora da grade, pelo complemento de 1 e 3 inteiros; 14 ÷ 17 = 82% e 53 ÷ 17 = 312%; 13,33 ÷ 16,67 = 80% e 53,33 ÷ 16,67 = 320%; 11 pontos ÷ 2 pontos por texel = 5,5 texels; 4 × 10 ÷ 3 = 13,33 pontos; 12 pixels de fonte × 5 ÷ 3 = 20 pontos; 440 − 32 = 408, 360 − 32 = 328 e 375 − 32 = 343 pontos; 40 × 5 × 3 ÷ 2 + 16 = 316 pontos a 2×; ⌈11 × 3 ÷ 10⌉ = 4 e ⌈11 × 2 ÷ 10⌉ = 3, e 10 × 3 ÷ 2 = 15 pontos; (360 − 48) × 3 ÷ 4 = 234 e (375 − 48) × 2 ÷ 3 = 218 pixels de fonte, e 218 ÷ 5 = 43 caracteres latinos e 218 ÷ 10 = 21 de largura total; 21 × 10 × 4 ÷ 3 + 16 = 296 e 21 × 10 × 3 ÷ 2 + 16 = 331 pontos; (440 − 32) × 3 = 1.224 pixels de dispositivo, ÷ 5 = 244,8, então 244 pixels de fonte (1.220 pixels de dispositivo), menos 2 × 8 = 228, ÷ 5 = 45 caracteres latinos e ÷ 10 = 22 de largura total; 15 × 3 ÷ 10 = 4,5, arredondado para cima para 5; 120 adições de 1/120 em precisão dupla = 0,9999999999999989, × 30 = 29,999999999999968, arredondado para baixo para 29; 1,5 segundo × 12 = 18 caracteres; 20 × 5 × 4 ÷ 3 = 133 pontos e 20 × 5 × 3 ÷ 2 = 150 pontos a 2×; 466 − 84 = 382 e 382 − 32 = 350 pontos, 466 − 32 = 434; 40 × 5 × 5 ÷ 3 + 16 = 349,33 pontos; (382 − 32) × 3 ÷ 5 = 210 pixels de fonte, menos 2 × 8 = 194, ÷ 5 = 38 caracteres latinos e ÷ 10 = 19 de largura total; (382 − 48) ÷ (5 × 5 ÷ 3) = 40,08 e ÷ (10 × 5 ÷ 3) = 20,04; 43 × 5 × 4 ÷ 3 + 16 = 302,67 pontos; 360 − 84 − 32 = 244 pontos; (3 − 1) × 2 × 1 = 4 toques; 16 texels × 2,67 pontos = 42,7 pontos e 13 × 2,67 = 34,7; 9 quadros ÷ 60 = 150 milissegundos, quatro passos 600 milissegundos; 3 pontos × 3 = 9 pixels de dispositivo entre scanlines, cada uma com 1 ponto × 3 = 3 pixels de dispositivo de altura. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩
-
pret,
pokeemerald/src/battle_message.cno commit731ad5b(linhas 3002 e 3003: rolagem automática ligada quandogBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED); linha 3010,speed = 1para a janela de mensagens em batalhas por cabo; linha 3012, ospeed = sRecordedBattleTextSpeeds[GetTextSpeedInRecordedBattle()]de uma batalha gravada, com a tabela{8, 4, 1, 0}na linha 1957) epokeemerald/src/scrcmd.c(ScrCmd_messageautoscrolla partir da linha 1286, definindoautoScrolleforceMidTextSpeednas linhas 1292 e 1293), lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/src/battle_message.c. ↩↩↩ -
pret,
pokeemerald/charmap.txtno commit731ad5b, linhas 1087 a 1089 ('\l' = FA @ scroll up window text,'\p' = FB @ new paragraph,'\n' = FE @ new line), einclude/constants/characters.h, linhas 175 e 176 (CHAR_PROMPT_SCROLL, “waits for button press and scrolls dialog”;CHAR_PROMPT_CLEAR, “waits for button press and clears dialog”), comsrc/text.c, linhas 968 a 971 (CHAR_NEWLINEdescendocurrentYuma linha) e 1102 a 1109 (os dois prompts entrando emRENDER_STATE_CLEAReRENDER_STATE_SCROLL_START), lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/charmap.txt. ↩ -
pret,
pokeemerald/src/text_window.c(message_box.pngna linha 53;LoadMessageBoxGfxna linha 93;LoadUserWindowBorderGfxna linha 110),pokeemerald/include/text_window.h(#define WINDOW_FRAMES_COUNT 20na linha 4),pokeemerald/src/option_menu.c(a opção FRAME) epokeemerald/graphics/text_window/no commit731ad5b, lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/src/text_window.c. ↩↩↩ -
pret,
pokeemerald/data/text/e oscripts.incde cada mapa no commit731ad5b, entre elesdata/maps/LittlerootTown/scripts.inc, linha 854 (.string "MOM: {PLAYER}, we're here, honey!\p"), lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/data/maps/LittlerootTown/scripts.inc. ↩↩ -
pret,
pokeemerald/src/international_string_util.c,src/contest.cesrc/battle_tower.c(os códigosEXT_CTRL_CODE_JPNeEXT_CTRL_CODE_ENGnos nomes) egraphics/fonts/(japanese_normal.png,japanese_small.png,japanese_bold.png,japanese_frlg_male.png) no commit731ad5b, lido em 5 de outubro de 2026, https://github.com/pret/pokeemerald/blob/master/src/international_string_util.c. ↩↩ -
pret,
pokefirered/src/new_menu_helpers.cno commit037335f(sStandardTextBox_WindowTemplatesa partir da linha 34,.width = 26na linha 40, e os atrasos de velocidade do texto) epokefirered/src/text_printer.c(AddTextPrinter,textSpeed = speedna linha 78 e--sTempTextPrinter.textSpeed;na linha 93) no commit037335f, lido em 5 de outubro de 2026, https://github.com/pret/pokefirered/blob/master/src/new_menu_helpers.c. ↩ -
Pan Docs, “Rendering” (“One frame: 70224 dots @ 59.7 fps”; um quadro “takes ~16.74 ms instead of ~16.67”), gbdev.io, acessado em 5 de outubro de 2026, https://gbdev.io/pandocs/Rendering.html. ↩
-
pret,
pokecrystal/gfx/font.asmno commit5beda23(as moldurasFRAME_1aFRAME_8,TEXTBOX_FRAME_TILES6 tiles de 1 bit cada, uma nona moldura sem uso) egfx/font/font.png, lido em 5 de outubro de 2026, https://github.com/pret/pokecrystal/blob/master/gfx/font.asm. ↩ -
everything8215, ff6,
tools/rip_list_en.jsonetools/rip_list_jp.jsonno commit8130132(assets/gfx/window/window_%s.4bppem0xED0000a0xED1BFF;window_%s.palem0xED1C00a0xED1CFF), lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/blob/master/tools/rip_list_en.json. ↩ -
everything8215, ff6,
src/menu/config.asm(ConfigWindowNumPos,LoadWindowPallendo de$1d57, a cor da fonte em$1d55,ConfigColorBarTextList,ChangeConfigOption_02) esrc/menu/menu_sram.asm(linha 102, “message speed = 3, battle speed = 3”) no commit8130132, lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/blob/master/src/menu/config.asm. ↩↩↩ -
everything8215, ff6, no commit
8130132:src/btlgfx/gfx_cmd.asm, linhas 1101 a 1103 (MsgSpeedTbl, “wait duration for each battle message speed”,$20,$30,$40,$50,$60,$70,$80,$90) e linhas 1151 a 1165 (mess_wait, lendo os bits 4 a 6 de$1d4dcomo índice e chamandoWaitFrameesse número de vezes);src/menu/config.asm, linha 1499 (“battle speed, battle message speed, magic order, and window pattern”) eChangeConfigOption_02a partir da linha 933;src/menu/menu_text.en.inc, linha 346 (os ajustes desenhados como 1 a 6). Arquivos cujo código mexe em$1d4d:src/battle/init.asm(o ajuste de comandos, o modo de batalha e a velocidade de batalha),src/btlgfx/gfx_cmd.asm,src/menu/config.asm,src/menu/field_menu.asmesrc/menu/menu_sram.asm; nenhum emsrc/field/. Lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/blob/master/src/btlgfx/gfx_cmd.asm. ↩ -
everything8215, ff6,
src/field/hdma.asm(DlgWindowFixedColorTbl) esrc/field/event.asm(linha 1591, “dialog window y position (1 or 18)”) no commit8130132, lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/blob/master/src/field/hdma.asm. ↩↩↩ -
everything8215, ff6,
src/field/text.asm(LoadLetterGfx, a linha de diálogo do x 4 a$e0,CalcTextWidth,NewLine,UpdateDlgTextOneLine, o título do mapa centralizado e mostrado por 100 quadros) esrc/gfx/font_gfx.asm(FontWidth) no commit8130132, lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/blob/master/src/field/text.asm. ↩↩↩↩↩ -
everything8215, ff6,
tools/rip_list_jp.json(large_font.jp.1bppem0xC494C0a0xC4FFFF) etools/char_table/no commit8130132, lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/tree/master/tools/char_table. ↩ -
everything8215, ff6,
src/field/reset.asm(o loop de campo: espera o vertical blank e chamaUpdateDlgTextuma vez por passagem) no commit8130132, lido em 5 de outubro de 2026, https://github.com/everything8215/ff6/blob/master/src/field/reset.asm. O disassembly gera a ROM em inglês e a japonesa a partir de uma única fonte. ↩↩ -
Data Crystal, “Chrono Trigger/ROM map”, datacrystal.tcrf.net, acessado em 5 de outubro de 2026: HTTP 403, não lido, https://datacrystal.tcrf.net/wiki/Chrono_Trigger/ROM_map. ↩↩↩
-
Kiradex,
docs/TestFlight.md, passo 5 (linhas 192 a 194: na Dex, escolher japonês mostra as coleções e as cartas com os nomes japoneses e o inglês embaixo), lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado). ↩ -
Num Kadoma, littlelimit.net, “8×8ドット日本語フォント「美咲フォント」” (a célula de 8 por 8 da apresentação, os níveis 1 e 2 do JIS e a tinta de 7 por 7; as descrições de Misaki Mincho e Misaki Gothic 2nd; a nota sobre a licença das M+ FONTS; o relato de como foi feita para o driver de tela japonês do PC-E500), acessado em 5 de outubro de 2026, https://littlelimit.net/misaki.htm. ↩↩↩↩
-
Num Kadoma, littlelimit.net, “8×12ドット日本語フォント「k8x12」” (a célula de 8 por 12, os níveis 1 e 2 do JIS, as variantes L e S, a nota da variante S sobre os gráficos de 8 por 8 dos consoles antigos), acessado em 5 de outubro de 2026, https://littlelimit.net/k8x12.htm. ↩
-
Google Fonts,
ofl/dotgothic16/OFL.txt(SIL Open Font License 1.1, Copyright 2020 The DotGothic16 Project Authors, sem Reserved Font Name) eMETADATA.pb(designer Fontworks Inc.; subconjuntos cyrillic, japanese, latin, latin-ext; adicionada em 2020-12-15) em9710da1, lido em 5 de outubro de 2026, https://github.com/google/fonts/blob/main/ofl/dotgothic16/OFL.txt. ↩↩↩↩ -
Google Fonts,
ofl/pressstart2p/OFL.txt(SIL Open Font License 1.1, “with Reserved Font Name “Press Start 2P””) eMETADATA.pb(designer CodeMan38) em9710da1, lido em 5 de outubro de 2026, https://github.com/google/fonts/blob/main/ofl/pressstart2p/OFL.txt. ↩↩↩ -
Google Fonts,
ofl/silkscreen/OFL.txt(SIL Open Font License 1.1, sem Reserved Font Name) eMETADATA.pb(designer Jason Kottke) em9710da1, lido em 5 de outubro de 2026, https://github.com/google/fonts/blob/main/ofl/silkscreen/OFL.txt. ↩↩ -
DaFont, “Pixel Operator Font” (de Jayvee Enaguas; “Bitmap (16 px)”; 15 arquivos de fonte; 241 glifos; as notas do autor, “Changed licence to Creative Commons Zero (CC0) 1.0” e “licensed under a Creative Commons Zero (CC0) 1.0”), acessado em 5 de outubro de 2026, https://www.dafont.com/pixel-operator.font. O repositório que as notas indicam, https://notabug.org/HarvettFox96/ttf-pixeloperator, retornou HTTP 404 em 5 de outubro de 2026. Fonte única. ↩↩↩↩
-
Daniel Linssen, “m5x7”, itch.io (“use font size 16, 32, 48, etc”; “free to use but attribution appreciated”; “Asset license Creative Commons Zero v1.0 Universal”; largura média 5, ascendente 7, descendente 2, altura-x 5; os caracteres descritos como “uppercase and lowercase letters, including many accented letters, numbers and most common symbols”; um download de pague quanto quiser), acessado em 5 de outubro de 2026, https://managore.itch.io/m5x7. O arquivo da fonte não foi baixado. ↩↩↩↩↩↩
-
Daniel Linssen, “m3x6”, itch.io (“use font size 16, 32, 48, etc”; “free to use with attribution”; sem campo de licença do recurso; um download de pague quanto quiser), acessado em 5 de outubro de 2026, https://managore.itch.io/m3x6. O arquivo da fonte não foi baixado. ↩↩↩↩↩
-
Apple Developer Documentation, “Core Text” (“Create text layouts, optimize font handling, and access font metrics and glyph data”; iOS 3.2, macOS 10.8), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/coretext. ↩
-
Medição do autor, 5 de outubro de 2026:
measure_hig.pyna pasta de evidências do autor para este post, saídameasure_hig.out: as tabelas da página Typography salva, os tamanhos padrão e mínimo e cada tabela de Dynamic Type do iOS de xSmall a AX5 (Body 14, 15, 16, 17, 19, 21, 23, 28, 33, 40, 47 e 53 pontos; Caption 1 11, 11, 11, 12, 14, 16, 18, 22, 26, 32, 37 e 43). ↩↩↩↩↩ -
Unicode Consortium, “Unicode Standard Annex #14: Unicode Line Breaking Algorithm”, revisão 57, Unicode 18.0.0, 2026-09-01 (a classe CJ e “Treating characters of class CJ as class NS will give CSS strict line breaking; treating them as class ID will give CSS normal breaking.”), acessado em 5 de outubro de 2026, https://www.unicode.org/reports/tr14/. ↩↩
-
Stardew Valley Wiki, “Modding:Dialogue”, editada pela última vez em 20 de maio de 2026 (
$b; os comandos de retrato;Strings\SpeechBubbles), acessado em 5 de outubro de 2026, https://stardewvalleywiki.com/Modding:Dialogue. ↩↩↩ -
Stardew Valley Wiki, “Modding:Custom languages”, editada pela última vez em 3 de janeiro de 2026 (
UseLatinFont, o arquivo.fnt,FontPixelZoomcom uma base recomendada de 1.5,FontApplyYOffset), acessado em 5 de outubro de 2026, https://stardewvalleywiki.com/Modding:Custom_languages. ↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_steam_languages.pyna pasta de evidências do autor para este post, saídameasure_steam_languages.out, que lê as tabelas de idiomas das páginas da loja Steam salvas 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/) e Eastward (https://store.steampowered.com/app/977880/), baixadas em 5 de outubro de 2026. ↩↩↩↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_apple_docs.pyna pasta de evidências do autor para este post, saídameasure_apple_docs.out: o título, a declaração, o resumo, as plataformas e os primeiros parágrafos de cada página de documentação da Apple salva citada na seção 10. ↩↩ -
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.”), acessado em 5 de outubro de 2026, https://developer.apple.com/design/human-interface-guidelines/designing-for-games. ↩
-
Apple Developer Documentation, “UIFontMetrics” (“A utility object for obtaining custom fonts that scale to support Dynamic Type”; iOS 11.0), “scaledFont(for:)”, “scaledFont(for:maximumPointSize:)” e “scaledValue(for:)” (“Scales an arbitrary layout value based on the current Dynamic Type settings”; iOS 11.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/uikit/uifontmetrics. ↩↩↩
-
Apple Developer Documentation, “custom(:size:relativeTo:)” (uma fonte personalizada “that scales relative to the given
textStyle”; iOS 14.0), https://developer.apple.com/documentation/swiftui/font/custom(:size:relativeto:), e “custom(:fixedSize:)” (uma fonte personalizada “that does not scale with Dynamic Type”; iOS 14.0), https://developer.apple.com/documentation/swiftui/font/custom(:fixedsize:), acessados em 5 de outubro de 2026. ↩↩↩↩↩ -
Apple Developer Documentation, “ScaledMetric” (“A dynamic property that scales a numeric value”; iOS 14.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/scaledmetric. ↩↩
-
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.”), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/view/dynamictypesize(:). ↩↩ -
Apple Developer Documentation, “accessibilityShowsLargeContentViewer()” (iOS 15.0; “Don’t use the large content viewer as a replacement for proper Dynamic Type support.”), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/view/accessibilityshowslargecontentviewer(). ↩↩
-
Apple Developer Documentation, “UIAppFonts” (“App-specific font files located in the bundle and that the system loads at runtime”; iOS 3.2), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/bundleresources/information-property-list/uiappfonts. ↩↩
-
Apple Developer Documentation, “CTFontManagerRegisterFontsForURL(:::)” (iOS 4.1), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/coretext/ctfontmanagerregisterfontsforurl(:::). ↩
-
Apple Developer Documentation, “interpolation(:)” (Image; iOS 13.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/image/interpolation(:). ↩
-
Apple Developer Documentation, “resizable(capInsets:resizingMode:)” (“Sets the mode by which SwiftUI resizes an image to fit its space”; iOS 13.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/image/resizable(capinsets:resizingmode:). ↩
-
Apple Developer Documentation, “baselineOffset(:)” (Text; “Sets the vertical offset for the text relative to its baseline”; iOS 13.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/text/baselineoffset(:). ↩
-
Apple Developer Documentation, “typesettingLanguage(:isEnabled:)” (iOS 17.0; o trecho sobre texto num idioma que não coincide com o idioma da interface do dispositivo; “this language does not affect text localization.”), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/view/typesettinglanguage(:isenabled:). ↩↩
-
Apple Developer Documentation, “NSParagraphStyle.LineBreakStrategy” (iOS 9.0) e as propriedades
standard(iOS 14.0),pushOut(iOS 9.0) ehangulWordPriority(iOS 14.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/uikit/nsparagraphstyle/linebreakstrategy-swift.struct. ↩ -
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.”; iOS 16.0 na forma com
LocalizedStringResource), https://developer.apple.com/documentation/swiftui/view/accessibilityvalue(:), e “accessibilityLabel(:)” (iOS 16.0), https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(:), acessados em 5 de outubro de 2026. ↩↩↩ -
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.”; a área precisa ser ativada primeiro), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/view/accessibilitydirecttouch(:options:). ↩↩
-
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.”), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/accessibilitytraits/updatesfrequently. ↩
-
Apple Developer Documentation, “AccessibilityNotification.Announcement” (iOS 17.0; o atributo
accessibilitySpeechAnnouncementPriority), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/accessibility/accessibilitynotification/announcement. ↩↩ -
Apple Developer Documentation, “accessibilityReduceMotion” (iOS 13.0; “If this property’s value is true, UI should avoid large animations”), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/swiftui/environmentvalues/accessibilityreducemotion. ↩↩
-
Apple Developer Documentation, “AccessibilityComponent” (RealityKit; “A component that stores accessibility information for an entity”; o nome que o “VoiceOver says aloud when it describes the entity”; iOS 17.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/realitykit/accessibilitycomponent. ↩↩↩↩
-
Apple Developer Documentation, “Improving the Accessibility of RealityKit Apps” (“set the properties on each visible
Entityin your scene”), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/realitykit/improving-the-accessibility-of-realitykit-apps. ↩ -
Apple Developer Documentation, “RealityView” (iOS 18.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/realitykit/realityview. ↩
-
Apple Developer Documentation, “XCUIVoiceOverService” (“Provides programmatic control of VoiceOver for UI testing”; iOS 27.0), acessado em 5 de outubro de 2026, https://developer.apple.com/documentation/xcuiautomation/xcuivoiceoverservice. ↩↩↩
-
Código do 941Kit,
Sources/Kit941/Tokens/Typography/FontFamily.swiftno commit50bc657(28 de setembro de 2026), linhas 21 a 63 (case custom(named: String), renderizado porFont.custom(_:size:relativeTo:)em tamanhos intrínsecos de 34, 22, 17, 13 e 12 pontos para display, title, body, label e caption), lido em 5 de outubro de 2026; repositório privado. ↩↩ -
Código do Kiradex,
server/app/names.py(linhas 7 a 14,FIRSTSeSECONDS, com 20 palavras cada, e o padrão de apelido de duas palavras com inicial maiúscula e quatro dígitos), lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado). ↩↩ -
Blake Crosley, “A Pixel Town Alive When Nobody Is Online: NPCs and Ghosts”, blakecrosley.com, 5 de outubro de 2026 (as 23 linhas predefinidas, 14 para os jogadores e 9 para os próprios colecionadores da cidade, e uma linha a cada 2,0 segundos por jogador), https://blakecrosley.com/blog/pixel-art-town-alive-when-nobody-is-online. ↩↩
-
Kiradex,
docs/WORLD.md, seção 5, “The stack”, linhas 524 e 525 (limites de frequência: “eight moves a second, a line every two seconds”), lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado). ↩↩ -
Código do Kiradex, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit
334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado):Kiradex/World/PlazaStage.swift, linhas 199 a 203 (o.gesture(DragGesture(minimumDistance: 0))do palco, chamandotouchedenquanto ele muda eliftedquando termina), linhas 329 a 359 (touched(_:in:): sem o direcional, o chão sob o dedo é alcançado na hora e seguido de tile em tile, enquanto um toque num colecionador ou nos tiles do quiosque retorna e espera o dedo subir), linhas 364 a 380 (lifted(_:in:): um toque que não caminhou nem direcionou é um toque simples; sobre outro colecionador ele definetapped, nos tiles do quiosque chamaonKiosk, e senão executarig.walk(to: tile)), linhas 31 e 32 (onKiosk, “The kiosk tapped: the town’s board.”), linha 82 (stickKey,"world.stick"), o argumento de inicialização de teste-plazaFacingDemo(o.taskdas linhas 255 a 270), e otellOnArrivaldePlazaView, que lê-plazaTell(linhas 515 a 522);KiradexUITests/KiradexUITests.swift, linhas 384 e 387, onde o teste de caminhada toca o palco em frações do frame dele. ↩↩↩↩ -
Código do Kiradex,
Kiradex/World/PlazaRig.swift, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado):fit(viewSize:displayScale:tilesTall:fitsWhole:)na linha 432 (pixelScale, a parte inteira da altura em pixels da view dividida portilesTall × TileMap.tileSize, 20 × 16 = 320, linha 439;TileMap.tileSizevale 16 na linha 60 deTileMap.swift) eheadPoint(of:)na linha 479 (a posição de quem caminha mais metade da altura do quadro da folha, mais 4, linhas 482 e 483). O arquivo mudou de novo até as 12:42; as duas funções estavam inalteradas nesse momento. ↩↩ -
Código do Kiradex, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit
334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado):Kiradex/World/PlazaStage.swift, linha 311 (.ignoresSafeArea()no palco, cujoGeometryReader, linhas 121 a 294, contém a sobreposição dos balões), e nenhuma leitura das margens da área segura em todo o arquivo;Kiradex/World/WorldHUD.swift, linhas 26 e 27 (.statusBarHidden()e.persistentSystemOverlays(.hidden)emWorldView, a raiz do mundo) e linhas 40 a 42 (os controles ficam “over its stage and inside the safe area, which on the Duo keeps it clear of the cameras and the side rail”); eKiradex/Views/Card/CardViewer.swift, linhas 19 a 24, onde umGeometryReaderfora do palco da carta passaproxy.safeAreaInsetsparaCardStage, que ignora a área segura, para que “the card rests inside the safe area, clear of the Duo’s outer camera and side rail”, com os mesmos dois modificadores nas linhas 56 e 57. Uma busca porsafeAreaInsetsemKiradex/só encontrou a linha deCardViewer. ↩↩↩↩↩ -
Blake Crosley, “Xcode 27.1 Beta: Your App in the iPhone Duo Simulator”, blakecrosley.com, 21 de setembro de 2026, atualizado em 2 de outubro de 2026 (rodar um app no simulador do iPhone Duo, telas externa e interna; fechado, vinculado ao SDK 27.1, uma janela de 466 por 678 pontos cujo conteúdo dentro de uma pilha de navegação mediu 382 por 562 pontos, com margens de área segura de 82 em cima, 34 embaixo e 84 no lado final, o trilho, numa sonda com uma barra de ferramentas e uma barra de abas nessa borda e a barra de status visível), https://blakecrosley.com/blog/xcode-27-1-beta-iphone-duo-simulator. ↩↩↩↩
-
Medição do autor, 5 de outubro de 2026:
measure_figure_width.pyna pasta de evidências do autor para este post, saídameasure_figure_width.out, reexecutado às 12:36, horário do Pacífico, sobre o Kiradex no commit334fbfecom as edições não commitadas daquele dia, com a saída inalterada. Entradas, só lidas do Kiradex:Kiradex/Resources/World/characters.json("cell": [32, 40], os quadros e as direções),scripts/forge/rig.py(linha 53,CELL_W, CELL_H = 32, 40) e a folha de corpofig-body.png(1.376 por 160 pixels, 43 quadros por 4 direções). No quadro paradoidle_a, a figura tem 30 texels de altura em cada direção, 16 de largura na fileira mais larga virada para baixo ou para cima, e 13 virada para a esquerda ou para a direita. ↩↩↩ -
Medição do autor, 5 de outubro de 2026:
measure_ios27_iphones.pyna pasta de evidências do autor para este post, saídameasure_ios27_iphones.out, reexecutado às 12:36, horário do Pacífico, com a saída inalterada. Ele lêxcrun simctl list runtimes -jpara o runtime do simulador do iOS 27.0 (24A434) instalado com o Xcode 27.0 no Mac do autor, e ocapabilities.plistde cada tipo de dispositivo iPhone (main-screen-width,main-screen-height,main-screen-scale): 33 iPhones, do iPhone 12 mini e 13 mini (1.080 por 2.340 pixels a 3×, 360 por 780 pontos) ao iPhone 16, 17 e 18 Pro Max (440 por 956 pontos), com três a 2×, o iPhone SE (2ª e 3ª geração, 375 por 667 pontos) e o iPhone 11 (414 por 896). Essa é a lista de dispositivos do simulador, não a página de compatibilidade da Apple, que não foi buscada. Também do arquivo de projeto do Kiradex no commit334fbfe, só lido: oTARGETED_DEVICE_FAMILY = 1(iPhone) do target do app nas duas configurações dele (linhas 1559 e 1705 na leitura das 12:28) e oIPHONEOS_DEPLOYMENT_TARGET = 27.0nas duas configurações do projeto (linhas 1608 e 1670), que o target do app não sobrescreve. ↩↩↩↩↩↩↩ -
Medição do autor, 5 de outubro de 2026:
probe_line_start_glyphs.swiftna pasta de evidências do autor para este post, compilado comswiftc -Oe executado no macOS 27.0 (build 26A428), não no iOS, saídaprobe_line_start_glyphs.out. Ele lê as dez classes de início de linha do JLREQ 3.1.7 nas tabelas do apêndice da especificação salva (83 entradas: 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), pergunta ao Core Text quais PixelMplus10 Regular mapeia (CTFontGetGlyphsForCharacters, sem fallback): 54, com 〜 U+301C, ゠ U+30A0 e a meia-risca de cl-03, ⦆, 〙 e 〗 de cl-02, ‼ ⁇ ⁈ ⁉ de cl-04, 〻 de cl-09 e 18 kana pequenos de cl-11 (ゕ, ゖ e U+31F0 a U+31FF) sem mapeamento. Ele desenha todos os 7.223 pontos de código que a fonte mapeia a 10 pixels, com o antialiasing e a suavização de fontes desligados, e agrupa as células idênticas pixel a pixel: das 54, ‐ U+2010 coincide com U+2011 e : U+003A coincide com U+A789, e nenhuma outra tem gêmeo. A 50 pixels (m= 5), com o antialiasing ligado e a suavização desligada, ー U+30FC desenha 200 pixels de dispositivo com tinta, com um gancho na ponta esquerda, e 一 U+4E00, 250, não idênticos, ambos com 0 pixels parciais. Cada caractere ASCII imprimível avança 5 pixels de fonte. Ampliado mais tarde em 5 de outubro, com o script e a saída anteriores guardados ao lado e cada linha anterior da saída inalterada: os 24 caracteres do bloco Halfwidth and Fullwidth Forms, U+FF01 a U+FFEF, cuja normalização de compatibilidade (NFKC) é um dos caracteres listados (! ) , . : ; ? ] } ⦆ 。 」 、 ・ ァ ィ ゥ ェ ォ ャ ュ ョ ッ ー), e ~ U+FF5E, acrescentado pelo autor: a fonte mapeia 24 dos 25, todos menos ⦆ U+FF60, os de largura total com 10 pixels de largura e os de meia largura com 5, e nenhum dos 24 tem um gêmeo idêntico pixel a pixel entre os pontos de código da fonte, então o conjunto de comparação tem 75 caracteres: as 54 entradas contêm 51 caracteres, já que ), 〕 e ] estão listados tanto em cl-02 quanto em cl-29, e as 24 formas completam 75 (a própria última linha da sonda, 54 + 24 = 78, conta entradas). ↩↩↩↩ -
Medição do autor, 15 de outubro de 2026:
measure_cell_masks.pyna pasta de evidências do autor para este post, saídameasure_cell_masks.oute a lista de resumoscanon_cell_masks.txt, executado sobre os arquivos do pret salvos para este post (graphics/fontsdo pokeemerald, 17 folhas, egraphics/text_window, 21;gfx/framesdo pokecrystal, 10; osgfx/font/font.pngdo pokecrystal e do pokered). Cada folha é decodificada em RGBA; um pixel é ligado onde difere do pixel superior esquerdo da folha; as células têm 8 por 8 a partir do canto superior esquerdo; células vazias e sólidas ficam de fora; o resumo de uma máscara é o SHA-256 dos 8 bytes de fileira dela, com o pixel mais à esquerda como bit mais alto.japanese_normal.png, 128 por 512 pixels: 1.024 células, 430 vazias, 594 mantidas, 478 distintas. Todas as 50 folhas: 7.746 células mantidas, 1.484 resumos distintos. ↩↩ -
Blake Crosley, “Pixel-Art People: Characters and a Creator on iPhone”, blakecrosley.com, 3 de outubro de 2026, lido em 5 de outubro de 2026: para a figura dos builds 21 a 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”; na atualização para o build 34, “The cell is 32 by 40 now, with eight rows of headroom and two under the feet, and the figure 30 pixels”; e as âncoras de cabeça e mão do manifesto para o balão e a carta 3D, “though both still take their place from the cell’s height for now”, https://blakecrosley.com/blog/pixel-art-characters-on-iphone. ↩↩
-
Leitura do autor da árvore de trabalho do Kiradex no commit
fc9d4876(refs/heads/main), 15 de outubro de 2026 às 22:29, horário do Pacífico:Kiradex/World/PlazaStage.swift(modificado às 17:13 daquele dia), linha 373 (.modifier(StageAccessibility(covered: covered, trace: stepTrace))), linha 520 (private struct StageAccessibility: ViewModifier), linhas 511 a 513 (o comentário: o palco é um único elemento de acessibilidade, “The plaza” com o último passo do jogador como valor, ou nenhum enquanto um lugar está sobreposto a ele), linha 525 (content.accessibilityRepresentation {), linha 526 (if !covered {), linha 533 (.accessibilityLabel("The plaza")) e linha 534 (.accessibilityValue(trace)). As outras leituras do Kiradex no post são de 5 de outubro e estão datadas. ↩ -
Código do Kiradex,
server/app/npcs.py, lido às 12:33, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfe(o arquivo com data de 3 de outubro; repositório privado): os quatro colecionadores da cidade deDEFINITIONS(linhas 36 a 58), Moss Keeper 1999, Prism Scout 2024, Comet Pocket 3030 e Dawn Sprout 4040, cujas linhas são índices emLINES; cada um diz a primeira linha entre 8 e 20 segundos depois (linha 77) e depois uma a cada 20 a 40 segundos (linha 108).server/app/main.py, linha 231, transmite a linha de um jogador para a sala inteira, incluindo o próprio jogador (oexcept_iddebroadcastnão está definido;rooms.py, linha 182), lido às 12:33. ↩↩ -
Código do Kiradex,
KiradexUITests/KiradexUITests.swift, lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado); o arquivo tinha horário de 10:32 e não mudou durante a leitura: linha 228 (ome-handleda aba Me contém o apelido, duas palavras e um número),testTheCollectorFacesTheWayItWalksa partir da linha 432 (lê o valor do palco para cada direção),testThePlazaKeepsTimea partir da linha 502 (lêplaza-statuse confere o prefixo, linha 509),testTheChimneysSmokea partir da linha 523 (iniciado com-airProbe, espera ovalue CONTAINS 'smoke 18 'do palco, linha 531),testVisitingARoomIsAVisit(linhas 469 a 472) etestYourRoomHasYourBinders(linhas 604 a 607), que esperam as primeiras palavras deroom-status,testTheShowHallHasEveryonesCards(linhas 490 a 494), que esperahall-status,testTheTownHasCollectors(linhas 672 a 676), que espera emplaza-statusuma contagem de cinco ou mais “here”, etestThePlazaHasCompanya partir da linha 689 (pulado semKIRADEX_PLAZA=1, o servidor, e pela mensagem de pulo também precisa de outro jogador, linhas 690 e 691; espera a contagem emplaza-status, escolhe “Hi!” no menu Say e espera 5 segundos pelo balãoSaid: Hi!, linha 714). ↩↩↩↩ -
Código do Kiradex,
server/requirements.txt(fastapi,uvicorn[standard],websockets,httpxepytest, nenhuma biblioteca de fontes) eserver/tests/(test_rooms.py), lido às 12:28, horário do Pacífico, em 5 de outubro de 2026, no commit334fbfecom as edições não commitadas daquele dia, de uma árvore de trabalho que estava sendo editada naquele dia (repositório privado). ↩ -
Blake Crosley, “Pixel-Art Routes: Map Connections and Districts on iPhone”, blakecrosley.com, 5 de outubro de 2026, seção 7, verificação 13 (uma lista de bloqueio no repositório, com o teste dela, que cada nome de área, cada placa e cada string precisam passar), https://blakecrosley.com/blog/pixel-art-world-beyond-the-plaza. ↩