Interfejsy w pixel art: pola tekstowe, czcionki i japoński
Pole dialogowe w Pokémon Emerald to ramka 30 na 6 kafelków, na całą szerokość ekranu, 240 pikseli, i na 30% jego wysokości, wokół okna tekstu 27 na 4 kafelki, które mieści dwie linie 16-pikselowej czcionki; pole w Crystal ma 20 na 6 kafelków, dwie linie po 18 znaków.123 Emerald wypisuje znak co 8, 4 lub 1 klatkę zależnie od ustawienia tempa, czyli 7,5, 15 lub 60 znaków na sekundę przy 60 hercach; szybkie ustawienie Crystal i Final Fantasy VI również wypisują jeden znak na klatkę.14 Scenarzyści Emerald łamali każdą linię ręcznie: w 24 100 liniach tekstu skryptów najszersza ma 208 pikseli przy oknie o szerokości 216 pikseli i żadna nie wychodzi poza okno.1 Japońska czcionka pikselowa, która obejmuje standardowy zestaw, rysuje około siedmiu tysięcy znaków: JIS X 0208 liczy 6879 znaków, Misaki rysuje je wszystkie w komórce 8 na 8, PixelMplus10 wszystkie poza trzema punktami kodowymi na siatce 10-pikselowej, a producent Final Fantasy Pixel Remaster określił nową japońską czcionkę swojego zespołu na „around 7,500 characters” (około 7500 znaków).5678 Na moim Macu, przy wyłączonym wygładzaniu czcionek, Core Text narysował pikselowe czcionki TrueType, których siatka leży na linii bazowej, a przesunięcia są całkowitopikselowe (Misaki, PixelMplus10 i Press Start 2P), bez żadnych częściowych pikseli przy całkowitych wielokrotnościach ich siatki, a między nimi z rozmazaniem od 19,4% do 82,2% pikseli z tuszem; PixelMplus12, którego siatka jest przesunięta względem linii bazowej, Silkscreen, z zabłąkanymi punktami konturu, i DotGothic16, bez siatki, wyszły z częściowymi pikselami w każdym zmierzonym rozmiarze, a k8x12 w każdym rozmiarze, gdy tylko glify ułożono dokładnie; przy domyślnym wygładzaniu kontekstu rysowania nawet PixelMplus10 w swoich własnych 10 pikselach dał 178 częściowych z 272 pikseli z tuszem.79 Przy 3× em o wysokości 10 pikseli trafia w całkowite piksele urządzenia tylko w jednym z dwunastu rozmiarów Body w iOS, 40 punktach.710 Kiradex World składa dziś każde słowo systemowym krojem o stałej szerokości, mówi tylko po angielsku i podaje VoiceOver współrzędne z testów chodu.11 Zmierzyłem więc w dekompilacjach pola, czcionki i tempa kanonu, zmierzyłem w Core Text pięć japońskich i dwie łacińskie czcionki pikselowe i napisałem brief: jeden licencjonowany krój dla obu języków, Dynamic Type zaokrąglany do siatki czcionki, ramka narysowana przez kuźnię, dwie linie i strony oraz scena, którą VoiceOver potrafi opisać.
W skrócie
- Pole to dolna jedna trzecia ekranu i dwie linie. Ramka dialogowa Emerald ma 30 na 6 kafelków (240 na 48 pikseli, 30% wysokości ekranu) wokół okna 27 na 4 kafelki z dwiema 16-pikselowymi liniami; pole w Crystal ma 20 na 6 kafelków, 33% wysokości, dwie linie po 18 znaków, a w Red takie samo, 20 na 6. Pole w Emerald nie ma rubryki na imię, a 934 z jego 14 412 akapitów skryptu (6,5%) podaje mówiącego w samym tekście.12312
- Tempo to liczba klatek. Emerald i FireRed potrzebują 8, 4 lub 1 klatki na znak (7,5, 15 lub 60 na sekundę przy 60 hercach) oraz jednej klatki, gdy A lub B jest przytrzymany po naciśnięciu w trakcie wypisywania; Crystal i Red czekają 5, 3 lub 1 klatkę (12, 20 lub 60); Final Fantasy VI rysuje dialogi w świecie gry po jednej literze na klatkę, bez żadnego ustawienia (jego sześciostopniowe tempo komunikatów określa, jak długo czeka komunikat w walce). Strzałka przejścia dalej w Emerald przesuwa się o 0, 1, 2 i 1 piksel od pozycji spoczynkowej, krok co 9 klatek, w cyklu 0,6 sekundy; ▼ w Crystal miga na zmianę z kafelkiem obramowania.1131415164
- Pięć darmowych czcionek już rysuje japoński, którego potrzebuje gra. Misaki rysuje wszystkie 6879 znaków JIS X 0208 w 8 na 8, każde kanji w obrębie 7 na 7; k8x12 w 8 na 12; PixelMplus12 i PixelMplus10 rysują wszystkie poza trzema punktami kodowymi w 12 i 10 pikselach; DotGothic16 rysuje wszystkie, ale nie na siatce pikseli. Trzyzdaniowa licencja Misaki, k8x12 i PixelMplus pozwala używać, kopiować i rozpowszechniać czcionki „either commercially or noncommercially” (komercyjnie lub niekomercyjnie).5671718
- Czcionka pikselowa jest ostra tylko przy całkowitej liczbie pikseli urządzenia na piksel czcionki i tylko wtedy, gdy plik jest czysty. Zmierzone w Core Text na macOS 27.0, nie na iPhonie, przy wyłączonym wygładzaniu czcionek: 0,0% częściowych pikseli przy całkowitych wielokrotnościach siatki, która leży na linii bazowej i ma całkowitopikselowe przesunięcia (Misaki, PixelMplus10, Press Start 2P), od 19,4% do 82,2% między nimi.7 Przy domyślnym wygładzaniu kontekstu rysowania PixelMplus10 w swoich własnych 10 pikselach dał 178 częściowych z 272 pikseli z tuszem, więc czysty wynik przy całkowitych wielokrotnościach obowiązuje tylko przy wyłączonym wygładzaniu.9 Siatka PixelMplus12 leży 0,39 piksela poniżej linii bazowej i przy wyłączonym wygładzaniu rozmazuje się w każdym rozmiarze; przesunięcie kany w k8x12, 8,01 piksela, rozmazuje się, gdy tylko glify zostaną ułożone dokładnie.719
- Dynamic Type trzeba zaokrąglać, a nie za nim podążać. Z dwunastu rozmiarów Body w iOS, od 14 do 53 punktów, em 10-pikselowy jest całkowity przy 3× tylko w 40 punktach, em 8-pikselowy w 16 i 40, a em 12-pikselowy w 16, 28 i 40. Apple prosi, by czcionka niestandardowa implementowała zachowania czcionek systemowych, w tym Dynamic Type, oraz by tekst aplikacji dało się w miarę możliwości powiększyć co najmniej o 200 procent.71020
- Japońskie łamanie linii jest ścisłe bez znacznika języka i luźne ze znacznikiem. W Core Text na macOS akapit o długości 131 znaków złamał się tak, że żadna linia nie zaczynała się od znaku z listy początków linii używanej przez sondę (listy częściowej, złożonej głównie ze znaków JLREQ, z siedmioma w formach pełnej szerokości), w żadnej zmierzonej szerokości, bez znacznika; ze znacznikiem
jamała kana otwierała linię w 6 z 7 szerokości w Hiragino Sans, Misaki, PixelMplus i DotGothic16, czyli według mniej ścisłej reguły, którą opisują wymagania W3C dotyczące składu japońskiego.721 - Co to daje Kiradex. Brief, nie gotową wersję: PixelMplus10 dla każdego słowa rysowanego nad światem (z dwóch zmierzonych krojów z japońskim, czystą siatką i całkowitopikselowymi przesunięciami, Misaki i PixelMplus10, ten większy), rozmiary zaokrąglone do całkowitej liczby pikseli urządzenia na piksel czcionki (16,67 punktu w rozmiarze domyślnym na iPhone’ach 3×, 15 na 2×), własna ramka narysowana przez kuźnię, dwie linie i strony, odsłanianie z prędkością 30 znaków na sekundę ze znacznikiem poruszającym się skokowo, jednoliniowe dymki dobrane tak, by mieściły się na najwęższym iPhonie z listy symulatora iOS 27.0, gdy jego boki nie mają marginesów, linia, która się nie mieści, trafia do pola, japońskie linie przejrzane przez osobę mówiącą po japońsku oraz scena, którą VoiceOver opisuje słowami zamiast współrzędnymi, każdy punkt ze sprawdzeniem, które skrypt, linia logu lub zrzut mogą zaliczyć albo oblać, z wyjątkiem jednego ręcznego sprawdzenia VoiceOver.2211
1. Emerald i FireRed: ramka na całą szerokość, dwie linie, pisane ręcznie
Ta seria zaczęła się od świata i posuwała się do wewnątrz. Światy pixel art na iPhonie ustaliły system kafelków, przepis na RealityKit i kierunek dla tekstu w jednej regule, „Text is art” (tekst to grafika), oraz w jednym zdaniu planu: „m5x7 at integer multiples in a 24-by-24 nine-slice box, two lines, typewriter reveal, a two-frame advance arrow” (m5x7 w całkowitych wielokrotnościach w polu nine-slice 24 na 24, dwie linie, odsłanianie jak na maszynie do pisania, dwuklatkowa strzałka przejścia dalej).23 Kolejne artykuły zbudowały kolekcjonera, budynki, ruch, własne życie miasteczka, pokoje i drogi. Tekst został na etapie tamtego zdania. Ten artykuł mierzy, skąd biorą się jego liczby, znajduje cztery miejsca, w których artykuł o świecie się myli albo jest nieprecyzyjny, i dodaje to, czego tamten nie miał: japoński w rastrach 8, 10 i 12 kropek, to, co Core Text faktycznie robi z pikselową czcionką TrueType, sposób, w jaki Dynamic Type i siatka pikseli mogą się ze sobą zgodzić, japońskie łamanie linii oraz VoiceOver nad sceną RealityKit.
Tekst w Kiradex World jest dziś prosty. Każde słowo to SwiftUI w systemowym kroju o stałej szerokości, który ustawia motyw. Dymki nad głowami kolekcjonerów to jedna linia roli caption z zestawu, pogrubiona, na kremowym papierze z 2-punktowym obramowaniem w kolorze tuszu, widoczna, dopóki linia ma mniej niż 4 sekundy. Linia miejsca stoi w kremowym polu tekstowym u dołu ekranu. Aplikacja jest tylko po angielsku, predefiniowane linie żyją na serwerze po angielsku, szyld kuźni to deska 12 na 11 z tuszowymi bazgrołami i bez liter, a VoiceOver czyta scenę jako jeden element, którego wartością jest ślad kroków z testów chodu.11242526272829 Szczegóły podaje sekcja 10; brief w sekcji 12 jest napisany jako diff względem tego stanu.
Każdą liczbę dotyczącą Pokémonów zmierzyłem na płytkich klonach dekompilacji pret, w pokeemerald 731ad5b (1 października 2026), pokefirered 037335f, pokecrystal 5beda23 i pokered d2704a6, jednym skryptem w Pythonie, measure_gen_text.py, którego wynik leży zapisany obok; liczby Final Fantasy VI pochodzą z drugiego skryptu, uruchomionego na deasemblacji everything8215/ff6. Oba uruchamiają się ponownie w kilka sekund, a przypisy podają plik źródłowy za każdą liczbą.3014
Czcionki
Emerald deklaruje swoje czcionki w jednej tabeli, sFontInfos w src/text.c, każdą z maksymalną szerokością i wysokością litery w pikselach. Mała czcionka ma 5 na 12; normalna, której używa pole dialogowe, 6 na 16; krótka 6 na 14, zadeklarowana czterokrotnie (ona sama i trzy kopie); wąska 5 na 16; mała wąska 5 na 8; pogrubiona 8 na 8; a brajlowska 8 na 16 z 8 pikselami interlinii. Wszystkie pozostałe mają interlinię 0: 16-pikselowa komórka jest linią.113
Normalna czcionka łacińska jest proporcjonalna. Po zindeksowaniu przez charmap.txt gry do gFontNormalLatinGlyphWidths w src/fonts.c małe litery od a do z mają średnio 5,77 piksela szerokości, od 4 do 6; wielkie litery od A do Z i cyfry od 0 do 9 mają wszystkie po 6; spacja i kropka po 3, a wykrzyknik 4.131 W tekście ciągłym daje to średnio 5,25 piksela na znak, zmierzone na każdej linii skryptu gry omówionej niżej.1
Tempo to liczba klatek
Tempo tekstu w Emerald to opóźnienie w klatkach. Trzy prędkości z ekranu opcji przekładają się przez sTextSpeedFrameDelays w src/menu.c na opóźnienia 8 (wolno), 4 (średnio) i 1 (szybko). AddTextPrinter w src/text.c zapisuje opóźnienie pomniejszone o jeden jako prędkość drukarki tekstu; drukarka ustawia licznik na tę zapisaną prędkość, gdy wypisuje znak, i odlicza go o jeden na klatkę, a następny znak wypisuje w klatce po tej, w której licznik doszedł do 0. Znak trwa więc tyle, ile samo opóźnienie: 8, 4 i 1 klatkę. Przy 60 hercach to 7,5, 15 i 60 znaków na sekundę.1213 To liczenie jest moim odczytaniem automatu stanów drukarki (AddTextPrinter i przypadek RENDER_STATE_HANDLE_CHAR w src/text.c), które skrypt sprawdza asercją, a nie uruchamia.1 Game Boy Advance nie działa dokładnie z częstotliwością 60 herców: GBATEK podaje jego klatkę jako „Total 228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz” (łącznie 228 linii, 16,743 ms, 280 896 cykli, około 59,737 Hz), z czego 160 linii jest widocznych, każda szeroka na 240 punktów, więc szybkie ustawienie daje na sprzęcie około 59,7 znaku na sekundę.3233
Przytrzymanie przycisku przyspiesza wolniejsze ustawienia. Nowe naciśnięcie A lub B w trakcie odliczania opóźnienia znaku, w komunikacie, który na to pozwala (gTextFlags.canABSpeedUpPrint), ustawia hasPrintBeenSpedUp. Od tej chwili drukarka sprawdza JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp i, gdy warunek jest spełniony, ustawia licznik na 0: jeden znak na klatkę, 60 na sekundę, tyle samo, ile w ustawieniu szybkim.131
Tekst przewijany automatycznie ma własne opóźnienie. Gdy gTextFlags.autoScroll jest włączone, drukarka ustawia licznik na 3 zamiast na zapisaną prędkość, czyli 4 klatki na znak, z wyjątkiem nagranych walk: warunek brzmi if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll).131 Komunikaty walki włączają automatyczne przewijanie w walkach przez kabel i w walkach nagranych, a polecenie skryptu messageautoscroll je włącza i wymusza średnie tempo.34 Opóźnienie 3 może więc dotyczyć innych okien walki w walkach przez kabel i automatycznie przewijanych komunikatów ze skryptów, ale nigdy walk nagranych, których komunikaty wypisują się z prędkością przekazaną drukarce: w przypadku komunikatów walki jest to tempo tekstu zapisane razem z nagraniem, z wyjątkiem nagranej walki przez kabel, której okno komunikatów dostaje 1, jak w walce przez kabel.341 W moim odczytaniu licznik działa tylko wtedy, gdy zapisana prędkość drukarki jest większa od 0, a okno komunikatów walki przez kabel dostaje opóźnienie 1, co przy włączonym automatycznym przewijaniu zostawia jego tekst na poziomie jednego znaku na klatkę.1334
Trzy kody łamania robią trzy różne rzeczy. \n przesuwa drukarkę do drugiej linii; \l czeka pod strzałką przejścia dalej na naciśnięcie, a potem przewija pole o jedną linię w górę; \p czeka tak samo, a potem czyści pole i zaczyna od góry.1335 Przy przewijaniu 16-pikselowa linia przesuwa się w górę o 1, 2 lub 4 piksele na klatkę zależnie od ustawienia tempa (sWindowVerticalScrollSpeeds), więc przewinięcie trwa 16, 8 lub 4 klatki.113
Tempo tekstu w każdej zmierzonej grze i dwa ustawienia proponowane w briefie; narysowane przez figures/make_figures.py na podstawie wyników pomiarów.
Strzałka
Gdy strona jest pełna, pole czeka pod małą strzałką. Emerald kopiuje komórkę strzałki 8 na 16 z pionowymi przesunięciami { 0, 1, 2, 1 } piksela (sDownArrowYCoords) i rysuje ją ponownie, gdy wyczerpie się jej opóźnienie, resetowane do 8. Daje to krok co 9 klatek i cykl 36 klatek, 0,60 sekundy przy 60 hercach.113 Strzałka nie miga; kołysze się, przez trzy pozycje i z powrotem.
Pole i jego ramka
Pole tekstowe Emerald w świecie gry definiuje sStandardTextBox_WindowTemplates w src/menu.c. Okno tekstu zaczyna się w kolumnie kafelków 2, w rzędzie 15, i ma 27 na 4 kafelki: 216 na 32 piksele, dwie linie 16-pikselowej czcionki normalnej. Jego ramka, rysowana przez WindowFunc_DrawDialogueFrame i wyliczona przez skrypt dla tego okna, biegnie od kolumny 0 do 29 i od rzędu 14 do 19: 30 na 6 kafelków, 240 na 48 pikseli, cała szerokość ekranu i 30% jego wysokości, wynoszącej 160 pikseli.12
Ta ramka nie jest nine-slice. LoadMessageBoxGfx ładuje ją z message_box.png, paska 56 na 16 pikseli, 14 kafelków, a ramka powstaje z ośmiu pozycji kafelków, przy czym jej dolny rząd to górny rząd odwrócony w pionie przez BG_TILE_V_FLIP.1362 Skrypt mierzy zasięg ramki i rozmiar pliku PNG; odwrócenie odczytano z wywołań wypełniających.1
Ramki wybierane przez gracza dotyczą innych okien: menu i okienek tak lub nie. WINDOW_FRAMES_COUNT wynosi 20, a dwadzieścia ramek to pliki PNG 24 na 24 piksele, trzy na trzy kafelki po 8, ładowane przez LoadUserWindowBorderGfx według rodzaju ramki zapisanego przez pozycję FRAME na ekranie opcji.136
To koryguje artykuł o świecie. Jego reguła 10 mówi: „Pokémon Emerald’s text-window frame is a 24-by-24 image, three by three hardware tiles, which is a nine-slice” (ramka okna tekstu w Pokémon Emerald to obraz 24 na 24, trzy na trzy kafelki sprzętowe, czyli nine-slice).23 Dwadzieścia obrazów 24 na 24 w graphics/text_window/ to wybieralne ramki okien menu. Pole dialogowe, to pole tekstowe, które pamiętają gracze, rysuje się z 56 na 16 pikseli pliku message_box.png jako ramkę 30 na 6 kafelków z odbitą dolną krawędzią, a nie jako nine-slice obrazu 24 na 24.136 Idea nine-slice pozostaje w mocy i menu z niej korzystają; przypisanie jej polu dialogowemu już nie.
Dwa pola dialogowe jako siatki kafelków, bez grafik z gier; narysowane przez figures/make_figures.py, który sprawdza każdy zasięg względem measure_gen_text.out.
Tekst pisano pod pole
Aby zobaczyć, jak bardzo pole jest zapełnione, skrypt przeczytał każdą linię tekstu skryptów Emerald: 504 pliki, 36 z data/text/*.inc i scripts.inc 468 map, 24 100 linii bez symbolu zastępczego i 3000 z nim, podzielone przy trzech kodach łamania gry, \n, \l i \p. Mediana linii to 29 znaków i 150 pikseli; 95. percentyl to 38 znaków i 196 pikseli; najdłuższa linia ma 53 znaki, najszersza 208 pikseli. Żadna nie jest szersza niż okno o szerokości 216 pikseli, a przeciętny znak ma 5,25 piksela szerokości.137
Przy 5,25 piksela na znak okno mieści 41 znaków; scenarzyści zatrzymali się na 38 w 95% linii.331 Każde złamanie to ręcznie wstawiony kod. Gra nigdy nie zawija linii; ufa piszącemu.
Pole ma jedno okno i nie ma rubryki na imię. Gdy scenarzyści chcieli wskazać mówiącego, wpisywali imię i dwukropek w samą linię, jak w „MOM: {PLAYER}, we’re here, honey!” (MAMA: {PLAYER}, jesteśmy na miejscu, kochanie!) z Littleroot Town. Z 14 412 akapitów 934, czyli 6,5%, zaczyna się od imienia pisanego wielkimi literami i dwukropka.137 To, że nie ma osobnej rubryki na imię, jest moim odczytaniem szablonu okna, w którym jest miejsce na dwie linie tekstu i nic więcej.2
Japoński w angielskiej grze
Angielski Emerald wciąż zawiera japońskie glify, na potrzeby imion stworzeń i trenerów pochodzących z japońskich kartridży. Drukarka przełącza zestaw glifów kodem sterującym EXT_CTRL_CODE_JPN i wraca kodem EXT_CTRL_CODE_ENG, które takie imiona zawierają (wstawiają je international_string_util.c, contest.c i battle_tower.c). W tym trybie normalna czcionka ma 8 pikseli szerokości dla każdego znaku i 15 wysokości (DecompressGlyph_Normal, a GetGlyphWidth_Normal zwraca 8).11338
Arkusze, z których buduje się angielski ROM: japanese_normal.png ma 128 na 512 pikseli w komórkach 8 na 16, z czego narysowano 302 z 512 komórek; japanese_small.png ma ten sam rozmiar, narysowanych 307; japanese_bold.png 128 na 256, narysowanych 246 z 256; a japanese_frlg_male.png z FireRed, również w drzewie Emerald, 256 na 512 pikseli w komórkach 16 na 16, narysowanych 269 z 512, z tabelą szerokości o 512 pozycjach od 0 do 10 pikseli, przy czym 10 występuje w 408 z nich.138 pret buduje angielskie wersje gier, więc to arkusze imion w angielskim ROM-ie; nie odczytano własnego pola, temp ani czcionki żadnego japońskiego ROM-u (sekcja 11).30
W kategoriach samego kanonu japoński znak w 16-pikselowej linii ma więc 8 pikseli szerokości w Emerald i przeważnie 10 w arkuszu FireRed, wobec łacińskiej litery o szerokości od 4 do 6.1
FireRed
FireRed zachowuje opóźnienia Emerald, 8, 4 i 1, zapisywane pomniejszone o jeden przez własny AddTextPrinter w src/text_printer.c, tak jak w Emerald, oraz standardowe okno tekstu 26 na 4 kafelki w kolumnie 2 (src/new_menu_helpers.c), o jeden kafelek węższe niż w Emerald.139 Jego drukarka ma też przytrzymanie z Emerald: if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp) ustawia licznik na 0, a nowe naciśnięcie A lub B w trakcie odliczania opóźnienia znaku, w komunikacie, który na to pozwala, ustawia hasPrintBeenSpedUp.14
2. Crystal i Red: osiemnaście znaków i migający znacznik
Pole z Game Boya jest przodkiem pola z Game Boya Advance w niższej rozdzielczości. Ekran Crystal ma 20 na 18 kafelków (SCREEN_WIDTH 20, SCREEN_HEIGHT 18), 160 na 144 piksele. Jego pole tekstowe to TEXTBOX_WIDTH = SCREEN_WIDTH na TEXTBOX_HEIGHT 6: cała szerokość i jedna trzecia wysokości, 33%. Przy BORDER_WIDTH 2 wnętrze ma 18 na 4 kafelki, a czcionka 8 na 8 daje 18 znaków w linii. Tekst zaczyna się dwa rzędy w głąb pola, TEXTBOX_INNERY = TEXTBOX_Y + 2, a linie dzielą dwa rzędy, więc pole pokazuje dwie linie z pustym rzędem pomiędzy; układ dwuliniowy jest moim odczytaniem tych stałych.13 Pole komunikatów w Red to db MESSAGE_BOX, 0, 12, 19, 17: te same 20 na 6 kafelków u dołu.112
Obie gry również liczą tempo w klatkach. Opóźnienia to TEXT_DELAY_FAST 1, średnie 3 i wolne 5 klatek na literę: 60, 20 i 12 liter na sekundę przy 60 hercach. PrintLetterDelay w Crystal mówi to wprost: „The text speed setting in wOptions is actually a frame count” (ustawienie tempa tekstu w wOptions to w rzeczywistości liczba klatek). Gdy A lub B jest przytrzymany, czeka jedną klatkę na literę.115 Szybkie ustawienie Crystal odpowiada więc szybkiemu w Emerald, 60 na sekundę, a przytrzymanie A lub B w Emerald daje to samo tempo; średnie i wolne ustawienie Crystal, 20 i 12, są szybsze niż 15 i 7,5 w Emerald.1 Pan Docs podaje klatkę Game Boya jako „One frame: 70224 dots @ 59.7 fps” (jedna klatka: 70 224 punkty przy 59,7 kl./s) i zauważa, że klatka „takes ~16.74 ms instead of ~16.67” (trwa około 16,74 ms zamiast około 16,67).40
Crystal pozwala graczowi wybrać jedną z ośmiu ramek, od FRAME_1 do FRAME_8, każda to TEXTBOX_FRAME_TILES, 6 kafelków 1-bitowych, zapisanych jako pliki PNG 24 na 16 pikseli; dziewiąta ramka leży w ROM-ie nieużywana. Arkusz czcionki, gfx/font/font.png, ma 128 na 64 piksele, 128 komórek 8 na 8, a arkusz Red ma ten sam rozmiar.141
Znacznik miga
Znacznik przejścia dalej w Crystal jest częścią ramki. WaitPressAorB_BlinkCursor czeka na A lub B, a BlinkCursor podmienia kafelek w (18, 17), w dolnym rzędzie samej ramki, między '▼' a kafelkiem obramowania '─'.16 Oba stany odczytano z kodu; okresu migania nie wyprowadzono (sekcja 11).
To druga nieścisłość artykułu o świecie. Jego plan tekstu wymaga „a two-frame advance arrow” (dwuklatkowej strzałki przejścia dalej).23 Znacznik o dwóch stanach to miganie z Crystal. Strzałka z Emerald, ta, za którą idzie reszta tamtego planu (16-pikselowa linia, pole u dołu), nie miga: przechodzi przez trzy pozycje, 0, 1, 2 i 1 piksel od pozycji spoczynkowej, w cyklu 36 klatek (kod przesuwa rzędy źródłowe bitmapy, co w moim odczytaniu unosi klin na ekranie; nie uruchamiałem tego).113 Brief w sekcji 12 przejmuje kroki z Emerald, ale nie ich kierunek.
3. Final Fantasy VI: okno do przemalowania, czcionka, która sama się zawija
Final Fantasy VI traktuje okno jako własność gracza. Jego liczby pochodzą tu z deasemblacji everything8215/ff6 w 8130132 (14 września 2026), która zawiera kod i listy zrzutów zamiast grafik z ROM-u, więc rozmiary wynikają z zakresów bajtów i z kodu, który je odczytuje.304
Osiem okien, po siedem kolorów
Angielska i japońska lista zrzutów podają grafiki okien jako 0xED0000 do 0xED1BFF, 7168 bajtów: osiem stylów po 896 bajtów, każdy z 28 kafelków 8 na 8 w 4 bitach na piksel. Ich palety następują dalej, od 0xED1C00 do 0xED1CFF, osiem palet po 16 kolorów.442 Ekran konfiguracji wymienia 8 wzorów okna (ConfigWindowNumPos).443
Kolor okna ustala gracz. LoadWindowPal odczytuje kolory wzoru z pamięci zapisu pod $1d57, po 14 bajtów na wzór, czyli siedem kolorów po dwa bajty, a ekran konfiguracji edytuje je suwakami R, G i B (ConfigColorBarTextList); jeszcze jeden kolor, dla czcionki, leży pod $1d55.443 Krok odczytano z kodu, a trzy kanały z listy suwaków.4
Tempo komunikatów na ekranie konfiguracji ma sześć ustawień, od 1 do 6 (ChangeConfigOption_02 zatrzymuje się na $05), a nowy zapis gry zaczyna od „message speed = 3, battle speed = 3” (tempo komunikatów = 3, tempo walki = 3), jak ujmuje to komentarz w menu_sram.asm.443 Jest to tempo komunikatów walki, jak nazywa je komentarz w config.asm: „battle speed, battle message speed, magic order, and window pattern” (tempo walki, tempo komunikatów walki, kolejność magii i wzór okna). Poza ekranem konfiguracji jedynym kodem, który czyta jego bity w bajcie konfiguracji, $1d4d, jest mess_wait w src/btlgfx/gfx_cmd.asm, który odszukuje ustawienie w MsgSpeedTbl, „wait duration for each battle message speed” (czas oczekiwania dla każdego tempa komunikatów walki), i czeka tyle klatek: 32, 48, 64, 80, 96 lub 112 dla ustawień od 1 do 6. Żaden plik kodu świata gry w ogóle nie czyta $1d4d, więc dialogi w świecie gry nie mają ustawienia tempa; wypisują się w jednym tempie opisanym niżej.444
Cieniowane okno bez kafelka
Okno dialogowe jest cieniowane na całej wysokości przez sprzętową matematykę kolorów. DlgWindowFixedColorTbl w src/field/hdma.asm to 200 bajtów od $E0 do $E7, stały kolor we wszystkich trzech kanałach o natężeniu od 0 do 7, a jej pierwszy ciąg to 72 wartości schodzące od 7, potem do 0 i z powrotem do 7.445 Okno otwiera się przy górnej lub dolnej krawędzi ekranu, „dialog window y position (1 or 18)” (pozycja y okna dialogowego, 1 lub 18) w komentarzu w event.asm.45 To, że tabela jest zużywana po jednej wartości na linię skanowania, odczytano z komentarzy, nie prześledzono, a wyglądu gradientu nie widziano, bo deasemblacja nie zawiera pikseli (sekcja 11).
Czcionka
Czcionka dialogów ma 1 bit na piksel i 22 bajty na literę: 11 rzędów po 16 bitów, komórka szeroka na 16 pikseli i wysoka na 11. Jej cień nie jest rysowany: LoadLetterGfx tworzy go, przesuwając każdy rząd o jeden piksel (lsr).446 Angielskie szerokości z FontWidth są proporcjonalne: wielkie litery mają średnio 8,04 piksela (od 5 do 12), małe 7,73 (od 4 do 12), cyfry 7,80 (od 5 do 9), a spacja 5.446
Czcionka dialogów japońskiego ROM-u, od 0xC494C0 do 0xC4FFFF na jego liście zrzutów, zajmuje 27 456 bajtów: 1248 liter po 22 bajty. Jej tabela szerokości ma 1280 pozycji w ciągach 1056 po 12 pikseli, 180 po 8 i 44 po 12, a tabele znaków w deasemblacji wymieniają 1010 kanji i 173 kana.447 Zakres angielskiej dużej czcionki, 5120 bajtów, nie jest całkowitą liczbą 22-bajtowych liter (232,7), czego nie umiem wyjaśnić na podstawie deasemblacji (sekcja 11).4
Linia, zawijanie, litera na klatkę
Linia dialogu biegnie od x 4 do maksymalnego x równego $e0, czyli 224: 220 pikseli, 18 japońskich liter po 12 pikseli.446 Angielski tekst zawija się według słów w kodzie. CalcTextWidth dodaje szerokość następnego słowa, a NewLine jest wywoływane, gdy przekroczyłoby ono maksimum, więc angielski skrypt nie potrzebuje ręcznie wstawianych łamań; skrypt sprawdza wywołania asercją, a odczytanie dotyczy UpdateDlgTextOneLine.446
Tekst wypisuje się po jednej literze na klatkę. Pętla świata gry czeka na wygaszanie pionowe i wywołuje UpdateDlgText raz na przebieg, a UpdateDlgTextOneLine rysuje jedną literę i wraca: 60 liter na sekundę przy 60 hercach.448 Jeden przebieg na klatkę to moje odczytanie pętli. Ponieważ oba ROM-y powstają z jednego źródła, oba języki dzielą tę pętlę, co w moim odczytaniu oznacza tyle samo liter na sekundę po angielsku i po japońsku.48
Tytuł mapy jest wyśrodkowany, ($e0 - width) / 2, i wyświetlany przez 100 klatek, 1,67 sekundy.446
W zestawieniu z Emerald Final Fantasy VI pokazuje drugi sposób budowania tego samego pola: Emerald wpisuje każde łamanie ręcznie i pozwala graczowi wybrać ramkę menu, nigdy pola dialogowego; Final Fantasy VI zawija w kodzie, pozwala graczowi przemalować okno i cieniuje je sprzętowo. Pole Emerald w świecie gry stoi u dołu, okno Final Fantasy VI u góry albo u dołu, a obie gry wypisują tekst w stałej liczbie klatek.
Chrono Trigger
Chciałem zestawić Chrono Trigger z Final Fantasy VI i nie mogłem. Nie sklonowano żadnej publicznej deasemblacji, a mapa ROM-u tej gry w Data Crystal zwróciła przy pobieraniu HTTP 403, więc nic z tego, co ten artykuł mówi o polach tekstowych, nie pochodzi z tej gry (sekcja 11).49
4. Japoński w rastrach 8, 10 i 12 kropek
Kiradex to aplikacja dla kolekcjonerów kart, a Dex już pokazuje japońskie nazwy kart z angielskimi pod spodem, w czcionkach systemowych.50 Tekst świata też będzie potrzebował japońskiego, a czcionka pikselowa, która nie potrafi go narysować, odpada. Pytanie brzmi: jak małe może być kanji i kto narysował już siedem tysięcy znaków?
Skala zadania
JIS X 0208, zestaw znaków, względem którego mierzy się japońskie czcionki pikselowe, liczy 6879 znaków według tabeli odwzorowań Unicode Consortium: 524 znaki niebędące kanji, w tym 169 kana w rzędach 4 i 5; 2965 kanji poziomu 1 w rzędach od 16 do 47; oraz 3390 kanji poziomu 2 w rzędach od 48 do 84.576 Tabela ma wersję 2.0, a konsorcjum zalicza ją do przestarzałych odwzorowań; wciąż jest jednak listą, względem której najłatwiej policzyć pokrycie czcionki.5
Misaki: 8 na 8
Misaki (美咲フォント) autorstwa Num Kadomy jest najmniejsza. Jej strona nazywa ją „8×8 ドットの日本語ビットマップフォント” (japońską czcionką bitmapową 8 na 8 kropek) obsługującą „JIS 第一・第二水準” (poziomy 1 i 2 JIS) i mówi „一部の記号を除いた全ての文字は 7×7 ドットの範囲に収まっている” (wszystkie znaki poza niektórymi symbolami mieszczą się w obszarze 7 na 7 kropek), dzięki czemu, jak ciągnie strona, znaki ustawione obok siebie zachowują odstępy między liniami i literami.51 Według historii podanej na stronie powstała dla japońskiego sterownika wyświetlacza komputerów kieszonkowych PC-E500.51
Zmierzona na plikach BDF wydania 2021-05-05, Misaki Gothic ma 7171 znaków: 168 półszerokich, o szerokości 4 pikseli, i 7003 pełnoszerokie, o szerokości 8 pikseli, w komórce 8 na 8 z wydłużeniem górnym 6 i dolnym 2. Spośród jej 6356 glifów od U+4E00 do U+9FFF (to zakres kodów, stąd inna liczba niż 6355 kanji obu poziomów JIS) żaden nie wypełnia 8-kropkowej komórki w żadnym kierunku, natomiast robią to 53 glify pełnoszerokie, symbole. Rysuje wszystkie 6879 znaków JIS X 0208.6 Misaki Mincho, którą jej strona opisuje jako Misaki Gothic z przerysowanymi w stylu Mincho znakami niebędącymi kanji, daje takie same wyniki we wszystkich pomiarach. Misaki Gothic 2nd, która według strony wykorzystuje pełne 3 na 7 pikseli dla znaków półszerokich i 7 na 7 dla pełnoszerokich kana, ma 7170 znaków (167 półszerokich), wydłużenie górne 7 i dolne 1, a w pozostałych pomiarach wypada tak samo.651
k8x12: 8 na 12
k8x12 tego samego autora zachowuje szerokość 8 kropek i dodaje wysokości: „8×12 ドットの日本語ビットマップフォント” (japońska czcionka bitmapowa 8 na 12 kropek), poziomy 1 i 2 JIS. Ma dwa warianty: k8x12L, z wysoko rysowanymi kana, i k8x12S, w którym znaki niebędące kanji mają 8 kropek wysokości, zrobiony, jak mówi strona, z myślą o ograniczeniach starych konsol: „8×8 ドット単位でグラフィックを扱うような、昔のゲーム機等の制限を意識しています” (z uwzględnieniem ograniczeń dawnych konsol, które obsługiwały grafikę w jednostkach 8 na 8 kropek).52
Zmierzona na pliku BDF, k8x12 to komórka 8 na 12 z wydłużeniem górnym 10 i dolnym 2: 7170 znaków, 168 po 4 piksele i 7002 po 8. Żadne kanji nie wypełnia komórki; wypełniają ją 53 glify pełnoszerokie w k8x12 i k8x12L oraz 45 w k8x12S. Wszystkie trzy rysują wszystkie 6879 znaków JIS X 0208.6
PixelMplus: 10 i 12, jako kontury
PixelMplus autorstwa itouhiro (2013) to drugie podejście: kontury TrueType, które wyglądają jak czcionka bitmapowa, bez osadzonych bitmap („埋め込みビットマップは なし”, brak osadzonych bitmap), ze wszystkimi kanji poziomów 1 i 2 JIS („JIS第1・第2水準のすべての漢字を収録”, zawiera wszystkie kanji poziomów 1 i 2 JIS) oraz ISO-8859-1, w odmianie zwykłej i pogrubionej i w dwóch rozmiarach, PixelMplus12 i PixelMplus10, obrysowane z M+ BITMAP FONTS.18 Zmierzona w Core Text, każda ma 7253 glify i rysuje 6876 z 6879 znaków JIS X 0208. Trzy, których nie odwzorowuje, to U+301C 〜, U+2016 ‖ i U+2212 −.7 Są to znaki, których odwzorowanie w Unicode różni się między JIS0208.TXT konsorcjum a tabelami innych producentów, więc w moim odczytaniu czcionki prawdopodobnie mają te glify pod innymi punktami kodowymi; tego nie sprawdzono (sekcja 11).5
DotGothic16: czcionka z kropek bez siatki
DotGothic16 firmy Fontworks jest dostępna w Google Fonts na licencji SIL Open Font License 1.1 bez Reserved Font Name, z podzbiorami cyrillic, japanese, latin i latin-ext, dodana 2020-12-15.53 Ma 9362 glify i rysuje wszystkie 6879 znaków JIS X 0208.7 Jest jednak narysowana jako oddzielne kropki, a nie jako piksele. W jej A krawędzie konturu w osi x występują parami, 91 i 102, 153 i 163, 214 i 225, 276 i 287, 338 i 349, 400 i 410 jednostek z 1000: kropki oddalone o około 62 jednostki, z przerwami po 10 lub 11 jednostek. Żadna siatka całkowitych pikseli do tego nie pasuje, a sonda nie znalazła żadnej w zakresie od 4 do 40 pikseli na em.197 Skok około 62 jednostek odpowiada mniej więcej 16 kropkom na em, liczbie z nazwy kroju, więc sonda rysuje go w nominalnych 16 kropkach na em i w ich wielokrotnościach; to mój wybór rozmiaru, a nie siatka deklarowana przez krój. Core Text na macOS 27.0 narysował go z 82,3% częściowych pikseli w 16 pikselach i 37,3% w 64, z częściowymi pikselami w każdym zmierzonym rozmiarze (sekcja 6).7
Co mierzą darmowe czcionki
Siatka projektowa w tabeli to najmniejsza liczba pikseli na em, na której 95% punktów konturu próbki glifów leży w odległości do 0,04 piksela, przy czym każda oś może mieć jedno stałe przesunięcie; skrypt, który ją znajduje, measure_coretext.swift, skompilowano z swiftc -O i uruchomiono na macOS 27.0 (kompilacja 26A428), więc są to liczby z Core Text na Macu, a nie na iPhonie.7
| Czcionka | Glify | Tabele bitmap | Siatka projektowa (pikseli na em) | Przesunięcie łacińskie / kana (piksele) | Narysowane z JIS X 0208 |
|---|---|---|---|---|---|
| Misaki Gothic | 7174 | EBDT, EBLC | 8, na linii bazowej | 4 / 8 | 6879 |
| k8x12 | 7173 | EBDT, EBLC | 12, na linii bazowej | 4 / 8,01 | 6879 |
| PixelMplus10 Regular i Bold | po 7253 | brak | 10, na linii bazowej | 5 / 10 | 6876 |
| PixelMplus12 | 7253 | brak | 12, 0,39 piksela poniżej linii bazowej | 6 / 12 | 6876 |
| DotGothic16 | 9362 | brak | brak (rysowana w nominalnych 16 kropkach) | 8 / 16 nominalnych kropek | 6879 |
W tej mierze PixelMplus10 Regular ma 0 z 1032 próbkowanych współrzędnych konturu poza swoją 10-pikselową siatką, a Bold 0 z 1000; Misaki Gothic 0 z 848; k8x12 0 z 988; PixelMplus12 0 z 1192 po uwzględnieniu przesunięcia o 0,39 piksela, a DotGothic16 wszystkie 1544 poza siatką.7 Przesunięcie PixelMplus12 widać w surowych współrzędnych: jego H biegnie od y −33 do 717 jednostek z 1000, na siatce 83,3 jednostki na piksel, więc linia bazowa wypada 0,396 piksela w głąb rzędu siatki; szacunek sondy, 0,39, pochodzi z rozłożenia części ułamkowych współrzędnych na przedziały o szerokości jednej pięćdziesiątej piksela i to o tę wartość przesunięto rendery opisane niżej.19733
W każdej zmierzonej czcionce z japońskim znak japoński jest dwa razy szerszy od łacińskiego: 8 wobec 4 w Misaki i k8x12, 10 wobec 5 i 12 wobec 6 w PixelMplus.7 Kanon mieści się w tym samym zakresie: japoński w Emerald ma 8 pikseli szerokości w 16-pikselowej linii, w arkuszu FireRed przeważnie 10, w Final Fantasy VI 12.14
Licencje
Wszystko to jest darmowe w najprostszych słowach. Licencja Misaki i k8x12 na stronie z czcionkami autora to trzy zdania: „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.” (te czcionki są wolnym oprogramowaniem; udziela się nieograniczonego pozwolenia na ich używanie, kopiowanie i rozpowszechnianie, ze zmianami lub bez, komercyjnie lub niekomercyjnie; są dostarczane w obecnej postaci, bez gwarancji). Plik readme w każdym archiwum zawiera te same warunki w nieco innych słowach, a strona Misaki podaje, że używa licencji M+ FONTS.1751 PixelMplus jest objęty M+ FONT LICENSE, której angielski tekst, LICENSE_E, to te same trzy zdania.18 DotGothic16 jest objęta licencją SIL Open Font License 1.1, której warunki zawiera jej własny OFL.txt.53 W moim odczytaniu trzyzdaniowa licencja niczego w zamian nie wymaga; podanie autorów i tak jest właściwe i brief to robi (sekcja 12). To odczytania, nie porada prawna.
Ile kosztuje czytelność
Kana w Misaki ma 8 pikseli czcionki szerokości, w PixelMplus12 dwanaście. Przy gęstości samego świata na iPhonie 18 Pro Max, gdzie teksel to 8 pikseli urządzenia, linia pola narysowana w Misaki mieści 36 znaków łacińskich albo 18 japońskich.11 Japońska linia niesie więcej na znak, więc pole nie musi mieścić tylu znaków, żeby powiedzieć to samo; to moje odczytanie, nie pomiar.
5. Łacińskie czcionki pikselowe i te, których nie zdołałem zmierzyć
Obok japońskich zmierzono dwie łacińskie czcionki pikselowe z Google Fonts.30
Press Start 2P autorstwa CodeMan38 jest objęta licencją SIL Open Font License 1.1 „with Reserved Font Name “Press Start 2P”” (z zastrzeżoną nazwą czcionki “Press Start 2P”).54 Ma 658 glifów na 8-pikselowej siatce na linii bazowej, każdy znak łaciński o szerokości 8 pikseli, i wszystkie 95 drukowalnych znaków ASCII. Nie ma kana ani kanji: z JIS X 0208 rysuje 150 znaków niebędących kanji (swoją grekę, cyrylicę i symbole) i żadnego z 6355 kanji.7
Silkscreen autorstwa Jasona Kottke jest objęta licencją SIL Open Font License 1.1 bez Reserved Font Name.55 Ma 228 glifów na 8-pikselowej siatce, przy czym 10 z 588 próbkowanych współrzędnych konturu leży poza nią (jej A i o mają wartości y równe 495 i 130 jednostek na siatce 125-jednostkowej), a jej A ma 6 pikseli szerokości. Z JIS X 0208 rysuje 21 znaków niebędących kanji.719
Trzech kolejnych czcionek, często polecanych do gier pikselowych, nie zmierzono i mówię to otwarcie, zamiast opisywać je z pamięci.
Pixel Operator autorstwa Jayvee Enaguasa: notatki autora na DaFont mówią „Changed licence to Creative Commons Zero (CC0) 1.0” (zmieniono licencję na Creative Commons Zero, CC0, 1.0) oraz że krój jest „licensed under a Creative Commons Zero (CC0) 1.0” (objęty licencją Creative Commons Zero, CC0, 1.0). DaFont wymienia go jako „Bitmap (16 px)”, w 15 plikach czcionek, z 241 glifami. Własne repozytorium autora, które notatki podają w serwisie NotABug, zwróciło HTTP 404, a czcionki nie pobrano ani nie zmierzono. Wszystko, co tu napisano, pochodzi z tej jednej strony.56
m5x7 autorstwa Daniela Linssena: jej strona na itch.io mówi „use font size 16, 32, 48, etc” (należy używać rozmiaru 16, 32, 48 i tak dalej) oraz „free to use but attribution appreciated” (do swobodnego użytku, choć podanie autora jest mile widziane), wymienia „Asset license Creative Commons Zero v1.0 Universal” (licencja zasobu: Creative Commons Zero v1.0 Universal) i podaje średnią szerokość 5, wydłużenie górne 7, dolne 2 i wysokość x równą 5. Swoje znaki opisuje jako „uppercase and lowercase letters, including many accented letters, numbers and most common symbols” (wielkie i małe litery, w tym wiele liter ze znakami diakrytycznymi, cyfry i najczęstsze symbole).57
m3x6 tego samego autora: „use font size 16, 32, 48, etc” oraz „free to use with attribution” (do swobodnego użytku z podaniem autora); jej strona nie pokazuje pola licencji zasobu.58 Żadnego z tych plików nie pobrano, bo oba są za pobraniem w modelu „zapłać, ile chcesz”, więc żadnego nie zmierzono.5758
To trzecia nieścisłość artykułu o świecie. Jego plan tekstu wskazuje m5x7 jako krój.23 Według opisu na jej własnej stronie m5x7 to czcionka łacińska i nic na stronie nie mówi, że ma kana czy kanji; aplikacja, która będzie pokazywać japoński, nie może w niej składać tekstu swojego świata.57 Brief zmienia krój na PixelMplus10, jeden z dwóch zmierzonych krojów z japońskim, czystą siatką i całkowitopikselowymi przesunięciami (drugim jest Misaki), i większy z nich (sekcje 6 i 12). Samej m5x7 nadal nie zmierzono.
6. Co Core Text robi z czcionką pikselową
Artykuł o świecie twierdził, że „Bitmap fonts render at integer multiples only” (czcionki bitmapowe renderują się tylko w całkowitych wielokrotnościach).23 To reguła, którą powtarza każdy poradnik pixel art, a ja chciałem zobaczyć ją, z liczbami, w silniku tekstu samego Apple.
Pomiar
Core Text to warstwa Apple służąca do tego, by „Create text layouts, optimize font handling, and access font metrics and glyph data” (tworzyć układy tekstu, optymalizować obsługę czcionek i uzyskiwać dostęp do metryk czcionek i danych glifów), dostępna od iOS 3.2 i macOS 10.8.59 measure_coretext.swift ładuje każdy kandydujący plik TrueType, rysuje linię próbną („Nice card! Which set? 0123” dla czcionek łacińskich, „カードを見せて! Nice card!” dla japońskich) do 8-bitowej szarej bitmapy w rozmiarach będących całkowitymi i ułamkowymi wielokrotnościami siatki każdej czcionki i liczy piksele z tuszem, które są częściowe: od 8 do 247 z 255, czyli oddalone o ponad 3% od pełnego lub pustego. Każdy rozmiar rysuje na trzy sposoby: z domyślnym antyaliasingiem Core Text, z wyłączonym antyaliasingiem oraz z włączonym pozycjonowaniem subpikselowym i wyłączoną kwantyzacją, tak by glify trafiały dokładnie tam, gdzie stawiają je ich przesunięcia.7
Przed jakąkolwiek liczbą liczą się dwa ograniczenia. Przebieg to macOS 27.0, kompilacja 26A428, na moim Macu, nie iOS, i rysuje on przez Core Text do kontekstu bitmapy, a nie przez Text w SwiftUI; to, co narysuje iPhone, pozostaje przewidywaniem, dopóki sprawdzenia przez zrzuty z briefu nie zostaną na nim uruchomione (sekcja 11).7 Poza tym skrypt rysuje z wyłączonym wygładzaniem czcionek (setAllowsFontSmoothing(false)). Gdy narysowałem poniższą ilustrację z domyślnym ustawieniem kontekstu bitmapy, 178 z 272 pikseli z tuszem w „Nice card!” w PixelMplus10 przy 10 pikselach wyszło częściowych, wobec 0 z 94 przy wyłączonym wygładzaniu.9 Wygładzanie czcionek to ustawienie macOS; czy coś podobnego działa w iOS, muszą rozstrzygnąć sprawdzenia przez zrzuty.
Całkowite wielokrotności są czyste
Przy całkowitych wielokrotnościach siatki, z włączonym antyaliasingiem (domyślne ustawienie kontekstu) i wyłączonym wygładzaniem czcionek, Misaki w 8, 16, 24 i 32 pikselach, PixelMplus10 Regular i Bold w 10, 20, 30 i 40 oraz Press Start 2P w 8, 16, 24 i 32 renderują się z 0,0% częściowych pikseli.7 Każdy piksel to tusz albo papier. Zależy to od ustawienia wygładzania: przy domyślnym wygładzaniu kontekstu bitmapy PixelMplus10 w swoich własnych 10 pikselach narysował częściowo 178 z 272 pikseli z tuszem w „Nice card!”, więc na Macu sama całkowita wielokrotność nie uczyniła tekstu ostrym.9
PixelMplus10 Regular narysowany przez Core Text na macOS 27.0 w jednym pikselu urządzenia na piksel czcionki, pokazany z wyłączonym antyaliasingiem. Przy włączonym antyaliasingu i wyłączonym wygładzaniu czcionek częściowych jest 0 z 94 pikseli z tuszem w „Nice card!”; przy domyślnym wygładzaniu kontekstu bitmapy 178 z 272. Czcionka jest objęta M+ FONT LICENSE, która pozwala ją kopiować i rozpowszechniać; narysowane przez figures/dump_glyphs.swift i figures/make_figures.py.
Pomiędzy nimi się rozmazują
Między całkowitymi wielokrotnościami te same czcionki się rozmazują, przy wyłączonym wygładzaniu, jak w całej sondzie. Misaki ma 57,7% częściowych pikseli w 12 pikselach (1,5 raza siatka), 39,9% w 20 (2,5 raza) i 82,2% w 10 (1,25 raza). PixelMplus10 ma 60,1% częściowych w 15 pikselach. Press Start 2P ma 44,0% częściowych w 12 pikselach, 29,1% w 20 i 21,7% w 28.7 We wszystkich 40 renderach przy 1,25, 1,5, 1,75, 2,5 i 3,5 raza siatce ośmiu krojów, których siatka leży na linii bazowej (obu Misaki, k8x12 i k8x12S, PixelMplus10 Regular i Bold, Press Start 2P i Silkscreen), udział częściowych pikseli wynosi od 19,4%, PixelMplus10 Bold w 35 pikselach, do 82,2%, Misaki w 10.7 Rozmazanie nie maleje równomiernie z rozmiarem: w każdym z ośmiu 1,75 raza siatka rozmazuje bardziej niż 1,5 raza (Misaki 65,7% w 14 pikselach wobec 57,7% w 12). Przy tym samym ułamku jednak maleje: przy 1,5, 2,5 i 3,5 raza siatce każdy z ośmiu krojów spada z każdym krokiem (Misaki 57,7%, 39,9% i 30,4%). W żadnym zmierzonym renderze między całkowitymi wielokrotnościami nie spada do zera; w moim odczytaniu spadek przy stałym ułamku bierze się stąd, że błąd o pół piksela na krawędzi glifu stanowi mniejszą część większego glifu.7
Wyłączenie antyaliasingu (setShouldAntialias(false)) zostawia każdy piksel pełny albo pusty w każdym rozmiarze, ale kształty są przepróbkowywane nierówno. 191 pikseli z tuszem w Misaki przy 8 pikselach zmienia się w 377 przy 10; równomierne skalowanie o 1,25 w każdym kierunku dałoby około 298.733 Niektóre rzędy i kolumny są podwajane, a inne nie, więc kreski szerokie na jeden piksel wychodzą na jeden albo dwa.
Częściowe piksele według rozmiaru, Core Text na macOS 27.0 z wyłączonym wygładzaniem czcionek, nie iOS; narysowane przez figures/make_figures.py na podstawie measure_coretext.out.
Czysty rozmiar nie wystarczy: plik musi być czysty
Przy wyborze czcionki dwie cichsze usterki mają większe znaczenie niż wielokrotności.
Siatka przesunięta względem linii bazowej rozmazuje się w każdym rozmiarze. Kontury PixelMplus12 leżą 0,39 piksela poniżej siatki linii bazowej, a krój renderuje się z 66,6% częściowych pikseli w swoich własnych 12 pikselach, 40,0% w 24 i 29,8% w 36. Przesunięcie linii bazowej o tę wartość, w transformacji kontekstu przy włączonym pozycjonowaniu subpikselowym, sprowadza 12 pikseli do 0,0%, ale zostawia 14,3% w 24 i 19,0% w 36, choć przesunięcie skalowano z rozmiarem (0,78 piksela w 24, 1,17 w 36).7 Dlaczego skalowane przesunięcie nie czyści większych rozmiarów, wynik nie mówi, a ja tego nie prześledziłem.
Przesunięcie poza siatką rozmazuje się, gdy tylko glify zostaną ułożone dokładnie. Przesunięcie kany w k8x12 wynosi 8,01 piksela, a nie 8. Przy domyślnych ustawieniach kontekstu bitmapy, które stawiają każdy glif na całkowitym pikselu, krój renderuje się z 0,0% częściowych pikseli w 12, 24 i 36 pikselach. Przy włączonym pozycjonowaniu subpikselowym i wyłączonej kwantyzacji te same rozmiary mają 62,3%, 42,8% i 32,6% częściowych pikseli.7 W moim odczytaniu każda kolejna kana ląduje wtedy o setną część piksela dalej od siatki niż poprzednia.
Zabłąkane punkty konturu zostawiają trochę. Dziesięć współrzędnych Silkscreen poza siatką daje 4,5% częściowych pikseli w 8 pikselach i 1,2% w 32.7
Czcionka z kropek nigdy się nie wyrównuje. DotGothic16 nie ma siatki, której wielokrotności można by brać, więc sonda rysuje ją w nominalnych 16 kropkach na em i od 1,25 do 4 razy więcej (sekcja 4). Przy włączonym antyaliasingu i wyłączonym wygładzaniu ma 82,3% częściowych pikseli w 16 pikselach, 52,5% w 32, 48,8% w 48 i 37,3% w 64, z częściowymi pikselami w każdym z dziewięciu rozmiarów od 16 do 64 pikseli, łącznie z całkowitymi wielokrotnościami 16; jej kropki nie leżą na żadnej siatce pikseli (sekcja 4).719
Misaki i k8x12 zawierają osadzone tabele bitmap (EBDT i EBLC, odczytane przez CTFontCopyAvailableTables). Zmierzone wyżej wyniki to to, co narysował Core Text; nie ustalono, czy narysował te bitmapy, czy kontury (sekcja 11).7
Misaki, PixelMplus10 i Press Start 2P nie mają żadnej z tych cichych usterek. Wynikająca z tego reguła dotyczy pliku czcionki w tym samym stopniu co rozmiaru: całkowite wielokrotności, siatka na linii bazowej i całkowitopikselowe przesunięcia.
Dynamic Type a siatka
Human Interface Guidelines Apple podają dla iOS domyślny rozmiar tekstu 17 punktów i minimum 11 oraz tabelę każdego rozmiaru Dynamic Type. Body przechodzi od xSmall do AX5 przez 14, 15, 16, 17, 19, 21, 23, 28, 33, 40, 47 i 53 punkty; Caption 1 od 11 do 43.6010
Na wyświetlaczu 3× piksel czcionki jest całkowitą liczbą pikseli urządzenia tylko wtedy, gdy rozmiar w punktach razy 3, podzielony przez em w pikselach czcionki, daje liczbę całkowitą. Z dwunastu rozmiarów Body em 12-pikselowy trafia w całość w 3 z nich (16, 28 i 40 punktów), em 8-pikselowy w 2 (16 i 40), a em 10-pikselowy w 1 (40). Z dwunastu rozmiarów Caption 1 em 12-pikselowy trafia w całość w 3 (12, 16 i 32).760
Które rozmiary Body trafiają w całkowite piksele urządzenia przy 3× i zaokrąglenie z briefu; narysowane przez figures/make_figures.py na podstawie measure_coretext.out i measure_kiradex_text.out.
Czcionka pikselowa nie może więc po prostu podążać za Dynamic Type. Jeśli skaluje się płynnie z rozmiarami systemowymi, wypada poza siatkę w każdym rozmiarze poza nielicznymi całkowitymi: w 11 z 12 rozmiarów Body dla emu 10-pikselowego, w 9 z 12 dla 12-pikselowego.733 Poza siatką Core Text na Macu, z włączonym antyaliasingiem, rozmazał każdy zmierzony krój; czy SwiftUI na iPhonie zrobi to samo, zależy od jego renderowania i pozycjonowania, a rozstrzygną to zrzuty z briefu. Czcionka musi wziąć rozmiar, o który prosi system, i zaokrąglić go do siatki; sekcja 10 zamienia to w przepis na API, a sekcja 12 w regułę briefu.
7. Japońskie łamanie linii
Krótka predefiniowana linia nigdy się nie zawija. Pole tekstowe już tak, a japoński ma reguły określające gdzie.
Reguły
Requirements for Japanese Text Layout konsorcjum W3C (Working Group Note z 11 sierpnia 2020) w punkcie 3.1.7 ustala regułę początku linii: „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)” (co do zasady żadna linia nie powinna zaczynać się od nawiasów zamykających, łączników, dzielących znaków interpunkcyjnych, kropek środkowych, kropek kończących zdanie, przecinków, znaków powtórzenia, znaku przedłużenia dźwięku, małych kana ani zamykających nawiasów warichu). Odnotowuje też luźniejszą praktykę: „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.” (niemało książek przyjmuje mniej ścisły zestaw reguł, który pozwala zaczynać linię od ideograficznego znaku powtórzenia 々, znaku przedłużenia dźwięku i małych kana).21
Algorytm łamania linii Unicode, UAX #14 (rewizja 57, dla Unicode 18.0.0, z datą 2026-09-01), umieszcza małe hiragana i katakana oraz znak przedłużenia dźwięku w swojej klasie CJ i mówi: „Treating characters of class CJ as class NS will give CSS strict line breaking; treating them as class ID will give CSS normal breaking.” (traktowanie znaków klasy CJ jak klasy NS daje ścisłe łamanie linii CSS; traktowanie ich jak klasy ID daje normalne łamanie CSS).61
Co zrobił Core Text
measure_coretext.swift, uruchomiony na macOS 27.0 (nie w iOS), składa jeden japoński akapit o długości 131 znaków za pomocą CTFramesetter w szerokościach 8, 10, 12, 14, 16, 18 i 20 em i porównuje pierwszy znak każdej linii po pierwszej z własną listą znaków zakazanych na początku linii: niektórymi nawiasami zamykającymi, japońskim przecinkiem i kropką, kropką środkową, dwukropkiem i średnikiem, znakami zapytania i wykrzyknikami, znakiem przedłużenia dźwięku, małymi kana i 々.7 Ta lista nie jest podzbiorem klas, które wymienia reguła JLREQ, a jedynie się z nimi pokrywa. Siedem z jej 40 znaków, ,.:;?! i ), to formy pełnej szerokości, których japoński tekst używa tam, gdzie JLREQ wymienia znaki ASCII; pozostałe 33 pochodzą z JLREQ. Z nawiasów zamykających zawiera 」』】〉》〕 i pełnoszeroki ), pomijając ’ ” ] } » 〟 ⦆ 〙 〗 i ) z ASCII; z 40 małych kana zawiera 22, pomijając ゕ, ゖ oraz od ㇰ do ㇿ; pomija też łączniki (cl-03), w tym ‐, 〜, ゠ i półpauzę, ‼ ⁇ ⁈ ⁉ z cl-04 oraz wszystkie znaki powtórzenia (cl-09) poza 々, w tym ヽ, ヾ, ゝ, ゞ i 〻.721 Poniższe wyniki dotyczą listy sondy, a o tym, które znaki w ogóle trafiają na początek linii, decyduje tekst akapitu testowego.
Bez znacznika żadna linia nie zaczęła się od znaku z listy sondy w żadnej szerokości, ani w Hiragino Sans (systemowym kroju japońskim, użytym jako kontrola), ani w żadnym z ośmiu krojów pikselowych: Misaki Gothic, Misaki Gothic 2nd, k8x12, k8x12S, PixelMplus10 Regular i Bold, PixelMplus12 oraz DotGothic16 w nominalnych 16 kropkach.7
Ze znacznikiem kCTLanguageAttributeName ustawionym na ja reguła się rozluźniła. W Hiragino Sans, obu Misaki, obu PixelMplus10, PixelMplus12 i DotGothic16 linia zaczęła się od małej kana (っ, ョ lub ァ) w 6 z 7 szerokości, we wszystkich poza 18 em.7 W k8x12 i k8x12S linia zaczęła się od małej kana (っ, ョ lub ょ) w 4 z 7 szerokości, a od znaku przedłużenia dźwięku ー w dwóch, 10 em (obok ョ) i 18 em, piątej szerokości.7
W moim odczytaniu tych liczb wobec obu cytatów znacznik ja daje mniej ścisłą regułę, którą opisuje JLREQ, tę, którą UAX #14 nazywa normalnym łamaniem CSS, a przebieg bez znacznika zachowuje się ściśle.2161 To odwrotność tego, czego się spodziewałem, skoro znacznik wskazuje język, i właśnie dlatego brief nie oznacza tekstu pola znacznikiem bez zrzutu.
Dwie uwagi o samym pomiarze, obie z porównania skryptu z jego wynikiem. Szerokości podano w em, pełnym rozmiarze czcionki, więc w k8x12, którego kana mają 8 pikseli w 12-pikselowym emie, każda szerokość mieści półtora raza więcej kana, niż mówi jej liczba; akapit w k8x12 zajął 12 linii przy 8 em tam, gdzie inne zajęły 17.7 Z kolei DotGothic16, który nie ma siatki, złożono w dwukrotności nominalnych 16 kropek, w 32 pikselach na em; w tym rozmiarze zajął tyle samo linii co Hiragino Sans w każdej szerokości, od 17 przy 8 em do 7 przy 20, a ze znacznikiem jego linie otwierały te same małe kana.7
8. Późniejsze gry pikselowe: pola, dymki, japoński i chiński
Stardew Valley: dwie formy mowy
Stardew Valley ma obie formy, które ma już Kiradex. Jego pole dialogowe dzieli się na strony poleceniem $b, które strona o moddingu dialogów w Stardew Valley Wiki (ostatnio edytowana 20 maja 2026) opisuje jako takie, które „Indicates pauses in dialogue, where the player will need to click for the next part to load in a new dialogue box” (oznacza pauzy w dialogu, w których gracz musi kliknąć, by następna część wczytała się w nowym polu dialogowym), oraz polecenia portretów, które „set the portrait shown in the dialogue box for the current line of dialogue” (ustawiają portret pokazywany w polu dialogowym dla bieżącej linii dialogu).62 Ma też dymki, Strings\SpeechBubbles, na „dialogue spoken by NPCs through speech bubbles when the player enters a given location” (dialogi wypowiadane przez NPC w dymkach, gdy gracz wchodzi do danego miejsca).62 Pole służy rozmowom, z twarzą na czele; dymki to to, co mówi miasteczko, gdy się do niego wchodzi.
Jego języki niełacińskie korzystają z czcionek bitmapowych. Strona wiki o własnych językach (ostatnio edytowana 3 stycznia 2026) każe nowemu językowi ustawić UseLatinFont na false i dostarczyć plik .fnt wzorowany na „the Chinese, Japanese, Korean, and Russian fonts in the Fonts folder” (chińskich, japońskich, koreańskich i rosyjskich czcionkach w folderze Fonts). Jego FontPixelZoom to „A factor by which to multiply the font size. The recommended baseline is 1.5” (współczynnik, przez który mnoży się rozmiar czcionki; zalecana wartość bazowa to 1.5), a FontApplyYOffset ustala „Whether to shift the font up by four pixels” (czy przesunąć czcionkę o cztery piksele w górę), aby wyrównać „languages with larger characters like Chinese and Japanese” (języki o większych znakach, takie jak chiński i japoński).63 Powiększenie 1,5 nie jest całkowitą wielokrotnością, ale strona podaje je jako punkt wyjścia dla języka dodawanego przez moddera, a nie jako wartość, której używają dostarczane z grą czcionki chińska, japońska i koreańska, a to, czy powiększenie przepróbkowuje obraz na ekranie, zależy też od skali gry względem pikseli urządzenia; nie mówi więc nic pewnego o tym, jak Stardew rysuje własny japoński.63
Final Fantasy Pixel Remaster: pracą była czcionka
Konsolowe wydanie Pixel Remaster dodało opcję czcionki pikselowej, a producent, Naofumi Takuma, opowiedział Siliconerze (Jenni Lada, 2 czerwca 2023), jak powstała. Dla języków zachodnich zespół znalazł dane czcionek wytworzone we własnym zakresie i z nich skorzystał. Dla japońskiego: „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.” (nie mieliśmy u siebie zapisanych żadnych alternatywnych danych japońskiej czcionki, więc postanowiliśmy stworzyć nową). I dalej: „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.” (o ile czcionka dla języków zachodnich wymagała od nas stworzenia zaledwie kilkuset znaków, o tyle w japońskiej musieliśmy stworzyć około 7500).8
To czwarta nieścisłość artykułu o świecie. Podawał tę samą liczbę, „about 7,500 Japanese characters” (około 7500 japońskich znaków), i cytował relację GoNintendo o tej pracy; źródłem pierwotnym jest własny wywiad producenta dla Siliconery, w którym pada „around 7,500 characters”.238 Ta sama liczba, lepsze źródło.
Siedem i pół tysiąca to mniej więcej tyle, ile 6879 znaków JIS X 0208, zwykłego celu pokrycia; to porównanie jest moje.533 Gra, której wszystkie linie są ustalone przed wydaniem, może narysować mniej, jak zrobił japoński ROM Final Fantasy VI z 1248 literami dialogów (sekcja 3), ale wtedy każdy ciąg trzeba sprawdzić względem czcionki; czcionka, która obejmuje cały zestaw, nie wymaga takiego sprawdzenia dla żadnego ciągu z niego zbudowanego.4 Lekcja dla małej aplikacji jest taka sama jak ta, której z drugiej strony uczą darmowe czcionki z sekcji 4: dla tekstu, który będzie dalej rósł, japońska typografia pikselowa jest problemem rozwiązanym tylko wtedy, gdy ktoś już narysował te siedem tysięcy znaków, a licencja pozwala je dystrybuować.
Co dostarczają współczesne gry pikselowe
Na Steamie, 5 października 2026, Stardew Valley wymienia 12 języków, w tym japoński, chiński uproszczony i koreański; Celeste 9, w tym japoński i chiński uproszczony; Sea of Stars 11, w tym japoński, koreański oraz chiński uproszczony i tradycyjny; a Eastward 7, w tym oba chińskie i japoński.64 Każda z czterech ma japoński.
Jak Celeste, Sea of Stars i Eastward rysują tekst japoński i chiński, nie znalazłem w żadnym źródle od twórców, więc ten artykuł nie mówi o tym nic poza listami języków (sekcja 11).
Dwie formy mowy w Kiradex
Kiradex ma już obie formy ze Stardew: pole WorldHUD u dołu ekranu i dymki placu nad głowami.2625 Brakuje mu typografii kanonu w ich wnętrzu i sposobu, by VoiceOver mógł usłyszeć którąkolwiek z nich. Komentarz przy dymku w kodzie mówi, do czego zmierzał: „A line said, as the old games drew it: a cream box with a dark border.” (wypowiedziana linia, tak jak rysowały ją stare gry: kremowe pole z ciemnym obramowaniem).25
9. Rzemiosło w postaci reguł z liczbami
Oto reguły, które potwierdzają pomiary, każda ze źródłem. Tam, gdzie reguła zamienia zmierzony fakt w zalecenie, zalecenie jest moje i reguła sygnalizuje to swoim czasownikiem.
| Element | Reguła | Źródło |
|---|---|---|
| Pole | Pole to dolna jedna trzecia ekranu i dwie linie. Należy umieścić je u dołu; gra, która potrzebuje dołu ekranu, może przenieść je na górę. | Ramka Emerald 30 na 6 kafelków, cała szerokość i 30% wysokości, wokół okna 27 na 4 kafelki z dwiema 16-pikselowymi liniami; pole Crystal 20 na 6, 33%, dwie linie po 18, a w Red to samo 20 na 6; okno Final Fantasy VI w rzędzie 1 lub 18 |
| Glify łacińskie | Od 4 do 12 pikseli szerokości w komórce o wysokości od 8 do 16 pikseli. | Komórka 6 na 16 w Emerald przy 5,25 piksela na znak w rzeczywistym tekście; od 4 do 12 w Final Fantasy VI w komórce 16 na 11; stałe 8 na 8 w Crystal |
| Glify japońskie | Od 8 do 12 pikseli, mniej więcej dwa razy więcej niż litera łacińska w tej samej czcionce. | Misaki 8 na 8 z tuszem 7 na 7; Emerald 8 szerokości w 16-pikselowej linii; arkusz FireRed i PixelMplus10 10; Final Fantasy VI i PixelMplus12 12; 8 wobec 4, 10 wobec 5, 12 wobec 6 w każdej zmierzonej czcionce z japońskim |
| Długość linii | Od 18 do 41 znaków w linii; pisać pod szerokość, a nie ją wypełniać. | Crystal 18; 216-pikselowe okno Emerald mieści 41 przy 5,25 piksela, a jego scenarzyści zatrzymali się na 38 w 95% linii; 220-pikselowa linia Final Fantasy VI mieści 18 japońskich znaków po 12 pikseli |
| Łamanie | Albo piszący łamie każdą linię, albo kod zawija według słów; nigdy jedno i drugie. | 24 100 ręcznie łamanych linii Emerald, żadna ponad 208 pikseli; CalcTextWidth i NewLine w Final Fantasy VI |
| Tempo | Liczyć znaki w czasie: 60 na sekundę jako pułap (szybkie ustawienie Emerald i Crystal oraz jedna litera na klatkę w Final Fantasy VI), od 15 do 20 dla ustawienia średniego, od 7,5 do 12 dla wolnego. | Emerald 7,5, 15 i 60, przytrzymane 60; Crystal i Red 12, 20 i 60; Final Fantasy VI 60 |
| Znacznik przejścia dalej | Poruszać znacznik w wolnym rytmie: krok co 150 milisekund, cykl około 0,6 sekundy. | 0, 1, 2, 1 piksel w Emerald, krok co 9 klatek, 36 klatek na cykl; ▼ w Crystal migający na zmianę z kafelkiem obramowania |
| Ramki | Ramki to kafelki, a nie rysunki: narożnik, krawędź i środek, powtarzane. Pozwolić graczowi wybrać jedną z kilku. | 20 wybieralnych ramek menu 24 na 24 w Emerald i jego 14-kafelkowy pasek dialogowy z odbitą dolną krawędzią; 8 ramek Crystal po 6 kafelków 1-bitowych; 8 stylów Final Fantasy VI po 28 kafelków z 7 edytowalnymi kolorami |
| Ostra typografia | Czcionka pikselowa jest ostra tylko przy całkowitej liczbie pikseli urządzenia na piksel czcionki, a na Macu tylko przy wyłączonym wygładzaniu czcionek; należy zrobić zrzut z iPhone’a, by zobaczyć, co obowiązuje tam. | Core Text na macOS 27.0 z wyłączonym wygładzaniem czcionek: 0,0% częściowych pikseli przy całkowitych wielokrotnościach siatki na linii bazowej z całkowitopikselowymi przesunięciami (Misaki, PixelMplus10, Press Start 2P), od 19,4% do 82,2% między nimi; przy domyślnym wygładzaniu kontekstu PixelMplus10 w swoich własnych 10 pikselach 178 częściowych z 272 pikseli z tuszem |
| Czyste pliki | Przed wyborem czcionki sprawdzić jej siatkę, przesunięcie względem linii bazowej i przesunięcia znaków. | Core Text na macOS 27.0: siatka PixelMplus12 0,39 piksela poza linią bazową, rozmazana w każdym rozmiarze; 8,01-pikselowe przesunięcie kany w k8x12, rozmazane po dokładnym ułożeniu glifów; 10 zabłąkanych punktów Silkscreen, od 1,2% do 4,5% |
| Dynamic Type | Respektować rozmiar wybrany przez użytkownika, zaokrąglając go do siatki, nigdy przez płynne skalowanie i nigdy poniżej 11 punktów. | Em 10-pikselowy jest całkowity w 1 z 12 rozmiarów Body przy 3×, em 12-pikselowy w 3, em 8-pikselowy w 2; 11-punktowe minimum z HIG |
| Łamanie japońskiego | Zachować ścisłą regułę początku linii dla tekstu pola; utrzymywać predefiniowane linie tak krótkie, by nigdy się nie zawijały. | JLREQ 3.1.7; Core Text na macOS 27.0 bez znacznika zachował ścisłą regułę dla częściowej listy początków linii sondy w każdej zmierzonej szerokości, a ze znacznikiem ja pozwolił małym kana zaczynać linie w 6 z 7 szerokości w większości krojów (w 4 z 7 w k8x12 i k8x12S) |
| Mówiący | Podawać mówiącego w linii, a nie w osobnej rubryce ani nad głową. | Pole Emerald nie ma rubryki na imię, a 6,5% jego akapitów zaczyna się od imienia i dwukropka |
| Pokrycie | Pokryć standardowy zestaw, około siedmiu tysięcy znaków, zanim będzie mógł trafić do użytkowników japoński tekst, którego scenarzyści nie ustalili; ustalony skrypt może obejść się mniejszą liczbą, sprawdzaną ciąg po ciągu. | 6879 znaków JIS X 0208; „around 7,500” w Pixel Remaster; 1248 liter dialogów w japońskim ROM-ie Final Fantasy VI; Misaki, k8x12 i DotGothic16 rysują cały JIS X 0208, PixelMplus wszystko poza trzema punktami kodowymi |
| Licencje | Dostarczać krój tylko z tekstem licencji w ręku i po przeczytaniu jej warunków. | Trzyzdaniowa licencja Misaki, k8x12 i PixelMplus; SIL Open Font License dla DotGothic16, Press Start 2P i Silkscreen, z Reserved Font Name dla Press Start 2P |
| Dwie formy | Zachować obie formy, pole i dymek. Co mówi która, to wybór gry; wybór Kiradex, mój, to pole dla tego, co mówi miejsce, i dymek dla tego, co mówi osoba. | Stardew Valley ma obie: pole do rozmów, dymki dla tego, co mówią NPC, gdy gracz wchodzi do danego miejsca |
Każda liczba w tabeli pochodzi z sekcji od 1 do 8 i ma tam swój przypis.1467921862
10. Po apple’owsku: krój, rozmiary, łamanie linii i VoiceOver
Kiradex World to scena RealityKit używana jako renderer 2D, z kamerą ortograficzną ustawioną na całkowitą liczbę pikseli urządzenia na teksel i ze SwiftUI nad nią dla wszystkiego, co czyta człowiek.23 Artykuł o świecie ustalił, że tekst dialogów „lives in SwiftUI at UI scale rather than in the world” (żyje w SwiftUI w skali interfejsu, a nie w świecie), a ta sekcja tę decyzję podtrzymuje i uzupełnia API: czego Apple wymaga od niestandardowej typografii, jak trafia do aplikacji dołączona czcionka pikselowa, jak można respektować Dynamic Type przez zaokrąglanie, jak łamie się japoński i jak VoiceOver może czytać scenę, która jest jednym widokiem RealityKit. Każda dostępność podana niżej pochodzi ze stron dokumentacji Apple zapisanych 5 października 2026; żadnego kodu nie skompilowano, a żadnego kosztu w tej sekcji nie zmierzono na telefonie.65
Czego Apple oczekuje od tekstu
Strona Typography w Human Interface Guidelines podaje dla iOS i iPadOS domyślny rozmiar tekstu 17 punktów i minimum 11 oraz prosi aplikacje, by „Follow the recommended default and minimum text sizes for each platform” (stosowały zalecane domyślne i minimalne rozmiary tekstu dla każdej platformy), zarówno dla czcionek niestandardowych, jak i systemowych.1060 Czcionki systemowe same obsługują Dynamic Type i funkcje dostępności takie jak Pogrubiony tekst; dla pozostałych strona mówi: „If you use a custom font, make sure it implements the same behaviors.” (jeśli używasz czcionki niestandardowej, upewnij się, że implementuje te same zachowania).10
Strona Accessibility określa skalę zadania: „Ideally, give people the option to enlarge text by at least 200 percent (or 140 percent in watchOS apps).” (najlepiej daj ludziom możliwość powiększenia tekstu co najmniej o 200 procent, a w aplikacjach watchOS o 140 procent).20 Strona o grach dodaje do tego czytelność: „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.” (aby tekst był wygodnie czytelny na każdym urządzeniu, zadbaj o dobry kontrast z tłem i używaj co najmniej zalecanego minimalnego rozmiaru tekstu na każdej platformie).66
Zakres Body, od 14 do 53 punktów, to od około 82% do około 312% domyślnych 17; zaokrąglone rozmiary pola przy 3×, od 13,33 do 53,33 punktu, to od 80% do 320% ich własnej wartości domyślnej, 16,67, ponad 200%, o które prosi strona Accessibility.601133
Skalowanie czcionki niestandardowej
Apple ma trzy sposoby skalowania wartości z Dynamic Type i jeden, by to skalowanie zatrzymać.
UIFontMetrics, „A utility object for obtaining custom fonts that scale to support Dynamic Type” (obiekt pomocniczy do uzyskiwania czcionek niestandardowych skalowanych na potrzeby Dynamic Type), dostępny od iOS 11.0, zscaledFont(for:),scaledFont(for:maximumPointSize:)iscaledValue(for:), które „Scales an arbitrary layout value based on the current Dynamic Type settings” (skaluje dowolną wartość układu według bieżących ustawień Dynamic Type).67Font.custom(_:size:relativeTo:)w SwiftUI tworzy czcionkę niestandardową, „that scales relative to the giventextStyle” (która skaluje się względem podanegotextStyle), od iOS 14.0.68ScaledMetric, „A dynamic property that scales a numeric value” (dynamiczna właściwość, która skaluje wartość liczbową), od iOS 14.0.69Font.custom(_:fixedSize:)tworzy czcionkę niestandardową, „that does not scale with Dynamic Type” (która nie skaluje się z Dynamic Type), od iOS 14.0.68
Czcionka pikselowa potrzebuje pierwszego lub trzeciego sposobu razem z ostatnim: odczytać rozmiar, którego użyłby system, zaokrąglić go do siatki czcionki i ustawić zaokrąglony rozmiar jako stały, by system nie skalował go ponownie. Samo zaokrąglenie to arytmetyka i znajduje się w briefie w sekcji 12: piksel czcionki staje się m pikselami urządzenia, m = max(m₀, ⌊size × displayScale / 10 + 0.5⌋) dla emu 10-pikselowego, czyli najbliższa liczba całkowita z połówką zaokrąglaną w górę, gdzie m₀, najmniejsze m dające co najmniej 11 punktów, wynosi 4 przy 3× i 3 przy 2×, a rozmiar w punktach to 10 × m / displayScale. Przy 3× przypisuje to dwunastu rozmiarom Body wartości m równe 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 i 16 (Small, 15 punktów, to jedyna dokładna połówka: 4,5 zaokrągla się do 5): 16,67 punktu w rozmiarze domyślnym, 53,33 w AX5.11
Aby ograniczyć rozmiar wewnątrz widoku, dynamicTypeSize(_:) (iOS 15.0) ustawia lub ogranicza rozmiar Dynamic Type, a jego strona radzi: „When limiting the Dynamic Type size, consider if adding a large content view with accessibilityShowsLargeContentViewer() would be appropriate.” (ograniczając rozmiar Dynamic Type, rozważ, czy właściwe byłoby dodanie widoku dużej zawartości za pomocą accessibilityShowsLargeContentViewer()).70 Ten modyfikator, również z iOS 15.0, ma własne ostrzeżenie: „Don’t use the large content viewer as a replacement for proper Dynamic Type support.” (nie używaj przeglądarki dużej zawartości jako zamiennika właściwej obsługi Dynamic Type).71 Brief ogranicza tylko dymki, które muszą pozostać jednoliniowe, i nigdy pole.
Dołączanie czcionki
Czcionka trafia do pakietu aplikacji i jest wymieniona w UIAppFonts, „App-specific font files located in the bundle and that the system loads at runtime” (pliki czcionek właściwe dla aplikacji, umieszczone w pakiecie i ładowane przez system w czasie działania) (iOS 3.2), albo rejestrowana w kodzie przez CTFontManagerRegisterFontsForURL(_:_:_:) (iOS 4.1).7273
Rysowanie ramki i ustawianie linii bazowej
Ramka narysowana z kilku kafelków to obraz skalowany bez wygładzania. Image.interpolation(_:) w SwiftUI ustawia jakość interpolacji od iOS 13.0, a Image.resizable(capInsets:resizingMode:), które „Sets the mode by which SwiftUI resizes an image to fit its space” (ustawia tryb, w jakim SwiftUI zmienia rozmiar obrazu, by zmieścił się w swoim miejscu), rozciąga lub powiela wszystko poza narożnikami, również od iOS 13.0.7475 Brief rysuje swoją ramkę zamiast tego w Canvas, tak by każdy piksel źródłowy miał dokładnie m na m pikseli urządzenia; w moim odczytaniu resizable(capInsets:) z .tile sprawdziłoby się równie dobrze tylko wtedy, gdyby jego powielanie trafiało w całkowite piksele urządzenia, czego nikt nie zmierzył.
Text.baselineOffset(_:), które „Sets the vertical offset for the text relative to its baseline” (ustawia pionowe przesunięcie tekstu względem jego linii bazowej) (iOS 13.0), jest narzędziem dla kroju, którego siatka leży poza linią bazową, jak w PixelMplus12; sekcja 6 zmierzyła, że przesunięcie naprawia taki krój tylko w jednym rozmiarze, co jest jednym z powodów, dla których brief go nie używa.767
Japońskie łamanie linii w SwiftUI i UIKit
typesettingLanguage(_:isEnabled:) (iOS 17.0) służy dla Text, który „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” (może zawierać tekst w danym języku niezgodnym z językiem interfejsu urządzenia; w takim przypadku warto wskazać język, aby wysokość linii, łamanie linii i odstępy respektowały pismo tego języka). Jego strona dodaje: „this language does not affect text localization.” (ten język nie wpływa na lokalizację tekstu).77
NSParagraphStyle.LineBreakStrategy w UIKit (iOS 9.0) ma trzy nazwane strategie. standard (iOS 14.0) „uses the same configuration of line-break strategies that it uses for standard UI labels” (używa tej samej konfiguracji strategii łamania linii co dla standardowych etykiet interfejsu) i „may be unsuitable for large amounts of text” (może nie nadawać się do dużych ilości tekstu); pushOut (iOS 9.0) ma zapobiegać sytuacji, w której zostaje „an orphan word on the last line” (osierocone słowo w ostatniej linii); hangulWordPriority (iOS 14.0) służy koreańskiemu.78
Sekcja 7 zmierzyła, że Core Text na macOS ze znacznikiem ja pozwolił małym kana zaczynać linie w 6 z 7 szerokości w większości krojów (w 4 z 7 w k8x12 i k8x12S), a bez znacznika nie.7 Nie zmierzono, co Text w SwiftUI robi z japońskim, gdy językiem urządzenia jest japoński, ani czy typesettingLanguage ustawia ten sam atrybut, który ustawił pomiar (sekcja 11). Dlatego brief zostawia tekst pola bez znacznika, chyba że zrzut pokaże, że ścisła reguła utrzymuje się ze znacznikiem.
VoiceOver nad sceną
Modyfikatory dostępności SwiftUI opisują widok: accessibilityLabel(_:) go nazywa, a accessibilityValue(_:) dodaje jego wartość, z regułą w dokumentacji: „Use this method to describe the value represented by a view, but only if that’s different than the view’s label.” (używaj tej metody do opisania wartości reprezentowanej przez widok, ale tylko wtedy, gdy różni się ona od etykiety widoku). Oba pochodzą z iOS 16.0 w formie z LocalizedStringResource, którą wymieniają strony.79
Scena gry potrzebuje więcej niż etykiety. accessibilityDirectTouch(_:options:) (iOS 17.0) tworzy obszar, w którym „Direct touch areas passthrough touch events to the app rather than being handled through an assistive technology, such as VoiceOver” (obszary bezpośredniego dotyku przekazują zdarzenia dotyku aplikacji, zamiast by obsługiwała je technologia wspomagająca, taka jak VoiceOver), po aktywowaniu go przez użytkownika.80 AccessibilityTraits.updatesFrequently (iOS 13.0) służy elementowi, który się zmienia: „Use this trait when you want an assistive technology to poll for changes when it needs updated information.” (używaj tej cechy, gdy chcesz, by technologia wspomagająca odpytywała o zmiany, gdy potrzebuje aktualnych informacji).81 AccessibilityNotification.Announcement (iOS 17.0) publikuje komunikat głosowy z atrybutem accessibilitySpeechAnnouncementPriority, który określa, jak ustawia się on w kolejce względem innej mowy.82 A accessibilityReduceMotion (iOS 13.0) zgłasza ustawienie Ogranicz ruch (Reduce Motion), które, jak mówi jego strona, oznacza, że „UI should avoid large animations” (interfejs powinien unikać dużych animacji).83
RealityKit potrafi opisywać same encje. AccessibilityComponent (iOS 17.0) to „A component that stores accessibility information for an entity” (komponent przechowujący informacje o dostępności encji), którego nazwę „VoiceOver says aloud when it describes the entity” (VoiceOver wypowiada na głos, gdy opisuje encję).84 Artykuł Apple „Improving the Accessibility of RealityKit Apps” każe „set the properties on each visible Entity in your scene” (ustawić właściwości na każdej widocznej Entity w scenie): własne właściwości dostępności Entity, isAccessibilityElement, accessibilityLabel i accessibilityDescription, które artykuł datuje na iOS 14 i których strony są obecnie oznaczone jako przestarzałe, wszystkie poza isAccessibilityElement (strona wymienia też accessibilityLabelKey, etykietę jako klucz zlokalizowanego ciągu), starsza droga obok komponentu.85 RealityView, widok SwiftUI, którego używa scena Kiradex, pochodzi z iOS 18.0.86 Czy RealityView z wirtualną kamerą ortograficzną, tak jak skonfigurował ją Kiradex, udostępnia te encje VoiceOver, nie jest opisane na tych stronach i nie zostało przetestowane (sekcja 11).
Testowanie tego da się już zautomatyzować. XCUIVoiceOverService „Provides programmatic control of VoiceOver for UI testing” (zapewnia programowe sterowanie VoiceOver na potrzeby testów interfejsu), dostępny od iOS 27.0, a dostęp do niego daje właściwość voiceOverService w XCUIDevice.87
Kiradex dzisiaj
Odczytane z repozytorium 5 października 2026, w commicie 334fbfe z niezatwierdzonymi zmianami z tego dnia, bez uruchamiania i bez zmian z mojej strony; aplikację edytowano w trakcie mojej lektury (sekcja 11):11
- Jeden krój, systemowy. Rodzina czcionek motywu to
.systemDesign(.monospaced), a widoki świata używają ról z zestawu: caption w 10 wywołaniach w czterech plikach, label w 3, body w 3 i title w 3.2411 - Ścieżka czcionki niestandardowej, która skaluje się płynnie.
Kit941.FontFamily.custom(named:)ze wspólnego zestawu rozwiązuje się doFont.custom(_:size:relativeTo:)w rozmiarach 34, 22, 17, 13 i 12 punktów dla display, title, body, label i caption.88 Z czcionką pikselową skalowałoby to przez każdy rozmiar Dynamic Type, a sekcja 6 liczy, że daje to wypadnięcie z siatki w 9 z 12 rozmiarów Body dla emu 12-pikselowego i w 11 z 12 dla 10-pikselowego.7 - Dymek.
Text(text).kit941Font(.caption, weight: .bold), tusz naWorldInk.paper(powierzchnia graficzna o dwóch stałych kolorach), z małym odstępem z zestawu w poziomie i bardzo małym w pionie, z 2-punktowym obramowaniem w kolorze tuszu i.fixedSize(), więc jedna linia. Jest umieszczony o duży krok odstępu nadrig.headPoint, widoczny, dopóki linia ma mniej niż 4 sekundy, i opisany etykietą"Said: \(text)"w nakładce z wyłączonym wykrywaniem dotknięć. Ta nakładka leży wewnątrz sceny, pod dwiema nakładkami, które scena kładzie na całości: liniami skanowania, czarną linią o krycie 16% co 3 punkty, oraz winietą.2511 - Pole.
WorldHUDumieszcza tytuł miejsca (caption, pogrubiony, wielkimi literami) i jego stan (caption, półgruby) w kremowym polu z tym samym 2-punktowym obramowaniem u dołu ekranu; przy półotwartym Duo trzyma się szerszej strony zgięcia. Nie przyjmuje dotknięć (.allowsHitTesting(false), „Taps go through to the ground under it”, dotknięcia przechodzą do podłoża pod nim) i leży nad całą sceną, ponad liniami skanowania i winietą.26 - Co się mówi. 23 predefiniowane linie na serwerze, od 3 do 40 znaków, z medianą 16; najdłuższa to „Anyone have a card from the first years?” (czy ktoś ma kartę z pierwszych lat?). Pseudonimy to dwa słowa z list po 20 i 20 oraz cztery cyfry, najwyżej 20 znaków („Glacier Shuffle 0000”). Nad głową kolekcjonera nie rysuje się nic poza dymkiem; pseudonim widać w panelu, który otwiera dotknięcie.288911 Artykuł z tej serii o własnym życiu miasteczka naliczył te same 23 linie i limit serwera wynoszący jedną linię co 2,0 sekundy na gracza.9091
- Bez czcionki, jeden język. Nie dołączono żadnego pliku czcionki (jedyny
.ttfw repozytorium toscripts/screenshots/MartianMono-latin.ttf, dla narzędzia do zrzutów ekranu);knownRegionsprojektu toBaseien, a nie ma żadnego pliku.xcstringsani.lproj.1127 - VoiceOver słyszy współrzędne. Scena to jeden element,
.accessibilityLabel("The plaza")i.accessibilityValue(stepTrace), gdziestepTraceto ostatni krok w postaci „x,y facing” (x,y kierunek), zachowany, bo „the walks read it, since nothing else the simulator logs reaches them” (czytają go testy chodu, bo nic innego z tego, co loguje symulator, do nich nie dociera). AniAccessibilityComponent, aniaccessibilityDirectTouch,AccessibilityNotification,typesettingLanguage,UIFontMetrics,ScaledMetricczydynamicTypeSizenie występują nigdzie w kodzie aplikacji.2511 - Szyldy bez liter.
sign()kuźni to „A hanging sign on a bracket, 12 × 11” (wiszący szyld na wsporniku, 12 × 11), którego lico to dwa rzędy tuszowych znaków, a nie litery; mapa placu ma jeden kafelek szyldu,S. Palec na podłożu natychmiast prowadzi gracza do kafelka pod spodem i jest śledzony od kafelka do kafelka; dotknięcie kolekcjonera otwiera jego panel po podniesieniu palca, a dotknięcie kiosku tablicę miasteczka; kafelek szyldu jest podłożem jak każdy inny, bez własnego dotknięcia.2992 - Skala świata. Według reguły
PlazaRig.fit, czyli części całkowitej z wysokości widoku w pikselach podzielonej przez 320, teksel to 8 pikseli urządzenia na iPhonie 18 Pro Max (440 na 956 punktów) i na wewnętrznym wyświetlaczu Duo w pionie, 6 na zewnętrznym wyświetlaczu Duo w pionie i na wewnętrznym w poziomie oraz 4 na zewnętrznym w poziomie: 2,67, 2,00 lub 1,33 punktu.9311
Dlaczego tekst zostaje poza światem
Arytmetyka artykułu o świecie jest tu poprawna: „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” (przy k = 8 na wyświetlaczu 3× czcionka pięciotekselowa ma 13,3 punktu, a przy k = 6 ma 10 punktów, poniżej 11-punktowego minimum z HIG).23 Dotyczy to jednak tylko czcionek poniżej 5,5 teksela przy k = 6 (11 punktów przy 2 punktach na teksel); em 8-tekselowy przy k = 6 ma 16 punktów.1133 Wniosek, tekst w skali interfejsu w SwiftUI, utrzymuje się z innego powodu: w gęstości świata em 10-pikselowy ma 26,67 punktu na telefonie przy 8 pikselach urządzenia na teksel, co zostawia 29 znaków łacińskich w linii pola na 18 Pro Max.11 Mierzone w kafelkach pole kanonu jest wąskie. Pole szerokie na 16 kafelków sprzętowych, 128 pikseli, mieści 16 znaków czcionki 8 na 8, około 24 łacińskich znaków Emerald przy ich średniej 5,25 piksela (21 w najgorszym przypadku, 6), 16 japońskich znaków Emerald po 8 pikseli i 10 japońskich znaków Final Fantasy VI po 12 pikseli. Szesnaście 16-tekselowych kafelków Kiradex to 256 tekseli, które w gęstości samego świata mieszczą 42 znaki łacińskie i 21 kana w PixelMplus12 albo 64 i 32 w Misaki.11 A pole mierzone we własnych kafelkach Kiradex nie mieści się w trzech z pięciu zmierzonych widoków: 16 kafelków po 16 tekseli to 683 punkty przy 8 pikselach urządzenia na teksel, więcej niż szerokość iPhone’a 18 Pro Max (440 punktów) i wewnętrznego wyświetlacza Duo w pionie (669), oraz 512 punktów przy 6, więcej niż zewnętrzny wyświetlacz Duo w pionie (466). Mieści się tylko w dwóch widokach poziomych: 512 punktów w 951 punktach wewnętrznego wyświetlacza i 341 punktów przy 4 pikselach urządzenia na teksel w 678 punktach zewnętrznego.11 Pole w Kiradex wymiaruje się w punktach, a nie w kafelkach.
Ta sama arytmetyka w rozmiarze z briefu: PixelMplus10 przy m = 5, 16,67 punktu, mieści 47 znaków łacińskich albo 23 japońskie w linii pola na 18 Pro Max w pionie, po dzisiejszym marginesie 48 punktów, czyli 16 punktach marginesu i 8 punktach dopełnienia z każdej strony, liczonych od pełnej szerokości, bez marginesów bocznych (założenie, nie pomiar). Pole leży wewnątrz obszaru bezpiecznego, więc na zewnętrznym wyświetlaczu Duo w pionie liczba zależy od jego marginesu bocznego: 50 albo 25 bez marginesu i 40 albo 20 z 84-punktowym marginesem po stronie końcowej, czyli bocznym paskiem, który ta seria zmierzyła na zamkniętym wyświetlaczu (sekcja 11). Z ramką z briefu (sekcja 12, punkt 3), która zajmuje 8 pikseli czcionki z każdej strony zamiast dopełnienia i zaokrągla pole w dół do całkowitych pikseli czcionki, te same linie mieszczą 45 albo 22 znaki na 18 Pro Max oraz 48 albo 24, lub 38 albo 19 z paskiem, na Duo.119495
Koszty
Nic z tego nie zostało zmierzone na urządzeniu. Dołączenie czcionki z 7253 glifami dodaje jej plik do aplikacji;7 rysowanie tekstu przez SwiftUI czcionką niestandardową kosztuje tyle, ile każdy Text; a ramka z briefu to Canvas, który przerysowuje się przy zmianie rozmiaru. W moim odczytaniu żaden z tych kosztów nie jest duży w porównaniu ze sceną RealityKit, ale to odczytanie, a sprawdzają je zrzuty z briefu.
11. Co zmierzono, a czego nie
Wszystko, co ma w tym artykule liczbę, pochodzi ze skryptów uruchamianych ponownie na zapisanych źródłach, ze stron zapisanych 5 października 2026 albo z datowanych odczytów kodu Kiradex; przypisy mówią, z czego. Ta sekcja to lista tego, czego to nie obejmuje.30
Zmierzone w kodzie, powtarzalne. measure_gen_text.py na czterech dekompilacjach pret: tabela czcionek Emerald (dziesięć pozycji, siedem czcionek); tempo tekstu w klatkach i znakach na sekundę; przewijanie; strzałka przejścia dalej; okno 27 na 4 i ramka 30 na 6; 20 wybieralnych ramek; szerokości glifów łacińskich; 24 100 linii skryptu w znakach i pikselach; 934 z 14 412 akapitów podających mówiącego; japońskie arkusze glifów; opóźnienia i okno FireRed; ekrany, pola, opóźnienia, ramki i arkusze czcionek Crystal i Red.1 measure_ff6.py na deasemblacji ff6: style i palety okien, komórka i szerokości czcionki dialogów, rozmiar i ciągi szerokości czcionki japońskiej, 220-pikselowa linia, jedna litera na klatkę, tytuł wyświetlany 100 klatek, zawijanie według słów, ekran konfiguracji i tabela gradientu okna.4 measure_bdf.py na plikach BDF Misaki i k8x12.6 measure_coretext.swift i measure_outline_coords.swift, skompilowane i uruchomione na macOS 27.0, na dziesięciu plikach TrueType i Hiragino Sans.719 measure_kiradex_text.py na Kiradex w stanie z dysku w commicie 334fbfe (uruchomiony ponownie 15 października na bieżącym drzewie, z wcześniejszymi liniami bez zmian) oraz measure_figure_width.py na arkuszu jego postaci.1196 measure_hig.py, measure_steam_languages.py i measure_apple_docs.py na zapisanych stronach.606465 measure_ios27_iphones.py na liście iPhone’ów środowiska uruchomieniowego symulatora iOS 27.0 oraz probe_line_start_glyphs.swift, skompilowany i uruchomiony na macOS 27.0, na tabelach klas JLREQ i PixelMplus10.9798 measure_cell_masks.py na zapisanych arkuszach pret: wzorzec masek komórek dla briefu.99 To trzynaście skryptów; czternasty, dump_glyphs.swift, przechowywany razem z ilustracjami do tego artykułu, narysował PixelMplus10 w jego własnym rozmiarze z włączonym i wyłączonym wygładzaniem czcionek.9
Niezmierzone albo słabsze, niż się wydaje.
- Core Text na macOS zastępuje iOS. Każda liczba dotycząca renderowania i łamania linii w sekcjach 6 i 7 pochodzi z Core Text w macOS 27.0 rysującego do kontekstu bitmapy, przy wyłączonym wygładzaniu czcionek, a nie ze SwiftUI na iPhonie. Pomiarem, który się liczy, są sprawdzenia przez zrzuty z briefu; dopóki nie zostaną uruchomione, to, co narysuje iPhone, jest przewidywaniem.79
- Bitmapy osadzone. Misaki i k8x12 mają tabele
EBDTiEBLC; nie ustalono, czy Core Text narysował te bitmapy, czy kontury w 8 i 12 pikselach.7 - Własne pozycjonowanie SwiftUI. Nie zmierzono, czy SwiftUI umieszcza początki
Textna całkowitych pikselach urządzenia ani jaką regułę łamania linii stosuje do japońskiego, gdy językiem urządzenia jest japoński. - Grubości. PixelMplus10 Bold to jedyny zmierzony krój pogrubiony; syntetycznego pogrubienia żadnego kroju pikselowego nie zmierzono.7
- Czytelność. Siatki i pokrycie są zmierzone; to, czy kanji 10 na 10 dobrze czyta się japońskiemu czytelnikowi przy 16,67 punktu, nie jest, a do tego potrzeba japońskiego czytelnika i urządzenia.
- Japońskie ROM-y. pret buduje angielskie wersje gier. Nie odczytano własnego pola, temp ani czcionki japońskiego Emerald; japońskie arkusze z sekcji 1 to te, które angielski ROM zawiera na potrzeby imion. Japońskich wersji Crystal i Red nie odczytano.30
- Grafika Final Fantasy VI. Deasemblacja ma zakresy i kod, a nie piksele; nie widziano układu kafelków okna ani wyglądu gradientu. Zakres angielskiej dużej czcionki, 5120 bajtów, nie jest całkowitą liczbą 22-bajtowych liter, bez wyjaśnienia. To, że tabela gradientu jest zużywana po jednej wartości na linię skanowania, pochodzi z komentarzy, a nie z prześledzenia.4
- Okresu migania w Crystal nie wyprowadzono; jego dwa stany odczytano z kodu.16
- Chrono Trigger nie został zmierzony w ogóle. Nie sklonowano żadnej publicznej deasemblacji, a mapa ROM-u w Data Crystal zwróciła HTTP 403.49
- Celeste, Sea of Stars i Eastward. Tylko ich listy języków na Steamie; tego, jak rysują tekst japoński i chiński, nie znaleziono w żadnym źródle od twórców.64
- Pixel Operator, m5x7 i m3x6 nie zostały pobrane ani zmierzone, a własne repozytorium Pixel Operator zwróciło HTTP 404; ich licencje podano tak, jak opisują je ich strony, w przypadku Pixel Operator wyłącznie według DaFont.565758
- Trzech nieodwzorowanych przez PixelMplus punktów kodowych JIS nie sprawdzono pod ich alternatywnymi punktami kodowymi.7
- Rozmiar DotGothic16. Nie ma siatki pikseli, więc rysowano go i składano w nominalnych 16 kropkach na em i w ich wielokrotnościach, co było moim wyborem na podstawie nazwy i skoku kropek; żadnego innego nominalnego rozmiaru nie próbowano, a jego liczby w sekcjach 6 i 7 dotyczą tylko tych rozmiarów.719
- Wygładzanie czcionek. Wszystkie rendery w sekcji 6 blokują wygładzanie czcionek; z domyślnym ustawieniem kontekstu bitmapy narysowano tylko jeden krój w jednym rozmiarze, PixelMplus10 w 10 pikselach, ze 178 częściowymi z 272 pikseli z tuszem. Pozostałych krojów i rozmiarów nie rysowano z włączonym wygładzaniem.9
- Aplikacja zmieniała się w trakcie lektury. Kiradex był edytowany 5 października, gdy powstawał ten artykuł. Wcześniej tego dnia przepisano obsługę dotknięć sceny, a jej linie przesunęły się o od 30 do 144, przy czym trzy pliki Swift świata nie były jeszcze w żadnym commicie; czternaście minut po mojej lekturze scena i rig znów się zmieniły. Artykuł odczytuje commit
334fbfez niezatwierdzonymi zmianami z tego dnia w stanie z chwili mojej lektury: pliki aplikacji o 12:28 czasu pacyficznego (kolekcjonerzy serwera i mapa miasteczka o 12:33), skrypty, które ją mierzą, o 12:34 i 12:36. Przypisy podają linie każdego pliku według tej lektury; brief nazywa symbole, a nie linie, a o 12:42 sprawdziłem, że każdy symbol, który wymienia, nadal tam był.11 - iPhone’y, na których działa aplikacja. Lista 33 iPhone’ów, z których trzy mają 2×, pochodzi ze środowiska uruchomieniowego symulatora iOS 27.0, odczytanego z Xcode na moim Macu; strony Apple z listą iPhone’ów obsługiwanych przez iOS 27 nie pobrano.97
- Marginesy obszaru bezpiecznego. Artykuł z tej serii o symulatorze iPhone’a Duo zmierzył zamknięty wyświetlacz zewnętrzny, szeroki na 466 punktów, z zawartością szeroką na 382 punkty wewnątrz 84-punktowego marginesu po stronie końcowej, czyli bocznego paska, w sondzie, która pokazywała pasek stanu, pasek narzędzi i pasek kart.95 Świat Kiradex ukrywa pasek stanu i wskaźnik ekranu początkowego i nie pokazuje żadnych pasków; kod umieszcza elementy świata wewnątrz obszaru bezpiecznego i mówi, że trzyma je to z dala od aparatów i bocznego paska, ale ani kod, ani tamten pomiar nie mówią, jak szeroki jest margines przy ukrytym pasku stanu, więc liczby dla Duo w sekcjach 10 i 12 podano w obu wariantach, bez marginesu bocznego i ze zmierzonymi 84 punktami. Marginesów bocznych pozostałych iPhone’ów w pionie nie zmierzono; liczby z briefu zakładają dla nich brak marginesów.94
- Dostępność RealityKit w konfiguracji Kiradex.
AccessibilityComponentjest udokumentowany dla iOS 17.0; nie zweryfikowano, czyRealityViewz wirtualną kamerą ortograficzną udostępnia elementy encji VoiceOver.84 - Liczby z briefu. Zaokrąglanie
m, dymek o krok mniejszy od pola i trzymany pod pułapem ustalanym przez każdy widok, 32-punktowy margines, kolejka pola i jej trzy linie, 8 pikseli czcionki ramki zamiast dopełnienia pola, 150-milisekundowe kroki znacznika, cztery ramki i sześć komórek dla komunikatów to propozycje, a nie fakty.
Czym ten artykuł różni się od artykułu o świecie. Sekcje 1, 2, 5 i 8 korygują po jednym jego stwierdzeniu, wraz z pomiarem; jego arytmetyka rozmiarów jest poprawna (sekcja 10). Trzy fakty, które ten artykuł bierze z innych artykułów serii, sprawdzono w aplikacji: 23 predefiniowane linie miasteczka zgadzają się z lines.py; uwaga z artykułu o postaciach, że dymek wciąż bierze swoje położenie z wysokości komórki, zgadza się z PlazaRig.headPoint, który dodaje do pozycji idącego połowę wysokości klatki arkusza i 4; a postać pod dymkiem to ta, którą opisuje aktualizacja tamtego artykułu, wysoka na 30 tekseli w komórce 32 na 40, co potwierdzają manifest aplikacji, rig.py i arkusz postaci. Tamten artykuł podaje szerokość tylko dla swojej wcześniejszej, 26-tekselowej postaci, około 14 w ramionach; sekcja 12 mierzy szerokość obecnej postaci na arkuszu.90100289396
Źródła, do których nie dotarłem. Mapa ROM-u Chrono Trigger w Data Crystal (HTTP 403); repozytorium Pixel Operator w NotABug (HTTP 404); pliki czcionek m5x7 i m3x6 (za pobraniem w modelu „zapłać, ile chcesz”, bez próby); oraz źródło od twórców na temat japońskiego i chińskiego tekstu w Celeste, Sea of Stars lub Eastward (nie znaleziono).49565758
12. Brief: co buduje Kiradex i jakie sprawdzenia musi przejść
Aplikacja poszła od tamtej pory naprzód. W commicie fc9d4876 z 15 października etykieta i wartość sceny są ustawiane w modyfikatorze widoku StageAccessibility (PlazaStage.swift, zastosowanym w linii 373, zdefiniowanym od linii 520), nadal „The plaza” ze śladem kroków jako wartością, a element znika, gdy scenę zakrywa jakieś miejsce. Poniższe sprawdzenia odnoszą się do wersji z 5 października i każde „dziś” oznacza tę wersję; punkt 8 dotyczy obu.101
Synteza autora. To, co następuje, jest moją propozycją, wyprowadzoną z sekcji od 1 do 11 i napisaną jako diff względem Kiradex w stanie z 5 października 2026, w commicie 334fbfe z niezatwierdzonymi zmianami z tego dnia w drzewie roboczym (sekcja 11): systemowy tekst o stałej szerokości w każdym widoku świata, jednoliniowy dymek w pogrubionym caption, pole WorldHUD u dołu, aplikacja tylko po angielsku z angielskimi predefiniowanymi liniami na serwerze, sprite szyldu bez liter i scena, którą VoiceOver czyta jako współrzędne.2211 Kierunek z artykułu o świecie pozostaje: tekst to grafika; dialog żyje w SwiftUI w skali interfejsu, a nie w tekselach świata; pole zbudowane z kafelków; dwie linie; maszyna do pisania; znacznik przejścia dalej.23 Zmienia się krój, z powodu podanego w sekcji 5, oraz arytmetyka, bo Dynamic Type trzeba zaokrąglać do siatki czcionki.7 Nic tu nie rysuje glifu, ramki, znacznika ani słowa z żadnej franczyzy; wszystko rysuje kuźnia albo jest złożone czcionką, której licencję cytuje sekcja 4. Uwagi prawne to moje odczytania, nie porada prawna. Nic z tego jeszcze nie zbudowano.
Każde poniższe sprawdzenie to takie, które skrypt, linia logu testu lub zrzut mogą zaliczyć albo oblać, z wyjątkiem jednego datowanego ręcznego sprawdzenia na urządzeniu w punkcie 8. Tam, gdzie wynik sprawdzenia zależy od urządzenia, rozmiaru tekstu, wybranej linii albo tego, co jeszcze pokazuje pole, sprawdzenie je wymienia. Brief nazywa kod aplikacji symbolami, a nie numerami linii, bo kod przesuwał się w trakcie mojej lektury; przypisy podają linie w stanie z mojej lektury, z commitem i godziną. Tam, gdzie dowodem za punktem jest pomiar na moim Macu, a sprawdzenie działa na iPhonie albo w jego symulatorze, rozstrzyga sprawdzenie, a nie Mac.
1. Jeden krój dla obu języków: PixelMplus10
Co. Dołączyć PixelMplus10-Regular.ttf i PixelMplus10-Bold.ttf (autorstwa itouhiro, z M+ BITMAP FONTS projektu M+ FONTS PROJECT; licencja: M+ FONT LICENSE, https://github.com/itouhiro/PixelMplus/blob/master/misc/mplus_bitmap_fonts/LICENSE_E) przez UIAppFonts, z plikiem LICENSE_E w pakiecie, i dodać w Ustawieniach, które dziś jej nie mają, sekcję podziękowań wymieniającą itouhiro i M+ FONTS PROJECT.1872 Używać go dla każdego słowa rysowanego nad światem: dymków, pola WorldHUD, tekstu szyldów, tablicy zadań i tabliczek wystawowych. Reszta aplikacji, Dex, skaner i Ustawienia, zachowuje krój systemowy.
Dlaczego ten. Dwa zmierzone kroje mają wszystkie trzy cechy, których wymagają reguły z sekcji 9: japoński, czystą siatkę i całkowitopikselowe przesunięcia. Misaki Gothic i Misaki Gothic 2nd rysują wszystkie 6879 znaków JIS X 0208 na 8-pikselowej siatce na linii bazowej, z 0 z 848 i 0 z 860 próbkowanych współrzędnych konturu poza nią i z przesunięciami 4 i 8. PixelMplus10 rysuje 6876 z 6879, każde kanji obu poziomów, na 10-pikselowej siatce na linii bazowej, z 0 z 1032 próbkowanych współrzędnych konturu poza nią w Regular i 0 z 1000 w Bold, z przesunięciami 5 dla łaciny i 10 dla kana.7 Brief wybiera PixelMplus10, co jest moim wyborem: to większy z dwóch krojów, 10 pikseli wobec 8 w Misaki, a kanji 8 na 8 w Misaki to te, których czytelność dla japońskiego czytelnika budzi najwięcej wątpliwości (sekcja 11); ma też zmierzoną odmianę Bold, więc pogrubiony tekst nie wymaga syntezy. Odrzucone, każdy z pomiarem, który go odrzuca:
- PixelMplus12 (M+ FONT LICENSE): jego siatka leży 0,39 piksela poniżej linii bazowej, a Core Text na macOS 27.0 rozmazał go w każdym rozmiarze.7
- k8x12 (trzyzdaniowa licencja na littlelimit.net, https://littlelimit.net/font.htm#license): przesunięcie jego kany wynosi 8,01 piksela, a litery łacińskie mają 4 piksele szerokości.717
- Misaki (ta sama licencja, https://littlelimit.net/font.htm#license): ma wszystkie trzy cechy, ale jej kanji są najmniejsze ze zmierzonych, 8 na 8, i ich czytelność dla japońskiego czytelnika budzi najwięcej wątpliwości (sekcja 11).617
- DotGothic16 (SIL Open Font License 1.1, https://github.com/google/fonts/blob/main/ofl/dotgothic16/OFL.txt): jego kropki nie leżą na żadnej siatce, a Core Text na macOS 27.0 narysował go z udziałem częściowych pikseli od 37,3% do 82,3% w rozmiarach od 16 do 64 pikseli.753
- Press Start 2P (SIL Open Font License 1.1 z Reserved Font Name, https://github.com/google/fonts/blob/main/ofl/pressstart2p/OFL.txt) i Silkscreen (SIL Open Font License 1.1, https://github.com/google/fonts/blob/main/ofl/silkscreen/OFL.txt): bez japońskiego.75455
- m5x7 (jej strona: „Creative Commons Zero v1.0 Universal”, https://managore.itch.io/m5x7) i m3x6 (jej strona: „free to use with attribution”, do swobodnego użytku z podaniem autora, bez pola licencji, https://managore.itch.io/m3x6): niezmierzone; łacińskie według opisów na ich własnych stronach; za pobraniem w modelu „zapłać, ile chcesz”.5758
- Pixel Operator (CC0 1.0 według notatek autora na DaFont, https://www.dafont.com/pixel-operator.font): niezmierzony; 241 glifów na DaFont, o wiele za mało dla japońskiego.56
Zestaw. Wspólny zestaw moich aplikacji kieruje każdy styl tekstu przez .kit941Font(role), a jego ścieżka czcionki niestandardowej skaluje czcionkę płynnie, co wyrzucałoby ten krój poza siatkę w 11 z 12 rozmiarów Body, czyli w warunkach, w których Core Text na Macu go rozmazał.887 Świat jest już zadeklarowaną powierzchnią graficzną, WorldInk, z własnymi dwoma kolorami.25 Należy dodać lokalny dla Kiradex modyfikator PixelText, zadeklarowany z komentarzem jako wyjątek tej powierzchni graficznej, który przyjmuje rolę z zestawu i stosuje zaokrąglenie z punktu 2; zaproponować go do zestawu jako przypadek rodziny czcionek dopiero po wydaniu.
Bez syntetycznych grubości. Pogrubiony tekst używa PixelMplus10-Bold; żadnego .weight(_:) na kroju pikselowym, bo nie zmierzono, co Core Text robi z grubością, którą musi syntetyzować (sekcja 11).
Sprawdzenia.
- Test jednostkowy, skoro Core Text jest dostępny w iOS, ładuje oba dołączone pliki i sprawdza: 1000 jednostek na em; każdy glif używany przez ciągi świata i predefiniowane linie, w obu językach, ma przesunięcie będące wielokrotnością 100 jednostek; punkty konturu glifów próbnych leżą w odległości do 4 jednostek od 100-jednostkowej siatki (0,04 piksela czcionki, tolerancja użyta przez sondę z sekcji 4). Krój, który nie przejdzie testu, nie może trafić do wydania.
- Test, że każdy ciąg, który świat może pokazać (predefiniowane linie, klucze świata w String Catalog, teksty szyldów), ma odpowiadające mu glify w kroju, bez czcionki zastępczej.
- Skrypt fazy budowania, który znajduje LICENSE_E w zbudowanym pakiecie aplikacji, oraz test interfejsu, który znajduje nazwę czcionki, jej autora i licencję w nowej sekcji podziękowań w Ustawieniach.
2. Rozmiary: Dynamic Type zaokrąglany do siatki
Co. Piksel czcionki to zawsze całkowita liczba m pikseli urządzenia. Dla danej roli należy odczytać rozmiar Dynamic Type, którego użyłby system (UIFontMetrics(forTextStyle:).scaledValue(for:) albo @ScaledMetric(relativeTo:) na rozmiarze bazowym roli), zaokrąglić ten rozmiar do setnej części punktu, tak by przeskalowana wartość odrobinę mniejsza od rozmiaru z tabeli, 14,999… zamiast 15, zaokrąglała się tak jak rozmiar z tabeli, a następnie ustawić m = max(m₀, ⌊size × displayScale / 10 + 0.5⌋), czyli najbliższą liczbę całkowitą z połówką zaokrąglaną w górę, gdzie m₀ = ⌈11 × displayScale / 10⌉ to najmniejsze m dające co najmniej 11 punktów, oraz pointSize = 10 × m / displayScale, i zastosować to przez Font.custom(_:fixedSize:), by system nie skalował rozmiaru ponownie.676968 Tytuł i tekst pola przechodzą z roli caption, której używają dziś, do roli body, tak by przy 3× pole przypisywało dwunastu rozmiarom Body m = 4, 5, 5, 5, 6, 6, 7, 8, 10, 12, 14 i 16: 16,67 punktu w rozmiarze domyślnym i 53,33 w AX5.2611 Najmniejsze, m₀ = 4 przy 3×, to 13,33 punktu, powyżej 11-punktowego minimum.1033 Przy 2×, które symulator iOS 27.0 wymienia dla trzech iPhone’ów (punkt 4), m₀ wynosi 3, czyli 15 punktów, a dwanaście rozmiarów Body daje 3, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9 i 11: 15 punktów w rozmiarze domyślnym.9711 Dymki używają o krok mniej, nigdy poniżej m₀ i nigdy powyżej pułapu ustalanego przez każdy widok (punkt 4).
Pozycjonowanie. Krój pikselowy jest ostry tylko wtedy, gdy jego linia bazowa trafia w piksel urządzenia (sekcja 6). Należy zaokrąglić każdy obliczony początek w nakładce świata do siatki pikseli urządzenia, (x × scale).rounded() / scale, łącznie z .position w PlazaStage.bubbles, nadać widokom tekstu szerokości w całkowitych pikselach urządzenia, a polu szerokość i wysokość w całkowitych pikselach czcionki (punkt 3).
Skok linii. PixelMplus10 ma 9 pikseli czcionki w górę i 2 w dół; skok linii należy ustawić na 12 pikseli czcionki, z jednym pustym rzędem między liniami, tak jak 15-pikselowe japońskie glify i 16-pikselowa łacińska komórka Emerald mieszczą się w 16-pikselowej linii.71 Przy m = 5 daje to 20 punktów na linię.33
Sprawdzenia.
- Test czystej funkcji zaokrąglania dla każdego DynamicTypeSize przy skalach wyświetlacza 2 i 3: wynik to całkowita liczba pikseli urządzenia na piksel czcionki, nigdy poniżej 11 punktów i nigdy mniejsza dla większego rozmiaru. Test podaje funkcji bezpośrednio rozmiary Body z tabeli HIG, a nie wartości odczytane z scaledValue(for:), których dokładnego wyniku nie widziałem: przy 3× dwanaście rozmiarów daje dwanaście wartości podanych wyżej, z 4,5 dla Small zaokrąglanym do 5, a przy 2× dwanaście wartości dla 2×; każdy rozmiar pomniejszony o jedną miliardową punktu daje to samo m dzięki zaokrągleniu do setnej.11
- Zrzuty placu z dymkiem i polem w rozmiarach Large, xxxLarge i AX3 w symulatorze iPhone’a 18 Pro Max oraz w Large w symulatorze iPhone’a SE (3. generacji) przy 2×, z nakładką dymków przeniesioną nad nakładki linii skanowania i winiety sceny (punkt 4), oraz skrypt, który wycina tekst i liczy częściowe piksele względem dwóch kolorów, tuszu i papieru: 0 wewnątrz prostokątów glifów, w dymku i w polu. Każdy zrzut wymaga uruchomionego serwera placu (KIRADEX_PLAZA=1) i jest wykonywany w ciągu 4 sekund od chwili, gdy gracz wypowie dzisiejszą najdłuższą linię z menu Say, bo każda linia wraca z pokoju102, a dymek jest widoczny tylko wtedy, gdy jego linia ma mniej niż 4 sekundy.25103 Ten sam skrypt, uruchomiony na obecnej wersji, zapisuje stan wyjściowy, który zastępuje. 0,0% z sekcji 6 to Core Text na Macu z wyłączonym wygładzaniem czcionek; ten zrzut jest sprawdzeniem, które rozstrzyga, czy iPhone rysuje tak samo.
3. Pole: ramka narysowana przez kuźnię, dwie linie, strony
Co. Pole WorldHUD zachowuje swoje miejsce: u dołu, wewnątrz obszaru bezpiecznego, a przy półotwartym Duo po szerszej stronie zgięcia.26 Jego 2-punktowy obrysowany prostokąt staje się ramką rysowaną przez kuźnię: frame-paper.png, 24 na 24 piksele czcionki, trzy na trzy kafelki po 8 (narożnik, krawędź i środek), w dwóch kolorach WorldInk i jednym odcieniu, oryginalna dla Kiradex. Widok PixelFrame rysuje ją w Canvas, każdy piksel źródłowy jako kwadrat m na m pikseli urządzenia: kafelki narożne w czterech narożnikach, a kafelki krawędzi i środka powtarzane na zewnątrz od lewego górnego narożnika, z ostatnim kafelkiem przyciętym tam, gdzie spotyka przeciwną stronę, tak że piksele ramki są pikselami tekstu. Aby każdy piksel źródłowy był pełnym kwadratem, szerokość pola to szerokość obszaru bezpiecznego, w którym dziś układa je WorldHUD, pomniejszona o 16 punktów z każdej strony i zaokrąglona w dół do całkowitych pikseli czcionki, czyli wielokrotności m pikseli urządzenia, a jego wysokość to również całkowite piksele czcionki: na 18 Pro Max w pionie przy m = 5, bez marginesów bocznych (założenie, nie pomiar), 1224 piksele urządzenia, czyli 244,8 piksela czcionki, zaokrąglone w dół do 1220, 244 pikseli czcionki.1194 Tekst leży wewnątrz ramki, 8 pikseli czcionki od krawędzi pola z każdej strony, zamiast dzisiejszych 8 punktów dopełnienia (mój wybór), co zostawia tam 228 pikseli czcionki na linię, 45 znaków łacińskich albo 22 pełnoszerokie.11 Tytuł miejsca stoi we własnej linii na górze każdej strony; pod nim tekst pokazuje po dwie linie naraz, jak w Emerald i Crystal.1 Tekst dłuższy niż dwie linie dzieli się na strony, a każda strona czeka pod znacznikiem przejścia dalej (punkt 6); jeśli strona kończy się spacją, spacja zostaje pominięta. box w WorldHUD (w WorldHUD.swift) przyjmuje strony zamiast jednego ciągu stanu.
Kolejka. Moje reguły, po to by to, co pokazuje pole, nigdy nie zależało od długości stanu:
- Strony stanu są własnymi stronami pola. Gdy stan się zmienia (na placu, gdy zmienia się cokolwiek w nim: stan połączenia, liczba obecnych, pora dnia, pora roku, zdobyte wstążki), jego strony są zastępowane, a pole pokazuje nowy stan od pierwszej strony, chyba że wyświetlana jest strona z następnej reguły.
- Tekst szyldu (punkt 5) i linia wypowiedziana w pobliżu przy dużych rozmiarach (punkt 4) to strony przed stanem: pokazywane od razu, każda po stronach tego rodzaju, które już czekają, w kolejności nadejścia, a po ostatniej z nich znów stan od pierwszej strony. Pole przechowuje strony co najwyżej trzech najnowszych linii; gdy przychodzi czwarta, odrzucana jest najstarsza z oczekujących, która nie należy do samego gracza (najstarsza z linii gracza tylko wtedy, gdy wszystkie oczekujące są jego), więc gracz, który wypowie jedną linię, nigdy jej nie traci. Nowy szyld zastępuje oczekujące strony poprzedniego szyldu.
- Tekst stanu zachowuje cały stan jako etykietę dostępności, niezależnie od wyświetlanej strony, tak jak dziś, by VoiceOver czytał go w całości, a istniejące testy interfejsu, które czytają
plaza-status,room-statusihall-status(frazę otwierającą, liczbę obecnych, pierwsze słowa pokoju), czytały to samo co teraz. Wyświetlana strona, w zakresie, w jakim została odsłonięta, jest wartością ukrytego elementu,plaza-pagena placu (<name>-page, nazwa moja). Zawiera linie tekstu strony bez tytułu miejsca i istnieje tylko przy argumencie uruchomienia testów chodu,-worldTrace(punkt 8); poniższe sprawdzenia go czytają i każde przekazuje-worldTrace.103
Ramki do wyboru. Emerald pozwala graczowi wybrać ramkę menu spośród 20 (pole dialogowe zachowuje jedną ramkę), a Final Fantasy VI ramkę okna spośród 8 z edytowalnymi kolorami.14 Należy zaoferować w Ustawieniach cztery ramki narysowane przez kuźnię, wszystkie darmowe, pierwsze trzy od początku, a czwartą po osiągnięciu poziomu; żadna ramka nigdy nie jest sprzedawana. Czy jakaś ramka będzie kiedyś nagrodą dla wspierających, to decyzja, której jeszcze nie podjąłem.
Sprawdzenia. Sprawdzenie w kuźni, że frame-paper.png i każda ramka alternatywna mają 24 na 24, używają tylko indeksów palety i mają identyczne przeciwległe krawędzie tam, gdzie tak mówi projekt; w symulatorze iPhone’a 18 Pro Max zrzut pola przy m = 4 (xSmall) i m = 5 (rozmiar domyślny), na którym skrypt ustala, że szerokość i wysokość pola w pikselach urządzenia są wielokrotnościami m, i porównuje jego ramkę, zewnętrzne 8 pikseli czcionki z każdej strony, piksel po pikselu z ramką, którą sam składa z pliku PNG w rozmiarze uchwyconego pola według tej samej reguły (narożniki w narożnikach, kafelki krawędzi i środka powtarzane od lewego górnego narożnika i przycinane przy przeciwnej stronie), każdy piksel źródłowy jako kwadrat m na m, z pominięciem każdego piksela, który PNG zostawia przezroczysty, gdzie prześwituje świat; oraz test interfejsu w tym samym symulatorze w rozmiarze domyślnym, bez serwera placu, tak by nie padła żadna linia, uruchomiony z -worldTrace i -worldBoxSample oraz stanem złożonym z trzech linii po 30 do 40 znaków połączonych znakami nowej linii, każda w granicach 45 znaków, które mieści tam linia, który czeka, aż plaza-page zrówna się z dwiema pierwszymi liniami, pomijając białe znaki, i dopiero wtedy, przy całej pierwszej stronie, dotyka pola i znajduje trzecią, wraz ze zrzutem pierwszej strony, na którym skrypt znajduje znacznik w prawym dolnym rogu pola.11 -worldBoxSample to argument testowy o nazwie mojego autorstwa, odczytywany w PlazaView tak jak -plazaTell (jego tellOnArrival) i przekazywany do WorldHUD jako stan zamiast stanu samego placu.92
4. Dymki: jedna linia, bez imienia, oba języki
Co. Zachować formę dymka: kremowy, z obramowaniem w kolorze tuszu, nad głową, krócej niż 4 sekundy.25 Złożyć go krojem pikselowym przy m dymka, z pikselami narożników ramki zamiast obrysowanego prostokąta, rysowanymi wewnątrz 8 punktów dopełnienia z każdej strony dymka, tak by, jak dzisiejsze obramowanie, nie dodawały szerokości. Żadnego imienia w dymku ani nad nim: mówiącym jest głowa, nad którą wisi dymek, a VoiceOver go nazywa (punkt 8). Predefiniowana linia nigdy się nie zawija; .fixedSize() zostaje.
Nad liniami skanowania. Dziś dymki są nakładką wewnątrz sceny, pod jej liniami skanowania, czarną linią o krycie 16% co 3 punkty (przy 3× pas 3 pikseli urządzenia na każde 9), i pod jej winietą, podczas gdy pole leży nad całą sceną.2526 Pod tymi warstwami papier dymka ciemnieje pasami przechodzącymi przez glify, więc jego tekst w żadnym rozmiarze nie jest tuszem i papierem. Brief przenosi nakładkę dymków nad nakładki linii skanowania i winiety, tam gdzie już leży pole, zamiast ograniczać sprawdzenie z punktu 2 do pola: to mój wybór, wynikający z reguły z sekcji 6, że krój pikselowy jest ostry tylko jako całkowite piksele urządzenia z tuszu i papieru, zastosowanej jednakowo do obu form mowy. W PlazaStage.swift .overlay dymków, ostatni modyfikator wewnątrz GeometryReader sceny, przechodzi za .overlay winiety (RadialGradient), z własnym GeometryReader dla rozmiaru widoku.25
Na ekranie. Dziś dymek jest wyśrodkowany nad głową mówiącego i nic nie utrzymuje go na ekranie, więc mówiący przy krawędzi wypycha część dymka poza ekran.25 Należy ograniczyć ramę dymka do obszaru bezpiecznego pomniejszonego o 16 punktów z każdej strony, czyli marginesu, który zostawia poniższa reguła szerokości, przesuwając dymek ze środka nad głową tylko tak daleko, jak wymaga tego ograniczenie. Obszar bezpieczny, a nie widok: scena ignoruje obszar bezpieczny (.ignoresSafeArea()), więc jej własna geometria sięga pod wszystko, co obszar bezpieczny utrzymuje wolne, a na Duo są to aparaty i boczny pasek, jak mówi komentarz w WorldHUD o jego własnym miejscu wewnątrz obszaru bezpiecznego.94 Dziś nic nie przekazuje scenie jej marginesów. PlazaView odczytuje je poza sceną i przekazuje do niej, tak jak CardViewer już robi to dla swojej karty (proxy.safeAreaInsets do CardStage); ograniczenie to czysta funkcja, BubblePlacement.frame(head:size:view:insets:), wywoływana z PlazaStage.bubbles(of:in:), a te same marginesy ustalają opisany niżej pułap dymka (nazwy są moje). Świat ukrywa pasek stanu i wskaźnik ekranu początkowego (.statusBarHidden() i .persistentSystemOverlays(.hidden) w widoku głównym); czy zwęża to marginesy, kod nie mówi, więc aplikacja je odczytuje, zamiast je zakładać.94
Reguła szerokości. Każda linia, w obu językach, mieści się jako jednoliniowy dymek na każdym iPhonie, na którym może działać aplikacja, gdy obszar bezpieczny nie ma marginesów bocznych; gwarantuje to opisane niżej ograniczenie serwera. Margines może zostawić za mało miejsca nawet dla m₀. Dla dzisiejszej najdłuższej linii, spośród wszystkich szerokości z listy symulatora iOS 27.0, dotyczy to każdej szerokości 3× do 393 punktów i obu szerokości 2× przy 84-punktowym marginesie Duo oraz 375-punktowych telefonów 2× przy 16 punktach z każdej strony. Linia na granicy ograniczenia serwera zawodzi w większej liczbie przypadków: japońska również na telefonach 402-punktowych z marginesem Duo (286 punktów miejsca wobec 296 przy m₀), łacińska tamże (302,67), na telefonie 3× o szerokości 414 punktów z marginesem (298) i na telefonach 360-punktowych przy 16 punktach z każdej strony (296): z 36 przypadków, dwunastu szerokości i trzech marginesów, 7 dla dzisiejszej linii, 8 dla japońskiej linii na granicy i 10 dla łacińskiej. Tam taka linia nie dostaje dymka i trafia do pola, a krótsze linie zachowują swoje dymki (niżej).11 Aplikacja jest zbudowana tylko na iPhone’a, od iOS 27.0, a środowisko uruchomieniowe symulatora iOS 27.0 zainstalowane z Xcode na moim Macu wymienia 33 iPhone’y: najwęższy przy 3× ma 360 punktów szerokości (iPhone 12 mini i 13 mini), a trzy mają 2×, iPhone SE 2. i 3. generacji o szerokości 375 punktów i iPhone 11 o szerokości 414.97 To lista urządzeń symulowanych przez SDK; nie pobrałem własnej listy Apple z iPhone’ami obsługiwanymi przez iOS 27. Miejsce na dymek to szerokość obszaru bezpiecznego pomniejszona o 32 punkty, czyli po 16 z każdej strony, które utrzymuje ograniczenie. Na telefonach w pionie, bez marginesów bocznych (założenie; żadnego nie zmierzono), to 408 na iPhonie 18 Pro Max, najwęższym z pięciu widoków zmierzonych w tej serii, ale 328 na telefonie 360-punktowym i 343 na 375-punktowych telefonach 2×. Na zewnętrznym wyświetlaczu Duo w pionie to 434 bez marginesu bocznego i 350, jeśli świat zachowa 84-punktowy margines po stronie końcowej, boczny pasek, który ta seria zmierzyła na zamkniętym wyświetlaczu (sekcja 11).11979533 Dzisiejsza najdłuższa linia, „Anyone have a card from the first years?”, licząca 40 znaków, ma jako dymek 283 punkty szerokości przy m = 4, 349 przy 5, 416 przy 6 i 549 przy 8 przy 3×, czyli glify plus dzisiejsze 8 punktów dopełnienia z każdej strony; 2-punktowe obramowanie rysuje się wewnątrz dymka i nie dodaje szerokości. Przy 2× ta sama linia ma 316 punktów przy m = 3 i 416 przy 4.1125 m dymka ma więc w każdym widoku pułap, największą wartość, przy której najdłuższa predefiniowana linia, w którymkolwiek języku, mieści się w dostępnym miejscu. Dla dzisiejszych linii, wszystkich po angielsku, to 5 na 18 Pro Max (349 w 408) i na każdym wymienionym iPhonie 3× od 390 do 440 punktów, 4 na telefonach 3× o szerokości 360 i 375 punktów i 3, najmniej, ile pozwala 2×, na telefonach 2×; na zewnętrznym wyświetlaczu Duo w pionie to 6 bez marginesu bocznego (416 w 434) i 5 ze zmierzonym paskiem (349,33 w 350). Japońska linia na granicy opisanego niżej ograniczenia serwera, 21 znaków pełnoszerokich, 210 pikseli czcionki przesunięć, obniżyłaby pułap Duo do 5 i 4, a telefonów 390- i 393-punktowych do 4 (siedem z wymienionych iPhone’ów ma 390 punktów szerokości, a cztery 393), pozostałe zostawiając bez zmian.1197 Aplikacja oblicza pułap z bezpiecznej szerokości przekazanej przez PlazaView i z linii przesłanych przez serwer, więc węższy obszar bezpieczny albo dłuższa linia, w tym japońska, mogą go obniżyć. Pułap jest jedną wartością dla wszystkich linii; to, co dzieje się tam, gdzie pułapu nie ma, rozstrzyga się dla każdej linii osobno, co jest moim wyborem: gdzie nawet m₀ nie mieści najdłuższej linii, linia, która mieści się przy m₀, zachowuje dymek przy m₀, a linia, która się nie mieści, nie dostaje dymka i trafia do pola, jako własne strony, jak przy dużych rozmiarach (niżej). Zachowanie dymka takiej linii przy m₀ złamałoby ograniczenie: w widoku 360-punktowym z 84-punktowym marginesem po stronie końcowej miejsca jest 244 punkty, a dzisiejsza najdłuższa linia przy m₀ ma 282,67.11 Serwer z kolei ogranicza każdą linię do tego, co mieści się na najwęższym telefonie danej skali w najmniejszym rozmiarze, bez marginesów bocznych: najwyżej 218 pikseli czcionki przesunięć, 43 znaki łacińskie albo 21 pełnoszerokich, mniejsza z wartości (360 − 48) × 3 ÷ 4 = 234 przy 3× i (375 − 48) × 2 ÷ 3 = 218 przy 2×. Japońska linia na tej granicy ma jako dymek 296 punktów przy m = 4 przy 3× i 331 przy m = 3 przy 2×; łacińska 302,67 i 338,50; obie mieszczą się w 350 punktach Duo z paskiem.1133 Marginesów bocznych telefonów w pionie nie zmierzono; poniższe zrzuty, które sprawdzają, czy dymek leży wewnątrz obszaru bezpiecznego, są miejscem, w którym jakiś by się ujawnił.
Najdłuższa predefiniowana linia jako dymek, według rozmiaru, przy 3×, na tle miejsca w każdym widoku; narysowane przez figures/make_figures.py na podstawie measure_kiradex_text.out.
Większy tekst. m dymka podąża za m pola, o krok mniej, nigdy poniżej m₀ i nigdy powyżej pułapu widoku: na 18 Pro Max osiąga 5, gdy pole jest w xLarge (m = 6), a od xxxLarge w górę, gdzie m pola wynosi 7 lub więcej, pozostaje na 5. Wszędzie tam, gdzie pułap trzyma dymek dwa lub więcej kroków poniżej pola (na 18 Pro Max od xxxLarge w górę, na telefonie 360-punktowym od xLarge w górę), czytelnik dostaje każdą linię wypowiedzianą w promieniu sześciu komórek od gracza także w polu, jako własne strony w rozmiarze pola: pseudonim mówiącego, dwukropek i linię, tak jak Emerald podaje mówiącego w linii (punkt 3 dzieli ją na strony; punkt 8 ją ogłasza). PlazaView, który przechowuje linie pokoju i dziś składa stan pola, składa te strony i stawia je przed stanem, według kolejki z punktu 3: pokazywane od razu, po stronach każdej linii, które już czekają, z najwyżej trzema przechowywanymi liniami. Umieszcza strony linii w kolejce pola w tej samej aktualizacji, która dodaje jej dymek, więc dymek takiej linii na ekranie oznacza, że jej strony są w kolejce. Nie zawsze jest to jedna strona. Pseudonim ma od 14 do 20 znaków, więc „pseudonim: linia” ma od 19 do 62, a przy ramce z punktu 3 linia pola na 18 Pro Max mieści 31 znaków łacińskich w xxxLarge, 17 w AX3 i 12 w AX5: dla każdego pseudonimu, jaki może utworzyć names.py, i każdej predefiniowanej linii daje to jedną lub dwie strony od xxxLarge do AX3 i do trzech w AX4 i AX5, a do czterech na telefonach 360-punktowych i 2× w największych rozmiarach oraz na zewnętrznym wyświetlaczu Duo, ze zmierzonym paskiem, w AX4 i AX5.1189 Samo pole nigdy nie jest ograniczane; zawija i dzieli się na strony. Strona dynamicTypeSize(_:) sugeruje widok dużej zawartości dla ograniczonego rozmiaru; brief na nim nie polega, bo własna strona tej przeglądarki przestrzega przed używaniem jej „as a replacement for proper Dynamic Type support” (jako zamiennika właściwej obsługi Dynamic Type), a żadna z tych stron nie mówi, czy otwiera się ona z nakładki z wyłączonym wykrywaniem dotknięć, takiej jak nakładka dymków.707125 Moje notatki badawcze proponowały zatrzymanie dymka na m = 8, ale najdłuższa linia przy 8 ma 549 punktów szerokości (notatki podają 553, licząc obramowanie, które leży wewnątrz dymka), więcej niż iPhone 18 Pro Max (440 punktów) i zewnętrzny wyświetlacz Duo w pionie (466), a notatki mierzyły granicę względem zewnętrznego wyświetlacza Duo, choć węższy jest 18 Pro Max, 440; brief idzie tu za szerokościami.2211
Sprawdzenia.
- Test czcionki w aplikacji (punkt 1) sprawdza też, czy tabela przesunięć PixelMplus10 zapisana w repozytorium, z jedną pozycją dla każdego znaku używanego przez predefiniowane linie w którymkolwiek języku, zgadza się z dołączoną czcionką. Zestaw testów serwera, który nie ma parsera czcionek (jego zależności to FastAPI, uvicorn, websockets, httpx i pytest), czyta tę tabelę, odrzuca linię zawierającą znak, którego w tabeli nie ma, i odrzuca linię, w którymkolwiek języku, której przesunięcia sumują się do ponad 218 pikseli czcionki, opisanej wyżej granicy.10411
- Test jednostkowy pułapu, czystej funkcji bezpiecznej szerokości, skali wyświetlacza i przesunięć linii: przy 3× daje, z dzisiejszą najdłuższą linią, 5 dla każdej wymienionej szerokości od 390 do 440 punktów, 4 dla 360 i 375, 6 dla 466 i 5 dla 382 (zewnętrzny wyświetlacz Duo bez zmierzonego paska i z nim), a przy 2× 3 dla 375 i 414; z japońską linią o 210 pikselach czcionki wśród linii 5 dla 466, 4 dla 382 i 4 dla 390 i 393; brak pułapu dla miejsca 244 punktów (360 punktów pomniejszone o 84-punktowy margines i 32) przy 3× z dzisiejszą najdłuższą linią; oraz test kompozytora w PlazaView, który w takim miejscu nie rysuje dymka dla dzisiejszej najdłuższej linii i umieszcza jej strony w polu, a „Hi!” rysuje jako dymek przy m₀ (36 punktów szerokości, w 244).11
- Test jednostkowy BubblePlacement stawia głowę mówiącego przy każdej krawędzi, w każdym narożniku i w środku widoków o szerokości 360, 375, 440 i 466 punktów, każdy bez marginesów obszaru bezpiecznego, z 16 punktami po stronie początkowej i końcowej oraz z 84-punktowym marginesem Duo po stronie końcowej, i dymki o różnych szerokościach aż do bezpiecznej szerokości danego przypadku pomniejszonej o 32 (od 244 punktów, czyli 360 minus 84 i 32, do 434), i stwierdza, że każdy dymek leży wewnątrz obszaru bezpiecznego pomniejszonego o 16 punktów z każdej strony oraz, wszędzie tam, gdzie x głowy leży wewnątrz tego pasa, obejmuje x głowy.11
- W symulatorach iPhone’a 18 Pro Max, iPhone’a 13 mini i iPhone’a SE (3. generacji) oraz na zewnętrznym wyświetlaczu iPhone’a Duo w jego symulatorze po cztery zrzuty (angielski i japoński, każdy w rozmiarze domyślnym i w AX3), z uruchomionym serwerem placu (KIRADEX_PLAZA=1) i aplikacją uruchomioną z -worldTrace, każdy z najdłuższą linią danego języka wypowiedzianą przez gracza; na każdym skrypt znajduje obramowanie dymka i sprawdza, że ma wysokość jednej linii tekstu, szerokość nie większą niż szerokość obszaru bezpiecznego pomniejszona o 32 punkty i że leży wewnątrz obszaru bezpiecznego pomniejszonego o 16 punktów z każdej strony. Skrypt bierze marginesy z testu interfejsu, który wykonuje zrzut i odczytuje je z ukrytego elementu włączanego przez -worldTrace (punkt 8), w którym PlazaView zapisuje odczytane marginesy (to moja propozycja); skrypt ustala też m dymka z pikseli jego glifów i sprawdza, że jest to mniejsza z wartości: m pola minus jeden (nigdy poniżej m₀) i pułap dla tej bezpiecznej szerokości, obliczony z linii danej wersji w obu językach, więc równa się pułapowi tylko tam, gdzie pułap go ogranicza. W rozmiarze domyślnym to 4 na telefonach 3× i na Duo, niezależnie od pułapu, oraz 3 na telefonie 2×; w AX3 to pułap, a na Duo rozstrzygają marginesy: przy dzisiejszych liniach angielskich 6 bez marginesu bocznego i 5 z paskiem, a z japońską linią na granicy ograniczenia serwera 5 i 4.119597
- Test interfejsu w symulatorze iPhone’a 18 Pro Max w AX3, gdzie m pola wynosi 12, a linia mieści 17 znaków łacińskich, wymaga serwera placu (KIRADEX_PLAZA=1) i żadnego innego gracza, uruchamia się z -worldTrace i tempem tekstu ustawionym na Natychmiast (punkt 6) przez klucz world.textSpeed wymieniony w punkcie 5, tak by każda strona pokazywała się w całości od razu po otwarciu, a dotknięcie pola przewracało dokładnie jedną stronę. Odczytuje pseudonim gracza z karty Me, wybiera „Hi!” z menu Say i czeka, w ciągu 5 sekund, na które pozwala istniejący test towarzystwa, na dymek Said: Hi!, tak że linia wróciła z pokoju i, zgodnie z regułą z punktu 4, jej strony są w kolejce pola, przed stanem, bez względu na długość stanu. Przed nią może stać linia jednego z czterech kolekcjonerów miasteczka, z których każdy mówi najwyżej raz na 20 sekund; kolejka przechowuje najwyżej trzy linie i nigdy nie odrzuca jedynej linii gracza (punkt 3), więc przed nią stoją najwyżej dwie linie, każda najwyżej po dwie strony w tym miejscu, a linie wypowiedziane później czekają za nią. Test czyta plaza-page przed każdym dotknięciem i dotyka pola tylko dopóty, dopóki strona nie jest pseudonimem, dwukropkiem, spacją i „Hi!”, co sekundę i najwyżej cztery razy, a zalicza, jeśli któryś odczyt znajdzie tę stronę, przy czym ostatni odczyt następuje sekundę po czwartym dotknięciu. Wybiera tę linię, bo „pseudonim: Hi!” jest tam jedną stroną dla każdego pseudonimu, jaki może utworzyć names.py, podczas gdy najdłuższa linia może zająć dwie. Loguje każdą odczytaną stronę.11103102
5. Szyldy i imiona
Szyldy. Grafika szyldu pozostaje bez liter, tak jak rysuje ją kit.py, by jeden atlas służył obu językom.29 Dotknięcie kafelka szyldu otwiera pole WorldHUD z tekstem szyldu, jako strony przed stanem (punkt 3), zamiast prowadzić tam gracza, jak dzieje się dziś. Scena odczytuje dziś dotknięcia przez DragGesture: touched(_:in:) natychmiast prowadzi gracza do podłoża pod palcem, z wyjątkiem kolekcjonera albo kiosku, które czekają na podniesienie palca, a lifted(_:in:) traktuje to podniesienie jako dotknięcie. Kafelek szyldu dołącza do kolekcjonera i kiosku w obu przypadkach: czeka na podniesienie palca, a lifted(_:in:) wywołuje dla niego domknięcie onSign, tak jak wywołuje onKiosk dla kiosku (nazwa jest moja).92 Teksty szyldów żyją razem z mapą: town.json zyskuje signs: { "7,10": { "en": ..., "ja": ... } }, z komórką jako kluczem, a sprawdzenia kuźni odrzucają komórkę szyldu bez obu języków.
Imiona. Żadnych stałych etykiet z imionami nad głowami. Pole kanonu podaje mówiącego w linii, Emerald w 6,5% swoich akapitów.1 Pseudonimy w Kiradex mają do 20 znaków; przy najmniejszym rozmiarze dymka, m = 4 przy 3× i 3 przy 2×, 20-znakowy pseudonim ma 133 punkty szerokości przy 3× i 150 przy 2×; przy 3× wisi to nad postacią szeroką na 16 tekseli przodem do kamery i 13 z profilu (30 wysokości, w komórce 32 na 40), czyli 42,7 punktu albo 34,7 na 18 Pro Max.119610033 Pseudonim jest widoczny w panelu, który otwiera dotknięcie, jak teraz, a VoiceOver go czyta (punkt 8).
Sprawdzenia. Test interfejsu nie może wiedzieć, w którym miejscu ekranu rysuje się komórka szyldu, bo kamera podąża za graczem, więc test uruchamia plac z -worldTrace i -plazaTapCell 7,10, argumentem testowym o nazwie mojego autorstwa, odczytywanym w ciele PlazaStage tak jak -plazaFacingDemo (w .task), który przesyła komórkę przez tę samą obsługę, wydzieloną z lifted(_:in:) do jednej metody. W symulatorze iPhone’a 18 Pro Max w rozmiarze domyślnym, bez serwera placu, tak by nie padła żadna linia, test przekazuje też -worldBoxSample z punktu 3 z jednostronicowym stanem „Sample.” oraz tempo tekstu ustawione na Natychmiast (punkt 6) przez jego klucz Ustawień w argumentach uruchomienia (klucz, world.textSpeed, jest mój, obok world.stick dla drążka). Czeka do 5 sekund, aż plaza-page zmieni się z „Sample.” na pierwszą stronę szyldu, potem ją odczytuje, dotyka pola i odczytuje znowu, najwyżej jedno dotknięcie na każde słowo tekstu szyldu (na każdy znak po japońsku), plus jedno, dopóki plaza-page nie pokaże „Sample.”, i stwierdza, że strony przed nią, połączone po kolei, są równe angielskiemu tekstowi szyldu, pomijając białe znaki, więc tekst szyldu na tyle długi, by dzielić się na strony, przechodzi tak samo jak krótki; po ponownym uruchomieniu z japońskim jako językiem aplikacji, tekstowi japońskiemu.92 Sprawdzenie kuźni odrzuca town.json z komórką S, której brakuje któregoś z języków.
6. Tempo tekstu i znacznik przejścia dalej
Co. Odsłanianie jak na maszynie do pisania, liczone w czasie, a nie w klatkach: shown = floor(elapsed × cps), czysta funkcja upływającego czasu, tempa i dwóch ustawień, Ogranicz ruch i działającego VoiceOver, tak by 60 i 120 herców odsłaniało tekst w tym samym tempie. Ustawienia oferują Wolno, 12 znaków na sekundę, czyli wolne ustawienie Crystal; Normalnie, 30, wartość domyślną, mój wybór między średnimi ustawieniami kanonu (15 w Emerald, 20 w Crystal) a jego szybkim ustawieniem 60; oraz Natychmiast. Dotknięcie w trakcie odsłaniania kończy stronę; dotknięcie skończonej strony ją przewraca.1 W tym celu pole przyjmuje dotknięcia, gdy odsłania tekst albo ma stronę po tej, która jest wyświetlana; na ostatniej stronie, pokazanej w całości, przepuszcza dotknięcia do podłoża, tak jak dziś robi to cały czas.26 To samo tempo w obu językach, tak jak Final Fantasy VI wypisuje oba po jednej literze na klatkę.4 Gdy włączone jest Ogranicz ruch albo działa VoiceOver, funkcja od razu zwraca całą stronę; to mój wybór, wynikający ze zdania z dokumentacji, że przy Ogranicz ruch „UI should avoid large animations”.83
Znacznik. Własny znacznik Kiradex narysowany przez kuźnię, klin skierowany w dół, 5 na 3 piksele czcionki, w prawym dolnym rogu pola, opadający o 0, 1, 2 i 1 piksel czcionki (kierunek to mój wybór; w Emerald, w moim odczytaniu, strzałka się unosi) co 150 milisekund, w cyklu 600 milisekund: 9-klatkowy krok Emerald przy 60 hercach.133
Sprawdzenia. Test jednostkowy funkcji odsłaniania przy 30 znakach na sekundę: 0 znaków przy t = 0 i 30 przy 1,000 sekundy, z upływającym czasem obliczanym z liczby taktów, czyli takty podzielone przez częstotliwość, przy 60 i 120 symulowanych taktach na sekundę, a nie sumowanym z przyrostów na takt (120 przyrostów po 1/120 zsumowanych w podwójnej precyzji daje 0,9999999999999989, co razy 30 daje 29,999999999999968, a po zaokrągleniu w dół 29); ta sama funkcja przy włączonym Ogranicz ruch, a osobno przy działającym VoiceOver, zwraca całą stronę przy t = 0; test interfejsu w symulatorze iPhone’a 18 Pro Max w rozmiarze domyślnym i w tempie Wolno, bez serwera placu, uruchomiony z -worldTrace i stanem -worldBoxSample z punktu 3, którego pierwsza strona to dwie linie po 30 do 40 znaków, co najmniej 60 znaków, a więc co najmniej 5 sekund odsłaniania przy 12 na sekundę, sprawdzający, że dotknięcie pola po sekundzie odsłaniania sprawia, że plaza-page w ciągu pół sekundy staje się całą stroną, podczas gdy samo odsłanianie pokazałoby do tego czasu 18 znaków (12 w chwili dotknięcia); test jednostkowy przesunięcia znacznika jako funkcji upływającego czasu, 0, 1, 2, 1 i 0 pikseli czcionki przy 0, 150, 300, 450 i 600 milisekundach; oraz dwusekundowe nagranie ekranu ze znacznikiem w symulatorze, w którym jego przesunięcia, po usunięciu powtórzeń, układają się w cykliczną kolejność 0, 1, 2, 1.33
7. Japoński
Co. Japoński najpierw dla tekstu świata, reszta aplikacji później. server/app/lines.py zachowuje kolejność indeksów, jak mówi jego komentarz, „add at the end only” (dodawać tylko na końcu), a każda linia staje się ciągiem angielskim i japońskim; serwer wysyła oba, a każdy klient pokazuje język, w którym czyta jego użytkownik, więc linia wypowiedziana na mieszanym placu jest czytana w dwóch językach na podstawie jednego indeksu.28 To, w moim odczytaniu, najlepszy argument za predefiniowanymi liniami w tej aplikacji. Ciągi świata, tytuły i stany WorldHUD oraz tytuły zadań, przechodzą do String Catalog z ja; teksty szyldów zostają przy mapie (punkt 5). Każdy japoński ciąg pisze lub przegląda osoba mówiąca po japońsku; żaden nie trafia do wydania przetłumaczony maszynowo i nieprzejrzany.
Łamanie linii. Predefiniowane linie nigdy się nie zawijają (punkt 4). Tekst pola pisze się tak, by w rozmiarze domyślnym mieścił się w dwóch liniach na stronę, tak jak skrypt Emerald łamano ręcznie.1 Tam, gdzie musi się zawijać, należy zachować ścisłą regułę: nie oznaczać tekstu pola językiem składu ja, chyba że zrzut pokaże, że ścisła reguła utrzymuje się z tym znacznikiem, bo Core Text na macOS ze znacznikiem ja pozwolił małym kana zaczynać linie w 6 z 7 szerokości w większości krojów (w 4 z 7 w k8x12 i k8x12S).777 Jeśli domyślne ustawienie systemu i tak zastosuje luźną regułę, zapisaną poprawką jest U+2060 WORD JOINER przed małą kana w tych nielicznych ciągach, które tego wymagają.
Sprawdzenia. Test, że każda linia i każdy klucz świata w String Catalog ma oba języki; test serwera z punktu 4, który odrzuca japońską predefiniowaną linię przekraczającą granicę 218 pikseli czcionki, 21 znaków pełnoszerokich; oraz, w symulatorze iPhone’a 18 Pro Max w rozmiarze domyślnym z językiem urządzenia ustawionym na japoński, bez serwera placu i w tempie Natychmiast, uruchomionym z -worldTrace, zrzut każdej strony akapitu testowego w polu, przekazanego jako stan -worldBoxSample z punktu 3 i napisanego tak, by przy 22 znakach pełnoszerokich, które mieści tam linia, łamanie wypadało przed małą kana, na którym żadna linia akapitu poza pierwszą nie zaczyna się od znaku z listy początków linii JLREQ ani od formy zgodności któregoś z nich.11 Ta lista jest pełna, 83 pozycje z dziesięciu klas wymienionych w regule 3.1.7 (trzy znaki należą do dwóch klas), zapisana w celu testowym; sonda z sekcji 7 używała listy częściowej (sekcja 7). Japońskie oprogramowanie do składu zwykle stawia na ich miejscu formy zgodności, które Unicode koduje na potrzeby konwersji w obie strony ze starszymi standardami, jak zauważa JLREQ o nawiasie otwierającym: „the compatibility counterpart U+FF08 FULLWIDTH LEFT PARENTHESIS is used in the Japanese context in most Japanese text layout software” (w kontekście japońskim większość japońskiego oprogramowania do składu używa odpowiednika zgodności U+FF08 FULLWIDTH LEFT PARENTHESIS).21 Lista dodaje więc każdy znak z bloku Halfwidth and Fullwidth Forms w Unicode, którego normalizacja zgodności (NFKC) jest jedną z 83 pozycji, 24 znaki, w tym ! (U+FF01), ? (U+FF1F), ) (U+FF09), :, ;, , i ., oraz półszerokie 。, 、, ー i małe katakana od ァ (U+FF67); a także ~ (U+FF5E), pełnoszeroką tyldę, obok falistej kreski 〜 z tej samej klasy, co jest moim dodatkiem, bo jej normalizacją jest tylda ASCII, której lista nie zawiera: razem 25.98 PixelMplus10 odwzorowuje 54 z 83 pozycji i 24 z 25 form. Nie rysuje pozostałych 29 pozycji, wśród nich 〜 (U+301C), ゠, półpauzy, ‼ i małych katakana od U+31F0, ani pełnoszerokiego ⦆ (U+FF60), a test pokrycia z punktu 1 trzyma każdy z tych znaków z dala od ciągów świata, więc zrzut może pokazać tylko 75 znaków, które krój rysuje (54 pozycje, z których trzy, ), 〕 i ], występują w dwóch klasach, co daje 51 znaków, oraz 24 formy).98 Skrypt porównuje pierwszą komórkę glifu każdej linii, piksel po pikselu, z glifami kroju dla tych 75 znaków przy m zrzutu, narysowanymi z dołączonej czcionki za pomocą CTFontGetGlyphsForCharacters, i nie znajduje żadnego dopasowania. Piksele pozwalają odróżnić znak z listy od pozostałych znaków akapitu tylko tam, gdzie krój rysuje je inaczej. Narysowane w 10 pikselach, dwa z 75 znaków mają w kroju identycznego co do piksela bliźniaka: ‐ (U+2010), identyczny z łącznikiem niełamiącym U+2011, oraz dwukropek, identyczny z literą modyfikującą dwukropek U+A789; żadna z 24 form go nie ma. Akapit testowy nie używa więc żadnego z bliźniaków; ー (U+30FC) i kanji 一 (U+4E00) się różnią, 200 pikseli urządzenia z tuszem wobec 250 przy m = 5, więc 一 może stać w akapicie w dowolnym miejscu.9821 Wynik z sekcji 7 to Core Text na Macu; ten zrzut rozstrzyga, co robi SwiftUI w iOS.
8. VoiceOver nad sceną
Dziś. VoiceOver słyszy „The plaza”, a potem ślad z testów chodu, na przykład „12,4 down” (12,4 w dół).25 Ta wartość jest dla testów, nie dla ludzi.
Co.
1. Etykieta sceny pozostaje „The plaza” albo nazwą pokoju, a jej wartość staje się zdaniem, z którego człowiek może skorzystać: gdzie stoi kolekcjoner, według punktów orientacyjnych, kto jest w pobliżu i co unosi (na przykład Przy stawie. W pobliżu trzech kolekcjonerów. Unosi kartę.), zgodnie z regułą accessibilityValue, że wartość mówi to, czego nie mówi etykieta.79 Wszystko, co dziś trafia przez stepTrace do wartości sceny, zostaje przeniesione, bez względu na to, co to zapisuje: gdy czytałem PlazaStage.swift, był to ślad kroków z rig.onStep, komunikat o błędzie budowania z domknięcia make w RealityView i raport sondy powietrza z .task, który odczytuje -airProbe, a kilka minut później czwarty zapis, z demonstracji chodu dodanej tego dnia. Trafia to do ukrytego elementu, który istnieje tylko wtedy, gdy aplikację uruchomiono z -worldTrace (nazwa moja), przekazywanym przez testy chodu, obok strony pola (punkt 3) i marginesów odczytanych przez PlazaView (punkt 4), a testy, które czytają wartość sceny, wśród nich testTheCollectorFacesTheWayItWalks i testTheChimneysSmoke, czekający na value CONTAINS 'smoke 18 ', czytają odtąd ten element.25103
2. accessibilityDirectTouch(_:options:) na scenie, tak by użytkownik VoiceOver mógł po aktywowaniu dotykać, żeby chodzić (iOS 17.0).80
3. Encja każdego kolekcjonera dostaje AccessibilityComponent z pseudonimem jako etykietą i tym, co trzyma, jako wartością, tak by VoiceOver mógł go opisać (iOS 17.0).84
4. Linia wypowiedziana w promieniu sześciu komórek od gracza jest publikowana jako AccessibilityNotification.Announcement (na przykład Glacier Shuffle 4471: Nice card!) z niskim priorytetem, najwyżej jedna na dwie sekundy, czyli z tą samą częstotliwością, z jaką serwer przyjmuje linie.8291
5. Otwierana strona pola jest ogłaszana w całości, chyba że powtarza linię, którą punkt 4 już ogłosił, jak robią to strony dużego tekstu z punktu 4.
Sprawdzenia. Test interfejsu, że wartość sceny nie zawiera współrzędnych bez -worldTrace; w symulatorze iOS 27 test z XCUIVoiceOverService, który włącza VoiceOver, przenosi go na scenę i sprawdza, że to, co zwraca currentSpeech(), zawiera etykietę sceny i żadnej pary liczb w formie x,y ze śladu kroków;87 ręczne sprawdzenie na urządzeniu z włączonym VoiceOver, z datą i numerem kompilacji, że encja kolekcjonera jest osiągalna i odczytywana, bo nie zweryfikowano, czy RealityView z wirtualną kamerą udostępnia elementy AccessibilityComponent (sekcja 11); oraz test jednostkowy ogranicznika komunikatów, w którym dwie linie w ciągu 2 sekund dają jeden komunikat.
9. Nigdy, w typografii i oknach
- Żadnego glifu, ramki, strzałki ani grafiki okna z ROM-u czy dekompilacji jakiejkolwiek gry w aplikacji ani w kuźni. Arkusze pret i ff6 są tu mierzone, a nigdy kopiowane. Ramkę, znacznik i szyld Kiradex rysuje kuźnia; jego typografia to licencjonowana czcionka.
- Żadnych znaków ani ligatur z franczyzy. Charmap Emerald zawiera specjalne glify dla słów z franczyzy; nic podobnego nie jest rysowane, a żaden ciąg nie używa nazw, jakie franczyza nadaje rzeczom.31
- Żadnej czcionki bez jej pliku licencji w ręku: krój trafia do wydania tylko z tekstem licencji zacytowanym w sekcji 4 lub 5 i zapisanym obok dowodów.
- Żadnego zmodyfikowanego kroju OFL pod jego Reserved Font Name. Press Start 2P taką nazwę deklaruje; jeśli którykolwiek krój OFL zostanie kiedyś zmodyfikowany, zmienia się jego nazwę.54
- Żadnego tekstu wtopionego w grafikę świata: szyldy, witryny sklepów i tablice pozostają bez liter, by żaden atlas nigdy nie był zależny od języka.
Sprawdzenie. Skrypt oblicza dwa skróty SHA-256 każdego obrazu, który zapisuje kuźnia i który zawiera katalog zasobów, jeden z bajtów pliku, a drugi z jego zdekodowanych pikseli, i porównuje je z tymi samymi dwoma skrótami każdego mierzonego tu pliku PNG (graphics/fonts i graphics/text_window z pret, ramek Crystal oraz gfx/font/font.png z Crystal i Red). Lista tych skrótów, a nie same obrazy, trafia do repozytorium jako scripts/forge/canon_digests.txt (nazwa mojego wyboru, obok kit.py kuźni), tak by sprawdzenie działało bez folderu dowodów, a każde dopasowanie przerywa budowanie. Jego własny test to kontrola negatywna, która nie wymaga żadnego pliku z gry: dodaje skróty jednego wyniku kuźni do kopii listy i sprawdza, że skrypt wtedy zawodzi. Ten sam skrypt wypisuje do przeglądu, bez przerywania budowania, każdą komórkę 8 na 8 z wyniku kuźni, której jednobitowa maska albo jej odwrotność pasuje do komórki z tamtych arkuszy. Skróty całych obrazów nie pozwalają odtworzyć tych komórek, więc lista zawiera też skrót SHA-256 każdej odrębnej maski komórki z arkuszy, według jednej reguły (mojej), stosowanej tak samo do wyników kuźni: piksel jest ustawiony tam, gdzie jego kolor różni się od lewego górnego piksela obrazu; komórki biegną po 8 na 8 od lewego górnego narożnika, a niepełna komórka przy krawędzi jest pomijana; pusta albo pełna komórka jest pomijana po obu stronach, bo arkusze mają ich wiele (sam japanese_normal.png ma 430 pustych komórek z 1024); a maska to jej 64 bity rząd po rzędzie, 8 bajtów. Dla 50 arkuszy daje to 7746 zachowanych komórek i 1484 odrębne skróty.99 Kopia przekolorowana, przeskalowana albo przerysowana przechodzi przez skróty; reguła 1 opiera się na tym, że kuźnia rysuje każdy zasób z kodu, oraz na tym przeglądzie, a nie na samym sprawdzeniu. Ponadto sprawdzenie ciągów proponowane we wcześniejszych briefach tej serii zostaje rozszerzone o abrewiacje z franczyzy z charmapu Emerald.105
Poza tym briefem
- Krój pikselowy dla całej aplikacji. Dex, skaner i Ustawienia zachowują krój systemowy; typografia pikselowa jest dla świata.
- Chiński i koreański. Wszystkie cztery sprawdzone współczesne gry pikselowe mają chiński, a dwie z nich, Stardew Valley i Sea of Stars, mają koreański, ale nic tu nie zmierzyło czcionki pikselowej dla żadnego z tych języków.64
- Misaki ani żaden 8-kropkowy krój do czytania tekstu. Ma najmniejsze ze zmierzonych kanji; czytelność w tym rozmiarze dla japońskiego czytelnika nie została zmierzona.6
- Etykiety z imionami nad głowami i rubryka na imię w polu.1
- Japoński tłumaczony maszynowo, którego nikt nie przejrzał.
- Syntetyczne pogrubienie lub kursywa na kroju pikselowym.
- Ramki na sprzedaż. Każda ramka jest darmowa; ramka dla wspierających czeka na decyzję.
- Tekst rysowany w scenie RealityKit. Powód z artykułu o świecie i arytmetyka z sekcji 10 trzymają go w SwiftUI.2311
Najważniejsze wnioski
Dla rysujących grafikę
- Budować ramki z kafelków: narożnik, krawędź i środek. Menu Emerald wybierają spośród 20 ramek 24 na 24 piksele, trzy na trzy kafelki; jego pole dialogowe rysuje się z 14-kafelkowego paska z odbitą dolną krawędzią; ramki Crystal mają po 6 kafelków 1-bitowych.1
- Dać znacznikowi przejścia dalej wolny rytm. Strzałka Emerald przesuwa się o 0, 1, 2 i 1 piksel od pozycji spoczynkowej, krok co 9 klatek, 0,6 sekundy na cykl.1
- Trzymać słowa z dala od grafiki świata. Szyld bez liter obsługuje każdy język z jednego atlasu; słowa trafiają do pola.29
- Rysować japoński w 8 do 12 pikselach i liczyć się z tym, że kana jest dwa razy szersza od litery łacińskiej: 8 wobec 4, 10 wobec 5, 12 wobec 6 w każdej zmierzonej pikselowej czcionce z japońskim.7
Dla budujących silnik
- Wybierać czcionkę pikselową po pliku, nie po nazwie: sprawdzić siatkę, przesunięcie względem linii bazowej i przesunięcia znaków. Siatka PixelMplus12, 0,39 piksela poza linią bazową, rozmazała się w każdym rozmiarze w Core Text na macOS; 8,01-pikselowe przesunięcie kany w k8x12 rozmazało się, gdy tylko glify ułożono dokładnie.7
- Rysować tylko przy całkowitej liczbie pikseli urządzenia na piksel czcionki: 0,0% częściowych pikseli przy całkowitych wielokrotnościach siatki na linii bazowej z całkowitopikselowymi przesunięciami (Misaki, PixelMplus10, Press Start 2P), od 19,4% do 82,2% między nimi, w Core Text na Macu z wyłączonym wygładzaniem czcionek. Przy domyślnym wygładzaniu kontekstu PixelMplus10 w swoim własnym rozmiarze miał 178 częściowych z 272 pikseli z tuszem, więc całkowita wielokrotność jest konieczna, ale nie wystarczająca; przed zaufaniem któremukolwiek z tych wyników na iPhonie należy zrobić z niego zrzut.79
- Respektować Dynamic Type przez zaokrąglanie do siatki. Em 10-pikselowy jest całkowity w 1 z 12 rozmiarów Body przy 3×; przeskalowany rozmiar należy zaokrąglić do całkowitej liczby pikseli urządzenia na piksel czcionki i ustawić jako rozmiar stały.768
- Liczyć tempo tekstu w czasie, a nie w klatkach, tak by 60 i 120 herców odsłaniało tekst jednakowo; 60 znaków na sekundę to najszybsze tempo w każdej zmierzonej grze, a od 15 do 20 to ustawienie średnie tam, gdzie gra je ma.14
- Dawać VoiceOver zdanie, a nie ślad.
accessibilityValuesłuży temu, czego nie mówi etykieta,AccessibilityComponentopisuje encję, aXCUIVoiceOverServicepozwala od iOS 27.0 testować, co mówi VoiceOver.798487
Dla projektujących pętlę rozgrywki
- Dwie linie naraz, u dołu. Pole Emerald zajmuje 30% ekranu, a pole Crystal 33%, każde po dwie linie.1
- Pisać pod szerokość. Scenarzyści Emerald utrzymali 95% z 24 100 linii w granicach 38 znaków w oknie, które mieści 41, i żadna nie wyszła poza okno.1
- Podawać mówiącego w linii, nie nad głową; pole Emerald nie ma rubryki na imię.1
- Planować japoński od początku: ma go każda z czterech sprawdzonych współczesnych gier pikselowych. Czcionka obejmująca JIS X 0208 rysuje około siedmiu tysięcy znaków; gra z ustalonym skryptem może obejść się mniejszą liczbą, jak japoński ROM Final Fantasy VI z 1248 literami, ale wtedy każdy nowy ciąg trzeba sprawdzić względem czcionki.64584
Często zadawane pytania
Jak duże powinno być pole tekstowe w grze pixel art?
W kieszonkowych grach Pokémon to dolna jedna trzecia ekranu i dwie linie. Ramka dialogowa Emerald ma 30 na 6 kafelków, 240 na 48 pikseli, 30% wysokości ekranu Game Boya Advance, wokół okna tekstu 27 na 4 kafelki, które mieści dwie linie 16-pikselowej czcionki. Pole w Crystal ma 20 na 6 kafelków, 33% ekranu Game Boya, dwie linie po 18 znaków, a w Red takie samo, 20 na 6. Final Fantasy VI otwiera swoje okno u góry albo u dołu ekranu.12345
Jak szybki powinien być efekt tekstu pisanego jak na maszynie?
Kanon liczy klatki. Emerald i FireRed wypisują znak co 8, 4 lub 1 klatkę, czyli 7,5, 15 lub 60 na sekundę przy 60 hercach, oraz jeden na klatkę, gdy A lub B jest przytrzymany po naciśnięciu w trakcie wypisywania; Crystal i Red wypisują 12, 20 lub 60 na sekundę; Final Fantasy VI wypisuje dialogi w świecie gry po jednej literze na klatkę, 60 na sekundę, bez żadnego ustawienia (jego sześciostopniowe tempo komunikatów określa, jak długo czeka komunikat w walce). Na współczesnym telefonie należy liczyć znaki w czasie, a nie w klatkach, by tempo nie zmieniało się przy 120 hercach. Każda zmierzona gra kończy na 60 na sekundę, a średnie ustawienia Emerald i Crystal to 15 i 20; poniższy brief proponuje 30 jako wartość domyślną, co jest moim wyborem między tymi średnimi ustawieniami a szybkimi 60, a nie zmierzonym ustawieniem.1144
Które czcionki pikselowe obsługują japoński?
Spośród zmierzonych Misaki (8 na 8 kropek), k8x12 (8 na 12) i DotGothic16 rysują wszystkie 6879 znaków JIS X 0208, a PixelMplus10 i PixelMplus12 wszystkie poza trzema punktami kodowymi. Misaki, k8x12 i PixelMplus są objęte trzyzdaniową darmową licencją, która pozwala używać, kopiować i rozpowszechniać je „either commercially or noncommercially” (komercyjnie lub niekomercyjnie); DotGothic16 jest objęta licencją SIL Open Font License 1.1. W pomiarach Core Text na macOS 27.0 Misaki i PixelMplus10 to dwa kroje z czystą siatką na linii bazowej i całkowitopikselowymi przesunięciami, w 8 i 10 pikselach; siatka PixelMplus12 leży 0,39 piksela poniżej linii bazowej, przesunięcie kany w k8x12 wynosi 8,01 piksela, a kropki DotGothic16 nie leżą na żadnej siatce pikseli.67171853
Dlaczego moja czcionka pikselowa wygląda na iPhonie na rozmytą?
Zwykle dlatego, że piksel czcionki nie jest całkowitą liczbą pikseli urządzenia. W Core Text na macOS 27.0 z wyłączonym wygładzaniem czcionek pikselowe czcionki TrueType z czystą siatką na linii bazowej i całkowitopikselowymi przesunięciami (Misaki, PixelMplus10 i Press Start 2P) rysowały się bez częściowych pikseli przy całkowitych wielokrotnościach siatki i z rozmazaniem od 19,4% do 82,2% pikseli z tuszem między nimi. Przy domyślnym wygładzaniu kontekstu rysowania rozmyła się nawet całkowita wielokrotność: PixelMplus10 w swoich własnych 10 pikselach dał 178 częściowych z 272 pikseli z tuszem.9 Dwie usterki w pliku czcionki powodują rozmycie w każdym rozmiarze: siatka przesunięta względem linii bazowej, jak w PixelMplus12 o 0,39 piksela, oraz niecałkowite przesunięcie znaków, jak 8,01-pikselowe przesunięcie kany w k8x12, gdy glify są ułożone dokładnie. Pomiar wykonano na Macu; rozstrzyga to dopiero sprawdzenie przez zrzut na iPhonie.7
Czy czcionka pikselowa może obsługiwać Dynamic Type?
Tylko przez zaokrąglanie. Z dwunastu rozmiarów Body w iOS, od 14 do 53 punktów, em 10-pikselowy trafia w całkowite piksele urządzenia przy 3× tylko w 40 punktach, więc czcionka pikselowa, która skaluje się płynnie przez Font.custom(_:size:relativeTo:), wypada z siatki w pozostałych jedenastu; w Core Text na Macu wypadnięcie z siatki oznaczało rozmazanie, a co SwiftUI narysuje na iPhonie, rozstrzyga zrzut. Przeskalowany rozmiar należy odczytać przez UIFontMetrics albo ScaledMetric, zaokrąglić do całkowitej liczby pikseli urządzenia na piksel czcionki i ustawić przez Font.custom(_:fixedSize:). Apple prosi, by czcionka niestandardowa implementowała zachowania czcionek systemowych, w tym Dynamic Type, oraz by aplikacja w miarę możliwości pozwalała powiększyć tekst co najmniej o 200 procent.710206768
Czy Core Text przestrzega japońskich reguł łamania linii?
W moim pomiarze na macOS 27.0 tak, ściśle, gdy tekst nie miał znacznika języka: akapit o długości 131 znaków złamał się w siedmiu szerokościach tak, że żadna linia nie zaczynała się od znaku z listy początków linii sondy (listy częściowej, złożonej głównie ze znaków JLREQ, z siedmioma w formach pełnej szerokości), w Hiragino Sans i ośmiu krojach pikselowych. Ze znacznikiem ja przeszedł na luźniejszą regułę, którą opisują wymagania W3C dotyczące składu japońskiego, i mała kana otwierała linię w 6 z 7 szerokości w większości krojów. Nie zmierzono, co robi SwiftUI na iPhonie z japońskim jako językiem urządzenia.721
Ilu znaków potrzebuje japońska czcionka pikselowa?
JIS X 0208, zwykły cel pokrycia, liczy 6879 znaków: 524 niebędące kanji, 2965 kanji poziomu 1 i 3390 kanji poziomu 2. Producent Final Fantasy Pixel Remaster powiedział Siliconerze, że japońska czcionka pikselowa wymagała „around 7,500 characters” (około 7500 znaków), wobec „a few hundred” (kilkuset) dla języków zachodnich. Gra z ustalonym skryptem może obejść się mniejszą liczbą: japoński ROM Final Fantasy VI ma w czcionce dialogów 1248 liter.584
Każda liczba w tym artykule pochodzi ze skryptu uruchamianego ponownie na zapisanych źródłach, ze strony zapisanej 5 października 2026 albo z datowanego odczytu kodu Kiradex, a przypisy mówią, z czego; liczby z Core Text pochodzą z macOS 27.0, nie z iOS. Brief w sekcji 12 jest moją propozycją i tak go oznaczono; nic z niego jeszcze nie zbudowano.
Powiązane na tej stronie: Światy pixel art na iPhonie to pierwszy artykuł tej serii, z przepisem na RealityKit i kierunkiem dla tekstu, który ten artykuł mierzy; Ludzie w pixel art: postacie i kreator na iPhonie to kolekcjoner, nad którym wiszą dymki; Budowle w pixel art: domy, hale i wnętrza na iPhonie zawiera kuźnię, której szyld pozostaje bez liter; Ruch w pixel art: chód, kamera i drzwi na iPhonie zawiera zegary 60 i 120 herców, względem których odmierza się odsłanianie; Pikselowe miasteczko żyje, gdy nikogo nie ma online zawiera predefiniowane linie i ich częstotliwość; Pokoje w pixel art i Trasy w pixel art to briefy, których sprawdzenia ten rozszerza; Jak oddać hołd starym grom Pokémon, niczego od nich nie pożyczając wyjaśnia, dlaczego kanon się mierzy, a nigdy nie kopiuje; a Xcode 27.1 beta: aplikacja w symulatorze iPhone Duo omawia symulator, w którym wykonuje się zrzut dymka.
Źródła
-
Pomiar autora, 5 października 2026:
measure_gen_text.pyw folderze dowodów autora do tego artykułu, wynikmeasure_gen_text.out. Dane wejściowe z pokeemerald od pret w731ad5b:src/text.c(sFontInfos, przypadekRENDER_STATE_HANDLE_CHAR,sWindowVerticalScrollSpeeds,sDownArrowYCoords, funkcje japońskich glifów),src/menu.c(sTextSpeedFrameDelays,sStandardTextBox_WindowTemplates,WindowFunc_DrawDialogueFramewyliczone dla standardowego okna),src/fonts.cicharmap.txt(szerokości glifów),graphics/text_window/*.png(rozmiary),include/text_window.h, każda linia zdata/text/*.incidata/maps/*/scripts.incpodzielona przy\n,\li\pi zmierzona przez tabelę szerokości (504 pliki, 24 100 linii bez symbolu zastępczego, 3000 z nim, 0 pominiętych) orazgraphics/fonts/japanese_*.png(komórki niepuste). Z pokefirered w037335f:src/new_menu_helpers.c. Z pokecrystal w5beda23:constants/hardware.inc,constants/text_constants.asm,home/print_text.asm,gfx/font.asm, pliki PNG ramek igfx/font/font.png. Z pokered wd2704a6:data/text_boxes.asm,constants/ram_constants.asm,gfx/font/font.png. Znaki na sekundę to 60 podzielone przez liczbę klatek na znak. Odczytanie drukarki Emerald (opóźnienie zapisywane pomniejszone o jeden przezAddTextPrinter, a potem odliczane, tak że znak trwa tyle klatek, ile wynosi opóźnienie), warunki opóźnienia automatycznego przewijania zsrc/battle_message.cisrc/scrcmd.coraz dwuliniowe odczytanie stałych Crystal skrypt sprawdza asercjami, a nie uruchamia. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/menu.cw commicie731ad5b(sTextSpeedFrameDelaysw linii 77;sStandardTextBox_WindowTemplatesw linii 84;LoadMessageBoxGfxiLoadUserWindowBorderGfxw liniach 212 i 213;WindowFunc_DrawDialogueFramewywoływane w linii 218, z wypełnieniami używającymiBG_TILE_V_FLIPod linii 378), odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/src/menu.c. ↩↩↩↩↩↩↩ -
pret,
pokecrystal/constants/text_constants.asmw commicie5beda23(linie od 24 do 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) iconstants/hardware.inc(linie 922 i 923,SCREEN_WIDTH20 iSCREEN_HEIGHT18), odczyt 5 października 2026, https://github.com/pret/pokecrystal/blob/master/constants/text_constants.asm. ↩↩↩↩ -
Pomiar autora, 5 października 2026:
measure_ff6.pyw folderze dowodów autora do tego artykułu, wynikmeasure_ff6.out. Dane wejściowe z everything8215/ff6 w8130132:tools/rip_list_en.jsonitools/rip_list_jp.json(zakresy bajtów grafik okien, palet i czcionek),src/menu/config.asm(ConfigWindowNumPos,LoadWindowPal,ConfigColorBarTextList,ChangeConfigOption_02),src/menu/menu_sram.asm,src/btlgfx/gfx_cmd.asm(MsgSpeedTbl,mess_wait), każdy plik wsrc/, który czyta$1d4d,src/field/text.asm(LoadLetterGfx, granice x linii,CalcTextWidth,NewLine,UpdateDlgTextOneLine, tytuł mapy),src/gfx/font_gfx.asm(FontWidth),src/field/reset.asm(pętla świata gry),src/field/hdma.asm(DlgWindowFixedColorTbl) itools/char_table/(kanji.json,kana.json). Jedna litera na klatkę i jedna wartość gradientu na linię skanowania to odczytania kodu, sprawdzane asercjami, a nie uruchamiane. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Unicode Consortium,
JIS0208.TXT, tabela odwzorowań JIS X 0208 na Unicode, wersja 2.0, umieszczona w OBSOLETE/EASTASIA, pobrana i zapisana 5 października 2026, https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT. Policzona przezmeasure_coretext.swiftimeasure_bdf.py: 6879 znaków; 524 niebędące kanji, w tym 169 kana w rzędach 4 i 5; 2965 kanji poziomu 1 w rzędach od 16 do 47; 3390 kanji poziomu 2 w rzędach od 48 do 84. ↩↩↩↩↩↩↩↩ -
Pomiar autora, 5 października 2026:
measure_bdf.pyw folderze dowodów autora do tego artykułu, wynikmeasure_bdf.out. Dane wejściowe: pliki BDF Misaki Gothic, Misaki Gothic 2nd i Misaki Mincho oraz k8x12, k8x12L i k8x12S (wydania 2021-05-05) i zapisanyJIS0208.TXT; skrypt podaje rozmiar w pikselach każdej czcionki, prostokąt ograniczający, wydłużenie górne i dolne, liczbę znaków, szerokości przesunięć, glify pełnoszerokie, których tusz wypełnia komórkę w którymś kierunku, to samo dla kanji (od U+4E00 do U+9FFF) oraz narysowane znaki JIS X 0208. ↩↩↩↩↩↩↩↩↩↩↩ -
Pomiar autora, 5 października 2026:
measure_coretext.swiftw folderze dowodów autora do tego artykułu, skompilowany zswiftc -Oi uruchomiony na macOS 27.0 (kompilacja 26A428), wynikmeasure_coretext.out, nie w iOS. Dane wejściowe: Misaki Gothic, Misaki Gothic 2nd, k8x12 i k8x12S (wydania TTF 2021-05-05), PixelMplus10 Regular i Bold oraz PixelMplus12 Regular (PixelMplus wd89b95f), DotGothic16, Press Start 2P i Silkscreen (google/fonts w9710da1), Hiragino Sans W3 jako kontrola systemowa oraz zapisanyJIS0208.TXT. Skrypt podaje liczbę glifów i tabele bitmap każdej czcionki (CTFontCopyAvailableTables); jej siatkę projektową (najmniejszą liczbę pikseli na em od 4 do 40, na której 95% punktów konturu glifów próbnych leży w odległości do 0,04 piksela, przy jednym stałym przesunięciu dozwolonym w każdej osi); jej przesunięcia łacińskie i kana; jej pokrycie ASCII i JIS X 0208; udział częściowych pikseli z tuszem (od 8 do 247 z 255) przy rysowaniu linii próbnej do 8-bitowej szarej bitmapy w rozmiarach od 1 do 4 razy siatka, z antyaliasingiem włączonym, wyłączonym oraz włączonym przy włączonym pozycjonowaniu subpikselowym i wyłączonej kwantyzacji, bez dopuszczenia wygładzania czcionek; dla PixelMplus12 to samo z linią bazową przesuniętą o wartość przesunięcia; rozmiary Body i Caption 1 z Human Interface Guidelines, w których piksel czcionki jest całkowity przy 3×; oraz pierwszy znak każdej linii 131-znakowego japońskiego akapitu złożonego przezCTFramesetterw szerokościach od 8 do 20 em, bez znacznika i zkCTLanguageAttributeName“ja”, w porównaniu z listą znaków zakazanych na początku linii. DotGothic16 nie ma siatki projektowej, więc rysuje się go w nominalnych 16 kropkach na em (62,5 jednostki na kropkę) i w rozmiarach od 1,25 do 4 razy większych, a składa w 32 pikselach na em, w rozmiarze wybranym na podstawie nazwy i skoku kropek zmeasure_outline_coords.out, który wynik drukuje. Każdy render blokuje wygładzanie czcionek (setAllowsFontSmoothing(false)). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Jenni Lada, „Interview: Preparing Final Fantasy Pixel Remaster for the Switch and PS4”, Siliconera, 2 czerwca 2023, producent Naofumi Takuma o nowej opcji czcionki („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” wobec „a few hundred”), dostęp 5 października 2026, https://www.siliconera.com/interview-preparing-final-fantasy-pixel-remaster-for-the-switch-and-ps4/. ↩↩↩↩↩↩
-
Pomiar autora, 5 października 2026:
figures/dump_glyphs.swiftw folderze szkicu tego artykułu, skompilowany zswiftc -Oi uruchomiony na macOS 27.0 (26A428), wynikfigures/glyphs.txt: PixelMplus10 Regular w 10 pikselach narysowany przez Core Text, „Nice card!” z 0 częściowymi z 94 pikseli z tuszem, gdy wygładzanie czcionek jest zablokowane (tak jak rysujemeasure_coretext.swift), i ze 178 z 272 przy domyślnym ustawieniu kontekstu bitmapy; japońska linia próbna 0 z 164 i 287 z 451. ↩↩↩↩↩↩↩↩↩↩ -
Apple, Human Interface Guidelines, „Typography” (domyślny i minimalny rozmiar tekstu, 17 i 11 punktów w iOS i 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”; tabele Dynamic Type dla iOS), dostęp 5 października 2026, https://developer.apple.com/design/human-interface-guidelines/typography. ↩↩↩↩↩↩↩
-
Pomiar autora, 5 października 2026:
measure_kiradex_text.pyw folderze dowodów autora do tego artykułu, wynikmeasure_kiradex_text.out, uruchomiony o 12:34 czasu pacyficznego na Kiradex w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, na drzewie roboczym edytowanym tego dnia; skrypt czyta pliki w całości, według symboli, a nie linii. Dane wejściowe, odczytane wyłącznie z repozytorium Kiradex w stanie z dysku:server/app/lines.py(23 linie, od 3 do 40 znaków, mediana 16),server/app/names.py(pseudonimy z dwóch słów z list po 20 i czterech cyfr, najwyżej 20 znaków), każde wywołanie.kit941FontwKiradex/World/*.swift(caption 10, label 3, body 3, title 3, w czterech plikach), rodzina czcionek motywu, modyfikatory dymka, modyfikatory dostępności sceny, przeszukanie kodu aplikacji pod kątemAccessibilityComponent,accessibilityDirectTouch,updatesFrequently,AccessibilityNotification,typesettingLanguage,UIFontMetrics,ScaledMetricidynamicTypeSize(po 0),knownRegions(Base, en) oraz liczba plików.xcstringsi.lproj(0); skala świata według regułyPlazaRig.fitdla rozmiarów urządzeń, które ta seria zmierzyła w profilach symulatora Xcode 27 (iPhone 18 Pro Max 440 na 956 punktów, zewnętrzny wyświetlacz iPhone’a Duo 466 na 678, wewnętrzny 669 na 951, wszystkie przy 3×); oraz, zmeasure_coretext.out, em i przesunięcia każdej kandydującej czcionki, dające liczbę znaków w linii pola po 48-punktowym marginesie, szerokości dymka dla najdłuższej predefiniowanej linii przym= 4, 5, 6 i 8 (283, 349, 416 i 549 punktów: jej przesunięcia w tym rozmiarze plus 8 punktów dopełnienia dymka z każdej strony, bo obramowanie rysuje się wewnątrz dymka) oraz rozmiary Body zaokrąglone domdla emu 10-pikselowego, z połówką zaokrąglaną w górę. Dodane później 5 października, z wcześniejszym skryptem i wynikiem zachowanymi obok: zaokrąglenie przy 2× z najmniejszymmdającym co najmniej 11 punktów (4 przy 3×, 3 przy 2×); pole z ramką z briefu (szerokość pomniejszona o 32 punkty i zaokrąglona w dół do całkowitych pikseli czcionki, 8 pikseli czcionki ramki z każdej strony; z 1224 pikseli urządzenia do 1220 na 18 Pro Max przym= 5, 45 znaków łacińskich albo 22 pełnoszerokie w linii); strony „pseudonim: linia” dla wszystkich 400 par słów znames.pyi wszystkich 23 linii w dwunastu rozmiarach Body w pięciu widokach (dwie linie na stronę, zachłanne łamanie przy spacjach, każdy drukowalny znak ASCII o szerokości 5 pikseli czcionki wedługprobe_line_start_glyphs.out); pułap dymka na każdym iPhonie zmeasure_ios27_iphones.out; ograniczenie serwera do 218 pikseli czcionki; oraz suma 120 dodawań 1/120. Dodane później tego dnia, z wcześniejszym skryptem i wynikiem zachowanymi obok i każdą wcześniejszą linią wyniku bez zmian: miejsce i pułap według bezpiecznej szerokości, bez marginesów bocznych na telefonach oraz, na zewnętrznym wyświetlaczu Duo w pionie, zarówno bez marginesu, jak i z 84-punktowym marginesem po stronie końcowej zmierzonym w artykule serii o Duo, odczytanym z tamtego artykułu (bezpieczna szerokość 382, miejsce 350, pułap 5 przy 349,33 punktu; pole w rozmiarze domyślnym 38 znaków łacińskich albo 19 pełnoszerokich pod ramką, 40 albo 20 po dzisiejszym marginesie); strony „pseudonim: linia” przy tej bezpiecznej szerokości; szerokości dla testu rozmieszczenia; ograniczenie serwera w tym miejscu (296 i 302,67 punktu przym= 4, w 350); oraz, zserver/app/npcs.py, czterej kolekcjonerzy miasteczka, ich pseudonimy, strony ich linii w AX3 na 18 Pro Max (najwyżej 2) i limit dotknięć dla testu z punktu 4, (3 − 1) linie × 2 strony, jedno dotknięcie na stronę w tempie Natychmiast: 4. 15 października tę ostatnią linię przeliczono dla testu w obecnym brzmieniu, z tych samych danych wejściowych, na ile pokazuje to wynik (plik sceny z kopii z 5 października odczytanej o 12:28;lines.py,names.pyinpcs.pybez zmian od 2 i 3 października; pozostałe dane wejściowe odczytane z bieżącego drzewa w commiciefc9d4876, który wydrukował te same linie), z wcześniejszym skryptem i wynikiem zachowanymi obok; każda inna linia wyniku drukuje się jak wcześniej. Dodane później 15 października, z wcześniejszym skryptem i wynikiem zachowanymi obok i każdą wcześniejszą linią bez zmian: przypadki testu rozmieszczenia, w których dzisiejsza najdłuższa linia nie mieści się przy żadnym dozwolonymm(360 i 375 punktów przy 3× i 375 przy 2× z 84-punktowym marginesem oraz 375 przy 2× z 16 z każdej strony: w pierwszym 244 punkty miejsca wobec 282,67 punktu przym₀); pułap z japońską linią na granicy ograniczenia serwera, 21 × 10 = 210 pikseli czcionki (5 na 18 Pro Max, 5 i 4 na Duo bez paska i z nim, 4 przy 360 punktach przy 3×, 3 przy 2×); orazmdymka, które powinny znaleźć zrzuty, mniejsza z wartościmpola minus jeden, nigdy poniżejm₀, i pułapu dla obu języków (4 w rozmiarze domyślnym w widokach 3×, 3 w widoku 2×; w AX3 pułap). Dodane tego samego wieczoru, w ten sam sposób: każda szerokość zmeasure_ios27_iphones.out, bez marginesów bocznych, z 16 punktami z każdej strony i z 84-punktowym marginesem po stronie końcowej, z dzisiejszym pułapem dla angielskiego, pułapem z japońską linią na granicy i oboma (bez marginesów: 4 przy 360 i 375, 4 przy 390 i 393 z linią japońską, 5 od 402 do 440, 3 przy 2×); przypadki, w których dzisiejsza najdłuższa linia nie mieści się przy żadnym dozwolonymm(z 84-punktowym marginesem przy 360, 375, 390 i 393 punktach przy 3× oraz przy 375 i 414 przy 2×; z 16 z każdej strony przy 375 przy 2×); oraz „Hi!” przym₀, 36 punktów przy 3× i 38,50 przy 2×, co mieści się we wszystkich przypadkach. Dodane po nim: ta sama tabela z łacińską linią na granicy, 43 × 5 = 215 pikseli czcionki, obok dzisiejszej linii i japońskiej, oraz przypadki, w których każda z nich nie mieści się przy żadnym dozwolonymm: 7, 8 i 10 z 36. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokered/data/text_boxes.asmw commicied2704a6, linia 10 (db MESSAGE_BOX, 0, 12, 19, 17), odczyt 5 października 2026, https://github.com/pret/pokered/blob/master/data/text_boxes.asm. ↩↩ -
pret,
pokeemerald/src/text.cw commicie731ad5b(sFontInfosod linii 119;sDownArrowYCoords{ 0, 1, 2, 1 }w linii 75 isWindowVerticalScrollSpeedsw linii 76;AddTextPrinterustawiającetextSpeed = speedw linii 281 i--sTempTextPrinter.textSpeed;w linii 296;downArrowDelay = 8strzałki w linii 832; w przypadkuRENDER_STATE_HANDLE_CHARif (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp)w linii 944, odliczanieif (textPrinter->delayCounter && textPrinter->textSpeed)w linii 947,canABSpeedUpPrinti nowe naciśnięcie A lub B ustawiającehasPrintBeenSpedUpw liniach od 950 do 953 orazif (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll)ustawiające licznik na 3, a w przeciwnym razie natextSpeed, w liniach od 958 do 961;EXT_CTRL_CODE_JPNw linii 1094;DecompressGlyph_Normalod linii 1853 iGetGlyphWidth_Normalod linii 1887), odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/src/text.c. ↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokefirered/src/text.cw commicie037335f, linie od 639 do 647 (w przypadkuRENDER_STATE_HANDLE_CHARif (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp)ustawiające licznik na 0 orazgTextFlags.canABSpeedUpPrint && JOY_NEW(A_BUTTON | B_BUTTON)ustawiającehasPrintBeenSpedUpw trakcie odliczania opóźnienia), odczyt 5 października 2026, https://github.com/pret/pokefirered/blob/master/src/text.c. ↩↩↩ -
pret,
pokecrystal/home/print_text.asmw commicie5beda23(linia 4, „The text speed setting in wOptions is actually a frame count”, orazPrintLetterDelayczekające jedną klatkę na literę, gdy A lub B jest przytrzymany) ipokered/constants/ram_constants.asmwd2704a6(linie od 38 do 40:TEXT_DELAY_FAST1,TEXT_DELAY_MEDIUM3,TEXT_DELAY_SLOW5), odczyt 5 października 2026, https://github.com/pret/pokecrystal/blob/master/home/print_text.asm. ↩↩ -
pret,
pokecrystal/home/joypad.asmw commicie5beda23(WaitPressAorB_BlinkCursorw linii 342;BlinkCursorw linii 442, podmieniające kafelek whlcoord 18, 17między'▼'a'─'), odczyt 5 października 2026, https://github.com/pret/pokecrystal/blob/master/home/joypad.asm. Okresu migania nie wyprowadzono. ↩↩↩ -
Num Kadoma, littlelimit.net, „font”, sekcja licencji Misaki, k8x12 i innych czcionek autora („These fonts are free software. Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.”), dostęp 5 października 2026, https://littlelimit.net/font.htm#license. ↩↩↩↩↩
-
itouhiro, PixelMplus,
README.md(TrueType wyłącznie z konturami, „埋め込みビットマップは なし”; „JIS第1・第2水準のすべての漢字を収録” i ISO-8859-1; PixelMplus12 i PixelMplus10; zbudowane z M+ BITMAP FONTS; licencja M+ FONT LICENSE) imisc/mplus_bitmap_fonts/LICENSE_E(trzy zdania M+ FONT LICENSE), w commicied89b95f, odczyt 5 października 2026, https://github.com/itouhiro/PixelMplus/blob/master/misc/mplus_bitmap_fonts/LICENSE_E. ↩↩↩↩↩ -
Pomiar autora, 5 października 2026:
measure_outline_coords.swiftw folderze dowodów autora do tego artykułu, skompilowany i uruchomiony na macOS 27.0, wynikmeasure_outline_coords.out: surowe współrzędne x i y konturuH,A,oiあw PixelMplus12 Regular i DotGothic16 orazH,Aiow Silkscreen, w jednostkach przy 1000 na 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).”), dostęp 5 października 2026, https://developer.apple.com/design/human-interface-guidelines/accessibility. ↩↩↩
-
W3C, „Requirements for Japanese Text Layout”, Working Group Note, 11 sierpnia 2020, sekcja 3.1.7 (reguła zakazu na początku linii i uwaga o mniej ścisłym zestawie reguł), dostęp 5 października 2026, https://www.w3.org/TR/jlreq/. ↩↩↩↩↩↩↩↩
-
Dossier badawcze autora do tego artykułu, 5 października 2026 (prywatne): powyższe pomiary, studia przypadków i pierwszy szkic briefu dla Kiradex. ↩↩↩
-
Blake Crosley, „Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew”, blakecrosley.com, 3 października 2026: reguła 10 („Text is art”, „use font size 16, 32, 48, etc” ze strony m5x7 i zdanie o ramce okna tekstu 24 na 24 w Emerald), arytmetyka rozmiarów tekstu przy k = 8 i k = 6, akapit o Pixel Remaster cytujący GoNintendo w sprawie około 7500 japońskich znaków oraz plan tekstu („m5x7 at integer multiples in a 24-by-24 nine-slice box, two lines, typewriter reveal, a two-frame advance arrow”), odczyt 5 października 2026, https://blakecrosley.com/blog/pixel-art-world-on-iphone. Tamten artykuł jest tu cytowany, a nie zmieniany. ↩↩↩↩↩↩↩↩↩↩
-
Kod Kiradex,
Kiradex/KiradexTheme.swift, linia 12 (let fontFamily: Kit941.FontFamily = .systemDesign(.monospaced)), odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne). ↩↩ -
Kod Kiradex,
Kiradex/World/PlazaStage.swift, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne): linie 146, 155 i 189 (stepTraceustawiany na błąd budowania w domknięciu make wRealityView, na każdy krok wrig.onSteporaz, przy-airProbe, na raport powietrza w.task; odczyt tego samego pliku o 12:42 pokazał czwarty,stepTrace = "lap done", przy nowym-plazaLapDemo), linie od 58 do 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.”), linie od 313 do 315 (.accessibilityElement(),.accessibilityLabel("The plaza"),.accessibilityValue(stepTrace)), linie od 289 do 293 (dymki, ostatni.overlaywewnątrzGeometryReadersceny z linii od 121 do 294), linie od 295 do 309 (dwie nakładki sceny nad nim: linie skanowania,.black.opacity(0.16)o wysokości jednego punktu co 3 punkty, oraz winietaRadialGradient), linie od 402 do 418 (bubbles(of:in:): linia pokazywana, dopókitimeline.date.timeIntervalSince(said.at) < 4, umieszczana w punkcie głowy w widoku pomniejszonym oKit941.Spacing.lg, bez żadnego ograniczania, z wyłączonym wykrywaniem dotknięć) oraz linie od 444 do 459 (widokBubble,.padding(.horizontal, Kit941.Spacing.sm), a potem.overlay(Rectangle().strokeBorder(...)), jego komentarz „A line said, as the old games drew it: a cream box with a dark border.”, pogrubiony caption,WorldInk.paper, obramowanie w kolorze tuszuKit941.Spacing.xxs,.fixedSize(),.accessibilityLabel("Said: \(text)")). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Kod Kiradex,
Kiradex/World/WorldHUD.swift, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne); plik nosił datę 4 października i nie zmienił się w trakcie odczytu: linie od 31 do 38 (WorldInk, „The world’s own colours, as the old games drew a text box: cream paper”, i jego kolor papieru), linie od 40 do 44 (linia miejsca „in a text box at the bottom. Half open, the box keeps to the wider side of the fold.”), linie od 61 do 79 (elementy wGeometryReader, z marginesem.padding(Kit941.Spacing.md), pole na tę szerokość lub, przy półotwarciu, na szerokość szerszego panelu pomniejszoną oKit941.Spacing.mdz każdej strony) oraz linie od 84 do 101 (pole: tytuł w pogrubionym caption, wielkimi literami; stan w półgrubym caption, identyfikator<name>-status, na placuplaza-status; tło z papieru; obramowanie w kolorze tuszuKit941.Spacing.xxs;.allowsHitTesting(false)pod komentarzem „Taps go through to the ground under it.”); orazKiradex/World/PlazaStage.swift, linie 581 i 582, gdziePlazaViewkładzieWorldHUDna całej scenie zwordsjako stanem, i linie od 725 do 736 (words: fraza pory dnia, liczba obecnych i linia porady, zdobyte wstążki i fraza pory roku). Odstępy zestawu,Sources/Kit941/Tokens/Spacing.swiftw 941Kit:xxs2,xs4,sm8,md16,lg24 punkty na iPhonie. ↩↩↩↩↩↩↩ -
Kod Kiradex,
Kiradex.xcodeproj/project.pbxproj, linie od 1138 do 1141 (knownRegions = (Base, en)), odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne); do 12:42 w pliku przybyło linii. ↩↩ -
Kod Kiradex,
server/app/lines.py, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne): docstring „The preset lines: everything anyone can say in the plaza.”, 23 pozycjeLINES(od linii 5) i komentarz w linii 20, przed indeksem 14, „Indices are fixed; add at the end only, the app shows them by number.” ↩↩↩↩ -
Kod Kiradex, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie
334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne):scripts/forge/kit.py, linie 529 i 530 (def sign():, „A hanging sign on a bracket, 12 × 11.”),Kiradex/World/TileMap.swift, linia 52 (litera mapy"S": .sign), orazKiradex/Resources/World/town.json(jedna komórkaS, w x 7, rząd 10, odczytana modułemjsonPythona o 12:33). ↩↩↩↩ -
Metoda. Dekompilacje pret, płytkie klony odczytane w pokeemerald
731ad5b(1 października 2026), pokefirered037335f(26 września 2026), pokecrystal5beda23(29 września 2026) i pokeredd2704a6(22 września 2026), https://github.com/pret. Final Fantasy VI z deasemblacji everything8215/ff6 w813013276c952fdd27edcf7b8b86f17291542cbf(14 września 2026), która zawiera kod i listy zrzutów, a nie grafiki z ROM-u, https://github.com/everything8215/ff6. PixelMplus sklonowany płytko wd89b95f04eeb95bf98bb09cb9234a2f99d186df5(2 czerwca 2013); Misaki i k8x12, wydania 2021-05-05 w TTF i BDF, pobrane ze strony autora; Press Start 2P, Silkscreen i DotGothic16 (pliki czcionek,OFL.txtiMETADATA.pb) zgoogle/fontsw9710da1(30 września 2026). Strony internetowe pobrane i zapisane 5 października 2026. Trzynaście skryptów w folderze dowodów autora do tego artykułu, dziesięć w Pythonie i trzy w Swifcie, oraz jeszcze jeden skrypt w Swifcie razem z ilustracjami do tego artykułu,dump_glyphs.swift, daje każdą zmierzoną liczbę, każdy z wynikiem zapisanym obok. pret buduje angielskie wersje gier; nie odczytano żadnego japońskiego ROM-u. ↩↩↩↩↩↩ -
pret,
pokeemerald/src/fonts.c(gFontNormalLatinGlyphWidthsod linii 148) ipokeemerald/charmap.txt(linie od 50 do 52, specjalne glify dla słów z franczyzy) w commicie731ad5b, odczyt 5 października 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 widocznych linii; 240 punktów na widoczną linię), dostęp 5 października 2026, https://problemkaputt.de/gbatek-lcd-dimensions-and-timings.htm. ↩
-
Arytmetyka autora na zmierzonych liczbach: 216 ÷ 5,25 = 41 znaków; 59,737 ÷ 1 = 59,7 znaku na sekundę; 191 × 1,25² = 298 pikseli; −33 jednostki PixelMplus12 ÷ 83,33 jednostki na piksel = −0,396 piksela (sonda podaje −0,39, środek swojego przedziału); 7500 wobec 6879; 11 z 12 i 9 z 12 rozmiarów Body poza siatką, jako dopełnienie 1 i 3 całkowitych; 14 ÷ 17 = 82% i 53 ÷ 17 = 312%; 13,33 ÷ 16,67 = 80% i 53,33 ÷ 16,67 = 320%; 11 punktów ÷ 2 punkty na teksel = 5,5 teksela; 4 × 10 ÷ 3 = 13,33 punktu; 12 pikseli czcionki × 5 ÷ 3 = 20 punktów; 440 − 32 = 408, 360 − 32 = 328 i 375 − 32 = 343 punkty; 40 × 5 × 3 ÷ 2 + 16 = 316 punktów przy 2×; ⌈11 × 3 ÷ 10⌉ = 4 i ⌈11 × 2 ÷ 10⌉ = 3, oraz 10 × 3 ÷ 2 = 15 punktów; (360 − 48) × 3 ÷ 4 = 234 i (375 − 48) × 2 ÷ 3 = 218 pikseli czcionki, oraz 218 ÷ 5 = 43 znaki łacińskie i 218 ÷ 10 = 21 pełnoszerokich; 21 × 10 × 4 ÷ 3 + 16 = 296 i 21 × 10 × 3 ÷ 2 + 16 = 331 punktów; (440 − 32) × 3 = 1224 piksele urządzenia, ÷ 5 = 244,8, a więc 244 piksele czcionki (1220 pikseli urządzenia), minus 2 × 8 = 228, ÷ 5 = 45 znaków łacińskich i ÷ 10 = 22 pełnoszerokie; 15 × 3 ÷ 10 = 4,5, zaokrąglone w górę do 5; 120 dodawań 1/120 w podwójnej precyzji = 0,9999999999999989, × 30 = 29,999999999999968, zaokrąglone w dół do 29; 1,5 sekundy × 12 = 18 znaków; 20 × 5 × 4 ÷ 3 = 133 punkty i 20 × 5 × 3 ÷ 2 = 150 punktów przy 2×; 466 − 84 = 382 i 382 − 32 = 350 punktów, 466 − 32 = 434; 40 × 5 × 5 ÷ 3 + 16 = 349,33 punktu; (382 − 32) × 3 ÷ 5 = 210 pikseli czcionki, minus 2 × 8 = 194, ÷ 5 = 38 znaków łacińskich i ÷ 10 = 19 pełnoszerokich; (382 − 48) ÷ (5 × 5 ÷ 3) = 40,08 i ÷ (10 × 5 ÷ 3) = 20,04; 43 × 5 × 4 ÷ 3 + 16 = 302,67 punktu; 360 − 84 − 32 = 244 punkty; (3 − 1) × 2 × 1 = 4 dotknięcia; 16 tekseli × 2,67 punktu = 42,7 punktu i 13 × 2,67 = 34,7; 9 klatek ÷ 60 = 150 milisekund, cztery kroki 600 milisekund; 3 punkty × 3 = 9 pikseli urządzenia między liniami skanowania, każda o wysokości 1 punkt × 3 = 3 piksele urządzenia. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩
-
pret,
pokeemerald/src/battle_message.cw commicie731ad5b(linie 3002 i 3003: automatyczne przewijanie włączone, gdygBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED); linia 3010,speed = 1dla okna komunikatów w walkach przez kabel; linia 3012,speed = sRecordedBattleTextSpeeds[GetTextSpeedInRecordedBattle()]w walce nagranej, z tabelą{8, 4, 1, 0}w linii 1957) ipokeemerald/src/scrcmd.c(ScrCmd_messageautoscrollod linii 1286, ustawiająceautoScrolliforceMidTextSpeedw liniach 1292 i 1293), odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/src/battle_message.c. ↩↩↩ -
pret,
pokeemerald/charmap.txtw commicie731ad5b, linie od 1087 do 1089 ('\l' = FA @ scroll up window text,'\p' = FB @ new paragraph,'\n' = FE @ new line), iinclude/constants/characters.h, linie 175 i 176 (CHAR_PROMPT_SCROLL, „waits for button press and scrolls dialog”;CHAR_PROMPT_CLEAR, „waits for button press and clears dialog”), wraz zsrc/text.c, linie od 968 do 971 (CHAR_NEWLINEprzesuwającecurrentYo linię w dół) i od 1102 do 1109 (dwa monity wchodzące wRENDER_STATE_CLEARiRENDER_STATE_SCROLL_START), odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/charmap.txt. ↩ -
pret,
pokeemerald/src/text_window.c(message_box.pngw linii 53;LoadMessageBoxGfxw linii 93;LoadUserWindowBorderGfxw linii 110),pokeemerald/include/text_window.h(#define WINDOW_FRAMES_COUNT 20w linii 4),pokeemerald/src/option_menu.c(opcja FRAME) ipokeemerald/graphics/text_window/w commicie731ad5b, odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/src/text_window.c. ↩↩↩ -
pret,
pokeemerald/data/text/iscripts.inckażdej mapy w commicie731ad5b, w tymdata/maps/LittlerootTown/scripts.inc, linia 854 (.string "MOM: {PLAYER}, we're here, honey!\p"), odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/data/maps/LittlerootTown/scripts.inc. ↩↩ -
pret,
pokeemerald/src/international_string_util.c,src/contest.cisrc/battle_tower.c(kodyEXT_CTRL_CODE_JPNiEXT_CTRL_CODE_ENGw imionach) orazgraphics/fonts/(japanese_normal.png,japanese_small.png,japanese_bold.png,japanese_frlg_male.png) w commicie731ad5b, odczyt 5 października 2026, https://github.com/pret/pokeemerald/blob/master/src/international_string_util.c. ↩↩ -
pret,
pokefirered/src/new_menu_helpers.cw commicie037335f(sStandardTextBox_WindowTemplatesod linii 34,.width = 26w linii 40 i opóźnienia tempa tekstu) ipokefirered/src/text_printer.c(AddTextPrinter,textSpeed = speedw linii 78 i--sTempTextPrinter.textSpeed;w linii 93) w commicie037335f, odczyt 5 października 2026, https://github.com/pret/pokefirered/blob/master/src/new_menu_helpers.c. ↩ -
Pan Docs, „Rendering” („One frame: 70224 dots @ 59.7 fps”; klatka „takes ~16.74 ms instead of ~16.67”), gbdev.io, dostęp 5 października 2026, https://gbdev.io/pandocs/Rendering.html. ↩
-
pret,
pokecrystal/gfx/font.asmw commicie5beda23(ramki odFRAME_1doFRAME_8,TEXTBOX_FRAME_TILESpo 6 kafelków 1-bitowych, dziewiąta ramka nieużywana) igfx/font/font.png, odczyt 5 października 2026, https://github.com/pret/pokecrystal/blob/master/gfx/font.asm. ↩ -
everything8215, ff6,
tools/rip_list_en.jsonitools/rip_list_jp.jsonw commicie8130132(assets/gfx/window/window_%s.4bppod0xED0000do0xED1BFF;window_%s.palod0xED1C00do0xED1CFF), odczyt 5 października 2026, https://github.com/everything8215/ff6/blob/master/tools/rip_list_en.json. ↩ -
everything8215, ff6,
src/menu/config.asm(ConfigWindowNumPos,LoadWindowPalczytające z$1d57, kolor czcionki pod$1d55,ConfigColorBarTextList,ChangeConfigOption_02) isrc/menu/menu_sram.asm(linia 102, „message speed = 3, battle speed = 3”) w commicie8130132, odczyt 5 października 2026, https://github.com/everything8215/ff6/blob/master/src/menu/config.asm. ↩↩↩ -
everything8215, ff6, w commicie
8130132:src/btlgfx/gfx_cmd.asm, linie od 1101 do 1103 (MsgSpeedTbl, „wait duration for each battle message speed”,$20,$30,$40,$50,$60,$70,$80,$90) i linie od 1151 do 1165 (mess_wait, czytające bity od 4 do 6 z$1d4djako indeks i wywołująceWaitFrametyle razy);src/menu/config.asm, linia 1499 („battle speed, battle message speed, magic order, and window pattern”) iChangeConfigOption_02od linii 933;src/menu/menu_text.en.inc, linia 346 (ustawienia rysowane jako od 1 do 6). Pliki, których kod dotyka$1d4d:src/battle/init.asm(ustawienie poleceń, tryb walki i tempo walki),src/btlgfx/gfx_cmd.asm,src/menu/config.asm,src/menu/field_menu.asmisrc/menu/menu_sram.asm; żaden wsrc/field/. Odczyt 5 października 2026, https://github.com/everything8215/ff6/blob/master/src/btlgfx/gfx_cmd.asm. ↩ -
everything8215, ff6,
src/field/hdma.asm(DlgWindowFixedColorTbl) isrc/field/event.asm(linia 1591, „dialog window y position (1 or 18)”) w commicie8130132, odczyt 5 października 2026, https://github.com/everything8215/ff6/blob/master/src/field/hdma.asm. ↩↩↩ -
everything8215, ff6,
src/field/text.asm(LoadLetterGfx, linia dialogu od x 4 do$e0,CalcTextWidth,NewLine,UpdateDlgTextOneLine, wyśrodkowany tytuł mapy wyświetlany przez 100 klatek) isrc/gfx/font_gfx.asm(FontWidth) w commicie8130132, odczyt 5 października 2026, https://github.com/everything8215/ff6/blob/master/src/field/text.asm. ↩↩↩↩↩ -
everything8215, ff6,
tools/rip_list_jp.json(large_font.jp.1bppod0xC494C0do0xC4FFFF) itools/char_table/w commicie8130132, odczyt 5 października 2026, https://github.com/everything8215/ff6/tree/master/tools/char_table. ↩ -
everything8215, ff6,
src/field/reset.asm(pętla świata gry: czekanie na wygaszanie pionowe i wywołanieUpdateDlgTextraz na przebieg) w commicie8130132, odczyt 5 października 2026, https://github.com/everything8215/ff6/blob/master/src/field/reset.asm. Deasemblacja buduje angielski i japoński ROM z jednego źródła. ↩↩ -
Data Crystal, „Chrono Trigger/ROM map”, datacrystal.tcrf.net, dostęp 5 października 2026: HTTP 403, nieodczytane, https://datacrystal.tcrf.net/wiki/Chrono_Trigger/ROM_map. ↩↩↩
-
Kiradex,
docs/TestFlight.md, krok 5 (linie od 192 do 194: w Dex wybór japońskiego pokazuje zestawy i karty z japońskimi nazwami i angielskimi pod spodem), odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne). ↩ -
Num Kadoma, littlelimit.net, „8×8ドット日本語フォント「美咲フォント」” (komórka 8 na 8 z przeglądu, poziomy 1 i 2 JIS i tusz 7 na 7; opisy Misaki Mincho i Misaki Gothic 2nd; uwaga o licencji M+ FONTS; opis powstania na potrzeby japońskiego sterownika wyświetlacza PC-E500), dostęp 5 października 2026, https://littlelimit.net/misaki.htm. ↩↩↩↩
-
Num Kadoma, littlelimit.net, „8×12ドット日本語フォント「k8x12」” (komórka 8 na 12, poziomy 1 i 2 JIS, warianty L i S, uwaga wariantu S o grafice 8 na 8 w starych konsolach), dostęp 5 października 2026, https://littlelimit.net/k8x12.htm. ↩
-
Google Fonts,
ofl/dotgothic16/OFL.txt(SIL Open Font License 1.1, Copyright 2020 The DotGothic16 Project Authors, bez Reserved Font Name) iMETADATA.pb(projektant Fontworks Inc.; podzbiory cyrillic, japanese, latin, latin-ext; dodano 2020-12-15) w9710da1, odczyt 5 października 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””) iMETADATA.pb(projektant CodeMan38) w9710da1, odczyt 5 października 2026, https://github.com/google/fonts/blob/main/ofl/pressstart2p/OFL.txt. ↩↩↩ -
Google Fonts,
ofl/silkscreen/OFL.txt(SIL Open Font License 1.1, bez Reserved Font Name) iMETADATA.pb(projektant Jason Kottke) w9710da1, odczyt 5 października 2026, https://github.com/google/fonts/blob/main/ofl/silkscreen/OFL.txt. ↩↩ -
DaFont, „Pixel Operator Font” (autor Jayvee Enaguas; „Bitmap (16 px)”; 15 plików czcionek; 241 glifów; notatki autora, „Changed licence to Creative Commons Zero (CC0) 1.0” i „licensed under a Creative Commons Zero (CC0) 1.0”), dostęp 5 października 2026, https://www.dafont.com/pixel-operator.font. Repozytorium wskazane w notatkach, https://notabug.org/HarvettFox96/ttf-pixeloperator, zwróciło 5 października 2026 HTTP 404. Jedno źródło. ↩↩↩↩
-
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”; średnia szerokość 5, wydłużenie górne 7, dolne 2, wysokość x 5; znaki opisane jako „uppercase and lowercase letters, including many accented letters, numbers and most common symbols”; pobranie w modelu „zapłać, ile chcesz”), dostęp 5 października 2026, https://managore.itch.io/m5x7. Pliku czcionki nie pobrano. ↩↩↩↩↩↩
-
Daniel Linssen, „m3x6”, itch.io („use font size 16, 32, 48, etc”; „free to use with attribution”; brak pola licencji zasobu; pobranie w modelu „zapłać, ile chcesz”), dostęp 5 października 2026, https://managore.itch.io/m3x6. Pliku czcionki nie pobrano. ↩↩↩↩↩
-
Apple Developer Documentation, „Core Text” („Create text layouts, optimize font handling, and access font metrics and glyph data”; iOS 3.2, macOS 10.8), dostęp 5 października 2026, https://developer.apple.com/documentation/coretext. ↩
-
Pomiar autora, 5 października 2026:
measure_hig.pyw folderze dowodów autora do tego artykułu, wynikmeasure_hig.out: tabele zapisanej strony Typography, rozmiary domyślny i minimalny oraz każda tabela Dynamic Type dla iOS od xSmall do AX5 (Body 14, 15, 16, 17, 19, 21, 23, 28, 33, 40, 47 i 53 punkty; Caption 1 11, 11, 11, 12, 14, 16, 18, 22, 26, 32, 37 i 43). ↩↩↩↩↩ -
Unicode Consortium, „Unicode Standard Annex #14: Unicode Line Breaking Algorithm”, rewizja 57, Unicode 18.0.0, 2026-09-01 (klasa CJ i „Treating characters of class CJ as class NS will give CSS strict line breaking; treating them as class ID will give CSS normal breaking.”), dostęp 5 października 2026, https://www.unicode.org/reports/tr14/. ↩↩
-
Stardew Valley Wiki, „Modding:Dialogue”, ostatnio edytowana 20 maja 2026 (
$b; polecenia portretów;Strings\SpeechBubbles), dostęp 5 października 2026, https://stardewvalleywiki.com/Modding:Dialogue. ↩↩↩ -
Stardew Valley Wiki, „Modding:Custom languages”, ostatnio edytowana 3 stycznia 2026 (
UseLatinFont, plik.fnt,FontPixelZoomz zalecaną wartością bazową 1.5,FontApplyYOffset), dostęp 5 października 2026, https://stardewvalleywiki.com/Modding:Custom_languages. ↩↩ -
Pomiar autora, 5 października 2026:
measure_steam_languages.pyw folderze dowodów autora do tego artykułu, wynikmeasure_steam_languages.out, odczytujący tabele języków z zapisanych stron sklepu Steam dla Stardew Valley (https://store.steampowered.com/app/413150/), Celeste (https://store.steampowered.com/app/504230/), Sea of Stars (https://store.steampowered.com/app/1244090/) i Eastward (https://store.steampowered.com/app/977880/), pobranych 5 października 2026. ↩↩↩↩↩ -
Pomiar autora, 5 października 2026:
measure_apple_docs.pyw folderze dowodów autora do tego artykułu, wynikmeasure_apple_docs.out: tytuł, deklaracja, streszczenie, platformy i pierwsze akapity każdej zapisanej strony dokumentacji Apple cytowanej w sekcji 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.”), dostęp 5 października 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:)” i „scaledValue(for:)” („Scales an arbitrary layout value based on the current Dynamic Type settings”; iOS 11.0), dostęp 5 października 2026, https://developer.apple.com/documentation/uikit/uifontmetrics. ↩↩↩
-
Apple Developer Documentation, „custom(:size:relativeTo:)” (czcionka niestandardowa, „that scales relative to the given
textStyle”; iOS 14.0), https://developer.apple.com/documentation/swiftui/font/custom(:size:relativeto:), i „custom(:fixedSize:)” (czcionka niestandardowa, „that does not scale with Dynamic Type”; iOS 14.0), https://developer.apple.com/documentation/swiftui/font/custom(:fixedsize:), dostęp 5 października 2026. ↩↩↩↩↩ -
Apple Developer Documentation, „ScaledMetric” („A dynamic property that scales a numeric value”; iOS 14.0), dostęp 5 października 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.”), dostęp 5 października 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.”), dostęp 5 października 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), dostęp 5 października 2026, https://developer.apple.com/documentation/bundleresources/information-property-list/uiappfonts. ↩↩
-
Apple Developer Documentation, „CTFontManagerRegisterFontsForURL(:::)” (iOS 4.1), dostęp 5 października 2026, https://developer.apple.com/documentation/coretext/ctfontmanagerregisterfontsforurl(:::). ↩
-
Apple Developer Documentation, „interpolation(:)” (Image; iOS 13.0), dostęp 5 października 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), dostęp 5 października 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), dostęp 5 października 2026, https://developer.apple.com/documentation/swiftui/text/baselineoffset(:). ↩
-
Apple Developer Documentation, „typesettingLanguage(:isEnabled:)” (iOS 17.0; fragment o tekście w języku niezgodnym z językiem interfejsu urządzenia; „this language does not affect text localization.”), dostęp 5 października 2026, https://developer.apple.com/documentation/swiftui/view/typesettinglanguage(:isenabled:). ↩↩
-
Apple Developer Documentation, „NSParagraphStyle.LineBreakStrategy” (iOS 9.0) i jego właściwości
standard(iOS 14.0),pushOut(iOS 9.0) ihangulWordPriority(iOS 14.0), dostęp 5 października 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 w formie z
LocalizedStringResource), https://developer.apple.com/documentation/swiftui/view/accessibilityvalue(:), i „accessibilityLabel(:)” (iOS 16.0), https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(:), dostęp 5 października 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.”; obszar trzeba najpierw aktywować), dostęp 5 października 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.”), dostęp 5 października 2026, https://developer.apple.com/documentation/swiftui/accessibilitytraits/updatesfrequently. ↩
-
Apple Developer Documentation, „AccessibilityNotification.Announcement” (iOS 17.0; atrybut
accessibilitySpeechAnnouncementPriority), dostęp 5 października 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”), dostęp 5 października 2026, https://developer.apple.com/documentation/swiftui/environmentvalues/accessibilityreducemotion. ↩↩
-
Apple Developer Documentation, „AccessibilityComponent” (RealityKit; „A component that stores accessibility information for an entity”; nazwa, którą „VoiceOver says aloud when it describes the entity”; iOS 17.0), dostęp 5 października 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”), dostęp 5 października 2026, https://developer.apple.com/documentation/realitykit/improving-the-accessibility-of-realitykit-apps. ↩ -
Apple Developer Documentation, „RealityView” (iOS 18.0), dostęp 5 października 2026, https://developer.apple.com/documentation/realitykit/realityview. ↩
-
Apple Developer Documentation, „XCUIVoiceOverService” („Provides programmatic control of VoiceOver for UI testing”; iOS 27.0), dostęp 5 października 2026, https://developer.apple.com/documentation/xcuiautomation/xcuivoiceoverservice. ↩↩↩
-
Kod 941Kit,
Sources/Kit941/Tokens/Typography/FontFamily.swiftw commicie50bc657(28 września 2026), linie od 21 do 63 (case custom(named: String), renderowane przezFont.custom(_:size:relativeTo:)w rozmiarach własnych 34, 22, 17, 13 i 12 punktów dla display, title, body, label i caption), odczyt 5 października 2026; repozytorium prywatne. ↩↩ -
Kod Kiradex,
server/app/names.py(linie od 7 do 14,FIRSTSiSECONDS, po 20 słów, oraz wzorzec pseudonimu z dwóch słów pisanych wielką literą i czterech cyfr), odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne). ↩↩ -
Blake Crosley, „A Pixel Town Alive When Nobody Is Online: NPCs and Ghosts”, blakecrosley.com, 5 października 2026 (23 predefiniowane linie, 14 dla graczy i 9 dla własnych kolekcjonerów miasteczka, oraz jedna linia co 2,0 sekundy na gracza), https://blakecrosley.com/blog/pixel-art-town-alive-when-nobody-is-online. ↩↩
-
Kiradex,
docs/WORLD.md, sekcja 5, „The stack”, linie 524 i 525 (limity: „eight moves a second, a line every two seconds”), odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne). ↩↩ -
Kod Kiradex, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie
334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne):Kiradex/World/PlazaStage.swift, linie od 199 do 203 (.gesture(DragGesture(minimumDistance: 0))sceny, wywołującetouchedw trakcie zmian iliftedna końcu), linie od 329 do 359 (touched(_:in:): bez drążka gracz od razu idzie do podłoża pod palcem i podąża za nim od kafelka do kafelka, natomiast dotknięcie kolekcjonera albo kafelków kiosku wraca i czeka na podniesienie palca), linie od 364 do 380 (lifted(_:in:): dotknięcie, które nie prowadziło ani nie sterowało, jest stuknięciem; na innym kolekcjonerze ustawiatapped, na kafelkach kiosku wywołujeonKiosk, a w pozostałych przypadkach uruchamiarig.walk(to: tile)), linie 31 i 32 (onKiosk, „The kiosk tapped: the town’s board.”), linia 82 (stickKey,"world.stick"), testowy argument uruchomienia-plazaFacingDemo(.taskw liniach od 255 do 270) oraztellOnArrivalwPlazaView, które czyta-plazaTell(linie od 515 do 522);KiradexUITests/KiradexUITests.swift, linie 384 i 387, gdzie test chodu dotyka sceny w ułamkach jej ramy. ↩↩↩↩ -
Kod Kiradex,
Kiradex/World/PlazaRig.swift, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne):fit(viewSize:displayScale:tilesTall:fitsWhole:)w linii 432 (pixelScalejako część całkowita wysokości widoku w pikselach podzielonej przeztilesTall × TileMap.tileSize, 20 × 16 = 320, linia 439;TileMap.tileSizewynosi 16 w linii 60TileMap.swift) iheadPoint(of:)w linii 479 (pozycja idącego plus połowa wysokości klatki arkusza, plus 4, linie 482 i 483). Plik zmienił się ponownie do 12:42; obie funkcje były wtedy bez zmian. ↩↩ -
Kod Kiradex, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie
334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne):Kiradex/World/PlazaStage.swift, linia 311 (.ignoresSafeArea()na scenie, którejGeometryReader, linie od 121 do 294, zawiera nakładkę dymków), i żadnego odczytu marginesów obszaru bezpiecznego w całym pliku;Kiradex/World/WorldHUD.swift, linie 26 i 27 (.statusBarHidden()i.persistentSystemOverlays(.hidden)naWorldView, korzeniu świata) oraz linie od 40 do 42 (elementy są „over its stage and inside the safe area, which on the Duo keeps it clear of the cameras and the side rail”); orazKiradex/Views/Card/CardViewer.swift, linie od 19 do 24, gdzieGeometryReaderpoza sceną karty przekazujeproxy.safeAreaInsetsdoCardStage, który ignoruje obszar bezpieczny, tak że „the card rests inside the safe area, clear of the Duo’s outer camera and side rail”, z tymi samymi dwoma modyfikatorami w liniach 56 i 57. PrzeszukanieKiradex/pod kątemsafeAreaInsetsznalazło tylko linię wCardViewer. ↩↩↩↩↩ -
Blake Crosley, „Xcode 27.1 Beta: Your App in the iPhone Duo Simulator”, blakecrosley.com, 21 września 2026, zaktualizowany 2 października 2026 (uruchamianie aplikacji w symulatorze iPhone’a Duo, wyświetlacze zewnętrzny i wewnętrzny; zamknięty, zlinkowany z SDK 27.1, okno 466 na 678 punktów, którego zawartość w stosie nawigacji miała 382 na 562 punkty, z marginesami obszaru bezpiecznego 82 u góry, 34 u dołu i 84 po stronie końcowej, czyli bocznym paskiem, w sondzie z paskiem narzędzi i paskiem kart przy tej krawędzi i z widocznym paskiem stanu), https://blakecrosley.com/blog/xcode-27-1-beta-iphone-duo-simulator. ↩↩↩↩
-
Pomiar autora, 5 października 2026:
measure_figure_width.pyw folderze dowodów autora do tego artykułu, wynikmeasure_figure_width.out, uruchomiony ponownie o 12:36 czasu pacyficznego na Kiradex w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, wynik bez zmian. Dane wejściowe, odczytane wyłącznie z Kiradex:Kiradex/Resources/World/characters.json("cell": [32, 40], klatki i kierunki),scripts/forge/rig.py(linia 53,CELL_W, CELL_H = 32, 40) i arkusz postacifig-body.png(1376 na 160 pikseli, 43 klatki na 4 kierunki). W klatce spoczynkowejidle_apostać ma 30 tekseli wysokości w każdym kierunku, 16 szerokości w najszerszym rzędzie przy zwróceniu w dół lub w górę i 13 przy zwróceniu w lewo lub w prawo. ↩↩↩ -
Pomiar autora, 5 października 2026:
measure_ios27_iphones.pyw folderze dowodów autora do tego artykułu, wynikmeasure_ios27_iphones.out, uruchomiony ponownie o 12:36 czasu pacyficznego, wynik bez zmian. Odczytujexcrun simctl list runtimes -jdla środowiska uruchomieniowego symulatora iOS 27.0 (24A434) zainstalowanego z Xcode 27.0 na Macu autora orazcapabilities.plistkażdego typu urządzenia iPhone (main-screen-width,main-screen-height,main-screen-scale): 33 iPhone’y, od iPhone’a 12 mini i 13 mini (1080 na 2340 pikseli przy 3×, 360 na 780 punktów) do iPhone’a 16, 17 i 18 Pro Max (440 na 956 punktów), z trzema przy 2×, iPhone’em SE (2. i 3. generacji, 375 na 667 punktów) i iPhone’em 11 (414 na 896). To lista urządzeń symulatora, a nie strona zgodności Apple, której nie pobrano. Ponadto z pliku projektu Kiradex w commicie334fbfe, odczytanego wyłącznie do odczytu:TARGETED_DEVICE_FAMILY = 1(iPhone) celu aplikacji w obu jego konfiguracjach (linie 1559 i 1705 w odczycie z 12:28) iIPHONEOS_DEPLOYMENT_TARGET = 27.0w obu konfiguracjach projektu (linie 1608 i 1670), którego cel aplikacji nie nadpisuje. ↩↩↩↩↩↩↩ -
Pomiar autora, 5 października 2026:
probe_line_start_glyphs.swiftw folderze dowodów autora do tego artykułu, skompilowany zswiftc -Oi uruchomiony na macOS 27.0 (kompilacja 26A428), nie w iOS, wynikprobe_line_start_glyphs.out. Odczytuje dziesięć klas początku linii JLREQ 3.1.7 z tabel w dodatku zapisanej specyfikacji (83 pozycje: 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), pyta Core Text, które z nich odwzorowuje PixelMplus10 Regular (CTFontGetGlyphsForCharacters, bez czcionki zastępczej): 54, przy czym nieodwzorowane są 〜 U+301C, ゠ U+30A0 i półpauza z cl-03, ⦆, 〙 i 〗 z cl-02, ‼ ⁇ ⁈ ⁉ z cl-04, 〻 z cl-09 oraz 18 małych kana z cl-11 (ゕ, ゖ i od U+31F0 do U+31FF). Rysuje wszystkie 7223 punkty kodowe odwzorowane przez krój w 10 pikselach, z wyłączonym antyaliasingiem i wygładzaniem czcionek, i grupuje komórki identyczne co do piksela: z 54 ‐ U+2010 pokrywa się z U+2011, a : U+003A z U+A789, i żaden inny nie ma bliźniaka. W 50 pikselach (m= 5), z włączonym antyaliasingiem i wyłączonym wygładzaniem, ー U+30FC rysuje 200 pikseli urządzenia z tuszem z haczykiem na lewym końcu, a 一 U+4E00 250, nieidentyczne, oba z 0 częściowych pikseli. Każdy drukowalny znak ASCII ma przesunięcie 5 pikseli czcionki. Rozszerzone później 5 października, z wcześniejszym skryptem i wynikiem zachowanymi obok i każdą wcześniejszą linią wyniku bez zmian: 24 znaki z bloku Halfwidth and Fullwidth Forms, od U+FF01 do U+FFEF, których normalizacja zgodności (NFKC) jest jednym z wymienionych znaków (! ) , . : ; ? ] } ⦆ 。 」 、 ・ ァ ィ ゥ ェ ォ ャ ュ ョ ッ ー), oraz ~ U+FF5E, dodany przez autora: krój odwzorowuje 24 z 25, wszystkie poza ⦆ U+FF60, pełnoszerokie o szerokości 10 pikseli, a półszerokie 5, i żaden z 24 nie ma bliźniaka identycznego co do piksela wśród punktów kodowych kroju, więc zestaw porównawczy liczy 75 znaków: 54 pozycje obejmują 51 znaków, bo ), 〕 i ] figurują zarówno w cl-02, jak i w cl-29, a 24 formy dają razem 75 (własna ostatnia linia sondy, 54 + 24 = 78, liczy pozycje). ↩↩↩↩ -
Pomiar autora, 15 października 2026:
measure_cell_masks.pyw folderze dowodów autora do tego artykułu, wynikmeasure_cell_masks.outi lista skrótówcanon_cell_masks.txt, uruchomiony na plikach pret zapisanych dla tego artykułu (graphics/fontsz pokeemerald, 17 arkuszy, igraphics/text_window, 21;gfx/framesz pokecrystal, 10;gfx/font/font.pngz pokecrystal i pokered). Każdy arkusz jest dekodowany do RGBA; piksel jest ustawiony tam, gdzie różni się od lewego górnego piksela arkusza; komórki mają 8 na 8 od lewego górnego narożnika; puste i pełne komórki są pomijane; skrót maski to SHA-256 jej 8 bajtów rzędów, z lewym skrajnym pikselem jako najstarszym bitem.japanese_normal.png, 128 na 512 pikseli: 1024 komórki, 430 pustych, 594 zachowane, 478 odrębnych. Wszystkie 50 arkuszy: 7746 zachowanych komórek, 1484 odrębne skróty. ↩↩ -
Blake Crosley, „Pixel-Art People: Characters and a Creator on iPhone”, blakecrosley.com, 3 października 2026, odczyt 5 października 2026: dla postaci z kompilacji od 21 do 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”; w aktualizacji dla kompilacji 34 „The cell is 32 by 40 now, with eight rows of headroom and two under the feet, and the figure 30 pixels”; oraz punkty zaczepienia głowy i dłoni z manifestu dla dymka i karty 3D, „though both still take their place from the cell’s height for now”, https://blakecrosley.com/blog/pixel-art-characters-on-iphone. ↩↩
-
Odczyt autora drzewa roboczego Kiradex w commicie
fc9d4876(refs/heads/main), 15 października 2026 o 22:29 czasu pacyficznego:Kiradex/World/PlazaStage.swift(zmodyfikowany tego dnia o 17:13), linia 373 (.modifier(StageAccessibility(covered: covered, trace: stepTrace))), linia 520 (private struct StageAccessibility: ViewModifier), linie od 511 do 513 (komentarz: scena jest jednym elementem dostępności, „The plaza” z ostatnim krokiem gracza jako wartością, albo żadnym, gdy nałożone jest na nią jakieś miejsce), linia 525 (content.accessibilityRepresentation {), linia 526 (if !covered {), linia 533 (.accessibilityLabel("The plaza")) i linia 534 (.accessibilityValue(trace)). Pozostałe odczyty Kiradex w artykule pochodzą z 5 października i są datowane. ↩ -
Kod Kiradex,
server/app/npcs.py, odczyt o 12:33 czasu pacyficznego 5 października 2026, w commicie334fbfe(plik z datą 3 października; repozytorium prywatne): czterej kolekcjonerzy miasteczka zDEFINITIONS(linie od 36 do 58), Moss Keeper 1999, Prism Scout 2024, Comet Pocket 3030 i Dawn Sprout 4040, których linie są indeksami doLINES; każdy wypowiada pierwszą linię po 8 do 20 sekundach (linia 77), a potem jedną co 20 do 40 sekund (linia 108).server/app/main.py, linia 231, rozsyła linię gracza do całego pokoju, łącznie z samym graczem (except_idwbroadcastnie jest ustawione;rooms.py, linia 182), odczyt o 12:33. ↩↩ -
Kod Kiradex,
KiradexUITests/KiradexUITests.swift, odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne); plik nosił godzinę 10:32 i nie zmienił się w trakcie odczytu: linia 228 (me-handlena karcie Me zawiera pseudonim, dwa słowa i liczbę),testTheCollectorFacesTheWayItWalksod linii 432 (czyta wartość sceny dla każdego kierunku),testThePlazaKeepsTimeod linii 502 (czytaplaza-statusi sprawdza jego prefiks, linia 509),testTheChimneysSmokeod linii 523 (uruchamiany z-airProbe, czeka navalue CONTAINS 'smoke 18 'sceny, linia 531),testVisitingARoomIsAVisit(linie od 469 do 472) itestYourRoomHasYourBinders(linie od 604 do 607), które czekają na pierwsze słowaroom-status,testTheShowHallHasEveryonesCards(linie od 490 do 494), który czeka nahall-status,testTheTownHasCollectors(linie od 672 do 676), który czeka, ażplaza-statuspokaże pięciu lub więcej „here”, oraztestThePlazaHasCompanyod linii 689 (pomijany bezKIRADEX_PLAZA=1, czyli serwera, a według komunikatu o pominięciu potrzebuje też drugiego gracza, linie 690 i 691; czeka na liczbę wplaza-status, wybiera „Hi!” z menu Say i czeka 5 sekund na dymekSaid: Hi!, linia 714). ↩↩↩↩ -
Kod Kiradex,
server/requirements.txt(fastapi,uvicorn[standard],websockets,httpxipytest, bez biblioteki czcionek) iserver/tests/(test_rooms.py), odczyt o 12:28 czasu pacyficznego 5 października 2026, w commicie334fbfez niezatwierdzonymi zmianami z tego dnia, z drzewa roboczego edytowanego tego dnia (repozytorium prywatne). ↩ -
Blake Crosley, „Pixel-Art Routes: Map Connections and Districts on iPhone”, blakecrosley.com, 5 października 2026, sekcja 7, sprawdzenie 13 (lista zakazów w repozytorium, z testem, którą musi przejść każda nazwa obszaru, każdy szyld i każdy ciąg), https://blakecrosley.com/blog/pixel-art-world-beyond-the-plaza. ↩