Pixel-Art in Bewegung: Laufen, Kamera und Türen auf dem iPhone
Pokémon Rote Edition, Pokémon Kristall-Edition und Pokémon Smaragd-Edition, je ein Spiel aus den drei Generationen von Game Boy und Game Boy Advance, laufen alle eine 16 Pixel große Zelle in 16 Frames bei etwa 59,73 Hertz ab, 3,73 Zellen pro Sekunde, und nichts an diesem Schritt bleibt einer Uhr überlassen: Smaragd bewegt die Spielfigur um genau ein Pixel pro Frame, zeigt pro Schritt eine Schrittzeichnung und eine Standzeichnung und scrollt die Kamera im selben Frame um dieselben Pixel.123 Rot und Kristall kommen auf dieselben sechzehn Frames, indem sie jeden zweiten Frame zwei Pixel weit springen.4 Die Tür in Smaragd öffnet sich in vier Zeichnungen, die je fünf Frames stehen, die Spielfigur wird einen erzwungenen Schritt hineingeführt, die Tür schließt sich, und der Bildschirm blendet in neun gestuften Helligkeitsstufen ab: mindestens 79 Frames, 1,32 Sekunden, bevor die nächste Karte überhaupt zu laden beginnen kann.15 Die Welt von Kiradex läuft nach verstrichener Zeit und lässt ihre Kamera nachgleiten, und ein Modell dieses Codes (ein Modell, keine Aufnahme von einem Telefon) sagt: Bei 60 Hertz ruckt der Sprite einmal pro Tile um zwei Pixel, die Kamera fällt bei 60 Hertz mit jedem Tile eines Laufwegs um etwa ein Pixel weiter zurück (9 Pixel beim fünften, 13 beim Rennen) und kommt 4 bis 9 Pixel zu kurz zur Ruhe, und die Laufzeichnungen folgen einer eigenen Uhr, ein Zyklus auf 3,00 Tiles, wo der von Smaragd zwei abdeckt.6 Apple räumt Spielen bei 30 und 60 Hertz besondere Priorität ein, und RealityKit rendert typischerweise mit 60; das Briefing lautet daher: ein fester 60-Hertz-Tick mit Schritten in ganzen Pixeln, Laufzeichnungen nach zurückgelegter Strecke gewählt (mein Vorschlag, nicht die Methode der Handhelds), eine fixierte Kamera, das Türzeremoniell von Smaragd in unserer eigenen Grafik, drei abschaltbare Haptikereignisse und 60 Hertz statt 120.78 Hier folgen der Kanon, gemessen an den Dekompilierungen, die iPhone-Seite nach Apples Dokumentation und das Briefing mit den Prüfungen, die es bestehen muss.
Kurzfassung
- Ein Schritt ist ein Vertrag, keine Geschwindigkeit. Jede Schritttabelle in Smaragd summiert sich auf 16 Pixel: Laufen ist 1 Pixel pro Frame über 16 Frames (268 Millisekunden pro Zelle), Rennen und Surfen 2 über 8, die Höchstgeschwindigkeit des Eilrads (Mach Bike) 4 über 4, und nur das 2-3-3-2-3-3 des Kunstrads (Acro Bike) ist ungleichmäßig. Rot und Kristall laufen dieselben 16 Frames in Sprüngen von 2 Pixeln bei 30 Aktualisierungen pro Sekunde.124
- Beine und Schritt sind aufeinander abgestimmt. Smaragd zählt Zeichnungen und Schritt getrennt in Frames und sorgt dafür, dass die Zählungen übereinstimmen: Der Lauf ist Schritt, Stand, Schritt, Stand mit 8 Frames pro Zeichnung über zwei Zellen zu je 16 Frames; schnellere Gangarten halbieren die Standzeit, statt Zeichnungen hinzuzufügen, sodass bei jeder Geschwindigkeit ein Auftritt auf 16 Pixel kommt. Das Drehen aus dem Stand dauert 8 Frames (134 Millisekunden), das Drehen im Gehen gar nichts, und wer gegen eine Wand läuft, sieht ein 32 Frames langes Anstoßen.19102
- Die Kamera ist die Spielfigur. Die Kamera von Smaragd übernimmt die Position der Spielfigur und scrollt im selben Frame um dieselben Pixel, ohne Nachlauf und ohne Vorlauf, und hält am Kartenrand nie an, weil das Äußere aus Randtiles gezeichnet wird. Game Freak hat eine Kamera geschrieben, die dem Fahrrad vorauseilt, und sie abgeschaltet ausgeliefert.231112
- Eine Tür ist ein Zeremoniell. Vier Zeichnungen zu fünf Frames (335 Millisekunden), ein erzwungener Schritt von 16 Frames, das Schließen der Tür in 20 und eine Abblende, deren letzte Mischung auf ihren 17. Frame fällt und die mit ihrem 22. endet: mindestens 79 Frames, 1,32 Sekunden, ohne jede Eingabe, bevor die Karte laden kann. Das bestätigt den Strukturen-Beitrag dieser Reihe, etwa 84 Millisekunden pro Zeichnung, und korrigiert die Recherchenotizen dahinter, in denen stand: „4 ticks each (16 frames, about 0.27 s at 59.7 Hz).“ (je 4 Ticks, also 16 Frames, etwa 0,27 s bei 59,7 Hz). Kristall blendet in 8 Frames zu Weiß ab; Rot in 32 zu Schwarz.1513144
- Auf dem iPhone gleichmäßig mit 60 takten.
preferredFrameRateRange(iOS 15.0) ist ein Hinweis; ProMotion-iPhones laufen in zwölf Stufen zwischen 10 und 120 Hertz; über 60 braucht esCADisableMinimumFrameDurationOnPhone; Spiele erhalten „special priority to 30Hz and 60Hz“ (besondere Priorität für 30 und 60 Hz); RealityKit „typically limits the refresh rate“ (begrenzt die Bildwiederholrate typischerweise) auf 60. Ein Lauf in ganzen Pixeln gewinnt bei 120 nichts: Jedes Pixel wird einfach zwei Bildwiederholungen lang gehalten.1571686 - Kiradex heute, modelliert statt gemessen. Der Code läuft nach verstrichener Zeit mit 4 Tiles pro Sekunde, also braucht ein Modell bei konstanten 60 Hertz 15 Frames pro Tile und bewegt sich in einem davon um 2 Pixel; die Kamera gleitet nach und wird gerundet, fällt im Lauf des Weges immer weiter zurück (5 Pixel nach dem ersten Tile, 9 nach dem fünften, 13 beim Rennen) und kommt bei 60 Hertz 4 Pixel neben der Spielfigur zur Ruhe, bei 120 Hertz 9; ihr Laufzyklus aus sechs Zeichnungen bei 8 pro Sekunde deckt im Gehen 3,00 Tiles pro Zyklus ab und im Rennen 4,50. Gemessen wurde auf keinem Telefon.176
- Was Kiradex davon hat: ein Briefing, kein ausgelieferter Build. Ein fester 60-Hertz-Tick mit 1-Pixel-Schritten und einer ausdrücklichen Regel für den Rückstau, Laufzeichnungen nach Strecke, eine fixierte Kamera auf ganzen Pixeln, der vollständige Türablauf mit gestufter Blende, ein sichtbar verweigerter Schritt, drei abschaltbare Haptikereignisse, keine Anforderung von 120 Hertz, und zu jedem Punkt eine Prüfung, die ein Bewegungsprotokoll, eine Aufnahme oder ein Skript bestätigen kann.
1. Laufen in Smaragd: sechzehn Pixel in sechzehn Frames
Die ersten beiden Beiträge dieser Reihe haben vermessen, wie eine Pixelwelt und ihre Bewohner aussehen; der dritte vermaß ihre Gebäude. Dieser hier vermisst die Zeit: wie viele Pixel pro Frame, wie viele Frames pro Schritt, welche Zeichnung auf welchem Frame erscheint, wie lange eine Drehung, eine Tür und eine Blende dauern und was die Kamera währenddessen tut. Die Spiele werden so gelesen wie in den früheren Beiträgen, anhand der Dekompilierungen der veröffentlichten Game-Boy- und Game-Boy-Advance-Spiele durch pret, bei denselben Commits: pokered d2704a6, pokecrystal 5beda23, pokeemerald 731ad5b.18 Gezählt haben fünf kleine Skripte; jedes ist in den Anmerkungen mit seiner gespeicherten Ausgabe genannt.
Zuerst eine Zahl, denn alles andere wird in Frames gerechnet. Der Game Boy Advance zeichnet einen Frame in 280.896 Takten eines 2^24-Hertz-Taktgebers, das sind 59,7275 Frames pro Sekunde, 16,743 Millisekunden pro Frame; GBATEK rundet das auf „ca. 59.737 Hz“.19 Der 4.194.304-Hertz-Takt des ursprünglichen Game Boy, geteilt durch 70.224 Punkte pro Frame, ergibt dieselben 59,7275; Pan Docs schreibt „@ 59.7 fps“.19 Millisekunden werden in diesem Beitrag mit 59,7275 gerechnet.19
Jede Geschwindigkeit ist eine Tabelle, die sechzehn ergibt
Smaragd bewegt eine Figur nicht nach Geschwindigkeit mal Zeit. Jede Geschwindigkeit auf der Oberwelt ist eine Tabelle von Pixelbewegungen pro Frame, und der Quellcode sagt, was alle Tabellen gemeinsam haben: „Over the course of the step animation, these sum to 16 pixels (one full metatile).“ (Über die gesamte Schrittanimation summieren sie sich auf 16 Pixel, ein volles Metatile.)2 sStep1Funcs sind sechzehn Bewegungen um ein Pixel, sStep2Funcs acht um zwei, sStep4Funcs vier um vier, sStep8Funcs zwei um acht, und sStep3Funcs ist der Ausreißer: Step2, Step3, Step3, Step2, Step3, Step3.2 Per Skript gegen den Bewegungscode gezählt, ergibt das:
| Geschwindigkeitskonstante | Pixel pro Frame | Frames pro Zelle | Zellen pro Sekunde | Millisekunden pro Zelle | Verwendet für |
|---|---|---|---|---|---|
MOVE_SPEED_NORMAL |
1 in jedem Frame | 16 | 3,73 | 267,9 | das Laufen; laufende NPCs |
MOVE_SPEED_FAST_1 |
2 in jedem Frame | 8 | 7,47 | 133,9 | Rennen, Surfen, Rutschen auf Eis |
MOVE_SPEED_FAST_2 |
2, 3, 3, 2, 3, 3 | 6 | 9,95 | 100,5 | das Kunstrad, Wasserströmungen |
MOVE_SPEED_FASTER |
4 in jedem Frame | 4 | 14,93 | 67,0 | das Eilrad bei Höchstgeschwindigkeit |
MOVE_SPEED_FASTEST |
8, 8 | 2 | 29,86 | 33,5 | Gleit-Bewegungsaktionen |
Quelle für jede Zeile: measure_gen3_motion.py über src/event_object_movement.c.1
Die Zahl, die man sich merken sollte, ist die des Laufens: ein Pixel in jedem Frame, sechzehn Frames pro Zelle, 3,73 Zellen pro Sekunde, 268 Millisekunden pro Zelle, verwendet von PlayerWalkNormal und von jedem laufenden NPC.1 Rennen mit gedrückter B-Taste ist PlayerRun, Surfen ist PlayerWalkFast, das der Quellcode mit „same speed as running“ (gleiche Geschwindigkeit wie Rennen) kommentiert; Rutschpartien auf Eis rufen dieselbe Funktion auf. Alle drei bewegen zwei Pixel pro Frame, acht Frames pro Zelle, 7,47 Zellen pro Sekunde.110 Daneben gibt es ein langsames Gehen, UpdateWalkSlowAnim, das bei geraden Zeitgeberwerten ein Pixel vorrückt, 31 bis 32 Frames pro Zelle, für geskriptete Szenen.1
Das Kunstrad ist die einzige Geschwindigkeit mit ungleichmäßigen Pixeln: 2, 3, 3, 2, 3, 3 über sechs Frames, im Mittel 2,67 Pixel pro Frame, 9,95 Zellen pro Sekunde.1 Es wird über PlayerRideWaterCurrent aus AcroBikeTransition_Moving erreicht, dieselbe Funktion, die auch die Wasserströmungen verwenden.120 Nach meiner Lesart ist die Ungleichmäßigkeit der Preis für eine Geschwindigkeit, die sechzehn nicht teilt: Drei Pixel pro Frame würden über die Zelle hinausschießen, also wechselt die Tabelle Zweien und Dreien ab, um genau auf sechzehn zu landen. Jede andere Geschwindigkeit ist ein ganzzahliger Teiler der Zelle.
Das Eilrad beschleunigt pro Schritt, nicht pro Frame. sMachBikeSpeedCallbacks lautet PlayerWalkNormal, PlayerWalkFast, PlayerWalkFaster, und bikeFrameCounter steigt pro Schritt um eins bis zu einer Obergrenze von 2, also dauert der erste Schritt aus dem Stand 16 Frames, der zweite 8 und jeder weitere 4: vier Pixel pro Frame, 14,93 Zellen pro Sekunde.120 Das Rad befindet sich nie zwischen zwei Geschwindigkeiten. Jeder Schritt ist eine der Tabellen, vollständig abgearbeitet, und die Geschwindigkeit ändert sich nur an einer Zellgrenze.
Jede in Rot, Kristall und Smaragd gemessene Gangart ist eine ganze Zahl von Frames pro 16-Pixel-Zelle; die Zahlen von Kiradex sind ein Modell seines Codes, keine Aufnahme.14621
Ein Auftritt pro sechzehn Pixel
Die Laufanimation ist Schritt, Stand, Schritt, Stand. sAnim_GoSouth lautet ANIMCMD_FRAME(3, 8), (0, 8), (4, 8), (0, 8): Zeichnung 3 für acht Frames, die Standzeichnung 0 für acht, Zeichnung 4 für acht, wieder die Standzeichnung für acht, insgesamt 32 Frames, also zwei Zellen Laufen.91 Die Standzeit ist exakt: sprite.c lädt die Dauer des Frames minus eins in den Verzögerungszähler und zählt bis null herunter, sodass ein Frame der Dauer 8 genau acht Frames lang zu sehen ist.91 Jeder Schritt zeigt daher eine Schrittzeichnung und eine Standzeichnung, und SetStepAnimHandleAlternation beginnt jeden neuen Schritt in der anderen Hälfte des Zyklus (animPos = {1, 3, 0, 2}), sodass linkes und rechtes Bein Schritt für Schritt abwechseln, selbst wenn die Spielfigur anhält und wieder losgeht.2 Der erste Beitrag dieser Reihe beschrieb denselben Zyklus von der Grafikseite: „step, stand, step, stand at eight ticks each, which is the bob everyone remembers.“ (Schritt, Stand, Schritt, Stand zu je acht Ticks, das Wippen, an das sich alle erinnern.)22
Schnellere Gangarten behalten die Zeichnungen und verkürzen die Standzeit. GoFast hält jede Zeichnung vier Frames (ein Zyklus von 16 Frames), GoFaster zwei (8 Frames), GoFastest einen (4 Frames).1 Das Rennen hat eigene Zeichnungen mit ungleichen Standzeiten: sAnim_RunSouth lautet (12, 5), (9, 3), (13, 5), (9, 3), ein Zyklus von 16 Frames.19
Legt man die beiden Tabellen nebeneinander, zeigt sich das Design. Der 32-Frame-Zyklus des Laufens deckt zwei Zellen zu 16 Frames ab; der 16-Frame-Zyklus des Rennens zwei Zellen zu 8; der 8-Frame-Zyklus GoFaster des Eilrads zwei Zellen zu 4.19 Bei jeder Geschwindigkeit kommt ein Auftritt auf 16 Pixel.1 Das sind zwei Uhren, nicht ein Zähler. Die Zeichnung rückt vor, wenn animDelayCounter abläuft, der in sprite.c aus der Dauer jedes Frames geladen wird; der Schritt rückt vor, wenn NpcTakeStep die Tabelle der Geschwindigkeit mit dem sTimer des Sprites indiziert, ein Eintrag pro Frame; und SetStepAnimHandleAlternation wählt zu Beginn jedes Schritts die Animation der Gangart.92 Zusammengehalten werden sie dadurch, dass beide in denselben Frames gezählt werden und die Dauern Gangart für Gangart passend gewählt wurden. Nach meiner Lesart ist genau das zu übernehmen: Die Kadenz der Zeichnungen kann sich nicht von der Bewegung lösen, weil die Zeichnungen jedes Schritts genau so viele Frames dauern wie der Schritt selbst. Nichts in diesen Tabellen oder in meinem Modell misst, wo ein aufgesetzter Fuß relativ zum Boden steht, also behaupte ich nicht mehr als das.
Drehen, Anstoßen und wann die Eingabe gelesen wird
Ein begonnener Schritt wird zu Ende geführt; das Steuerkreuz wird gelesen, wenn er abgeschlossen ist, sodass eine gehaltene Richtung Schritte lückenlos aneinanderreiht und ein Richtungswechsel im Gehen nichts kostet.10 Aus dem Stand ist es anders. CheckMovementInputNotOnBike gibt TURN_DIRECTION nur zurück, wenn die neue Richtung von der Blickrichtung abweicht und die Spielfigur sich nicht schon bewegt, und PlayerTurnInPlace spielt das schnelle Auf-der-Stelle-Gehen ab, dem InitMoveInPlace eine Dauer von 8 Frames gibt: 134 Millisekunden Drehen auf der Stelle.101 Diese Mechanik erlaubt es, sich einem Schild oder einer Person zuzuwenden, ohne auf sie zuzugehen. Läuft man gegen eine Wand, wird stattdessen das langsame Auf-der-Stelle-Gehen gespielt, 32 Frames, 536 Millisekunden, mit einem Anstoßgeräusch: Ein verweigerter Schritt wird gezeigt, nicht verschluckt.110 Das normale und das schnellere Auf-der-Stelle-Gehen dauern 16 Frames (268 Millisekunden) und 4 (67).1
Nach meiner Lesart machen diese vier Zahlen den größten Teil dessen aus, was „reaktionsschnell“ in einem Rasterspiel bedeutet. Die Welt bewegt sich nie um Bruchteile einer Zelle, also drückt sich die Absicht der Spielerin oder des Spielers in ganzen Schritten aus, und die einzige Latenz ist der Rest des laufenden Schritts: höchstens 268 Millisekunden beim Gehen, 134 beim Rennen.1 Die Drehung aus dem Stand ist keine Latenz, sondern eine eigene Aktion mit eigenem sichtbaren Ergebnis.
2. Kamera, Türen, Blenden und Wackeln in Smaragd
Die Kamera hat weder Nachlauf noch Vorlauf
Die Kamera von Smaragd ist ein unsichtbarer Sprite, der der Spielfigur folgt. CameraObject_UpdateMove kopiert x und y des verfolgten Sprites und speichert die Differenz zum letzten Frame in sCamera_MoveX und sCamera_MoveY; CameraUpdateCallback übergibt diese Differenz an CameraUpdate, das die Karte um genau so viele Pixel scrollt.212 Die Oberwelt-Schleife führt in jedem Frame RunTasks(); AnimateSprites(); CameraUpdate(); UpdateCameraPanning(); in dieser Reihenfolge aus, und weil AnimateSprites die Sprite-Callbacks in Slot-Reihenfolge ausführt und das Kameraobjekt nach der Spielfigur erzeugt wird (InitPlayerAvatar, dann InitCameraUpdateCallback(gPlayerAvatar.spriteId)), liest die Kamera die Position, die die Spielfigur in demselben Frame erreicht hat.3 Diese Reihenfolge habe ich im Code nachverfolgt, nicht ausgeführt; sie ist also eine Lesart, keine Messung, aber das Ergebnis, das sie nahelegt, ist einfach. Die Bewegung der Spielfigur und das Scrollen fallen auf denselben Frame, um dieselben Pixel. Auf dem Bildschirm bewegt sich die Spielfigur überhaupt nicht, während die Welt unter ihr hindurchgleitet.
Itay Keren nennt das in seinem Vortrag auf der GDC 2015 über Kameras in Side-Scrollern position-locking: Die Kamera bleibt auf der Spielfigur, „keeping the car in focus at all times and the camera motion completely predictable.“ (das Auto jederzeit im Fokus, die Kamerabewegung vollständig vorhersagbar.)23 Smaragd ergänzt etwas, das seine Definition offenlässt, nämlich was am Kartenrand passiert. Sie hält nie an. Zellen außerhalb des Kartenlayouts werden über GetBorderBlockAt gelesen, das die 2 mal 2 Randmetatiles des Layouts wiederholt zurückgibt und als MAPGRID_IMPASSABLE markiert, sodass der Ausschnitt die Spielfigur an derselben Bildschirmposition hält und das Äußere mit Randbäumen oder Wasser füllt.11 Kerens edge-snapping, die Alternative, „simply snaps the camera to the edge of the level, allowing the character to move away from its anchor point.“ (rastet die Kamera einfach am Levelrand ein, sodass sich die Figur von ihrem Ankerpunkt entfernen kann.)23 Smaragd hat sich aus diesem Bedarf herausgezeichnet.
Die Kamera, die Game Freak schrieb und abschaltete
field_camera.c enthält eine fertige Vorausschau für das Fahrrad. CameraPanningCB_PanAhead verschiebt den vertikalen Schwenk um 2 Pixel pro Aktualisierung, vom Ruhewert 32 je nach Fahrtrichtung in Richtung 72 oder minus 8, also 40 Pixel in jede Richtung: eine Kamera, die der Spielfigur dorthin vorauseilt, wohin sie unterwegs ist.12 Sie läuft nur, wenn gUnusedBikeCameraAheadPanback wahr ist, die Variable wird nur je auf FALSE gesetzt (in bike.c), und der Zweig trägt den Kommentar „this code is never reached.“ (dieser Code wird nie erreicht.)1220 In Kerens Vokabular ist das ein dual-forward-focus oder target-focus, die Kamera, die seiner Regel folgt: „When you walk left, you want to see more to the left.“ (Wenn man nach links geht, will man mehr von links sehen.)23 Aus welchem Grund auch immer sie abgeschaltet blieb, das ausgelieferte Spiel hielt die Kamera fixiert, selbst bei den 4 Pixeln pro Frame des Eilrads.1
Eine Tür öffnet sich in zwanzig Frames, nicht in sechzehn
Die Türtabelle in field_door.c liest sich, als dauere jede Zeichnung vier Frames: sDoorOpenAnimFrames lautet {4, -1}, {4, 0}, {4, 0x100}, {4, 0x200}, geschlossen plus drei geöffnete Zeichnungen.24 Aber AnimateDoorFrame zeichnet, wenn sein Zähler 0 ist, und rückt vor, wenn der Zähler der Zeit des Eintrags entspricht, sodass jede Zeichnung fünf Aktualisierungen lang steht: 84 Millisekunden pro Zeichnung, 335 für alle vier.241 Der Strukturen-Beitrag dieser Reihe nannte dieselbe Zahl, fünf Aktualisierungen und etwa 84 Millisekunden.13 Die Recherchenotizen, auf denen er beruhte, lagen falsch, „4 ticks each (16 frames, about 0.27 s at 59.7 Hz),“ (je 4 Ticks, also 16 Frames, etwa 0,27 s bei 59,7 Hz), und ebenso ein Kommentar im Türcode der App selbst, der sagt, sie öffne sich „at about Emerald’s four ticks a frame“ (mit etwa den vier Ticks pro Frame von Emerald) und dann die erste Zeichnung 70 Millisekunden hält; beides wird hier und im Briefing korrigiert.1417
Der Eintritt selbst, Task_DoDoorWarp, besteht aus fünf Zuständen, von denen keiner übersprungen werden kann: die übrigen Objekte einfrieren, das Türgeräusch abspielen und die Tür über der Spielfigur öffnen; einen Schritt MOVEMENT_ACTION_WALK_NORMAL_UP in den Eingang erzwingen; sobald die Spielfigur stillsteht, die Tür schließen und die Spielfigur ausblenden; wenn der Tür-Task endet, Musik und Bildschirm abblenden; die Karte laden.25 Das Verlassen spielt das Zeremoniell rückwärts ab: Task_ExitDoor zeigt die Tür bereits geöffnet, wartet auf die Einblendung, erzwingt einen Schritt WALK_NORMAL_DOWN, schließt die Tür und gibt erst dann die Steuerung zurück.25
Aus den obigen Zählungen und dem Ablauf der Blende weiter unten zusammengerechnet, öffnet sich die Tür in 20 Frames, der Schritt dauert 16 und die Tür schließt sich in 20; die anschließende Blende setzt ihre letzte Mischung auf ihren 17. Frame, also ist der Bildschirm nach mindestens 73 Frames, 1,22 Sekunden, vollständig dunkel. Die Karte kann erst zu laden beginnen, wenn die Blende inaktiv geworden ist, auf ihrem 22. Frame, und Task_WarpAndLoadMap das bemerkt hat und weitergegangen ist: mindestens 79 Frames, 1,32 Sekunden.525 Die Zustandswechsel zwischen den Phasen der Tür und das Warten auf das Ende der Musik können nur Frames hinzufügen, also sind beide Werte Untergrenzen.5
Der Eintritt in Smaragd ist eine Untergrenze aus seinen Framezählungen; die Zeile von Kiradex ist aus dem Türcode der App gelesen, nicht auf einem Telefon gemessen.151721
Eine Blende besteht aus neun Stufen
Eine Warp-Blende in Smaragd ist keine gleichmäßige Rampe. BeginNormalPaletteFade setzt eine Schrittweite von 2 auf einen Mischkoeffizienten, der von 0 bis 16 läuft, UpdateNormalPaletteFade mischt bei einem Aufruf die Hintergrundpaletten und beim nächsten die Sprite-Paletten und erhöht dann den Koeffizienten, und IsSoftwarePaletteFadeFinishing hängt am Ende fünf Aufrufe an.26 Auf welchen Frame jeder Aufruf fällt, hängt davon ab, wer ihn auslöst. Der Task der Tür startet die Blende aus RunTasks heraus, und BeginNormalPaletteFade führt selbst eine Aktualisierung aus, kopiert das Ergebnis sofort in den Palettenspeicher und löscht das Flag, das die nächste Aktualisierung sonst auf die vertikale Austastlücke warten ließe; später im selben Frame ruft OverworldBasic erneut UpdatePaletteFade auf.25263 Der erste Frame erhält also zwei Aktualisierungen, beide auf Stufe 0, und jeder spätere Frame eine. Eine Python-Portierung von palette.c mit diesem Ablauf, die den Frame des Tasks als 0 zählt, ergibt neun Stufen, 0, 2, 4 und so weiter bis 16: Die Hintergrundpaletten erreichen Stufe 2 auf Frame 1 und Stufe 16 auf Frame 15, die Sprite-Paletten jeweils einen Frame später, die letzte Mischung auf Frame 16 (285 Millisekunden, Frame 0 mitgezählt), und die Blende ist auf Frame 21 inaktiv (368 Millisekunden).5 Eine in einem Frame berechnete Mischung erreicht den Bildschirm in der vertikalen Austastlücke, die diesen Frame beendet. Die Portierung nimmt an, dass vorher keine Blende lief und das normale Oberwelt-Update gilt; bei aktivem Regen, Schnee, Nebel, Schatten oder Dürre läuft die Abblende genauso ab, nur ausgehend von den wettergetönten Farben (FadeScreen kopiert zuerst den getönten Puffer und ruft dann dasselbe BeginNormalPaletteFade auf), die Einblendung aber übernimmt der Wettercode, und dieser Pfad wurde nicht simuliert.5
Neun Stufen im Abstand von zwei Sechzehnteln, Hintergrund und Sprites einen Frame auseinander: die Rampe, die das Briefing für einen einzigen Schleier übernimmt.521
Warps blenden zu Schwarz ab, mit einer einzigen Familie von Ausnahmen, und die Ausnahme hat eine Richtung. WarpFadeOutScreen fragt GetMapPairFadeToType nach dem Paar von Kartentypen, WarpFadeInScreen fragt GetMapPairFadeFromType, und jede ruft FadeScreen mit Weiß auf, wenn die Antwort wahr ist, sonst mit Schwarz.25 Beide schlagen das Paar in sTransitionTypes nach, deren 16 Zeilen jeden Kartentyp beim Betreten und Verlassen von MAP_TYPE_UNDERGROUND abdecken; die erste gibt das Eintritts-Flag einer Zeile zurück und die zweite ihr Austritts-Flag, die nur in den Zeilen in eine Höhle hinein beziehungsweise nur in den Zeilen aus einer Höhle heraus wahr sind.27 Beim Betreten einer Höhle blendet der Bildschirm also zu Weiß ab und aus Schwarz wieder auf, beim Verlassen zu Schwarz ab und aus Weiß wieder auf. Jede Zeile nennt außerdem eine eigene Höhlen-Übergangsroutine, die ich nicht nachverfolgt habe.27 Eine langsamere weiße Blende, FadeInFromWhite mit einer Verzögerung von 8, wird nach 86 oder 87 Frames inaktiv, 1,44 bis 1,46 Sekunden; sie wird aus dem Callback zum Laden der Karte gestartet, dessen ersten Frame ich nicht nachverfolgt habe, und die Portierung gibt beide Fälle an.525
Wackeln ist ein geskripteter Schwenk
Das Bildschirmwackeln in Smaragd ist ein Skriptbefehl, kein Physikeffekt. ShakeCamera liest einen vertikalen Schwenk, einen horizontalen Schwenk, eine Anzahl von Wacklern und die Frames zwischen ihnen aus vier Skriptvariablen und kehrt den Schwenk bei jedem Wackler um.28 In den Skripten des Spiels gibt es 24 Aufrufe in 9 Dateien, und ein Skript hat sie alle gezählt:29
| Vertikal px | Horizontal px | Wackler | Frames Abstand | Aufrufe | Dauer |
|---|---|---|---|---|---|
| 1 | 1 | 8 | 5 | 10 | 40 Frames, 670 ms |
| 1 | 1 | 8 | 3 | 4 | 24 Frames, 402 ms |
| 1 | 2 | 8 | 5 | 3 | 40 Frames, 670 ms |
| 2 | 2 | 8 | 5 | 2 | 40 Frames, 670 ms |
| 0 | 3 | 4 | 2 | 2 | 8 Frames, 134 ms |
| 1 | 3 | 20 | 5 | 1 | 100 Frames, 1.674 ms |
| 1 | 1 | 16 | 3 | 1 | 48 Frames, 804 ms |
| 1 | 1 | 32 | 2 | 1 | 64 Frames, 1.072 ms |
Quelle: measure_gen3_shake.py über data/**/*.inc.29
Zehn der 24 sind dasselbe Wackeln: ein Pixel in jede Richtung, acht Umkehrungen, fünf Frames Abstand, 670 Millisekunden.29 Das größte ist 3 Pixel horizontal; das längste dauert 100 Frames, 1,67 Sekunden.29 Das Wackeln des Aufzugs, das der Strukturen-Beitrag behandelt hat (ein Wackler alle drei Frames, so oft, wie es mit den zurückgelegten Stockwerken zunimmt), ist eine eigene Routine und nicht in dieser Zählung enthalten.13 Nach meiner Lesart liegt die Lehre in der Zurückhaltung: Ein Wackeln ist in Smaragd ein oder zwei Pixel, eingesetzt für ein Ereignis, das das Skript für wichtig erklärt hat, nie für einen Schritt oder eine Tür.
3. Rot und Kristall: derselbe Schritt bei 30 Aktualisierungen pro Sekunde
Rot bewegt sich jeden zweiten Frame um zwei Pixel
Pokémon Rote Edition hat keine Schritttabellen. Ihre Oberwelt-Schleife OverworldLoop ruft DelayFrame auf und fällt in OverworldLoopLessDelay, das es erneut aufruft, sodass die Welt nur jeden zweiten Frame aktualisiert wird.30 Ein Schritt setzt wWalkCounter auf 8, und bei jedem Durchlauf dekrementiert AdvancePlayerSprite ihn und scrollt die Hintergrundregister hSCX und hSCY um den einmal nach links verschobenen Schrittvektor: 2 Pixel.30 Acht Durchläufe zu 2 Pixeln sind 16 Pixel in 16 Frames, dieselben 268 Millisekunden und 3,73 Zellen pro Sekunde wie in Smaragd, in Sprüngen von 2 Pixeln bei 30 Aktualisierungen pro Sekunde.430 Das Fahrrad ist ein zweites Vorrücken pro Durchlauf: DoBikeSpeedup ruft AdvancePlayerSprite erneut auf (außer auf dem Radweg (Cycling Road), solange oben, links oder rechts gedrückt ist), sodass ein Schritt 8 Frames dauert.430
Die Laufzeichnungen in Rot wechseln alle vier Durchläufe, acht Frames, durch einen Zyklus aus vier Bildern, Stand, Schritt, Stand und gespiegelter Schritt, sodass auch hier pro Schritt eine Schrittzeichnung erscheint.3122 UpdatePlayerSprite erhöht bei jedem Durchlauf den Zähler innerhalb der Animation und schaltet die Zeichnung weiter, wenn er 4 erreicht.3122
Rot dreht sich in einem Schleifendurchlauf, zwei Frames: Eine neue Richtung aus dem Stand schreibt die Blickrichtung und kehrt ohne Schritt in die Schleife zurück.30 Seine 180-Grad-Drehung hat im Code eine Zwischenrichtung, die laut dem eigenen Kommentar des Quellcodes niemand sieht: „It is unlikely for it to ever be visible because DelayFrame is called at the start of OverworldLoop.“ (Sie wird kaum je sichtbar sein, weil DelayFrame am Anfang von OverworldLoop aufgerufen wird.)30 Das habe ich aus dem Code gelesen und nicht in einem Emulator ausgeführt.
Der Warp ist ein Geräusch und eine Blende, ohne gezeichnete Tür. PlayMapChangeSound spielt SFX_GO_INSIDE, wenn das Tile eine Tür ist (Tile $0b), sonst SFX_GO_OUTSIDE, danach schreibt GBFadeOutToBlack vier Paletten und hält jede acht Frames lang: 32 Frames, 536 Millisekunden.432 Die Einblendung in Rot habe ich nicht nachverfolgt.
Auch Kristall aktualisiert nur jeden zweiten Frame
Kristall behält die Kadenz von Rot mit einem anderen Mechanismus bei. MaxOverworldDelay ist db 2, und der VBlank-Handler zählt wOverworldDelay herunter, sodass Kartenobjekte jeden zweiten Frame aktualisiert werden.33 StepVectors gibt dem Laufen acht Aktualisierungen zu 2 Pixeln und dem Fahrrad vier zu 4: 16 Frames und 8 Frames pro Zelle, wie in Rot und Smaragd.4 Der langsame Schritt besteht aus sechzehn Aktualisierungen zu 1 Pixel, 32 Frames, 1,87 Zellen pro Sekunde.433
Die Türen in Kristall blenden zu Weiß ab, und zwar schnell. MapSetupScript_Door beginnt mit FadeOutToWhite und MapSetupScript_Warp endet mit FadeInFromWhite; beide sind vier Palettenschritte im Abstand von zwei Frames, 8 Frames, 134 Millisekunden in jede Richtung.434 Die Drehung ist eine vierteilige Schrittfunktion, StepFunction_Turn, deren Teile ineinander durchfallen. Bei der ersten Aktualisierung setzt .init1 eine OBJECT_STEP_DURATION von 2 und fällt in .step1, das sie auf 1 herunterzählt; bei der zweiten zählt .step1 sie auf 0 und fällt durch .init2, das die neue Blickrichtung schreibt und wieder 2 setzt, in .step2, das sie auf 1 zählt; bei der dritten erreicht .step2 die 0 und übergibt das Objekt zurück an STEP_TYPE_FROM_MOVEMENT.33 Anweisung für Anweisung nachgespielt, braucht die Routine drei Aktualisierungen, sechs Frames bei zwei Frames pro Aktualisierung, wobei die neue Blickrichtung bei der zweiten geschrieben wird. Das ist nur die Routine selbst, aus dem Code gelesen; die Zeit vom Tastendruck bis zum Start der Routine habe ich nicht nachverfolgt.33
Drei Generationen, drei Blenden
| Rot | Kristall | Smaragd | |
|---|---|---|---|
| Tür | keine gezeichnet; das Geräusch hängt vom Tile ab | keine gezeichnet | 4 Zeichnungen zu 5 Frames (335 ms), mit einem Schiebe- oder Scharniergeräusch |
| In die Tür | der Schritt, der auf ihr landet | der Schritt, der auf ihr landet | ein erzwungener Schritt nach oben (16 Frames), dann schließt sich die Tür |
| Abblende | zu Schwarz, 4 Paletten, je 8 Frames gehalten: 32 Frames (536 ms) | zu Weiß, 4 Schritte im Abstand von 2 Frames: 8 Frames (134 ms) | zu Schwarz (zu Weiß beim Betreten einer Höhle), 9 Stufen, letzte Mischung auf Frame 16, den Frame des Tür-Tasks als 0 gezählt (285 ms), inaktiv auf Frame 21 |
| Einblendung | nicht nachverfolgt | aus Weiß, 8 Frames | aus Schwarz (aus Weiß beim Verlassen einer Höhle), dieselben 9 Stufen |
| Aus der Tür | ein erzwungener Schritt nach unten | ein erzwungener Schritt | die Tür geöffnet gezeigt, ein erzwungener Schritt nach unten, die Tür schließt sich, dann Steuerung |
Quellen: die Zählungen in den Abschnitten 1 bis 3;14525 der erzwungene Schritt aus einer Tür in Rot, PlayerStepOutFromDoor, stammt aus dem Strukturen-Beitrag.13
Die Zeilen, in denen alle drei übereinstimmen, sind die, die sich zu bewahren lohnen: eine Blende zwischen Karten, ein erzwungener Schritt, der die Spielfigur über die Schwelle trägt, und keine Eingabe während des Zeremoniells. Die Längen der Blenden unterscheiden sich um den Faktor vier, also ist die Länge nach meiner Lesart eine Gestaltungsentscheidung und kein Kanon. Die neunstufige Blende von Smaragd ist diejenige, die um gezeichnete Türen herum gebaut ist, und Kiradex zeichnet seine Türen,13 also übernimmt das Briefing sie.
Rot, Kristall und Smaragd, je ein Spiel aus den drei Generationen auf Game Boy und Game Boy Advance, zwei verschiedenen Geräten, haben sich auf denselben Vertrag geeinigt: Ein Schritt ist 16 Pixel lang, dauert 16 Frames bei etwa 59,73 Hertz und kann nach dem Beginn nicht unterbrochen werden.14 Rot erreicht das in Sprüngen von 2 Pixeln, weil seine Schleife pro Durchlauf zwei Frames wartet; Kristall durch dieselbe Verzögerung von zwei Frames mit 2-Pixel-Vektoren; Smaragd mit voller Bildrate und Bewegungen um 1 Pixel. Rennen, Surfen und das Fahrrad sind derselbe Vertrag mit 8 oder 4 Frames.14 Wenn Leute sagen, diese Spiele fühlten sich an „wie auf Schienen“, dann ist das meiner Meinung nach die Schiene: Schritt, Zeichnung und Kamera werden alle in denselben Frames gezählt, mit Dauern, die so gewählt sind, dass sie übereinstimmen, also können sie nicht auseinanderdriften.
4. Die modernen Referenzen: die Kamera von Celeste, Kerens Vokabular und die Smartphone-Portierungen
Wann eine Kamera glätten sollte und wann nicht
Die fixierte Kamera von Smaragd ist eine Antwort in einem Vokabular, das Itay Keren in „Scroll Back: The Theory and Practice of Cameras in Side-Scrollers“ dargelegt hat, einer überarbeiteten Fassung seines Vortrags auf dem Independent Games Summit der GDC 2015, veröffentlicht auf Game Developer am 11. Mai 2015.23 Die Begriffe, die ich in diesem Beitrag verwende, stammen von ihm. Position-locking fixiert die Kamera auf der Spielfigur. Edge-snapping hält sie am Levelrand an. Ein camera-window bewegt die Kamera nur, wenn die Spielfigur gegen den Rand des Fensters drückt. Lerp-smoothing lässt die Kamera zu ihrem Ziel hin gleiten, und er nennt es „a standard tool in reducing jarring camera speeds, particularly jumps.“ (ein Standardwerkzeug, um ruckartige Kamerageschwindigkeiten zu dämpfen, besonders bei Sprüngen.) Target-focus und dual-forward-focus lassen die Kamera der Spielfigur in Bewegungsrichtung vorauseilen. Er nennt außerdem platform-snapping, region-focus und cue attractors, mit denen eine Figur auf einem Raster nichts anfangen kann.23
Er nennt den Grund, warum Kamerabewegung überhaupt eine Rolle spielt: „conflicting sensory signals (Visual vs. Vestibular) may lead to discomfort and nausea, and though it’s worse in 3D (especially VR), it is still very much in effect in 2D games.“ (widersprüchliche Sinnessignale, visuell gegen vestibulär, können Unbehagen und Übelkeit auslösen; in 3D, besonders in VR, ist das schlimmer, in 2D-Spielen aber durchaus ebenfalls wirksam.) Und er nennt den Fall, in dem das schlichteste Schema richtig ist: position-locking für „a crafting adventure game like Terraria, with a small character relative to the screen with pretty small jumps, it works very well.“ (ein Crafting-Abenteuer wie Terraria, mit einer im Verhältnis zum Bildschirm kleinen Figur und recht kleinen Sprüngen, funktioniert es sehr gut.)23 Eine Stadt in Draufsicht mit einem 30 Pixel großen Sammler, der Figur, auf die der Figuren-Beitrag dieser Reihe mit Build 34 umgestiegen ist, ist genau dieser Fall, nur ohne Sprünge.35
Celeste ist die moderne Referenz für die andere Wahl. Die Entwickler haben die Klasse Player veröffentlicht, „as a learning resource and for general interest,“ (als Lernressource und aus allgemeinem Interesse), wobei die MIT-Lizenz nur diesen Code abdeckt, und die Kamera besteht darin aus ein paar Zeilen: level.Camera.Position = from + (target - from) * (1f - (float)Math.Pow(0.01f / multiplier, Engine.DeltaTime)), unter dem Kommentar „Camera (lerp by distance using delta-time)“ (Kamera, Interpolation nach Abstand mit Delta-Zeit).3637 Mit einem Multiplikator von 1 und einem stillstehenden Ziel schließt das 99 Prozent der Lücke pro Sekunde, unabhängig von der Bildrate; das Ziel in Celeste steht aber nicht still, da es jeden Frame aus Position und Zustand der Spielfigur neu berechnet wird, also beschreibt diese Zahl das Gleiten, nicht wo die Kamera am Ende landet.36 Das Ziel ist die Spielfigur, zentriert im Bildausschnitt des Spiels (X - Celeste.GameWidth / 2), begrenzt auf die Grenzen des Raums, mit Versätzen für einige Zustände: 48 Pixel voraus in Richtung des Dashs StRedDash, 64 nach oben für den Gipfelstart.36 Der erste Beitrag dieser Reihe hielt fest, dass Celeste seine Welt mit 320 mal 180 rendert und mit sechs multipliziert.22
Nach meiner Lesart stehen die beiden nicht im Widerspruch. Celeste glättet, weil die Sprünge eines Plattformers den Ausschnitt bei jedem Satz auf und ab reißen würden; Kerens eigene Definition von lerp-smoothing handelt von Sprüngen. Eine Figur auf einem Raster bewegt sich mit konstanter Geschwindigkeit in geraden Linien, also erzeugt eine fixierte Kamera keinen Ruck, den man wegglätten müsste, und eine geglättete Kamera fügt einer ohnehin gleichmäßigen Bewegung nur eine Schleppe hinzu. Abschnitt 7 zeigt, wie groß diese Schleppe im Modell von Kiradex ausfällt.
Was andere Spiele über Geschwindigkeit verraten
Stardew Valley gibt die Geschwindigkeit der Spielfigur als einheitenlosen Wert an: „2 when walking,“ (2 beim Gehen), „5 when running,“ (5 beim Rennen), „6.6 when riding a Horse (7 if the horse was fed a carrot that day),“ (6,6 beim Reiten, 7, wenn das Pferd an diesem Tag eine Karotte bekommen hat), nie unter 1.38 Das Wiki sagt nicht, wie viele Pixel pro Tick eine Einheit sind, deshalb steht hier für Stardew keine Angabe in Tiles pro Sekunde.
Ich habe außerdem nach einer Primärquelle zu den Kameras von Sea of Stars, Eastward und CrossCode gesucht und keine gefunden: Interviews über Fortbewegung und Licht, nichts über die Kamera, und die Option „Pixel Perfect“ von Sea of Stars nur in Guides und Foren beschrieben. Auch ein Vortrag oder Artikel von Maddy Thorson über Kameras tauchte nicht auf, weshalb Celeste über seinen Code zitiert wird.39
Auf dem Smartphone: Tippen zum Gehen, mit Notausgang
Die mobile Version von Stardew Valley bringt neun Steuerungsschemata mit, und das Standardschema ist „Tap-to-move & Auto-Attack“ (Tippen zum Bewegen und automatischer Angriff): „Tap anywhere on screen and the farmer will walk to where you tapped.“ (Tippen Sie irgendwo auf den Bildschirm, und die Spielfigur geht dorthin.)40 Den Finger gedrückt zu halten „will cause the character to follow the touch,“ (lässt die Figur der Berührung folgen), und das Wiki warnt, dass der Folgemodus „is very literal, moving directly towards the finger without routing around blocking objects.“ (sehr wörtlich arbeitet und sich direkt auf den Finger zubewegt, ohne Hindernisse zu umgehen.) Das Schema mit unsichtbarem Joystick belegt „the left half of the screen“ (die linke Bildschirmhälfte), mit dem Mittelpunkt dort, wo Sie den Bildschirm berühren. Und das Wiki ist ehrlich, was die Grenzen der Standardsteuerung angeht: Manche Aufgaben, die genaue Positionierung erfordern, „can not be completed using the default controls; temporarily switching to a control style with a movement joystick is necessary in such cases.“ (lassen sich mit der Standardsteuerung nicht erledigen; in solchen Fällen muss man vorübergehend zu einem Schema mit Bewegungsjoystick wechseln.)40 Die Schemata kamen mit einem Update, über das TouchArcade am 1. November 2018 berichtete, samt einem Schalter, der zurück zu „the default tap-to-move and auto-attack controls.“ (der Standardsteuerung mit Tippen zum Bewegen und automatischem Angriff) führt.41
Die Pixel Remaster des ersten Final Fantasy von Square Enix für iOS erhielt in ihrer Version 1.2.0, laut dem App-Store-Versionsverlauf der App vom 11. März 2025, eine Voreinstellung für Gehen oder Rennen (die Reihe erscheint als einzelne Apps, und gelesen habe ich nur diese eine): „In tap based movement mode the character controlled will always run as the default speed when moving.“ (Im Modus mit Tipp-Bewegung rennt die gesteuerte Figur beim Bewegen standardmäßig immer.)42 Controller-Unterstützung war für die Mobilversionen mit einem Update gekommen, über das TouchArcade am 30. Januar 2024 berichtete.43 Das genaue Schema der Touch-Bewegung fand ich über diese Hinweise hinaus nirgends dokumentiert, ebenso wenig eine Beschreibung der mobilen Bewegung von Terraria auf der Wiki-Seite, die ich abgerufen habe.39
Apples Human Interface Guidelines sagen dasselbe aus Sicht der Plattform. Für Touch-Spiele: „consider letting players tap objects to select them instead of adding a virtual selection button“ (lassen Sie Spieler Objekte möglichst durch Antippen auswählen, statt eine virtuelle Auswahltaste hinzuzufügen); „For movement control, opt to show a virtual thumbstick wherever the player lands their thumb instead of a static thumbstick position“ (zeigen Sie für die Bewegung einen virtuellen Thumbstick dort, wo der Daumen aufsetzt, statt an einer festen Position); „Make sure frequently used controls are a minimum size of 44x44 pt“ (häufig genutzte Bedienelemente mindestens 44 × 44 pt groß); „Always include visible and tactile press states“ (immer sichtbare und spürbare Druckzustände); und für Gehen und Sprinten „consider combining the actions into a single control.“ (beide Aktionen möglichst in einem Bedienelement zusammenfassen.) Das Änderungsprotokoll der Seite datiert diese Empfehlungen für Touch-Steuerung auf den 9. Juni 2025.44 Auf der WWDC25 brachte Apples Session zu Touch-Steuerungen die Prämisse klar auf den Punkt: „the vast majority of players won’t have a controller available.“ (die große Mehrheit der Spieler wird keinen Controller zur Hand haben.)45
Keine dieser Quellen sagt, dass Tippen zum Gehen die Regel für Smartphone-Spiele sei, und das behaupte ich auch nicht. Was ich daraus mitnehme, als meine Empfehlung für Kiradex und nicht als Befund, ist das Schema, dem Kiradex bereits zur Hälfte folgt: Tippen zum Gehen als Standard, wie es die mobile Version von Stardew ausliefert; ein Thumbstick, der dort erscheint, wo der Daumen aufsetzt, wie es die HIG empfehlen, für Präzision; und kein fest platziertes Steuerkreuz auf dem Bildschirm.4044
5. Das Handwerk als Regeln mit Zahlen
Dies sind die Regeln, gegen die das Briefing in Abschnitt 7 geschrieben ist, jede auf eine der obigen Messungen zurückgeführt. „Tick“ bedeutet einen Simulationsschritt von 1/60 Sekunde; so wird aus einem 59,73-Hertz-Frame etwas, das ein iPhone einhalten kann; bei 16 Ticks pro Zelle läuft man 3,75 Zellen pro Sekunde statt 3,73.119
| Element | Regel | Quelle |
|---|---|---|
| Laufen | 1 Pixel pro Tick auf 16-Pixel-Tiles: 16 Ticks pro Tile, 3,75 Tiles pro Sekunde | Rot, Kristall und Smaragd laufen alle 16 Pixel in 16 Frames, 3,73 Zellen pro Sekunde |
| Rennen | 2 Pixel pro Tick, 8 Ticks pro Tile; keine Geschwindigkeit, die 16 nicht teilt | Rennen und Surfen in Smaragd sind 2, das Fahrrad 4; nur das 2-3-3 des Kunstrads ist ungleichmäßig |
| Laufzyklus | ein Auftritt pro 16 Pixel bei jeder Geschwindigkeit; mein Vorschlag für Kiradex ist, die Zeichnung nach zurückgelegter Strecke zu wählen, bei sechs Zeichnungen über 32 Pixel also Zeichnung floor(distance × 6 / 32) mod 6 |
Smaragd taktet Zeichnungen und Schritte getrennt in Frames, mit passenden Längen: Das Laufen ist ein 32-Frame-Zyklus über zwei 16-Frame-Zellen, das Rennen ein 16-Frame-Zyklus über zwei 8-Frame-Zellen |
| Schritt | einmal begonnen, wird er beendet; die Eingabe wird am Tile gelesen | Smaragd und Rot lesen das Steuerkreuz, wenn der Schritt abgeschlossen ist |
| Drehen | 8 Ticks, etwa 133 ms (die 8 Frames von Smaragd sind 134), aus dem Stand; im Gehen keine | Smaragd WalkInPlaceFast ist 8; die Drehroutine von Kristall 6 Frames; die von Rot 2 |
| Verweigerter Schritt | gezeigt, nicht ignoriert: 32 Ticks Gehen auf der Stelle mit Anstoßen | Smaragd WalkInPlaceSlow ist 32 |
| Kamera | an die Spielfigur gebunden: kein Nachlauf, kein Vorlauf, im selben Tick um dieselben Pixel bewegt | das Kameraobjekt von Smaragd; der einzige Vorlauf, den Game Freak schrieb, ist abgeschaltet |
| Kartenrand | das Äußere zeichnen und die Fixierung halten, oder begrenzen (edge-snapping); niemals gleiten | die Randtiles von Smaragd; Kerens edge-snapping; die Raumgrenzen von Celeste |
| Tür | 4 Zeichnungen zu je 5 Ticks: 83 ms pro Zeichnung, 333 ms zum Öffnen (in Smaragd 84 und 335) | Smaragd field_door.c |
| Blende | 9 gestufte Stufen, eine alle 2 Ticks, die letzte auf Tick 16, insgesamt 18 Ticks (0,30 s), Ab- und Einblendung; standardmäßig Schwarz. Eine Adaption, keine Kopie | die normale Blende von Smaragd erreicht jede Stufe in ihren Sprite-Paletten auf denselben geraden Frames, einen Frame nach ihren Hintergrundpaletten, setzt ihre letzte Mischung auf Frame 16 und wird auf Frame 21 inaktiv; beim Betreten einer Höhle blendet sie zu Weiß ab, beim Verlassen aus Weiß auf; die 8 Frames von Kristall sind das schnelle Ende, die 32 von Rot das langsame |
| Eintrittszeremoniell | 74 Ticks, etwa 1,23 s, ohne Eingabe: öffnen 20, hineingehen 16, schließen 20, Blende 18 | der Türeintritt in Smaragd: dunkel nach mindestens 73 Frames, Laden der Karte frühestens auf Frame 79 (1,32 s) |
| Wackeln | 1 Pixel, 8 Umkehrungen, 5 Ticks Abstand (40 Ticks, 0,67 s), nur für geskriptete Ereignisse | das häufigste der 24 Wackler in Smaragd |
| Bildrate | mit 60 simulieren, was auch immer das Display tut; 60 anfordern, nicht 120 | Apples Spielepriorität bei 30 und 60; RealityKit rendert typischerweise mit 60 |
| Haptik | Ereignisse bestätigen, nicht Schritte; abschaltbar machen | Apples HIG zum Abspielen von Haptik |
| Eingabe | meine Empfehlung: standardmäßig Tippen zum Gehen; ein schwebender Thumbstick als Option für Präzision; kein festes Steuerkreuz | der Standard der mobilen Stardew-Version, der schwebende Thumbstick der HIG; keine der beiden Quellen formuliert eine Regel |
Quellen für die Tabelle: die Zählungen von Laufen, Animation und Tür in Smaragd;1 seine getrennten Zeitgeber für Zeichnung und Schritt;92 seine Kamera;123 seine Blende und seine Wackler;529 Rot und Kristall;433 Keren und Celeste;2336 Apples Hinweise zu Frame-Pacing, RealityKit und Haptik;7846 die Referenzen zur Eingabe.404244
Zwei dieser Regeln brauchen je einen Satz mehr. Die Regel zum Laufzyklus ist die, die man in einer modernen Engine am leichtesten falsch macht, weil ein Animationssystem seine eigenen Sekunden zählt, während ein Laufweg die Strecke in der Welt zählt. Die Handhelds hielten beides zusammen, indem sie beides in denselben Frames zählten und die Längen passend wählten; auf einem Telefon, wo die Framezeit schwankt, schlage ich vor, die Zeichnung aus der zurückgelegten Strecke abzulesen, was dasselbe Ergebnis liefert, ohne eine zweite Uhr, die im Gleichschritt bleiben muss. Und die Regel zur Bildrate ist kein Mangel an Ehrgeiz. Eine Welt, die sich pro Tick um ein ganzes Pixel bewegt, hat zwischen den Ticks nichts zu zeigen, also kann ein schnelleres Display nur dasselbe Bild wiederholen, und Abschnitt 6 zeigt, dass es genau das tut.
6. Der Apple-Weg: Frame-Pacing, die Uhr von RealityKit und Haptik
Der erste Beitrag dieser Reihe hat die Engine beschrieben, auf der die Welt von Kiradex läuft: eine RealityKit-Szene als 2D-Renderer, eine orthografische Kamera, der Boden als ein einziges Mesh aus texturierten Quads, laufende Figuren als Quads, und die Position jedes Sprites wird in jedem Frame auf ganze Welteinheiten gerundet.22 Dieser Abschnitt behandelt den Teil von Apples Dokumentation, der entscheidet, wie sich diese Welt in der Zeit bewegt. Jede der folgenden Seiten wurde so gelesen, wie Apple sie am 4. Oktober 2026 veröffentlicht hat, und die angegebene Verfügbarkeit ist die, die die jeweilige Seite nennt.
Frame-Pacing auf ProMotion
CADisplayLink.preferredFrameRateRange (iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0, visionOS 1.0) ist eine Bitte, keine Einstellung.15 Der Rat der Seite lautet „Choose a frame rate range that your app can consistently maintain,“ (wählen Sie einen Bildratenbereich, den Ihre App durchgehend halten kann), und sie beschreibt, was das System mit der Bitte macht: „The system typically provides a consistent frame rate by choosing one that’s a factor of the display’s maximum refresh rate.“ (Das System liefert typischerweise eine gleichmäßige Bildrate, indem es eine wählt, die ein Teiler der maximalen Bildwiederholrate des Displays ist.) Standardmäßig entspricht der Bereich dem Maximum des Displays.15 Der Bereich selbst ist ein CAFrameRateRange (iOS 15.0) aus Minimum, Maximum und bevorzugter Rate.47
Apples Artikel über ProMotion liefert die Zahlen. ProMotion-Displays wechseln auf dem iPad Pro zwischen 24 und 120 Hertz und auf unterstützten iPhones zwischen 10 und 120, und die Raten des iPhone sind zwölf Stufen: 120, 80, 60, 48, 40, 30, 24, 20, 16, 15, 12 und 10 Hertz; die des iPad Pro sind fünf davon.7 Die Geräteliste nennt inzwischen iPhone Air und „iPhone 17 and later“ (iPhone 17 und neuer) neben „iPhone 13 Pro and later.“ (iPhone 13 Pro und neuer).7 Auf dem iPhone geschieht oberhalb von 60 nichts, solange die Info.plist der App CADisableMinimumFrameDurationOnPhone (iOS 15.0) nicht auf true setzt: „If you don’t enable this support, Core Animation won’t access higher frame rates (above 60Hz).“ (Ohne diese Unterstützung greift Core Animation nicht auf höhere Bildraten über 60 Hz zu.)716 Zwei Sätze des Artikels sind für ein Spiel wichtiger als der Rest. Der eine: „In iOS 15 and later, the system provides games with special priority to 30Hz and 60Hz refresh rates to ensure optimal performance,“ (ab iOS 15 räumt das System Spielen besondere Priorität für 30 und 60 Hz ein, um optimale Leistung sicherzustellen), erreichbar mit CAFrameRateRange(minimum: 30, maximum: 60, preferred: 60).7 Der andere: „Prepare your app to operate at any refresh rate, not just those it requests.“ (Bereiten Sie Ihre App darauf vor, mit jeder Bildwiederholrate zu laufen, nicht nur mit den angeforderten.)7 Und für alles, was animiert wird: „Always use targetTimestamp to drive any animation, physics, or other time-related content provided in your CADisplayLink callback“ (verwenden Sie immer targetTimestamp, um Animation, Physik und andere zeitabhängige Inhalte in Ihrem CADisplayLink-Callback anzutreiben) (targetTimestamp ist iOS 10.0).748
Die WWDC21-Session „Optimize for variable refresh rate displays“ behandelt sowohl ProMotion auf dem iPad Pro als auch Adaptive-Sync-Displays am Mac.49 Für die Adaptive-Sync-Displays des Mac ändert sie Apples frühere Empfehlung: Auf einem Display mit fester Rate „we’ve previously recommended that you slow down your rendering to hit the next factor of the display’s fastest refresh rate“ (haben wir bisher empfohlen, das Rendering zu verlangsamen, um den nächsten Teiler der höchsten Bildwiederholrate zu treffen); bei Adaptive-Sync gilt: „You should instead attempt to present frames at the highest rate your app can do so evenly.“ (Versuchen Sie stattdessen, Frames mit der höchsten Rate darzustellen, die Ihre App gleichmäßig schafft.)49 Das Wort, das ich daraus für ein Telefon mitnehme, ist evenly, gleichmäßig.
Die Uhr von RealityKit
Die Welt von Kiradex besitzt keinen eigenen Display-Link. Sie tickt auf dem Ereignis pro Frame von RealityKit, SceneEvents.Update (iOS 13.0), „An event invoked once per frame interval,“ (ein Ereignis, das einmal pro Frameintervall ausgelöst wird), dessen deltaTime „The elapsed time since the last update.“ (die seit der letzten Aktualisierung verstrichene Zeit) ist.5051 RealityView (iOS 18.0) dokumentiert genau diesen Weg für Arbeit pro Frame, „you can use a System or directly subscribe to the engine’s SceneEvents.Update,“ (Sie können ein System verwenden oder direkt SceneEvents.Update der Engine abonnieren), und bietet selbst keine Einstellung für die Bildrate.52 Apples Artikel zur Leistung von RealityKit sagt, welche Rate zu erwarten ist: „RealityKit typically limits the refresh rate,“ (RealityKit begrenzt die Bildwiederholrate typischerweise), die er als die Rate definiert, mit der das Framework Aktualisierungen für den Bildschirm rendert, „to 60 frames per second (fps).“ (auf 60 Bilder pro Sekunde.)8 Ob RealityView auf einem iPhone 18 Pro Max oder einem iPhone Duo tatsächlich mit 60 oder 120 rendert, habe ich nicht gemessen, und Abschnitt 7 macht diese Messung zur ersten Prüfung des Briefings.
Warum 120 Hertz einem Laufen in ganzen Pixeln nichts bringt
Hier die Rechnung, aus demselben Modellskript, das Abschnitt 7 für den aktuellen Code der App verwendet, diesmal auf den Vorschlag angewandt: eine Welt, die mit einem festen 60-Hertz-Tick fortschreitet, ein Pixel pro Tick, wobei das Display zeigt, was der jeweils letzte Tick erzeugt hat. Auf einem 60-Hertz-Display wird jedes Weltpixel genau eine Bildwiederholung lang gezeigt.6 Auf einem 120-Hertz-Display werden innerhalb einer Sekunde 59 der 61 Positionen genau zwei Bildwiederholungen lang gehalten und zwei eine.6 Auf einem 80-Hertz-Display wechseln die Haltezeiten zwischen einer und zwei Bildwiederholungen, 42 Positionen einmal und 19 zweimal.6 Nach meiner Lesart ist der 120-Hertz-Fall der 60-Hertz-Fall, zweimal gezeichnet, und der 80-Hertz-Fall ist ungleichmäßiges Pacing: ein Pixel, das mal 12,5 Millisekunden steht und mal 25.
Die Frage für eine Rasterwelt auf einem iPhone lautet also nicht 120 Hertz, sondern gleichmäßiges Pacing bei 60: ein fester 60-Hertz-Simulationstick, ganzzahlige Bewegungen pro Tick, in Ticks gezählte Animationen und ein Renderer, der den letzten Tick zeigt. Genau das macht die Bewegung auch unabhängig davon, welche Rate RealityKit wählt. Die WWDC21-Session macht einen verwandten Punkt zu Zeitdifferenzen. Wenn ein langsamer Frame den Display-Link einen Callback auslassen lässt, beträgt die Differenz, um die fortgeschritten werden muss, „not 8ms, but rather 16ms“ (nicht 8 ms, sondern 16 ms); die Session sagt dann, dass eine App, die „uses time delta to advance the state of your custom drawing“ (die Zeitdifferenz nutzt, um den Zustand ihrer eigenen Zeichnung fortzuschreiben), bei jedem ausgelassenen Callback „slow down your custom drawing by one frame“ (ihre eigene Zeichnung um einen Frame verlangsamt), was ich als eine App lese, die um die erwarteten 8 Millisekunden fortschreitet statt um die tatsächlich verstrichene Zeit, und sie sagt, man „can instead keep track of a previous targetTimestamp so that you can advance the state correctly.“ (könne stattdessen den vorherigen targetTimestamp festhalten, um den Zustand korrekt fortzuschreiben.)49
Haptik
Core Haptics (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0, visionOS 1.0) baut ein CHHapticPattern, „An object representing a haptic waveform,“ (ein Objekt, das eine haptische Wellenform darstellt), aus Dictionaries, aus Arrays von CHHapticEvent-Objekten oder aus einer AHAP-Datei.53 Ereignisse gibt es in zwei haptischen Typen, hapticTransient und hapticContinuous; transiente sind „brief impulses that occur at a specific point in time.“ (kurze Impulse zu einem bestimmten Zeitpunkt.)54 Jedes nimmt die Parameter hapticIntensity, hapticSharpness, attackTime, decayTime, releaseTime und sustained.55 Eine CHHapticEngine spielt sie ab, und capabilitiesForHardware() sagt, ob das Gerät es kann.56
Der einfachere Weg ist UIImpactFeedbackGenerator (iOS 10.0), „A concrete feedback generator subclass that creates haptics to simulate physical impacts,“ (eine konkrete Unterklasse des Feedback-Generators, die Haptik zur Simulation physischer Stöße erzeugt), deren Stile „The mass of the objects in the collision“ (die Masse der Objekte bei der Kollision) beschreiben (light, medium, heavy, soft, rigid); impactOccurred(intensity:) ist iOS 13.0, und die Seite führt init(style:view:) unter „Initializing the feedback generator“ und init(style:) unter Deprecated.575859 prepare() senkt die Latenz nur, wenn es Zeit zum Wirken hat: „Calling prepare() and then immediately triggering feedback (without any time in between) does not improve latency,“ (prepare() aufzurufen und sofort Feedback auszulösen, ohne Zeit dazwischen, verbessert die Latenz nicht), und die Engine kehrt in den Leerlauf zurück, nachdem „A short period of time passes (typically seconds).“ (eine kurze Zeit vergeht, typischerweise Sekunden.)60 In SwiftUI gilt für sensoryFeedback(_:trigger:) (iOS 17.0): Es „Plays the specified feedback when the provided trigger value changes,“ (spielt das angegebene Feedback ab, wenn sich der übergebene Trigger-Wert ändert), einschließlich .impact(weight:intensity:).61
Die HIG-Seite zum Abspielen von Haptik ist die gestalterische Hälfte. „Avoid overusing haptics,“ (Haptik nicht übermäßig einsetzen)46 mit der Begründung: „Often, the best haptic experience is one that people may not be conscious of, but miss when it’s turned off.“ (Oft ist die beste haptische Erfahrung eine, die man nicht bewusst wahrnimmt, aber vermisst, wenn sie abgeschaltet ist.) „Make haptics optional.“ (Machen Sie Haptik abschaltbar.) Passen Sie „the intensity and sharpness of a haptic with the intensity and sharpness of the animation it accompanies“ (Intensität und Schärfe einer Haptik an Intensität und Schärfe der begleitenden Animation) an.46 Schärfe kann eine Erfahrung vermitteln, „that’s soft, rounded, or organic, or one that’s crisp, precise, or mechanical.“ (die weich, rund oder organisch ist, oder eine, die knackig, präzise oder mechanisch ist.)46 Und eigene Haptik kann „a collision or a hit“ (eine Kollision oder einen Treffer) ganz anders wirken lassen „from subtle experiences like the approach of footsteps or a looming danger.“ (als subtile Erfahrungen wie nahende Schritte oder eine drohende Gefahr.)46 Ein Laufen mit 3,75 Schritten pro Sekunde, minutenlang, ist genau der Fall, um den es in der ersten Regel geht.1
Eingabe
Die APIs für Touch-Steuerung in den Begriffen von Abschnitt 4: Touch Controller (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, visionOS 26.0) wird auf seiner Seite als „Integrate onscreen touch controls into your Metal-based games“ (Touch-Steuerung auf dem Bildschirm in Ihre Metal-basierten Spiele integrieren) zusammengefasst: Tasten, Steuerkreuze, Thumbsticks, Schubregler und Touchpads, bereitgestellt über einen GCController.62 Sein TCDirectionPad (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0) lässt sich so konfigurieren, dass es sich „to behave as either a composite direction pad“ (entweder wie ein zusammengesetztes Steuerkreuz) oder „as four separate buttons.“ (wie vier getrennte Tasten) verhält.6263 Der ältere GCVirtualController (iOS 15.0) ist „A software emulation of a real controller that you configure specifically for your game.“ (eine Software-Emulation eines echten Controllers, die Sie speziell für Ihr Spiel konfigurieren.)64 Eine Adresse, die ich probiert habe, developer.apple.com/documentation/touchcontrols, lieferte einen 404; die Seite des Frameworks ist touchcontroller.39
Was es kostet
Nichts in diesem Abschnitt wurde auf einem Telefon gemessen. Die Framezeit der Welt auf dem Gerät, die Kosten eines 60-Hertz-Akkumulators und die Kosten einer Haptik-Engine, die läuft, solange die Welt auf dem Bildschirm ist, sind alle ungemessen; die Prüfungen des Briefings in Abschnitt 7 sind so geschrieben, dass sie die ersten beiden messen.
7. Das Briefing: was Kiradex baut und welche Prüfungen es bestehen muss
Wo das Laufen heute steht
Ich habe den Code für Laufen, Kamera und Übergänge der Welt im Kiradex-Repository am 4. Oktober 2026 gelesen, ohne ihn zu ändern, und ein Frame-für-Frame-Modell davon geschrieben. Alles in diesem Unterabschnitt ist entweder aus diesem Code gelesen oder vom Modell berechnet, und es wird jeweils gesagt, was davon. Das Modell führt jede Float-Operation des Codes in 32 Bit aus, in der Reihenfolge des Codes, mit Swifts Rundung, bei exakt 1/60 oder 1/120 Sekunde pro Frame, und lässt die Kartenränder, den Falz des iPhone Duo und den Fußversatz weg, die alle einen geraden Laufweg in der Kartenmitte nicht verändern. Es ist ein Modell des Codes. Es ist keine Aufnahme, und gemessen wurde auf keinem Telefon.176
Was der Code tut, aus ihm gelesen:
- Die Eingabe ist Tippen zum Gehen, und nur das. Die Bühne wandelt ein Antippen in einen Weltpunkt um und tippt dann einen Sammler oder den Kiosk an oder ruft
walk(to:)auf. Es gibt kein Ziehen, kein Steuerkreuz und nirgends im Target der App einen Haptikaufruf: Eine Suche nachUIImpactFeedbackGenerator,sensoryFeedbackundCHHapticfindet nichts.17 - Der Pfad ist eine Kürzeste-Wege-Suche in acht Richtungen, geordnet nach den bisher gelaufenen Kosten ohne Schätzung der Reststrecke, was sie zur Dijkstra-Suche statt zu A* macht; Diagonalen kosten √2 und schneiden nie eine Wandecke. Ein diagonaler Schritt blickt seitwärts und teilt seinen Fortschritt durch √2, nachdem der Rennfaktor 1,6 angewandt wurde, sodass ein diagonaler Schritt im Modell bei 60 Hertz im Gehen 22 Frames und im Rennen 14 dauert, gegenüber 15 und 10 für einen geraden.176
- Geschwindigkeit ist Zeit.
tilesPerSecondist 4, also 64 Pixel pro Sekunde, und das Gehen wird zum Rennen mit dem 1,6-Fachen, wenn der Pfad 6 Schritte oder mehr umfasst; ein Laufweg in der App ist also 1 bis 5 Tiles lang, alles Längere wird gerannt. Jeder Frame addiertdt × speed / lengthzum Fortschritt eines Schritts, und wenn der Fortschritt 1 erreicht, springt die Figur auf das nächste Tile und der Fortschritt geht auf 0 zurück, wobei der Überschuss verworfen wird.17 - Die Laufzeichnung wird nach Zeit gewählt. Die Spalte ist
cycle[Int(walker.clock * framesPerSecond) % count], mitframesPerSecondgleich 8 und den Lauf- und Rennzyklen der Schmiede aus je sechs Zeichnungen. Der Figuren-Beitrag dieser Reihe nannte dieselbe Kadenz, „the walk at eight frames a second“ (das Laufen mit acht Frames pro Sekunde), und das beschreibt den Code zutreffend.1735 - Die Kamera gleitet und rundet dann. In jedem Frame bewegt sie sich um
(target - current) * min(1, dt * 6)auf die Spielfigur zu und rundet dann beide Achsen auf ganze Einheiten; sie wird auf die Karte begrenzt, wenn die Karte größer ist als der Ausschnitt. Der Satz des ersten Beitrags, dass jeder Sprite und die Kamera „are rounded to whole world units each frame, after easing“ (in jedem Frame nach dem Gleiten auf ganze Welteinheiten gerundet werden), ist ebenfalls zutreffend.1722 - Türen und Warps. Die Tür zeigt sofort die halb geöffnete Zeichnung des Sheets und nach 70 Millisekunden die geöffnete, und sie entfernt die Tür 1,4 Sekunden später; der Schritt auf den Warp wartet 220 Millisekunden und wechselt dann den Ort. Der Strukturen-Beitrag beschrieb das als bekannte Lücke.1713
- Übergänge sind Navigation-Pushes mit ihrer Standardanimation; ein Etagenwechsel tauscht die Bühne per Identität ohne Blende aus; das Verlassen ruft
dismiss()auf. Der einzige Schleier in der App ist das schwarze Overlay des Kartenbetrachters, animiert mit.easeOut(duration: 0.25).17 - Keine Anforderung einer Bildrate. Kein
CADisplayLink, keinpreferredFrameRateRangeund keinCADisableMinimumFrameDurationOnPhonein der App oder ihrer Projektdatei; die Welt tickt aufSceneEvents.Updatemit demdeltaTimedes Ereignisses.17
Was dieser Code laut Modell bei konstanter Framezeit auf dem Bildschirm tut:
- Ein Ruck um zwei Pixel einmal pro Tile bei 60 Hertz. Bei 60 Hertz dauert ein Laufweg 15 Frames pro Tile, 4,00 Tiles pro Sekunde, und die 16 Pixel jedes Tiles verteilen sich auf 14 Frames zu 1 Pixel und einen Frame zu 2: ein 2-Pixel-Sprung pro Tile, fünf auf einem Weg von fünf Tiles. Smaragd bewegt sich in jedem Frame um genau 1 Pixel.61
- Unregelmäßige Nullen und Einsen bei 120 Hertz. Bei 120 Hertz dauert ein Laufweg 30 Frames pro Tile, und die 16 Pixel jedes Tiles verteilen sich auf 16 Frames zu 1 Pixel und 14 ohne Bewegung, unregelmäßig verschränkt.6
- Das Rennen ist langsamer als seine Konstante. Bei 60 Hertz dauert das Rennen 10 Frames pro Tile, 6,00 Tiles pro Sekunde, wo 4 mal 1,6 eigentlich 6,4 ergäbe, weil der Überschuss jedes Tiles verworfen wird; die 16 Pixel jedes Tiles verteilen sich auf 4 Frames zu 1 Pixel und 6 zu 2. Bei 120 Hertz dauert es 19 Frames pro Tile, 6,32 Tiles pro Sekunde, sodass die Renngeschwindigkeit von der Bildrate abhängt.6
- Eine nachlaufende Kamera, die nie ankommt. Beim Gehen bei 60 Hertz fällt die Kamera mit jedem Tile um etwa ein Pixel weiter zurück, von 5 Pixeln am Ende des ersten Tiles bis 9 am Ende des fünften, des längsten Laufwegs, den die App macht, und die Position der Spielfigur auf dem Bildschirm ändert sich auf 8 der 73 Frames des Laufwegs nach dem ersten. Beim Rennen, also auf jedem Pfad von 6 Tiles oder mehr, läuft sie ab dem zweiten Tile 13 nach. Bei 120 Hertz erreicht sie 9 innerhalb des ersten Tiles, im Gehen wie im Rennen, und bleibt dann dort. Wenn die Spielfigur anhält, kommt die Kamera bei 60 Hertz 4 Pixel neben ihr zur Ruhe und bei 120 Hertz 9 und bleibt dort: Sobald die Lücke mal
min(1, dt × 6)unter einem halben Pixel liegt, liefert die Rundung in jedem Frame dieselbe Position. Auf welcher Seite sie zur Ruhe kommt, hängt von der Richtung des letzten Laufwegs ab. In Smaragd sind Nachlauf und Ruheversatz beide null.6 - Die Zeichnungen folgen ihrer eigenen Uhr. Der Zyklus aus sechs Zeichnungen mit 8 pro Sekunde dauert 0,75 Sekunden. Gemessen an den Positionen des Modells zwischen den Anfängen aufeinanderfolgender Zyklen bei 60 Hertz deckt er im Gehen 3,00 Tiles und im Rennen 4,50 ab (4,80 bei der nominellen Renngeschwindigkeit von 6,4 Tiles pro Sekunde, die der verworfene Überschuss nie erreichen lässt); der Zyklus von Smaragd mit zwei Auftritten deckt 2,00 Tiles ab. Nach meiner Lesart zeigt sich ein Zyklus, der anderthalbmal so viel Boden abdeckt wie seine Auftritte, als rutschende Füße, aber das Modell misst nicht, wo ein Fuß auf dem Boden steht. Die Zeichnung steht außerdem bei jedem 15. Frame auf Messers Schneide, bei 60 wie bei 120 Hertz, wenn die Uhr mal 8 genau auf eine ganze Zahl fällt, die Grenze zwischen zwei Zeichnungen: Die Uhr in 32 Bit statt 64 zu führen, wie es die App tut, ändert bei 60 Hertz auf 12 Tiles Gehen die gezeigte Zeichnung auf 9 der 11 solcher Frames und bei 120 Hertz auf 14 von 23.6
- Ein zweites Antippen mitten im Schritt reißt die Figur zurück. Das ist aus dem Code gelesen, nicht modelliert oder aufgenommen:
walk(to:)setzt den Fortschritt auf 0, während das Tile der Figur noch der Ursprung des Schritts ist, sodass der nächste Frame die Figur bis zu 15 Pixel hinter ihrer bisherigen Position zeichnet; Bewegungen anderer Sammler vom Server tun dasselbe.17
Pixel pro angezeigtem Frame: Der Kanon ist gleichmäßig, der heutige Code (ein Modell, keine Aufnahme) nicht, und ein fester Tick ist auch bei 120 gleichmäßig.621
Die gleitende, gerundete Kamera laut Modell: Sie läuft beim Gehen oder Rennen nach und bleibt am Ende des Laufwegs zu kurz stehen.621
Nichts davon ist ein Urteil darüber, wie es sich in der Hand anfühlt, denn auf keinem Telefon wurde gemessen. Echte Framezeiten schwanken, was das genaue Muster aus Einsen und Zweien verändern wird. Nachlauf und Ruheversatz hängen ebenfalls von der Framezeit ab, weil der Gleitschritt min(1, dt × 6) davon abhängt, weshalb das Modell bei 60 Hertz 4 Pixel im Ruhezustand ergibt und bei 120 Hertz 9. Nach meiner Lesart verändert Schwankung im üblichen Bereich eines Telefons ihre Größe, beseitigt sie aber nicht; die Bedingung ist, dass Frames kürzer als 1/6 Sekunde bleiben, etwa 167 Millisekunden, denn bei dieser Länge erreicht der Gleitfaktor 1 und die Kamera landet in einem Frame auf der Spielfigur, sodass ein ausreichend langer Hänger die Lücke in diesem Frame schließt.176 Die Abweichung zwischen Zeichnungen und Laufen hängt nicht von der Bildrate ab: Eine Zeichnungsuhr mit 8 pro Sekunde gegen einen Laufweg mit 4 Tiles pro Sekunde ergibt 3,00 Tiles pro Zyklus bei 60 Hertz und etwa dasselbe bei 120. Die des Rennens schon, 4,50 Tiles pro Zyklus bei 60 Hertz und 4,75 bei 120, weil der Überschuss, den es an jedem Tile verwirft, davon abhängt.6
Das Briefing
Jeder Punkt besteht aus einer Änderung an Kiradex/World/, ihrer Begründung und einer Prüfung, die eine Protokollzeile, ein Skript oder eine Aufnahme bestätigen kann. Kein Asset, kein Name und kein Geräusch aus einem der Spiele wird vorgeschlagen: Die Zahlen sind Mechanik, Grafik, Klänge und Worte gehören Kiradex selbst.
1. Nach Tick laufen, nicht nach Uhr.
Änderung. Dem Update des Rigs einen 60-Hertz-Akkumulator hinzufügen. Jeder Callback addiert sein dt; enthält der Akkumulator danach mehr als 8 Ticks an Zeit (133 Millisekunden), wird der Überschuss verworfen und als Zeile dropped mit seinen Millisekunden ins Bewegungsprotokoll geschrieben; dann läuft jeder ganze Tick im Akkumulator, und jeder zieht einen Tick ab. Der Akkumulator zählt Zeit in ganzzahligen Einheiten (Nanosekunden oder die 1/60.000.000 Sekunde des Modells), nie in Gleitkommasekunden: Mit einem Double- oder Float-Akkumulator landet der 600. Tick eines Zehn-Sekunden-Tests bei manchen Raten um einen Rundungsfehler vor seiner Grenze, und die Zählung ergibt 599. Das ist die Regel für den Rückstau: Bis zu 8 Ticks Verspätung werden im nächsten Callback aufgeholt, alles darüber gilt als Unterbrechung, sodass die Welt dort weitermacht, wo sie stehen geblieben ist, statt hinterherzujagen. Acht ist so gewählt, dass jede Rate auf Apples Liste für ProMotion-iPhones abgedeckt ist, bis hinunter zu 10 Hertz, das 6 Ticks pro Callback braucht.7 Im Modell laufen zehn Sekunden Callbacks bei jeder der zwölf Raten exakt 600 Ticks und verwerfen nichts, wohingegen eine Obergrenze von 4 Ticks pro Callback bei 12 Hertz 480 und bei 10 Hertz 400 liefe.6 Nach einem Hänger von einer Sekunde bei 60 Hertz führt die Regel im nächsten Callback 8 Ticks aus und danach je 1, und sie protokolliert 866,7 Millisekunden als verworfen; derselbe Hänger mit behaltenem Rückstau und einer Obergrenze von 4 führt 19 Callbacks hintereinander je 4 Ticks aus, genau den Schnellvorlauf, den die Regel verhindern soll.6 Jede Figur führt statt eines Bruchfortschritts eine Tickzählung pro Schritt, und ihr gezeichneter Versatz ist diese Zählung mal ihre Pixel pro Tick: exakte Ganzzahlen, sodass Figuren nicht mehr gerundet werden müssen. Gehen ist 1 Pixel pro Tick, 16 Ticks pro Tile; Rennen ist 2 Pixel pro Tick, 8 Ticks, wobei die heutige Regel bleibt, dass ein Pfad von 6 Schritten oder mehr gerannt wird. Diagonale Schritte, die die Pfadsuche zulässt, behalten das √2, das der Code bereits vorsieht, und seine Reihenfolge, also den Renntakt vor der Division durch √2: Eine gegangene Diagonale dauert 23 Ticks (16√2 ist etwa 22,6, aufgerundet) und eine gerannte 12 (8√2 ist etwa 11,3, aufgerundet), jeweils mit 16 Pixeln auf jeder Achse, bewegt auf den Ticks, auf denen sich floor(16 × t / 23) beziehungsweise floor(16 × t / 12) ändert.17 Im Gehen ist das 1 Pixel auf 16 der 23 Ticks und keines auf den übrigen 7; im Rennen 1 Pixel auf 8 der 12 Ticks und 2 auf 4, die einzige ungleichmäßige Gangart im Briefing, so wie das Kunstrad die einzige ungleichmäßige Gangart in Smaragd ist.1 Es gibt keinen Überschuss, der verworfen werden müsste.
Begründung. Die Regeln für Gehen und Rennen in Abschnitt 5; und der 2-Pixel-Ruck pro Tile bei 60 Hertz sowie die unregelmäßigen Nullen und Einsen bei 120 im Modell.61
Prüfungen. Ein Startargument -motionLog protokolliert bei jedem Tick den Tick und x, y der Spielfigur sowie jede Zeile dropped. In der vorhandenen Blickrichtungs-Demo, die in jede Richtung ein Tile geht, stellt ein Skript sicher, dass jeder Gehtick genau 1 Pixel bewegt, jedes Tile 16 Ticks dauert und kein Tick 2 bewegt. Eine Diagonal-Demo, je ein gegangener und ein gerannter diagonaler Schritt in jede Richtung, stellt 23 und 12 Ticks sicher, 16 Pixel auf jeder Achse pro Schritt, Bewegungen pro Achse von 0 oder 1 Pixel im Gehen und 1 oder 2 im Rennen, und eine gerannte Diagonale, die schneller ist als eine gegangene. Unit-Tests füttern den Akkumulator mit erfundenen dt-Folgen in exakten ganzzahligen Einheiten: Zehn Sekunden bei jeder der zwölf iPhone-Raten von 120 bis hinunter zu 10 Hertz ergeben 600 Ticks ohne Verwerfen, und eine Lücke von einer Sekunde bei 60 Hertz ergibt 8 Ticks im nächsten Callback, danach 1 pro Callback, mit 866,7 Millisekunden als verworfen protokolliert. Dasselbe Bewegungsprotokoll auf einem iPhone 18 Pro Max und auf dem inneren Display eines iPhone Duo liefert dieselben Ticks pro Tile: Die Rate des Displays darf das Laufen nicht verändern.
2. Die Laufzeichnung nach Strecke wählen.
Änderung. Dieser Punkt ist mein Vorschlag, keine Kopie: Smaragd taktet seine Zeichnungen in Frames, abgestimmt auf seine Schritte, und liest sie nicht aus der Strecke ab.92 Beim Gehen die Spalte aus dem in Schritten gezählten Fortschritt des Laufwegs wählen, also den seit Beginn des Laufwegs abgeschlossenen Schritten plus den Ticks des aktuellen Schritts geteilt durch seine Länge: walkCycle[floor(steps × walkCycle.count / 2) % walkCycle.count], zwei Auftritte pro zwei Schritte, und beim Rennen genauso. Auf einem geraden Schritt ist das die zurückgelegte Strecke über 32 Pixel, floor(distancePx × 6 / 32) mod 6. Auf einer Diagonale zählt sie den Schritt statt seiner √2-Länge, sodass ein Auftritt weiterhin am Anfang jedes Schritts landet, mit längerem Schritt; das ist meine Wahl für Zeichnungen, die für gerade Schritte gemacht sind. Am Ende des Laufwegs die Standzeichnung zeigen, wie Smaragd es tut. Die Uhren für Leerlauf, Blinzeln und Emotes bleiben, wie sie sind. Ein Tick-getakteter Zyklus könnte ebenfalls zu den Schritten passen, wie der von Smaragd, aber sechs Zeichnungen über 32 Ticks bräuchten ungleiche Standzeiten von 5 und 6 Ticks; den Fortschritt zu zählen braucht keine zweite Tabelle und gilt für jede Gangart, die die App noch hinzufügt.
Begründung. Ein Auftritt pro 16 Pixel bei jeder Geschwindigkeit und eine Kadenz, die sich nicht von der Bewegung lösen kann; der heutige Zyklus deckt im Modell im Gehen 3,00 Tiles und im Rennen 4,50 ab.16 Das klärt auch die „eight frames a second“ (acht Frames pro Sekunde) des Figuren-Beitrags: Bei 1 Pixel pro Tick wechseln sechs Zeichnungen über 32 Pixel alle 5,33 Pixel, das sind 11,25 Zeichnungen pro Sekunde, nicht 8.35
Prüfung. Aus dem Bewegungsprotokoll plus der gezeigten Spalte: Die beiden Kontaktzeichnungen (walk_a und walk_d im Zyklus der Schmiede) erscheinen bei 0 und 16 Pixeln, plus oder minus eins, von je 32, bei 60 wie bei 120 Hertz; in der Diagonal-Demo erscheinen sie auf dem ersten Tick jedes Schritts.
3. Den Schritt beenden; das Tippen behalten; einen schwebenden Stick hinzufügen.
Änderung. Ein Antippen mitten im Schritt plant vom Tile aus, auf das gerade gegangen wird, nicht vom Ursprung, und der aktuelle Schritt wird zuerst beendet; die Schrittzählung wird nie zurückgesetzt. Für Serverbewegungen anderer Sammler gilt dieselbe Regel. Aus dem Stand dreht sich die Figur bei einem Antippen, dessen erster Schritt die Blickrichtung ändert, 8 Ticks lang, bevor sie sich bewegt. Ein Antippen auf eine blockierte Zelle neben der Spielfigur oder auf das Tile vor ihr dreht sie in diese Richtung und spielt ein 32-Tick-Anstoßen mit der Verweigerungshaptik aus Punkt 6. Hält man einen Finger gedrückt, folgt die Figur ihm, wie im Standardschema von Stardew, aber jedes Mal, wenn der Finger ein Tile überquert, über die vorhandene Pfadsuche geführt, da das Folgen in Stardew Hindernisse nicht umgeht und unseres es kann. Ein schwebender Thumbstick, vier Richtungen, standardmäßig aus, erscheint dort, wo der Daumen aufsetzt, gebaut auf einer SwiftUI-Ziehgeste über der Welt; alles Gezeichnete ist mindestens 44 mal 44 Punkte groß. Der Thumbstick von Touch Controller (iOS 26) ersetzt die Ziehgeste nur, wenn ein Test-Build zeigt, dass er über der RealityView zeichnet: Apple beschreibt das Framework als für „Metal-based games“ (Metal-basierte Spiele), seine WWDC25-Session sagt, es „integrates directly with Metal,“ (ist direkt in Metal integriert), und die Welt von Kiradex wird von RealityView gezeichnet, ohne eigenen Render-Pass.4562
Begründung. Die Regeln für Schritt, Drehen, verweigerten Schritt und Eingabe aus Abschnitt 5.14044624517
Prüfungen. Ein UI-Test tippt ein Tile 6 nach Osten an, dann 120 Millisekunden später ein Tile 6 nach Norden; das Bewegungsprotokoll zeigt keinen Tick, auf dem x der Spielfigur abnimmt, und das Tile des ersten Schritts wird vor der Drehung abgeschlossen. Ein UI-Test tippt die Wand neben dem Haus an; das Protokoll zeigt einen Blickrichtungswechsel und ein 32-Tick-Anstoßen und keine Positionsänderung. Mit eingeschaltetem Stick schließt ein Ziehen, das ab dem ersten Bewegungstick 60 Ticks lang nach rechts gehalten wird, bis Tick 48 drei Tiles ab, beginnt ein viertes, während der Stick noch gehalten wird, und beendet es nach dem Loslassen auf Tick 64: Das Protokoll zeigt die Spielfigur 4 Tiles östlich, auf einem ganzen Tile, mit dem beim Loslassen laufenden Schritt abgeschlossen und ohne Halt zwischen den Tiles.
4. Die Kamera fixieren.
Änderung. Das Gleiten durch eine Kamera ersetzen, die im selben Tick gesetzt wird, in dem sich die Spielfigur bewegt, nur aus ganzen Weltpixeln: camera = clamp(player + foldOffset, low, high). Jeder Term wird ganzzahlig gehalten, denn heute macht erst die abschließende Rundung die Kamera ganzzahlig: Grenzen und Falzversatz sind gebrochen. Die Position der Spielfigur ist durch Punkt 1 ganzzahlig. Der Falzversatz, den follow in Punkten mal displayScale / pixelScale berechnet, wird einmal, wenn sich der Falz ändert, auf ganze Weltpixel gerundet. Die Grenzen der Begrenzung werden nach innen gerundet, die untere auf, die obere ab, weil die halbe Ausschnittgröße in Weltpixeln nicht ganzzahlig sein muss: fit teilt die Bildschirmpixel des Ausschnitts durch eine ganze Zahl von Bildschirmpixeln pro Texel, sodass ein zur Veranschaulichung gewählter Ausschnitt von 393 mal 852 Punkten bei 3x den Wert 7 erhält, einen Ausschnitt von 168,43 Weltpixeln Breite und eine halbe Breite von 84,21, und die Grenzen der Kamera werden 85 und die Kartenbreite minus 85, sodass kein Frame über den Kartenrand hinaus zeigt.176 Ist die Karte kleiner als der Ausschnitt, tauschen die Grenzen, wie es die heutige Begrenzung zulässt, und dieselbe Rundung nach innen hält die ganze Karte im Bild. Die Begrenzung auf die Karte bleibt; die Regel aus Abschnitt 5 erlaubt Begrenzen oder das Zeichnen des Äußeren, und die Kamera von Kiradex geht nur dann über den Kartenrand hinaus in den Wald, wenn das Duo halb geöffnet ist, und zwar um einen Rand aus ganzen Tiles.17 Den Falzversatz als fest behandeln, bis sich der Falz ändert; ändert er sich von a zu b, ihn über a + round((b − a) × i / 8) für i von 0 bis 8 stufen, eine Stufe alle 2 Ticks, im Takt der Blende, sodass jede Position auf dem Weg ein ganzes Pixel ist; eine Änderung von 0 auf minus 37 etwa durchläuft 0, −5, −9, −14, −19, −23, −28, −32 und −37.6 Die Parallaxe des Waldes bleibt, berechnet aus der fixierten Kamera und gerundet wie heute. Keine Vorausschau zum angetippten Ziel: Game Freak hat eine gebaut und abgeschaltet gelassen, und ein Ziel beim Tippen zum Gehen ist ohnehin schon auf dem Bildschirm.12
Begründung. Die Kameraregel aus Abschnitt 5 und der Nachlauf im Modell, der im Verlauf eines Laufwegs bis zum fünften Tile auf 9 Pixel wächst und beim Rennen 13 beträgt, seine Änderungen der Bildschirmposition der Spielfigur sowie seine Ruheversätze von 4 und 9 Pixeln.6
Prüfungen. Aus dem Bewegungsprotokoll: Kamera minus Spielfigur ist auf jedem Tick eines Laufwegs auf dem offenen Platz konstant (null oder der Falzversatz) und nach dem Anhalten der Spielfigur gleich, und jeder Kamerawert ist eine ganze Zahl. Ein Unit-Test ruft fit mit dem Ausschnitt von 393 mal 852 Punkten bei 3x auf, lässt die Spielfigur zu beiden Rändern einer Karte laufen und stellt ganzzahlige Kamerapositionen sicher, die bei 85 und bei Kartenbreite minus 85 anhalten; derselbe Test ändert dann den Falz und stellt sicher, dass die Kamera den neuen Versatz über 9 ganzzahlige Pixelpositionen im Abstand von 2 Ticks erreicht und dass kein Tick dazwischen die Grenzen verlässt. Für eine Aufnahme taugen die Laufzeichnungen nicht als Markierung, weil die Füße von Zeichnung zu Zeichnung ihre Form ändern; ein Debug-Build zeichnet an der Position der Spielfigur-Entity eine Markierung von einem Texel in einer Farbe, die sonst nirgends vorkommt, und ein Skript findet sie in jedem Frame eines Laufwegs von 6 Tiles auf dem offenen Platz: in jedem Frame dasselbe Bildschirmpixel. Nahe am Kartenrand bewegt sich die Markierung und die Kamera nicht, und zwar nur um ganze Pixel.
5. Tür, Schritt und Blende in Kiradex’ eigener Grafik.
Änderung. Das Betreten einer Tür, also eines Warps mit Tür-Sheet. Heute öffnet sich die Tür, nachdem die Figur auf der Türzelle gelandet ist: onStep des Schritts findet den Warp auf diesem Tile und ruft dort openDoor auf.17 Smaragd lässt die Spielfigur nie in einer geschlossenen Tür stehen: TryDoorWarp löst nur aus, wenn die Spielfigur auf der Zelle darunter nach Norden gegen eine Tür drückt, und Task_DoDoorWarp öffnet die Tür eine Zelle darüber und erzwingt dann den Schritt hinein.6525 Das Briefing verlegt den Auslöser passend dazu eine Zelle zurück:
- Ist der nächste Schritt des Pfads einer auf einen Tür-Warp, hält der Laufweg auf der Zelle davor an, und der Eintritt beginnt dort, auf Tick 0. Eingabe einfrieren und das Türgeräusch abspielen, ein Kiradex-Geräusch.
- Die Tür in vier Slots zu 5 Ticks öffnen, den vier Zeichnungen von Smaragd zu je fünf Frames entsprechend (83 Millisekunden pro Slot bei 60 Ticks pro Sekunde, wo die fünf Frames von Smaragd 84 sind; insgesamt 20 Ticks), anstelle der heutigen halb geöffneten Zeichnung sofort und der geöffneten nach 70 Millisekunden: geschlossen, halb, offen, offen. Das Tür-Sheet von Kiradex hat drei Zeichnungen, geschlossen, halb offen und offen (
door_sheet()der Schmiede zeichnet drei, und die App lädt es mitSpriteSheet.bundled(name, columns: 3, rows: 1)), wo die Tür von Smaragd geschlossen plus drei ist, also hält die geöffnete Zeichnung bis in den vierten Slot. Eine vierte Zeichnung aus der Schmiede, zwischen halb und offen, würde diesen Slot stattdessen füllen; sie ist optionale Grafik, keine Voraussetzung. Den Kommentar korrigieren, der „four ticks“ (vier Ticks) sagt.2417 - Die Spielfigur einen erzwungenen Schritt von dieser Zelle auf die Türzelle gehen lassen, 16 Ticks. Die Türen der Stadt liegen in der untersten Reihe eines Gebäudes, mit Gebäudezellen auf beiden Seiten und darüber, und der Pfad schneidet nie eine Wandecke, also führt dieser Schritt immer von der Zelle darunter nach oben, wie der von Smaragd.17
- Die Figur ausblenden und die Tür schließen, die Slots rückwärts (offen, offen, halb, geschlossen), 20 Ticks.
- Einen schwarzen Schleier in 9 gestuften Deckkraftstufen über die Welt blenden (0, 2/16 und so weiter bis 16/16), Stufe
iauf Tick2ider Blende, sodass die letzte Stufe auf Tick 16 landet und bis Tick 17 steht: 18 Ticks. Das ist eine bewusst gewählte Adaption, nicht das Timing von Smaragd. Die Sprite-Paletten von Smaragd erreichen jede Stufe auf denselben geraden Frames wie dieser Schleier, die Hintergrundpaletten einen Frame früher, und nach der letzten Mischung auf Frame 16 laufen fünf abschließende Aktualisierungen, bevor sie auf Frame 21 inaktiv wird; ein einzelner Schleier hat keine zweite Ebene, die nachhinken könnte, und nichts abzuschließen.5 Niemals die Deckkraft der View animieren, die die RealityView selbst enthält; diese Lektion hat der Kartenbetrachter dem ersten Beitrag erteilt.22 - Das Ziel mit deaktivierten Animationen pushen und den Schleier in denselben 9 Stufen wieder ausblenden.
- Auf der Fußmatte drinnen mit Blick nach oben ankommen. Das Verlassen kehrt es um: Ankunft auf der Türzelle bei offener Tür, ein erzwungener Schritt nach unten von 16 Ticks, die Tür schließt sich, dann Eingabe.
Etagen, die heute die Bühne ohne Blende tauschen, erhalten denselben Schleier ohne Tür und ohne erzwungenen Schritt. Das Verlassen eines Raums, das heute dismiss() aufruft, erhält den Schleier und den erzwungenen Schritt hinaus.
Begründung. Die Regeln für Tür, Blende und Zeremoniell aus Abschnitt 5; heute wechselt der Ort 220 Millisekunden nach dem Schritt, die Tür zeigt zwei Zeichnungen im Abstand von 70 Millisekunden, und der Bildschirm gleitet seitwärts.1517
Prüfungen. Das Bewegungsprotokoll zählt Ticks ab dem, auf dem der Laufweg unterhalb der Tür anhält. Es zeigt die Spielfigur noch auf dieser Zelle und die Slots der Tür auf den Ticks 0, 5, 10 und 15 (geschlossen, halb, offen, offen); den erzwungenen Schritt auf den Ticks 20 bis 35, 1 Pixel pro Tick, der die Türzelle auf Tick 35 erreicht und nie früher; die schließenden Slots auf den Ticks 36, 41, 46 und 51; die erste Stufe des Schleiers auf Tick 56; und den Push nicht vor Tick 74. Ein Laufweg, der ohne diese Abfolge auf einer Türzelle endet, besteht die Prüfung nicht. Das Bewegungsprotokoll hält außerdem auf jedem Tick die Deckkraft des Schleiers fest: 9 verschiedene Werte, jeder 2 Ticks lang, auf den Ticks 56 bis 73 beim Hinausgehen, und dieselben 9 beim Hereinkommen. Eine Bildschirmaufnahme bestätigt das an einem Bildteil, der stillsteht: Der ganze Frame taugt nicht, weil das Wasser des Bodens auf einer Karte mit Wasser 4-mal pro Sekunde die Zeichnung wechselt und andere Sammler herumlaufen können; also tastet ein Skript ein vorab gewähltes Stück Gebäudewand ab, ohne animiertes Tile und ohne darüberlaufende Figur in der Aufnahme, und zählt 9 verschiedene Stufen beim Hinausgehen und 9 beim Hereinkommen, jede 2 Frames lang, plus oder minus eins bei 60 Hertz.17 In einer Bildschirmaufnahme eines Warps verschiebt sich die Welt nie horizontal: kein Navigations-Slide.
6. Haptik: drei Ereignisse und ein Schalter.
Änderung. Eine CHHapticEngine im Besitz der Welt, gestartet, wenn sie erscheint, und gestoppt, wenn sie verschwindet, mit einem Schalter „Haptik“ in den Einstellungen, der standardmäßig eingeschaltet ist und die Haptikeinstellung des Systems respektiert. Die Muster, als AHAP-Dateien im Bundle:
| Ereignis | Muster | Warum |
|---|---|---|
| Verweigerter Schritt (das Anstoßen) | ein hapticTransient, Intensität 0,4, Schärfe 0,2 |
der Stoß der HIG ist „a thud when two heavy objects collide“ (ein dumpfer Schlag, wenn zwei schwere Objekte zusammenstoßen)46; weich, weil die Grafik weich ist |
| Tür öffnet sich | ein hapticTransient mit Intensität 0,3 und Schärfe 0,6 auf jedem Slot, der beim Öffnen das Bild ändert, die halb geöffnete Zeichnung auf Tick 5 und die geöffnete auf Tick 10 (83 und 167 ms) |
zur begleitenden Animation passend |
| Karte angehoben (die 3D-Karte) | ein hapticTransient mit 0,7 und 0,8, wenn sie oben ankommt |
das einzige echte Objekt in der Welt |
| Schritte | standardmäßig keine | 3,75 Schritte pro Sekunde, minutenlang, sind der übermäßige Einsatz, vor dem die HIG warnt |
Die Werte für Intensität und Schärfe sind meine Ausgangspunkte, keine Messungen, und zum Abstimmen auf einem Gerät gedacht. UIImpactFeedbackGenerator(style: .soft, view:), mit prepare() aufgerufen beim Start eines Laufwegs, ist der Rückfall, wo capabilitiesForHardware() meldet, dass Core Haptics nicht verfügbar ist.
Begründung. Die Haptikregel; Apples in Abschnitt 6 zitierte Hinweise.46565760
Prüfung. Ein Argument -hapticLog protokolliert jedes Ereignis. Ein geskripteter Türeintritt protokolliert genau 2 Türereignisse, auf den Ticks 5 und 10 der Zählung aus Punkt 5, und keines pro Schritt; mit ausgeschaltetem Schalter überhaupt keines.
7. Bildrate: 60, gleichmäßig.
Änderung. Keine an der Info.plist: CADisableMinimumFrameDurationOnPhone nicht hinzufügen, denn die Welt gewinnt durch 120 nichts. Die RealityView bleibt; der Tick aus Punkt 1 macht die Bewegung unabhängig von der Rate, mit der sie rendert. Falls je ein Display-Link hinzukommt, CAFrameRateRange(minimum: 30, maximum: 60, preferred: 60) setzen, um Apples Spielepriorität zu erhalten.76
Prüfungen. Ein Histogramm von SceneEvents.Update.deltaTime über einen 60-sekündigen Laufweg auf einem iPhone 18 Pro Max und auf einem iPhone Duo protokollieren, äußeres und inneres Display. Das Briefing nimmt an, dass der Modalwert 16,7 Millisekunden ist; ist er 8,3, hält der Akkumulator aus Punkt 1 das Laufen trotzdem bei 16 Ticks pro Tile, und das Protokoll beweist es. Die Zeilen dropped im Bewegungsprotokoll: keine bei einem normalen Laufweg, bei keiner der beiden Raten.
8. Wackeln, nur für einen Moment.
Änderung. Ein Kamerawackeln in ganzen Texeln, 1 Texel, 8 Umkehrungen, 5 Ticks Abstand, eingesetzt für ein Ereignis, das es sich verdient, etwa die Enthüllung einer seltenen Karte auf dem Platz, zusammen mit der Haptik für die angehobene Karte. Nicht für Türen, Schritte oder Ankünfte.
Begründung. Das häufigste der 24 Wackler in Smaragd und ihre Zurückhaltung.29
Prüfung. Das Bewegungsprotokoll zeigt den Kameraversatz abwechselnd plus und minus 1 Texel auf den Ticks 0, 5 und so weiter bis 35 und zurück auf 0 auf Tick 40.
Nicht in diesem Briefing
- Kameraglättung oder Vorausschau auf dem Raster. Es gibt keine Sprünge zu glätten, und Game Freak hat seine Vorausschau abgeschaltet ausgeliefert.1223
- 120 Hertz für die Welt. Gleichmäßiges Pacing bei 60 ist das Ziel; 120 zeigt jedes Pixel zweimal.6
- Standardmäßig Haptik pro Schritt.46
- Ein festes Steuerkreuz auf dem Bildschirm. Meine Empfehlung ist Tippen zum Gehen plus ein optionaler schwebender Stick.44
- Irgendwelche Tür- oder Warp-Geräusche oder Jingles aus den Spielen. Klänge sind Ausdruck, und Kiradex braucht eigene.
- Fahrräder, Surfen, Eis und Strömungen. Die Figuren der App gehen und rennen und sonst nichts, und die obigen Geschwindigkeiten sind für den Fall festgehalten, dass sich das ändert.17
Die offene Frage: Timing auf einem Gerät
Jede Modellzahl in diesem Abschnitt setzt konstante 1/60 oder 1/120 Sekunde zwischen den Frames voraus. Ob RealityView auf einem iPhone 18 Pro Max oder einem iPhone Duo mit 60 oder 120 rendert und wie stark sein deltaTime schwankt, ist ungemessen; das Histogramm aus Punkt 7 ist das Erste, was laufen sollte, und Punkt 1 ist so geschrieben, dass die Antwort am Laufen bei keiner Rate auf Apples iPhone-Liste etwas ändert.687
Wichtigste Erkenntnisse
Wenn Sie die Grafik zeichnen
- Zeichnen Sie das Laufen für eine Strecke. Das Laufen in Smaragd besteht aus zwei Schritten und zwei Ständen über 32 Pixel, gehalten in Frames, die zu seinen Schritten passen, und schnellere Gangarten verwenden die Zeichnungen mit kürzeren Standzeiten wieder, statt Frames hinzuzufügen, sodass bei jeder Geschwindigkeit ein Auftritt auf 16 Pixel kommt.192
- Ein Zyklus aus sechs Zeichnungen ist in Ordnung, sofern die Engine ihn über eine feste Strecke abspielt; mit fester Rate gegen einen getrennt getakteten Laufweg abgespielt, driftet seine Kadenz von der Bewegung weg, im Modell unseres Codes 3,00 Tiles pro Zyklus im Gehen und 4,50 im Rennen.6
- Die Tür von Smaragd ist geschlossen plus drei Zeichnungen, jede 84 Millisekunden auf dem Bildschirm. Ein Sheet aus drei, geschlossen, halb und offen, wie das von Kiradex, füllt die vier Slots, indem es die geöffnete Zeichnung zwei davon lang hält, oder die Schmiede zeichnet eine vierte; so oder so sollten Sie den geöffneten Zustand als etwas zeichnen, das sich anzusehen lohnt.12417
Wenn Sie die Engine bauen
- Lassen Sie die Welt mit einem festen Tick fortschreiten, mit Bewegungen in ganzen Pixeln, die das Tile teilen, lesen Sie die Eingabe am Tile und lassen Sie den Renderer den letzten Tick zeigen. Legen Sie fest, was mit einem Rückstau passiert: Unserer holt bis zu 8 Ticks auf und verwirft den Rest, protokolliert. Die Schritttabellen von Smaragd sind das Vorbild: Jede summiert sich auf 16.216
- Fixieren Sie die Kamera im selben Tick an der Spielfigur, mit Grenzen und Versätzen in ganzen Pixeln, sodass hinterher nichts gerundet werden muss. Gleiten und anschließendes Runden läuft einer gehenden Spielfigur hinterher und bleibt im Modell unseres Codes 4 bis 9 Pixel zu kurz stehen, bis zum nächsten Laufweg.36
- Fordern Sie auf dem iPhone für Pixelbewegung keine 120 Hertz an. Apple priorisiert für Spiele 30 und 60, RealityKit rendert typischerweise mit 60, und ein Laufen in ganzen Pixeln hält bei 120 jedes Pixel lediglich zwei Bildwiederholungen lang.786
- Blenden Sie in Stufen, nicht in einer glatten Rampe. Die Türblende von Smaragd hat neun Stufen im Abstand von zwei Sechzehnteln, ihre letzte Mischung auf dem 17. Frame und das Ende der Blende auf dem 22.; unser 18-Tick-Schleier ist eine Adaption dieser Rampe, keine Kopie.5
- Animieren Sie niemals die Deckkraft der SwiftUI-View, die eine RealityView enthält; legen Sie einen Schleier darüber.22
Wenn Sie den Spielablauf gestalten
- Ein Türeintritt ist in Smaragd ein Zeremoniell von mindestens 1,32 Sekunden ohne Eingabe, und der erzwungene Schritt über die Schwelle sorgt dafür, dass er sich wie Hineingehen liest.525
- Ein Drehen auf der Stelle von 8 Frames lässt eine Spielfigur sich etwas zuwenden, ohne sich zu bewegen; ein Anstoßen von 32 Frames sagt ihr, dass ein Schritt verweigert wurde.1
- Auf einem Telefon empfehle ich: Tippen zum Gehen als Standard, wie es die mobile Version von Stardew ausliefert, einen schwebenden Stick als Rückfall für Präzision, wie es die HIG empfehlen, und kein festes Steuerkreuz.4044
- Haptik bestätigt Ereignisse, keine Schritte, und kommt mit einem Schalter.46
Häufige Fragen
Wie schnell läuft die Spielfigur in Pokémon?
In Rot, Kristall und Smaragd legt ein Schritt im Gehen eine 16-Pixel-Zelle in 16 Frames bei 59,7275 Frames pro Sekunde zurück: 268 Millisekunden pro Zelle, 3,73 Zellen pro Sekunde. Smaragd bewegt in jedem Frame 1 Pixel; Rot und Kristall bewegen jeden zweiten Frame 2 Pixel. Rennen und Surfen in Smaragd sowie die Fahrräder in Rot und Kristall brauchen 8 Frames pro Zelle, 7,47 Zellen pro Sekunde.1419
Wie viele Frames hat ein Laufzyklus in Pokémon Smaragd?
Vier Einträge über 32 Frames: eine Schrittzeichnung für 8 Frames, die Standzeichnung für 8, die andere Schrittzeichnung für 8, Stand für 8. Das sind zwei Zellen Laufen, also zeigt jeder Schritt einen Schritt und einen Stand, und die Beine wechseln Schritt für Schritt ab. Das Rennen ist ein 16-Frame-Zyklus über zwei 8-Frame-Zellen.192
Läuft die Kamera in Pokémon Smaragd der Spielfigur hinterher?
Nein. Die Kamera von Smaragd übernimmt die Position der Spielfigur und scrollt die Karte im selben Frame um dieselben Pixel, sodass die Spielfigur auf dem Bildschirm fest steht, während sich die Welt bewegt. Am Kartenrand hält sie ebenfalls nicht an: Das Äußere wird aus den Randtiles des Layouts gezeichnet. Eine Vorausschau-Kamera für das Fahrrad existiert im Code, wird aber nie eingeschaltet.231112
Wie lange dauert der Übergang mit Tür und Blende in Pokémon Smaragd?
Die Tür öffnet sich in vier Zeichnungen zu fünf Frames (335 Millisekunden), die Spielfigur macht einen erzwungenen Schritt von 16 Frames hinein, die Tür schließt sich in 20 Frames, und der Bildschirm blendet in neun Stufen ab, die letzte Mischung auf dem 17. Frame der Blende und das Ende der Blende auf ihrem 22.: mindestens 79 Frames, 1,32 Sekunden, bevor die nächste Karte zu laden beginnen kann. Beim Betreten einer Höhle blendet der Bildschirm stattdessen zu Weiß statt zu Schwarz ab, und beim Verlassen blendet er aus Weiß wieder auf.152527
Sollte ein Pixel-Art-Spiel auf einem ProMotion-iPhone mit 120 Hz laufen?
Nicht für Bewegung in ganzen Pixeln. Eine Welt, die sich um ein Pixel pro 60-Hertz-Tick bewegt, zeigt bei 120 Hertz jedes Pixel nur zwei Bildwiederholungen lang und bei 80 mit ungleichmäßigen Haltezeiten. Apples ProMotion-Artikel sagt, dass Spiele „special priority to 30Hz and 60Hz“ (besondere Priorität für 30 und 60 Hz) erhalten, eine iPhone-App muss CADisableMinimumFrameDurationOnPhone setzen, um über 60 zu gehen, und RealityKit rendert typischerweise mit 60. Simulieren Sie mit einem festen 60-Hertz-Tick und lassen Sie das Display sein, was es ist.67168
Warum rutschen die Füße meines Sprites beim Laufen?
Meist, weil die Laufzeichnungen nach einer Uhr laufen und die Bewegung nach einer anderen, mit Längen, die nicht übereinstimmen. Der aktuelle Code von Kiradex spielt einen Zyklus aus sechs Zeichnungen mit 8 Zeichnungen pro Sekunde ab, während er 4 Tiles pro Sekunde läuft, sodass ein Zyklus 3 Tiles statt 2 abdeckt, wie ein Modell des Codes zeigt. Die Handhelds zählen beides in Frames und lassen die Zeichnungen jedes Schritts so lange dauern wie den Schritt; die Zeichnung aus der zurückgelegten Strecke zu wählen, was ich für Kiradex vorschlage, erreicht dieselbe Übereinstimmung ohne zweite Uhr. So oder so kann sich die Kadenz nicht von der Bewegung lösen; ob ein Fuß fest aufsetzt, hängt außerdem von den Zeichnungen selbst ab.619
Sollte ein mobiles Pixelspiel ein virtuelles Steuerkreuz verwenden?
Nach meiner Lesart der Quellen nicht als Standard. Der mobile Standard von Stardew Valley ist Tippen zum Bewegen, mit einem unsichtbaren Joystick unter den übrigen Schemata für präzise Aufgaben; Apples HIG empfehlen, Objekte direkt anzutippen, und einen Thumbstick, der „wherever the player lands their thumb instead of a static thumbstick position.“ (dort erscheint, wo der Daumen aufsetzt, statt an einer festen Position.)4044
Verwandt auf dieser Website: Pixel-Art-Welten auf dem iPhone ist der erste Leitfaden dieser Reihe, mit dem Schritt-und-Stand-Laufen von Smaragd, dem RealityKit-Rezept, auf dem diese Welt läuft, und der Deckkraft-Lektion des Kartenbetrachters; Pixel-Art-Menschen: Figuren und ein Editor auf dem iPhone ist der zweite, mit dem Laufzyklus aus sechs Zeichnungen, dessen Timing dieser Beitrag von einer Uhr auf eine Strecke umstellt; Pixel-Art-Bauten: Häuser, Hallen und Innenräume auf dem iPhone ist der dritte, mit den Türen, Warps und Etagen, deren Timing Abschnitt 2 misst; iPhone Duo für Entwickler und Bereiten Sie Ihre App auf das iPhone Duo vor behandeln die beiden Displays und den Falz, von dem sich die Kamera des Briefings fernhält; Das räumliche Denkmodell von RealityKit erklärt das Entity- und System-Modell hinter SceneEvents.Update.
Quellen
-
Messung des Autors, 4. Oktober 2026:
measure_gen3_motion.pyim Belegordner des Autors zu diesem Beitrag, ausgeführt über pretpokeemeraldbei Commit731ad5b; es parst die Schrittfunktionstabellen und die Dauern vonInitMoveInPlaceinsrc/event_object_movement.c, die Animationstabellen insrc/data/object_events/object_event_anims.h, die Regel für den Verzögerungszähler insrc/sprite.cund die Türframes insrc/field_door.cund rechnet Frames mit 59,7275 Hertz um. Ausgabe gespeichert alsmeasure_gen3_motion.out.txtdaneben (Geschwindigkeiten 3,73, 7,47, 9,95, 14,93 und 29,86 Zellen pro Sekunde; Gehen auf der Stelle 32, 16, 8 und 4 Frames;sAnim_GoSouth32 Frames; Türzeichnungen 5 Aktualisierungen gehalten, 83,7 ms, 335 ms für vier). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/event_object_movement.c(sStep1FuncsbissStep8Funcsund der Kommentar „Over the course of the step animation, these sum to 16 pixels (one full metatile)“;sStepTimesundNpcTakeStep, das die Schritttabelle mitsTimerindiziert, ein Eintrag pro Frame;SetStepAnimHandleAlternation, das zu Beginn eines Schritts die Animation der Gangart und den Wechsel festlegt;CameraObject_UpdateMove), Commit731ad5b, abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/event_object_movement.c. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/overworld.c(die Reihenfolge inOverworldBasic,RunTasks(); AnimateSprites(); CameraUpdate(); UpdateCameraPanning();, später in derselben Funktion gefolgt vonUpdatePaletteFade();VBlankCB_FieldruftTransferPlttBufferauf;InitPlayerAvatarvorInitCameraUpdateCallback(gPlayerAvatar.spriteId)) undsrc/sprite.c(AnimateSpritesführt Callbacks in Slot-Reihenfolge aus), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/overworld.c und https://github.com/pret/pokeemerald/blob/master/src/sprite.c. Die Schlussfolgerung „im selben Frame“ ist die Lesart des Autors aus dem Code, kein Lauf. ↩↩↩↩↩↩↩ -
Messung des Autors, 4. Oktober 2026:
measure_gen12_motion.pyim Belegordner des Autors zu diesem Beitrag, ausgeführt über pretpokeredbeid2704a6(home/overworld.asm,home/fade.asm) undpokecrystalbei5beda23(engine/overworld/events.asm,engine/overworld/map_objects.asm,data/maps/setup_scripts.asm,engine/tilesets/timeofday_pals.asm). Ausgabe gespeichert alsmeasure_gen12_motion.out.txt(Rot: 16 px in 16 Frames, Fahrrad 8 Frames, Warp-Blende 32 Frames; Kristall: Gehen 8 Aktualisierungen zu 2 px, Fahrrad 4 zu 4, langsam 16 zu 1 bei 1,87 Zellen pro Sekunde, Türblende 8 Frames in jede Richtung). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Messung des Autors, 5. Oktober 2026:
measure_gen3_fade.pyim Belegordner des Autors zu diesem Beitrag, eine Python-Portierung vonBeginNormalPaletteFade,UpdatePaletteFademit seinem Flag für ausstehende Übertragungen,UpdateNormalPaletteFadeundIsSoftwarePaletteFadeFinishingaus pretpokeemerald/src/palette.cbei731ad5b, ausgeführt nach dem Ablauf ihrer Aufrufer: Der Tür-Task startet die Blende innerhalb vonRunTasks(Task_DoDoorWarp,src/field_screen_effect.c);BeginNormalPaletteFadeaktualisiert einmal, kopiert den Puffer in den Palettenspeicher und löscht das Flag;OverworldBasic(src/overworld.c) aktualisiert im selben Frame erneut; jeder spätere Frame aktualisiert einmal, und die VBlank-Übertragung löscht das Flag. Frames werden ab dem Frame des Tasks als 0 gezählt. Angenommen wird, dass keine Blende lief; bei aktivem Regen, Schnee, Nebel, Schatten oder Dürre kopiertFadeScreeninsrc/field_weather.cden wettergetönten Puffer und ruft dann dasselbeBeginNormalPaletteFadeauf (der eigene Handler des Wetters für die Abblende istDoNothing), sodass der Ablauf der Abblende gilt, während die Einblendung über den Wettercode läuft und nicht simuliert wurde; die Einblendung nach einem Warp startet aus dem Callback zum Laden der Karte, dessen erster Frame nicht nachverfolgt wurde, daher werden beide Fälle angegeben. Ausgabe gespeichert alsmeasure_gen3_fade.out.txt(Stufen 0 bis 16 in Schritten von 2; erste sichtbare Mischung auf Frame 1; letzte Mischung, die Sprite-Paletten bei 16, auf Frame 16, 285 ms mit Frame 0 gezählt; inaktiv auf Frame 21, 368 ms;FadeInFromWhitemit Verzögerung 8 inaktiv auf Frame 85 oder 86, ab Frame 0 gezählt, also nach 86 oder 87 Frames, 1.440 oder 1.457 ms; Türeintritt: öffnen 20, Schritt 16 und schließen 20 Frames, die letzte Mischung der Blende nach mindestens 73 Frames, 1,22 s, undWarpIntoMapfrühestens auf Frame 79, 1,32 s, daTask_WarpAndLoadMapwartet, bis die Blende inaktiv ist). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Modell des Autors, 5. Oktober 2026:
measure_kiradex_motion.pyim Belegordner des Autors zu diesem Beitrag liesttilesPerSecond(4),framesPerSecond(8),runPace(1,6), die Rennschwelle (6 Schritte) und dasmin(1, dt * 6)der Kamera ausKiradex/World/PlazaRig.swift,centreausKiradex/World/TileMap.swiftund diewalk- undrun-Zyklen aus je sechs Zeichnungen ausscripts/forge/rig.pyund modelliertadvance,place,animateundfollowFrame für Frame, wobei jedeFloat-Operation in 32 Bit (numpyfloat32) in der Reihenfolge des Codes und mit Swifts Rundung „halb von null weg“ ausgeführt wird, bei exakt 1/60 und 1/120 s, für einen Weg von 5 Tiles, das über 12 Tiles gehaltene Gehtempo und einen Rennweg von 12 Tiles, jeweils gefolgt von 3 s Stehen; Kartenbegrenzung, Falz und Fußversatz sind weggelassen. Es ist ein Modell des Codes, keine Aufnahme von einem Gerät. Ausgabe gespeichert alsmeasure_kiradex_motion.out.txt. Gehen bei 60 Hz: 15 Frames pro Tile, pro Tile 14 Frames zu 1 px und einer zu 2; Kameraabstand 5, 6, 7, 8 und 9 px am Ende der ersten fünf Tiles, 13 ab dem neunten bei gehaltenem Gehtempo; Bildschirmposition ändert sich auf 8 der 73 Bewegungsframes eines Wegs von 5 Tiles nach dem ersten (der Weg im Modell hat 74 Bewegungsframes, der letzte Frame des letzten Tiles zählt als Stehen); Ruheversatz 4 px. Bei 120 Hz: 30 Frames pro Tile, 16 zu 1 px und 14 zu 0; Abstand 9; Ruhe 9. Rennen bei 60 Hz: 10 Frames pro Tile, 6,00 Tiles pro Sekunde, pro Tile 4 Frames zu 1 px und 6 zu 2; Abstand 13 ab dem zweiten Tile; Ruhe 4. Rennen bei 120 Hz: 19 Frames pro Tile, 6,32 Tiles pro Sekunde; Abstand 9; Ruhe 9. Zyklusstrecke aus simulierten Positionen zwischen aufeinanderfolgenden Zyklusanfängen bei 60 Hz: 3,00 Tiles im Gehen, 4,50 im Rennen (4,80 bei den nominellen 6,4 Tiles pro Sekunde); bei 120 Hz 3,00 und 2,94 im Gehen, 4,75 im Rennen. Diagonale Schritte im aktuellen Code: 22 Frames im Gehen und 14 im Rennen bei 60 Hz, 43 und 27 bei 120. Gegenüber demselben Modell mit Uhr, Positionen und Kamera in doppelter Genauigkeit bleibt jede Position und jeder Kamerawert unverändert, und 9 Gehzeichnungen bei 60 Hz und 14 bei 120 unterscheiden sich, alle auf Frames, auf denen die Uhr mal 8 eine ganze Zahl ist (11 solcher Frames auf 12 Tiles Gehen bei 60 Hz, 23 bei 120). Der Vorschlag: Ein fester 60-Hz-Tick hält jedes Pixel bei 60 Hz 1 Bildwiederholung lang, bei 120 Hz 2 Bildwiederholungen für 59 von 61 Positionen und bei 80 Hz 1 oder 2 Bildwiederholungen, für 42 beziehungsweise 19 Positionen. Der Akkumulator: Zehn Sekunden Callbacks bei jeder der zwölf iPhone-Raten von 120 bis 10 Hz ergeben mit einer Rückstaugrenze von 8 Ticks 600 Ticks ohne Verwerfen, gegenüber 480 bei 12 Hz und 400 bei 10 Hz mit einer Obergrenze von 4 Ticks pro Callback; nach einem Hänger von 1 s bei 60 Hz führt die 8-Tick-Regel 8 Ticks aus, danach 1 pro Callback, und verwirft 866,7 ms, während eine 4-Tick-Obergrenze, die den Rückstau behält, 19 aufeinanderfolgende Callbacks lang 4 Ticks ausführt. Die Kamerarechnung:fitergibt für einen Ausschnitt von 393 mal 852 pt bei 3x 7 Bildschirmpixel pro Texel und einen Ausschnitt von 168,43 mal 365,14 Weltpixeln, halbe Breite 84,21, nach innen gerundete Grenzen 85 und Kartenbreite minus 85; eine Falzänderung von 0 auf −37 in 9 Stufen durchläuft 0, −5, −9, −14, −19, −23, −28, −32, −37. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, „Optimizing iPhone and iPad apps to support ProMotion displays“ (der iPhone-Bereich von 10 bis 120 Hz und seine zwölf Raten; die Geräteliste;
CADisableMinimumFrameDurationOnPhone; die Spielepriorität bei 30 und 60 Hz; „Prepare your app to operate at any refresh rate“;targetTimestamp), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/quartzcore/optimizing-iphone-and-ipad-apps-to-support-promotion-displays. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, „Improving the Performance of a RealityKit App“ („RealityKit typically limits the refresh rate“ auf 60 Bilder pro Sekunde), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/realitykit/improving-the-performance-of-a-realitykit-app. ↩↩↩↩↩↩↩
-
pret,
pokeemerald/src/data/object_events/object_event_anims.h(sAnim_GoSouth,sAnim_GoFastSouth,sAnim_GoFasterSouth,sAnim_GoFastestSouth,sAnim_RunSouth) undsrc/sprite.c(animDelayCountermit der Dauer des Frames minus eins geladen, vonContinueAnimheruntergezählt, der nächste Frame bei null), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/data/object_events/object_event_anims.h und https://github.com/pret/pokeemerald/blob/master/src/sprite.c. ↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/field_player_avatar.c(PlayerWalkNormal,PlayerRun,PlayerWalkFastmit dem Kommentar „same speed as running“,CheckMovementInputNotOnBikeundTURN_DIRECTION,PlayerTurnInPlace, das Anstoßen an der Wand), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/field_player_avatar.c. ↩↩↩↩↩ -
pret,
pokeemerald/src/fieldmap.c(GetBorderBlockAt: die 2 mal 2 Randmetatiles des Layouts, markiert alsMAPGRID_IMPASSABLE), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/fieldmap.c. ↩↩↩ -
pret,
pokeemerald/src/field_camera.c(CameraUpdate;CameraPanningCB_PanAhead, dassVerticalCameraPanvom Ruhewert 32 um 2 in Richtung 72 oder minus 8 bewegt, abgesichert durchgUnusedBikeCameraAheadPanbackund kommentiert mit „this code is never reached“), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/field_camera.c. ↩↩↩↩↩↩↩↩ -
Blake Crosley, „Pixel-Art Structures: Houses, Halls and Interiors on iPhone“, blakecrosley.com, 3. Oktober 2026 (der Türframe von Smaragd fünf Aktualisierungen gehalten, etwa 84 Millisekunden;
PlayerStepOutFromDoorin Rot; das Aufzugswackeln in Smaragd; die 70-Millisekunden-Tür und der 220-Millisekunden-Warp von Kiradex unter den Lücken aufgeführt), https://blakecrosley.com/blog/pixel-art-structures-on-iphone. ↩↩↩↩↩↩ -
Recherchenotizen des Autors zum Strukturen-Beitrag, Kiradex-Repository (privat),
docs/research/structures/01-structures-in-the-canon.md, 3. Oktober 2026, die die Türframes von Smaragd mit „4 ticks each (16 frames, about 0.27 s at 59.7 Hz)“ angaben; korrigiert durch die in diesem Beitrag gegebene Lesart vonAnimateDoorFrameinfield_door.c. ↩↩ -
Apple Developer Documentation, „preferredFrameRateRange“ (
CADisplayLink; iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0, visionOS 1.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/quartzcore/cadisplaylink/preferredframeraterange. ↩↩↩ -
Apple Developer Documentation, „CADisableMinimumFrameDurationOnPhone“ (Schlüssel der Information Property List; iOS 15.0, iPadOS 15.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/bundleresources/information-property-list/cadisableminimumframedurationonphone. ↩↩↩
-
Lektüre des Kiradex-Repositorys (privat) durch den Autor bei Commit
b1b78b1, 4. Oktober 2026, nur lesend:Kiradex/World/PlazaRig.swift(tilesPerSecond,framesPerSecond,runPace,walk(to:)und seine Rennregelsteps.count >= 6,advance, dessen Fortschrittsschrittdt * Self.tilesPerSecond * (walker.running ? Self.runPace : 1) / lengthist,animate,place,fit, das die Bildschirmpixel des Ausschnitts durch ein ganzzahligespixelScaleteilt,follow, das die Punkte des Falzes mitdisplayScale / pixelScaleumrechnet, ummin(1, dt * 6)gleitet und danach rundet und dessen Begrenzung die Kamera nur bei halb geöffnetem Duo über den Kartenrand in den Wald lässt, umbeyondMargin, 12 Tiles, währendbuildden Wald unabhängig vom Falz anlegt,ripple, das auf einer Karte mit Wasser die Wasserzeichnung des Bodens 4-mal pro Sekunde wechselt,openDoorund sein Kommentar „at about Emerald’s four ticks a frame“),Kiradex/World/PlazaStage.swift(die Verarbeitung von Antippen, die 220-Millisekunden-Verzögerung des Warps, das Abonnement vonSceneEvents.Update, der Etagentausch per.id),Kiradex/World/TileMap.swift(derpathin acht Richtungen, der das offene Tile mit den bisher niedrigsten Kosten nimmt, pro Schritt 1 oder √2 addiert und keine Schätzung der Entfernung zum Ziel verwendet und dessen diagonale Schritte übersprungen werden, sofern nicht beide Seitenzellen begehbar sind),Kiradex/World/WorldMap.swift(das Tür-Sheet eines Warps, „closed, half open, open“),openDoor, das dieses Sheet mitSpriteSheet.bundled(name, columns: 3, rows: 1)lädt und erst Spalte 1, dann Spalte 2 zeigt,scripts/forge/kit.py(door_sheet(), „The three frames side by side, 48 × 32“),scripts/forge/town.pyundscripts/forge/buildings.py(jeder Tür-Warp der Stadt liegt auf der Türzelle in der untersten Reihe eines Gebäudes, und die Gebäudezellen links, rechts und oberhalb jeder Tür sind blockiert; geprüft für alle sieben Stadttüren in der ausgeliefertentown.json),Kiradex/Views/Card/CardViewer.swift(das.easeOut(duration: 0.25)des Schleiers) undscripts/forge/rig.py(die Zyklen). Das Fehlen vonUIImpactFeedbackGenerator,sensoryFeedback,CHHaptic,CADisplayLink,preferredFrameRateRangeundCADisableMinimumFrameDurationOnPhoneberuht auf einer Suche überKiradex/undproject.ymlam 4. Oktober 2026, die nichts davon fand; dieselbe Suche findet inKiradex/wederMTKViewnochMTLRenderCommandEncodernochTouchController, die Welt hat also keinen eigenen Render-Pass. Das Zurückreißen bei einem Antippen mitten im Schritt ist auswalk(to:)gelesen, nicht aufgenommen. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret, flache Klone von
pokered(Commitd2704a6, 22. September 2026),pokecrystal(5beda23, 29. September 2026) undpokeemerald(731ad5b, 1. Oktober 2026), die Community-Dekompilierungen der veröffentlichten Spiele, https://github.com/pret. ↩ -
Martin Korth, GBATEK, „LCD Dimensions and Timings“ (280.896 Takte und 16,743 ms pro Frame, „ca. 59.737 Hz“), abgerufen am 4. Oktober 2026, https://problemkaputt.de/gbatek-lcd-dimensions-and-timings.htm; Pan Docs, „Rendering“ („One frame: 70224 dots @ 59.7 fps“), abgerufen am 4. Oktober 2026, https://gbdev.io/pandocs/Rendering.html. Der Wert 59,7275 ist die Rechnung des Autors: 2^24 / 280.896 und 4.194.304 / 70.224. ↩↩↩↩↩
-
pret,
pokeemerald/src/bike.c(sMachBikeSpeedCallbacks,bikeFrameCountermit Obergrenze 2,AcroBikeTransition_MovingruftPlayerRideWaterCurrentauf, und die einzige ZuweisunggUnusedBikeCameraAheadPanback = FALSE), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/bike.c. ↩↩↩ -
Abbildungen gezeichnet vom Skript des Autors
make_figures.py(mitsvgkit.py), 4. Oktober 2026, nur aus Daten auf dem Datenträger: die Geschwindigkeiten des Kanons ausmeasure_gen3_motion.out.txtundmeasure_gen12_motion.out.txt; Laufen und Kamera von Kiradex aus dem eigenensimulate()vonmeasure_kiradex_motion.py(die erste Sekunde eines Wegs von 5 Tiles; die Kamera für einen Weg von 5 Tiles bei 60 und 120 Hz und einen Rennweg von 12 Tiles bei 60 Hz) und die Tickschleife des Vorschlags aus demselben Skript; die Blende von Smaragd ausrun()vonmeasure_gen3_fade.py, der Portierung mit dem Ablauf ihrer Aufrufer, und die Blende und das früheste Laden der Karte im Türdiagramm aus derselben Quelle; die Türzeiten von Kiradex (70 ms, 1.400 ms, 220 ms) gelesen ausPlazaRig.swiftundPlazaStage.swiftbeib1b78b1. Es wird keine Spielgrafik verwendet. ↩↩↩↩↩ -
Blake Crosley, „Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew“, blakecrosley.com, 3. Oktober 2026 (das Laufen in Smaragd „step, stand, step, stand at eight ticks each“; in Rot „stand, step, stand, step-flipped“; Celeste mit 320 mal 180, mal sechs; die RealityKit-Engine; Positionen „rounded to whole world units each frame, after easing“; die Deckkraft-Lektion des Kartenbetrachters), https://blakecrosley.com/blog/pixel-art-world-on-iphone. ↩↩↩↩↩↩↩↩
-
Itay Keren, „Scroll Back: The Theory and Practice of Cameras in Side-Scrollers“, Game Developer, 11. Mai 2015, eine überarbeitete Fassung seines Vortrags auf dem Independent Games Summit, GDC 2015 (position-locking, edge-snapping, camera-window, lerp-smoothing, target-focus, dual-forward-focus; die Zitate), abgerufen am 4. Oktober 2026, https://www.gamedeveloper.com/design/scroll-back-the-theory-and-practice-of-cameras-in-side-scrollers. ↩↩↩↩↩↩↩↩
-
pret,
pokeemerald/src/field_door.c(sDoorOpenAnimFrames{4, -1}, {4, 0}, {4, 0x100}, {4, 0x200};AnimateDoorFrame, das bei Zähler 0 zeichnet und vorrückt, wenn der Zähler der Zeit des Frames entspricht), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/field_door.c. ↩↩↩↩ -
pret,
pokeemerald/src/field_screen_effect.c(die Zustände vonTask_DoDoorWarp, deren fünfterWarpFadeOutScreenaufruft und den Task anTask_WarpAndLoadMapübergibt, das vorWarpIntoMapaufPaletteFadeActive(), alsogPaletteFade.active, undBGMusicStopped()wartet;Task_ExitDoor,WarpFadeOutScreenmit Aufruf vonGetMapPairFadeToType,WarpFadeInScreenmit Aufruf vonGetMapPairFadeFromType, undFadeInFromWhitemitFadeScreen(FADE_FROM_WHITE, 8)), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/field_screen_effect.c. ↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/palette.c(BeginNormalPaletteFademitdeltaY = 2, sein eigener Aufruf vonUpdatePaletteFade, seinCpuCopy32in den Palettenspeicher undsPlttBufferTransferPending = FALSE;UpdatePaletteFade, das sofort zurückkehrt, solange dieses Flag gesetzt ist, und es nach jeder Aktualisierung ausgPaletteFade_selectedPalettessetzt;TransferPlttBuffer, das es löscht;UpdateNormalPaletteFade;IsSoftwarePaletteFadeFinishing), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/palette.c. ↩↩ -
pret,
pokeemerald/src/fldeff_flash.c(sTransitionTypesmit seinen 16 Zeilen zu und vonMAP_TYPE_UNDERGROUND, jede mit einem Eintritts-Flag, einem Austritts-Flag und einer Übergangsroutine;GetMapPairFadeToTypegibt das Eintritts-Flag einer Zeile zurück undGetMapPairFadeFromTypeihr Austritts-Flag), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/fldeff_flash.c. ↩↩↩ -
pret,
pokeemerald/src/field_specials.c(ShakeCamera, das vertikalen Schwenk, horizontalen Schwenk, Anzahl der Wackler und Verzögerung ausVAR_0x8004bisVAR_0x8007liest und den Schwenk bei jedem Wackler umkehrt), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/field_specials.c. ↩ -
Zählung des Autors, 4. Oktober 2026:
measure_gen3_shake.pyim Belegordner des Autors zu diesem Beitrag liest jeden Aufrufspecial ShakeCameraund die viersetvar-Werte davor überdata/**/*.incin pretpokeemeraldbei731ad5b. Ausgabe gespeichert alsmeasure_gen3_shake.out.txt(24 Aufrufe in 9 Dateien, die acht Parametersätze und ihre Häufigkeit in der Tabelle). ↩↩↩↩↩↩ -
pret,
pokered/home/overworld.asm(OverworldLoop,OverworldLoopLessDelay,wWalkCounter,AdvancePlayerSprite,DoBikeSpeedupund der Kommentar zur 180-Grad-Drehung), Commitd2704a6, abgerufen am 4. Oktober 2026, https://github.com/pret/pokered/blob/master/home/overworld.asm. Das Drehverhalten ist aus dem Code gelesen, nicht in einem Emulator ausgeführt. ↩↩↩↩↩↩ -
pret,
pokered/engine/overworld/movement.asm(UpdatePlayerSprite, der Zähler innerhalb der Animation schaltet die Zeichnung bei 4 weiter), abgerufen am 4. Oktober 2026, https://github.com/pret/pokered/blob/master/engine/overworld/movement.asm. ↩↩ -
pret,
pokered/home/fade.asm(GBFadeOutToBlack) undhome/overworld.asm(PlayMapChangeSound,SFX_GO_INSIDEfür das Türtile$0b, sonstSFX_GO_OUTSIDE), abgerufen am 4. Oktober 2026, https://github.com/pret/pokered/blob/master/home/fade.asm. ↩ -
pret,
pokecrystal/engine/overworld/events.asm(MaxOverworldDelay: db 2) undengine/overworld/map_objects.asm(StepVectors;StepFunction_Turn, dessen.init1,.step1,.init2und.step2ineinander durchfallen, undObjectStep_AnonJumptable), Commit5beda23, abgerufen am 4. Oktober 2026, https://github.com/pret/pokecrystal/blob/master/engine/overworld/events.asm und https://github.com/pret/pokecrystal/blob/master/engine/overworld/map_objects.asm. Die drei Aktualisierungen der Drehung sind das Ergebnis eines Nachspielens der Routine Anweisung für Anweisung durch den Autor, kein Emulatorlauf. ↩↩↩↩↩ -
pret,
pokecrystal/data/maps/setup_scripts.asm(MapSetupScript_Doorbeginnt mitFadeOutToWhite,MapSetupScript_Warpendet mitFadeInFromWhite) undengine/tilesets/timeofday_pals.asm, abgerufen am 4. Oktober 2026, https://github.com/pret/pokecrystal/blob/master/data/maps/setup_scripts.asm und https://github.com/pret/pokecrystal/blob/master/engine/tilesets/timeofday_pals.asm. ↩ -
Blake Crosley, „Pixel-Art People: Characters and a Creator on iPhone“, blakecrosley.com, 3. Oktober 2026 (das Laufen mit sechs Frames; „the walk at eight frames a second“ auf dem Platz; unter „Since publishing“ die 26-Pixel-Figur, mit TestFlight-Build 34 ersetzt durch eine 30-Pixel-Figur in einer Zelle von 32 mal 40, die
scripts/forge/rig.pybeib1b78b1alsCELL_W, CELL_H = 32, 40festlegt), https://blakecrosley.com/blog/pixel-art-characters-on-iphone. ↩↩↩ -
Die Entwickler von Celeste, das Repository
NoelFB/Celesteauf GitHub,Source/Player/Player.cs(das Kamera-Update unter „Camera (lerp by distance using delta-time)“ und der GetterCameraTarget), abgerufen am 4. Oktober 2026, https://raw.githubusercontent.com/NoelFB/Celeste/master/Source/Player/Player.cs. ↩↩↩↩ -
Die Entwickler von Celeste, Repository
NoelFB/Celeste,README.md(die Klassendateien veröffentlicht „as a learning resource and for general interest“; die MIT-Lizenz gilt nur für diesen Code), abgerufen am 4. Oktober 2026, https://raw.githubusercontent.com/NoelFB/Celeste/master/README.md. ↩ -
Stardew Valley Wiki, „Speed“ (die Grundgeschwindigkeit der Spielfigur: 2 beim Gehen, 5 beim Rennen, 6,6 auf einem Pferd, 7 nach einer Karotte), abgerufen am 4. Oktober 2026, https://stardewvalleywiki.com/Speed. ↩
-
Recherchenotizen des Autors zu diesem Beitrag, zusammengestellt am 4. Oktober 2026, die festhalten, wonach gesucht und was nicht gefunden wurde: eine Primärquelle zu den Kameras von Sea of Stars, Eastward oder CrossCode; ein Vortrag oder Artikel von Maddy Thorson über Kameras; das genaue Touch-Schema der Pixel Remaster; die mobile Bewegung von Terraria auf https://terraria.wiki.gg/wiki/Mobile_version; und
developer.apple.com/documentation/touchcontrols, das 404 lieferte. ↩↩↩ -
Stardew Valley Wiki, „Mobile Controls“ (die Schemata; „Tap-to-move & Auto-Attack“ als Standard; die Zitate zu Tippen zum Bewegen, Folgen der Berührung, dem unsichtbaren Joystick und den Grenzen der Standardsteuerung), abgerufen am 4. Oktober 2026, https://stardewvalleywiki.com/Mobile_Controls. ↩↩↩↩↩↩↩
-
Jared Nelson, „’Stardew Valley’ is Getting A TON of New Control Options in the Next Update“, TouchArcade, 1. November 2018, abgerufen am 4. Oktober 2026, https://toucharcade.com/2018/11/01/stardew-valley-mobile-controls-update/. ↩
-
App Store, „FINAL FANTASY“ von SQUARE ENIX, Versionsverlauf (Version 1.2.0, 03/11/2025, und der Hinweis zur Tipp-Bewegung), abgerufen am 4. Oktober 2026, https://apps.apple.com/us/app/final-fantasy/id1492041278. ↩↩
-
Mikhail Madnani, TouchArcade, 30. Januar 2024, über das Update der Final Fantasy Pixel Remaster, das Controller-Unterstützung auf Mobilgeräte brachte, abgerufen am 4. Oktober 2026, https://toucharcade.com/2024/01/30/final-fantasy-pixel-remaster-mobile-controller-support-update-boosts-cheats-font-not-fixed-steam-deck-patch-notes/. ↩
-
Apple Human Interface Guidelines, „Game controls“ (Best Practices für Touch-Steuerung; Eintrag im Änderungsprotokoll vom 9. Juni 2025), abgerufen am 4. Oktober 2026, https://developer.apple.com/design/human-interface-guidelines/game-controls. ↩↩↩↩↩↩↩
-
Apple, WWDC25-Session 209 (das Framework für Touch-Steuerung; „the vast majority of players won’t have a controller available“; „integrates directly with Metal“), Transkript abgerufen am 4. Oktober 2026, https://developer.apple.com/videos/play/wwdc2025/209/. ↩↩↩
-
Apple Human Interface Guidelines, „Playing haptics“ (Best Practices, eigene Haptik und die iOS-Kategorie für Stöße; die Zitate), abgerufen am 4. Oktober 2026, https://developer.apple.com/design/human-interface-guidelines/playing-haptics. ↩↩↩↩↩↩↩↩↩
-
Apple Developer Documentation, „CAFrameRateRange“ (iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0, visionOS 1.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/quartzcore/caframeraterange. ↩
-
Apple Developer Documentation, „targetTimestamp“ (
CADisplayLink; iOS 10.0, iPadOS 10.0, Mac Catalyst 13.1, visionOS 1.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/quartzcore/cadisplaylink/targettimestamp. ↩ -
Apple, WWDC21-Session 10147, „Optimize for variable refresh rate displays“ (Adaptive-Sync-Displays am Mac und ProMotion auf dem iPad Pro; die Zitate), Transkript abgerufen am 4. Oktober 2026, https://developer.apple.com/videos/play/wwdc2021/10147/. ↩↩↩
-
Apple Developer Documentation, „SceneEvents.Update“ (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/realitykit/sceneevents/update. ↩
-
Apple Developer Documentation, „deltaTime“ (
SceneEvents.Update; iOS 13.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/realitykit/sceneevents/update/deltatime. ↩ -
Apple Developer Documentation, „RealityView“ (iOS 18.0, iPadOS 18.0, Mac Catalyst 18.0, visionOS 1.0; Code pro Frame über ein
SystemoderSceneEvents.Update), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/realitykit/realityview. ↩ -
Apple Developer Documentation, „CHHapticPattern“ (Core Haptics; iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0, visionOS 1.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/corehaptics/chhapticpattern; Seite des Frameworks Core Haptics, https://developer.apple.com/documentation/corehaptics. ↩
-
Apple Developer Documentation, „CHHapticEvent“ und „CHHapticEvent.EventType“ (
hapticTransient,hapticContinuous; iOS 13.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/corehaptics/chhapticevent und https://developer.apple.com/documentation/corehaptics/chhapticevent/eventtype. ↩ -
Apple Developer Documentation, „CHHapticEvent.ParameterID“ (iOS 13.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/corehaptics/chhapticevent/parameterid. ↩
-
Apple Developer Documentation, „CHHapticEngine“ (iOS 13.0;
capabilitiesForHardware()), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/corehaptics/chhapticengine. ↩↩ -
Apple Developer Documentation, „UIImpactFeedbackGenerator“ (iOS 10.0, iPadOS 10.0, Mac Catalyst 13.1;
init(style:view:)unter „Initializing the feedback generator“ undinit(style:)unter Deprecated), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator. ↩↩ -
Apple Developer Documentation, „UIImpactFeedbackGenerator.FeedbackStyle“ (iOS 10.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle. ↩
-
Apple Developer Documentation, „impactOccurred(intensity:)“ (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.1), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/impactoccurred(intensity:). ↩
-
Apple Developer Documentation, „prepare()“ (
UIFeedbackGenerator; iOS 10.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/uikit/uifeedbackgenerator/prepare(). ↩↩ -
Apple Developer Documentation, „sensoryFeedback(:trigger:)“ und „SensoryFeedback“ (SwiftUI; iOS 17.0, iPadOS 17.0, Mac Catalyst 17.0, visionOS 26.0;
impact(weight:intensity:)), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/swiftui/view/sensoryfeedback(:trigger:) und https://developer.apple.com/documentation/swiftui/sensoryfeedback. ↩ -
Apple Developer Documentation, „Touch Controller“ (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, visionOS 26.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/touchcontroller. ↩↩↩↩
-
Apple Developer Documentation, „TCDirectionPad“ (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/touchcontroller/tcdirectionpad. ↩
-
Apple Developer Documentation, „GCVirtualController“ (Game Controller; iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0), abgerufen am 4. Oktober 2026, https://developer.apple.com/documentation/gamecontroller/gcvirtualcontroller. ↩
-
pret,
pokeemerald/src/field_control_avatar.c(TryDoorWarp, aufgerufen mit der Zelle vor der Spielfigur, solange die gehaltene Richtung der Blickrichtung entspricht, und das nur dann warpt, wenn diese Richtung Norden ist und die Zelle ein Tür-Warp ist), abgerufen am 4. Oktober 2026, https://github.com/pret/pokeemerald/blob/master/src/field_control_avatar.c. ↩