Movimiento en pixel art: caminatas, cámaras y puertas en el iPhone
Pokémon Rojo, Cristal y Esmeralda, un juego de cada una de las tres generaciones de Game Boy y Game Boy Advance, recorren todos una celda de 16 pixeles en 16 fotogramas a unos 59,73 hercios, 3,73 celdas por segundo, y nada del paso queda en manos de un reloj: Esmeralda mueve al jugador exactamente un pixel por fotograma, muestra un dibujo de zancada y uno de pie en cada paso, y desplaza la cámara en ese mismo fotograma y en los mismos pixeles.123 Rojo y Cristal llegan a los mismos dieciséis fotogramas moviendo dos pixeles cada dos fotogramas.4 La puerta de Esmeralda se abre en cuatro dibujos que duran cinco fotogramas cada uno, el jugador da un paso forzado hacia dentro, la puerta se cierra y la pantalla se funde en nueve niveles escalonados: al menos 79 fotogramas, 1,32 segundos, antes de que el mapa siguiente pueda empezar a cargarse.15 El mundo de Kiradex camina según el tiempo transcurrido y suaviza su cámara, y un modelo de ese código (un modelo, no una captura de un teléfono) dice que el sprite da un salto de dos pixeles una vez por tile a 60 hercios, que la cámara se queda cerca de un pixel más atrás con cada tile de una caminata a 60 hercios (9 pixeles en el quinto, y 13 en una carrera) y se detiene entre 4 y 9 pixeles antes de alcanzar al jugador, y que los dibujos de la caminata llevan un reloj propio, un ciclo por cada 3,00 tiles donde el de Esmeralda cubre dos.6 Apple da a los juegos una prioridad especial a 30 y 60 hercios y RealityKit suele renderizar a 60, así que el encargo es un tick fijo de 60 hercios con pasos de pixeles enteros, dibujos de caminata elegidos según la distancia recorrida (propuesta mía, no lo que hacían las portátiles), una cámara fija, la ceremonia de puerta de Esmeralda con nuestro propio arte, tres eventos hápticos opcionales, y 60 hercios en lugar de 120.78 Esto es el canon medido a partir de las descompilaciones, el lado del iPhone según las páginas de Apple, y el encargo con las comprobaciones que debe superar.
Lo esencial
- Un paso es un contrato, no una velocidad. Cada tabla de pasos de Esmeralda suma 16 pixeles: la caminata es 1 pixel por fotograma durante 16 fotogramas (268 milisegundos por celda), correr y hacer surf son 2 durante 8, la velocidad máxima de la Bici Carrera es 4 durante 4, y solo el 2-3-3-2-3-3 de la Bici Acrobática es irregular. Rojo y Cristal recorren los mismos 16 fotogramas a saltos de 2 pixeles, con 30 actualizaciones por segundo.124
- Las piernas y el paso están sincronizados a propósito. Esmeralda cuenta los dibujos y el paso en fotogramas, por separado, y hace que las cuentas coincidan: la caminata es zancada, de pie, zancada, de pie, a 8 fotogramas por dibujo a lo largo de dos celdas de 16 fotogramas; los modos de andar más rápidos reducen a la mitad la duración de cada dibujo en vez de añadir dibujos, así que hay una pisada por cada 16 pixeles a cualquier velocidad. Girar desde parado lleva 8 fotogramas (134 milisegundos), girar mientras se camina no lleva ninguno, y caminar contra una pared reproduce un choque de 32 fotogramas.19102
- La cámara es el jugador. La cámara de Esmeralda copia la posición del jugador y se desplaza los mismos pixeles en el mismo fotograma, sin retraso ni adelanto, y nunca se detiene en el borde de un mapa porque el exterior se dibuja con tiles de borde. Game Freak escribió una cámara que se adelanta a la bici y la lanzó desactivada.231112
- Una puerta es una ceremonia. Cuatro dibujos de cinco fotogramas (335 milisegundos), un paso forzado de 16 fotogramas, la puerta que se cierra en 20, y un fundido cuya última mezcla cae en su fotograma 17 y que termina en el 22: al menos 79 fotogramas, 1,32 segundos, sin ninguna entrada del jugador antes de que el mapa pueda cargarse. Eso confirma el artículo de esta serie sobre estructuras, unos 84 milisegundos por dibujo, y corrige las notas de investigación en que se basó, que decían «4 ticks each (16 frames, about 0.27 s at 59.7 Hz)» (4 ticks cada uno, 16 fotogramas, unos 0,27 s a 59,7 Hz). Cristal funde a blanco en 8 fotogramas; Rojo funde a negro en 32.1513144
- En el iPhone, ritmo uniforme a 60.
preferredFrameRateRange(iOS 15.0) es una sugerencia; los iPhone con ProMotion van de 10 a 120 hercios en doce escalones; superar los 60 requiereCADisableMinimumFrameDurationOnPhone; los juegos reciben «special priority to 30Hz and 60Hz» (prioridad especial a 30 Hz y 60 Hz); RealityKit «typically limits the refresh rate» (suele limitar la frecuencia de actualización) a 60. Una caminata de pixeles enteros no gana nada a 120: cada pixel simplemente se mantiene durante dos refrescos.1571686 - Kiradex hoy, modelado y no medido. El código camina a 4 tiles por segundo según el tiempo transcurrido, así que un modelo a 60 hercios constantes tarda 15 fotogramas por tile y en uno de ellos avanza 2 pixeles; suaviza y redondea la cámara, que se va quedando más atrás a medida que avanza la caminata (5 pixeles tras el primer tile, 9 tras el quinto, 13 en una carrera) y se detiene a 4 pixeles del jugador a 60 hercios y a 9 a 120; su caminata de seis dibujos a 8 por segundo cubre 3,00 tiles por ciclo al caminar y 4,50 al correr. No se ha medido ningún teléfono.176
- Lo que esto le da a Kiradex: un encargo, no una compilación publicada. Un tick fijo de 60 hercios con pasos de 1 pixel y una regla explícita para el atraso acumulado, dibujos de caminata elegidos por distancia, una cámara fija en pixeles enteros, la secuencia de puerta completa con un fundido escalonado, un paso rechazado que se muestra, tres eventos hápticos opcionales, ninguna petición de 120 hercios, y para cada punto una comprobación que pueda verificar un registro de movimiento, una captura o un script.
1. La caminata de Esmeralda: dieciséis pixeles en dieciséis fotogramas
Los dos primeros artículos de esta serie midieron cómo se ven un mundo de pixeles y su gente; el tercero midió sus edificios. Este mide el tiempo: cuántos pixeles por fotograma, cuántos fotogramas por paso, qué dibujo se muestra en qué fotograma, cuánto tardan un giro, una puerta y un fundido, y qué hace la cámara mientras ocurre todo eso. Los juegos se leen como los leyeron los artículos anteriores, a partir de las descompilaciones de pret de los juegos publicados de Game Boy y Game Boy Advance, en los mismos commits que usaron esos artículos: pokered d2704a6, pokecrystal 5beda23, pokeemerald 731ad5b.18 Cinco scripts pequeños hicieron las cuentas; cada uno aparece nombrado en las notas junto con su salida guardada.
Primero un número, porque todo lo demás está en fotogramas. La Game Boy Advance dibuja un fotograma en 280.896 ciclos de un reloj de 2^24 hercios, lo que da 59,7275 fotogramas por segundo, 16,743 milisegundos por fotograma; GBATEK lo redondea a «ca. 59.737 Hz».19 El reloj de 4.194.304 hercios de la Game Boy original sobre 70.224 puntos por fotograma da el mismo 59,7275; Pan Docs dice «@ 59.7 fps».19 Los milisegundos de este artículo usan 59,7275.19
Cada velocidad es una tabla que suma dieciséis
Esmeralda no mueve a un caminante multiplicando velocidad por tiempo. Cada velocidad del mundo exterior es una tabla de desplazamientos en pixeles por fotograma, y el código dice qué tienen en común todas las tablas: «Over the course of the step animation, these sum to 16 pixels (one full metatile).» (a lo largo de la animación del paso, suman 16 pixeles, un metatile completo).2 sStep1Funcs son dieciséis desplazamientos de un pixel, sStep2Funcs ocho de dos pixeles, sStep4Funcs cuatro de cuatro, sStep8Funcs dos de ocho, y sStep3Funcs es la rara, Step2, Step3, Step3, Step2, Step3, Step3.2 Contado con un script sobre el código de movimiento, eso da:
| Constante de velocidad | Pixeles por fotograma | Fotogramas por celda | Celdas por segundo | Milisegundos por celda | Se usa para |
|---|---|---|---|---|---|
MOVE_SPEED_NORMAL |
1 en cada fotograma | 16 | 3,73 | 267,9 | la caminata; las caminatas de los NPC |
MOVE_SPEED_FAST_1 |
2 en cada fotograma | 8 | 7,47 | 133,9 | correr, hacer surf, deslizarse por el hielo |
MOVE_SPEED_FAST_2 |
2, 3, 3, 2, 3, 3 | 6 | 9,95 | 100,5 | la Bici Acrobática, las corrientes de agua |
MOVE_SPEED_FASTER |
4 en cada fotograma | 4 | 14,93 | 67,0 | la Bici Carrera a velocidad máxima |
MOVE_SPEED_FASTEST |
8, 8 | 2 | 29,86 | 33,5 | acciones de movimiento deslizante |
Fuente de cada fila: measure_gen3_motion.py sobre src/event_object_movement.c.1
La caminata es el número que hay que recordar: un pixel en cada fotograma, dieciséis fotogramas por celda, 3,73 celdas por segundo, 268 milisegundos por celda, usado por PlayerWalkNormal y por todos los NPC que caminan.1 Correr con el botón B es PlayerRun, y hacer surf es PlayerWalkFast, que el código anota como «same speed as running» (la misma velocidad que correr); los deslizamientos por el hielo llaman a la misma función. Las tres son dos pixeles por fotograma, ocho fotogramas por celda, 7,47 celdas por segundo.110 Hay también una caminata lenta, UpdateWalkSlowAnim, que avanza un pixel en los valores pares del temporizador, de 31 a 32 fotogramas por celda, para escenas guionizadas.1
La Bici Acrobática (Acro Bike) es la única velocidad con pixeles irregulares: 2, 3, 3, 2, 3, 3 en seis fotogramas, 2,67 pixeles por fotograma de media, 9,95 celdas por segundo.1 Se llega a ella mediante PlayerRideWaterCurrent desde AcroBikeTransition_Moving, la misma función que usan las corrientes de agua.120 Según mi lectura, la irregularidad es el precio de una velocidad que no divide a dieciséis: tres pixeles por fotograma se pasarían de la celda, así que la tabla alterna doses y treses para caer exactamente en dieciséis. Todas las demás velocidades son divisores enteros de la celda.
La Bici Carrera (Mach Bike) acelera por pasos, no por fotogramas. sMachBikeSpeedCallbacks es PlayerWalkNormal, PlayerWalkFast, PlayerWalkFaster, y bikeFrameCounter sube uno por paso hasta un tope de 2, así que el primer paso desde parado tarda 16 fotogramas, el segundo 8, y todos los siguientes 4: cuatro pixeles por fotograma, 14,93 celdas por segundo.120 La bici nunca queda entre dos velocidades. Cada paso es una de las tablas, ejecutada hasta el final, y la velocidad solo cambia en el límite de una celda.
Cada modo de andar medido en Rojo, Cristal y Esmeralda es un número entero de fotogramas por celda de 16 pixeles; las cifras de Kiradex son un modelo de su código, no una captura.14621
Una pisada por cada dieciséis pixeles
La animación de la caminata es zancada, de pie, zancada, de pie. sAnim_GoSouth es ANIMCMD_FRAME(3, 8), (0, 8), (4, 8), (0, 8): el dibujo 3 durante ocho fotogramas, el dibujo de pie 0 durante ocho, el dibujo 4 durante ocho, y otra vez el de pie durante ocho, 32 fotogramas en total, que son dos celdas de caminata.91 La duración es exacta: sprite.c carga la duración del fotograma menos uno en el contador de retardo y cuenta hacia atrás hasta cero, así que un fotograma de duración 8 está en pantalla durante ocho fotogramas.91 Por tanto, cada paso muestra un dibujo de zancada y un dibujo de pie, y SetStepAnimHandleAlternation empieza cada paso nuevo en la otra mitad del ciclo (animPos = {1, 3, 0, 2}), así que la pierna izquierda y la derecha se alternan paso a paso incluso cuando el jugador se detiene y vuelve a arrancar.2 El primer artículo de esta serie describió el mismo ciclo desde el lado del arte: «step, stand, step, stand at eight ticks each, which is the bob everyone remembers.» (paso, de pie, paso, de pie a ocho ticks cada uno, que es el balanceo que todo el mundo recuerda).22
Los modos de andar más rápidos conservan los dibujos y acortan su duración. GoFast mantiene cada dibujo cuatro fotogramas (un ciclo de 16 fotogramas), GoFaster dos (8 fotogramas), GoFastest uno (4 fotogramas).1 La carrera tiene dibujos propios, con duraciones desiguales: sAnim_RunSouth es (12, 5), (9, 3), (13, 5), (9, 3), un ciclo de 16 fotogramas.19
Si pones las dos tablas una al lado de la otra, el diseño salta a la vista. El ciclo de 32 fotogramas de la caminata cubre dos celdas de 16 fotogramas; el ciclo de 16 fotogramas de la carrera cubre dos celdas de 8; el ciclo GoFaster de 8 fotogramas de la Bici Carrera cubre dos celdas de 4.19 A cualquier velocidad cae una pisada por cada 16 pixeles.1 Son dos relojes, no un contador. El dibujo avanza cuando se agota animDelayCounter, cargado en sprite.c con la duración de cada fotograma; el paso avanza cuando NpcTakeStep indexa la tabla de la velocidad con el sTimer del sprite, una entrada por fotograma; y SetStepAnimHandleAlternation elige la animación del modo de andar al empezar cada paso.92 Lo que los mantiene juntos es que ambos se cuentan en los mismos fotogramas y que las duraciones se eligieron para coincidir, modo de andar por modo de andar. Según mi lectura, eso es lo que hay que copiar: la cadencia de los dibujos no puede separarse del movimiento, porque los dibujos de cada paso duran exactamente tantos fotogramas como el paso. Nada en estas tablas ni en mi modelo mide dónde se apoya un pie contra el suelo, así que no afirmo más que eso.
Girar, chocar y cuándo se lee la entrada
Un paso, una vez empezado, termina; la cruceta se lee cuando el paso se completa, así que una dirección mantenida encadena pasos sin hueco y un cambio de dirección mientras se camina no cuesta nada.10 Desde parado es distinto. CheckMovementInputNotOnBike devuelve TURN_DIRECTION solo cuando la nueva dirección difiere de la orientación actual y el jugador no se está moviendo ya, y PlayerTurnInPlace reproduce la caminata rápida en el sitio, a la que InitMoveInPlace da una duración de 8 fotogramas: 134 milisegundos de giro sin moverse del sitio.101 Esa es la mecánica que permite al jugador ponerse de cara a un cartel o a una persona sin dar un paso hacia ellos. Caminar contra una pared reproduce en cambio la caminata lenta en el sitio, 32 fotogramas, 536 milisegundos, con un sonido de choque: un paso rechazado se muestra, no se traga.110 Las acciones de caminata en el sitio normal y más rápida duran 16 fotogramas (268 milisegundos) y 4 (67).1
Según mi lectura, esos cuatro números son casi todo lo que significa «responsivo» en un juego de cuadrícula. El mundo nunca se mueve una fracción de celda, así que la intención del jugador se expresa en pasos enteros, y la única latencia es lo que queda del paso en curso: como mucho 268 milisegundos caminando, 134 corriendo.1 El giro desde parado no es latencia sino una acción aparte con su propio resultado visible.
2. La cámara, las puertas, los fundidos y las sacudidas de Esmeralda
La cámara no tiene retraso ni adelanto
La cámara de Esmeralda es un sprite invisible que sigue al jugador. CameraObject_UpdateMove copia la x y la y del sprite seguido y guarda la diferencia respecto al fotograma anterior en sCamera_MoveX y sCamera_MoveY; CameraUpdateCallback le pasa esa diferencia a CameraUpdate, que desplaza el mapa exactamente esos pixeles.212 El bucle del mundo exterior ejecuta RunTasks(); AnimateSprites(); CameraUpdate(); UpdateCameraPanning(); en ese orden en cada fotograma, y como AnimateSprites ejecuta las callbacks de los sprites en orden de ranura y el objeto cámara se crea después del jugador (InitPlayerAvatar, luego InitCameraUpdateCallback(gPlayerAvatar.spriteId)), la cámara lee la posición que el jugador alcanzó en ese mismo fotograma.3 Seguí ese orden a través del código en lugar de ejecutarlo, así que es una lectura, no una medición; pero el resultado que implica es sencillo. El movimiento del jugador y el desplazamiento de la pantalla caen en el mismo fotograma, por los mismos pixeles. En pantalla, el jugador no se mueve nunca mientras el mundo se desliza por debajo.
Itay Keren, en su charla de la GDC 2015 sobre cámaras en juegos de desplazamiento lateral, llama a esto position-locking (bloqueo de posición): la cámara se queda sobre el jugador, «keeping the car in focus at all times and the camera motion completely predictable» (manteniendo el coche enfocado en todo momento y el movimiento de la cámara completamente predecible).23 Esmeralda añade algo que su definición deja abierto, que es qué pasa en el borde del mapa. Nunca se detiene. Las celdas fuera del diseño de un mapa se leen mediante GetBorderBlockAt, que devuelve los metatiles de borde de 2 por 2 del diseño, repetidos y marcados como MAPGRID_IMPASSABLE, así que la vista mantiene al jugador en la misma posición de pantalla y rellena el exterior con árboles o agua de borde.11 El edge-snapping (ajuste al borde) de Keren, la alternativa, «simply snaps the camera to the edge of the level, allowing the character to move away from its anchor point.» (simplemente pega la cámara al borde del nivel y deja que el personaje se aleje de su punto de anclaje).23 Esmeralda se libró de necesitarlo dibujando.
La cámara que Game Freak escribió y desactivó
field_camera.c contiene una cámara de anticipación terminada para la bici. CameraPanningCB_PanAhead desplaza el paneo vertical 2 pixeles por actualización, desde su valor de reposo de 32 hacia 72 o hacia menos 8 según la dirección de la marcha, es decir 40 pixeles en cada sentido: una cámara que se adelanta al jugador hacia donde va.12 Solo se ejecuta si gUnusedBikeCameraAheadPanback es verdadero, la variable solo se asigna alguna vez a FALSE (en bike.c), y la rama lleva el comentario «this code is never reached.» (este código nunca se alcanza).1220 En el vocabulario de Keren es un dual-forward-focus (enfoque doble hacia delante) o un target-focus (enfoque en el objetivo), la cámara que sigue su regla «When you walk left, you want to see more to the left.» (cuando caminas hacia la izquierda, quieres ver más hacia la izquierda).23 Fuera cual fuera el motivo para dejarla apagada, el juego publicado mantuvo la cámara fija incluso a los 4 pixeles por fotograma de la Bici Carrera.1
Una puerta se abre en veinte fotogramas, no en dieciséis
La tabla de puertas de field_door.c se lee como si cada dibujo durara cuatro fotogramas: sDoorOpenAnimFrames es {4, -1}, {4, 0}, {4, 0x100}, {4, 0x200}, cerrada más tres dibujos abiertos.24 Pero AnimateDoorFrame dibuja cuando su contador vale 0 y avanza cuando el contador iguala el tiempo de la entrada, así que cada dibujo se mantiene cinco actualizaciones: 84 milisegundos por dibujo, 335 para los cuatro.241 El artículo de esta serie sobre estructuras dio la misma cifra, cinco actualizaciones y unos 84 milisegundos.13 Las notas de investigación a partir de las cuales se escribió ese artículo se equivocaban, «4 ticks each (16 frames, about 0.27 s at 59.7 Hz),» (4 ticks cada uno, 16 fotogramas, unos 0,27 s a 59,7 Hz), y también se equivoca un comentario del propio código de puertas de la app, que dice que se abre «at about Emerald’s four ticks a frame» (a más o menos los cuatro ticks por fotograma de Esmeralda) y luego mantiene el primer dibujo 70 milisegundos; ambos se corrigen aquí y en el encargo.1417
La entrada en sí, Task_DoDoorWarp, son cinco estados sin nada que se pueda saltar: congelar los demás objetos, reproducir el sonido de la puerta y abrir la puerta que está encima del jugador; forzar un paso MOVEMENT_ACTION_WALK_NORMAL_UP hacia el umbral; cuando el jugador se queda quieto, cerrar la puerta y ocultar al jugador; cuando termina la tarea de la puerta, fundir la música y la pantalla; cargar el mapa.25 Salir ejecuta la ceremonia al revés: Task_ExitDoor muestra la puerta ya abierta, espera al fundido de entrada, fuerza un paso WALK_NORMAL_DOWN, cierra la puerta, y solo entonces devuelve los controles.25
Sumando las cuentas anteriores y el rastreo del fundido que viene después, la puerta se abre en 20 fotogramas, el paso tarda 16 y la puerta se cierra en 20; el fundido que sigue hace su última mezcla en su fotograma 17, así que la pantalla está totalmente oscura tras al menos 73 fotogramas, 1,22 segundos. El mapa no puede empezar a cargarse hasta que el fundido se ha vuelto inactivo, en su fotograma 22, y Task_WarpAndLoadMap lo ha visto y ha seguido adelante: al menos 79 fotogramas, 1,32 segundos.525 Los cambios de estado entre las fases de la puerta y la espera a que se detenga la música solo pueden añadir fotogramas, así que ambas cifras son cotas inferiores.5
La entrada de Esmeralda es una cota inferior a partir de sus cuentas de fotogramas; la fila de Kiradex se lee del código de puertas de la app, no se cronometró en un teléfono.151721
Un fundido son nueve niveles
Un fundido de warp en Esmeralda no es una rampa suave. BeginNormalPaletteFade fija un paso de 2 sobre un coeficiente de mezcla que va de 0 a 16, UpdateNormalPaletteFade mezcla las paletas de fondo en una llamada y las paletas de sprites en la siguiente y después avanza el coeficiente, e IsSoftwarePaletteFadeFinishing añade cinco llamadas al final.26 En qué fotograma cae cada llamada depende de quién la hace. La tarea de la puerta arranca el fundido desde dentro de RunTasks, y BeginNormalPaletteFade ejecuta ella misma una actualización, copia el resultado a la memoria de paletas en el acto y borra el indicador que, de otro modo, haría que la siguiente actualización esperara al borrado vertical; más adelante en el mismo fotograma, OverworldBasic vuelve a llamar a UpdatePaletteFade.25263 Así que el primer fotograma recibe dos actualizaciones, ambas en el nivel 0, y cada fotograma posterior una. Un port en Python de palette.c con ese calendario, contando el fotograma de la tarea como 0, da nueve niveles, 0, 2, 4 y así hasta 16: las paletas de fondo llegan al nivel 2 en el fotograma 1 y al nivel 16 en el fotograma 15, las paletas de sprites un fotograma por detrás cada vez, la última mezcla en el fotograma 16 (285 milisegundos contando el fotograma 0), y el fundido inactivo en el fotograma 21 (368 milisegundos).5 Una mezcla calculada en un fotograma llega a la pantalla en el borrado vertical que lo cierra. El port supone que antes no había ningún fundido en marcha y la actualización normal del mundo exterior; con lluvia, nieve, niebla, sombra o sequía activas, el fundido de salida funciona igual a partir de los colores teñidos por el clima (FadeScreen copia primero el búfer teñido y después llama a la misma BeginNormalPaletteFade), pero el fundido de entrada lo ejecuta el código del clima, y esa ruta no se simuló.5
Nueve niveles separados por dos dieciseisavos, fondo y sprites con un fotograma de diferencia: la rampa que el encargo adapta a un solo velo.521
Los warps funden a negro, con una familia de excepciones, y la excepción tiene dirección. WarpFadeOutScreen consulta a GetMapPairFadeToType sobre el par de tipos de mapa, WarpFadeInScreen consulta a GetMapPairFadeFromType, y cada una llama a FadeScreen con blanco cuando la respuesta es verdadera y con negro en caso contrario.25 Las dos buscan el par en sTransitionTypes, cuyas 16 filas son todos los tipos de mapa que entran en MAP_TYPE_UNDERGROUND y salen de él; la primera devuelve el indicador de entrada de una fila y la segunda su indicador de salida, que son verdaderos solo en las filas que entran en una cueva y solo en las que salen de una.27 Así que al entrar en una cueva la pantalla funde a blanco y vuelve desde negro, y al salir de una funde a negro y vuelve desde blanco. Cada fila nombra además una rutina de transición de cueva propia, que no rastreé.27 Un fundido blanco más lento, FadeInFromWhite con un retardo de 8, se vuelve inactivo tras 86 u 87 fotogramas, de 1,44 a 1,46 segundos; arranca desde la callback de carga del mapa, cuyo primer fotograma no rastreé, y el port da los dos casos.525
La sacudida es un paneo con guion
La sacudida de pantalla en Esmeralda es un comando de script, no un efecto físico. ShakeCamera lee un paneo vertical, un paneo horizontal, un número de sacudidas y los fotogramas entre sacudidas de cuatro variables de script, y niega el paneo en cada sacudida.28 En todos los scripts del juego hay 24 llamadas en 9 archivos, y un script las contó todas:29
| Px verticales | Px horizontales | Sacudidas | Fotogramas entre ellas | Llamadas | Duración |
|---|---|---|---|---|---|
| 1 | 1 | 8 | 5 | 10 | 40 fotogramas, 670 ms |
| 1 | 1 | 8 | 3 | 4 | 24 fotogramas, 402 ms |
| 1 | 2 | 8 | 5 | 3 | 40 fotogramas, 670 ms |
| 2 | 2 | 8 | 5 | 2 | 40 fotogramas, 670 ms |
| 0 | 3 | 4 | 2 | 2 | 8 fotogramas, 134 ms |
| 1 | 3 | 20 | 5 | 1 | 100 fotogramas, 1.674 ms |
| 1 | 1 | 16 | 3 | 1 | 48 fotogramas, 804 ms |
| 1 | 1 | 32 | 2 | 1 | 64 fotogramas, 1.072 ms |
Fuente: measure_gen3_shake.py sobre data/**/*.inc.29
Diez de las 24 son la misma sacudida: un pixel en cada eje, ocho inversiones, separadas por cinco fotogramas, 670 milisegundos.29 La mayor es de 3 pixeles en horizontal; la más larga dura 100 fotogramas, 1,67 segundos.29 La sacudida del ascensor, que trató el artículo sobre estructuras (una sacudida cada tres fotogramas durante una cuenta que crece con los pisos recorridos), es una rutina aparte y no entra en esta cuenta.13 Según mi lectura, la lección es la contención: una sacudida en Esmeralda es un pixel o dos, usada para un evento que el guion ha decidido que importa, nunca para un paso ni para una puerta.
3. Rojo y Cristal: el mismo paso a 30 actualizaciones por segundo
Rojo mueve dos pixeles cada dos fotogramas
Pokémon Rojo no tiene tablas de pasos. Su bucle del mundo exterior, OverworldLoop, llama a DelayFrame y cae en OverworldLoopLessDelay, que la vuelve a llamar, así que el mundo se actualiza una vez cada dos fotogramas.30 Un paso pone wWalkCounter a 8, y en cada pasada AdvancePlayerSprite lo decrementa y desplaza los registros de fondo hSCX y hSCY según el vector del paso desplazado una vez a la izquierda: 2 pixeles.30 Ocho pasadas de 2 pixeles son 16 pixeles en 16 fotogramas, los mismos 268 milisegundos y 3,73 celdas por segundo que Esmeralda, a saltos de 2 pixeles con 30 actualizaciones por segundo.430 La bici es un segundo avance por pasada: DoBikeSpeedup vuelve a llamar a AdvancePlayerSprite, salvo en la Ruta Ciclista (Cycling Road) mientras se mantiene pulsado arriba, izquierda o derecha, así que un paso tarda 8 fotogramas.430
Los dibujos de la caminata de Rojo cambian cada cuatro pasadas, ocho fotogramas, a lo largo de un ciclo de cuatro imágenes, de pie, paso, de pie y el paso volteado, así que también muestra un dibujo de zancada por paso.3122 UpdatePlayerSprite incrementa el contador interno de la animación en cada pasada y avanza el dibujo cuando llega a 4.3122
Rojo gira en una pasada del bucle, dos fotogramas: una dirección nueva desde parado escribe la orientación y vuelve al bucle sin dar un paso.30 Su giro de 180 grados tiene en el código una orientación intermedia que, según el propio comentario del código, nadie ve: «It is unlikely for it to ever be visible because DelayFrame is called at the start of OverworldLoop.» (es poco probable que llegue a verse, porque DelayFrame se llama al principio de OverworldLoop).30 Eso lo leí del código y no lo ejecuté en un emulador.
El warp es un sonido y un fundido, sin ninguna puerta dibujada. PlayMapChangeSound reproduce SFX_GO_INSIDE cuando el tile es una puerta (el tile $0b) y SFX_GO_OUTSIDE en caso contrario, y después GBFadeOutToBlack escribe cuatro paletas y mantiene cada una durante ocho fotogramas: 32 fotogramas, 536 milisegundos.432 No rastreé el fundido de vuelta de Rojo.
Cristal también actualiza cada dos fotogramas
Cristal mantiene la cadencia de Rojo con un mecanismo distinto. MaxOverworldDelay es db 2, y el manejador de VBlank va descontando wOverworldDelay, así que los objetos del mapa se actualizan cada dos fotogramas.33 StepVectors da a la caminata ocho actualizaciones de 2 pixeles y a la bici cuatro de 4: 16 fotogramas y 8 fotogramas por celda, igual que Rojo y Esmeralda.4 El paso lento son dieciséis actualizaciones de 1 pixel, 32 fotogramas, 1,87 celdas por segundo.433
Las puertas de Cristal funden a blanco, y rápido. MapSetupScript_Door empieza con FadeOutToWhite y MapSetupScript_Warp termina con FadeInFromWhite; cada uno son cuatro pasos de paleta separados por dos fotogramas, 8 fotogramas, 134 milisegundos en cada sentido.434 Su giro es una función de paso en cuatro partes, StepFunction_Turn, y las partes caen unas en otras. En la primera actualización, .init1 fija un OBJECT_STEP_DURATION de 2 y cae en .step1, que lo baja a 1; en la segunda, .step1 lo baja a 0 y cae a través de .init2, que escribe la nueva orientación y vuelve a fijar 2, en .step2, que lo baja a 1; en la tercera, .step2 llega a 0 y devuelve el objeto a STEP_TYPE_FROM_MOVEMENT.33 Reproducida instrucción por instrucción, la rutina tarda tres actualizaciones, seis fotogramas a dos fotogramas por actualización, con la nueva orientación escrita en la segunda. Eso es la rutina sola, leída del código; no rastreé el tiempo que pasa desde que se pulsa el botón hasta que empieza la rutina.33
Tres generaciones, tres fundidos
| Rojo | Cristal | Esmeralda | |
|---|---|---|---|
| Puerta | ninguna dibujada; el sonido se elige según el tile | ninguna dibujada | 4 dibujos de 5 fotogramas (335 ms), con un sonido de puerta corredera o de bisagra |
| Hacia la puerta | el paso que cae sobre ella | el paso que cae sobre ella | un paso forzado hacia arriba (16 fotogramas), después la puerta se cierra |
| Fundido de salida | a negro, 4 paletas mantenidas 8 fotogramas cada una: 32 fotogramas (536 ms) | a blanco, 4 pasos separados por 2 fotogramas: 8 fotogramas (134 ms) | a negro (a blanco al entrar en una cueva), 9 niveles, última mezcla en el fotograma 16 contando como 0 el fotograma de la tarea de la puerta (285 ms), inactivo en el fotograma 21 |
| Fundido de entrada | no rastreado | desde blanco, 8 fotogramas | desde negro (desde blanco al salir de una cueva), los mismos 9 niveles |
| Al salir por la puerta | un paso forzado hacia abajo | un paso forzado | la puerta se muestra abierta, un paso forzado hacia abajo, la puerta se cierra, y después el control |
Fuentes: las cuentas de las secciones 1 a 3;14525 el paso forzado de Rojo al salir por una puerta, PlayerStepOutFromDoor, viene del artículo sobre estructuras.13
Las filas que coinciden en los tres son las que vale la pena conservar: un fundido entre mapas, un paso forzado que lleva al jugador al otro lado del umbral, y ninguna entrada durante la ceremonia. La duración de los fundidos varía en un factor de cuatro, así que, según mi lectura, la duración es una decisión de diseño y no un canon. El fundido de nueve niveles de Esmeralda es el que se construyó alrededor de puertas dibujadas, y Kiradex dibuja sus puertas,13 así que es el que adopta el encargo.
Rojo, Cristal y Esmeralda, un juego de cada una de las tres generaciones en la Game Boy y la Game Boy Advance, dos máquinas distintas, llegaron al mismo contrato: un paso son 16 pixeles, tarda 16 fotogramas a unos 59,73 hercios y no se puede interrumpir una vez empezado.14 Rojo llega ahí a saltos de 2 pixeles porque su bucle espera dos fotogramas por pasada; Cristal, con el mismo retardo de dos fotogramas y vectores de 2 pixeles; Esmeralda, a la frecuencia de fotogramas completa con desplazamientos de 1 pixel. Correr, hacer surf y la bici son el mismo contrato a 8 o 4 fotogramas.14 Cuando la gente dice que estos juegos se sienten «sobre rieles», creo que este es el riel: el paso, el dibujo y la cámara se cuentan todos en los mismos fotogramas, con duraciones elegidas para coincidir, así que no pueden desacompasarse.
4. Las referencias modernas: la cámara de Celeste, el vocabulario de Keren y los ports para teléfono
Cuándo debe suavizar una cámara, y cuándo no
La cámara fija de Esmeralda es una respuesta dentro de un vocabulario que Itay Keren expuso en «Scroll Back: The Theory and Practice of Cameras in Side-Scrollers», una versión modificada de su charla en el Independent Games Summit de la GDC 2015, publicada en Game Developer el 11 de mayo de 2015.23 Los términos que uso en este artículo son suyos. El position-locking (bloqueo de posición) fija la cámara sobre el jugador. El edge-snapping (ajuste al borde) la detiene en el borde del nivel. Una camera-window (ventana de cámara) mueve la cámara solo cuando el jugador empuja el borde de la ventana. El lerp-smoothing (suavizado por interpolación) acerca la cámara a su objetivo de forma gradual, y él lo llama «a standard tool in reducing jarring camera speeds, particularly jumps.» (una herramienta estándar para reducir velocidades de cámara bruscas, sobre todo en los saltos). El target-focus y el dual-forward-focus adelantan la cámara al jugador en la dirección de la marcha. También nombra el platform-snapping, el region-focus y los cue attractors, que a un caminante de cuadrícula no le sirven de nada.23
Da la razón por la que el movimiento de cámara importa en absoluto: «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.» (las señales sensoriales contradictorias, visuales frente a vestibulares, pueden causar malestar y náuseas, y aunque es peor en 3D, sobre todo en realidad virtual, sigue ocurriendo de lleno en los juegos 2D). Y da el caso en el que el esquema más simple es el correcto: el bloqueo de posición, para «a crafting adventure game like Terraria, with a small character relative to the screen with pretty small jumps, it works very well.» (en un juego de aventura y construcción como Terraria, con un personaje pequeño respecto a la pantalla y saltos bastante pequeños, funciona muy bien).23 Un pueblo en vista superior con un coleccionista de 30 pixeles, la figura a la que pasó el artículo de esta serie sobre personajes en la compilación 34, es ese caso sin los saltos.35
Celeste es la referencia moderna para la otra opción. Sus desarrolladores publicaron la clase Player «as a learning resource and for general interest» (como recurso de aprendizaje y por interés general), con la licencia MIT cubriendo solo ese código, y la cámara son unas pocas líneas dentro de ella: level.Camera.Position = from + (target - from) * (1f - (float)Math.Pow(0.01f / multiplier, Engine.DeltaTime)), bajo el comentario «Camera (lerp by distance using delta-time)» (cámara, interpolación por distancia usando el delta de tiempo).3637 Con un multiplicador de 1 y un objetivo que se queda quieto, eso cierra el 99 por ciento de la distancia cada segundo, sea cual sea la frecuencia de fotogramas; el objetivo de Celeste no se queda quieto, porque se recalcula a partir de la posición y el estado del jugador en cada fotograma, así que esa cifra describe el suavizado, no dónde acaba la cámara.36 El objetivo es el jugador centrado en la vista del juego (X - Celeste.GameWidth / 2), limitado a los bordes de la sala, con desplazamientos para unos pocos estados: 48 pixeles por delante en la dirección del dash StRedDash, 64 hacia arriba para el lanzamiento de la cumbre.36 El primer artículo de esta serie señaló que Celeste renderiza su mundo a 320 por 180 y lo multiplica por seis.22
Según mi lectura, las dos cosas no están en conflicto. Celeste suaviza porque los saltos de un juego de plataformas arrastrarían la vista arriba y abajo en cada brinco; la propia definición de Keren del suavizado por interpolación habla de saltos. Un caminante de cuadrícula se mueve a velocidad constante en línea recta, así que la cámara fija no produce ninguna sacudida que suavizar, y una cámara suavizada solo añade una estela detrás de un movimiento que ya era uniforme. La sección 7 muestra cuánto mide esa estela en el modelo de Kiradex.
Lo que los otros juegos dicen sobre la velocidad
Stardew Valley expresa la velocidad del jugador como una estadística sin unidades: «2 when walking,» «5 when running,» «6.6 when riding a Horse (7 if the horse was fed a carrot that day)» (2 al caminar, 5 al correr, 6,6 a caballo, 7 si ese día se le dio una zanahoria al caballo), nunca por debajo de 1.38 La wiki no dice a cuántos pixeles por tick equivale una unidad, así que aquí no se da ninguna cifra de tiles por segundo para Stardew.
También busqué una fuente primaria sobre las cámaras de Sea of Stars, Eastward y CrossCode, y no encontré ninguna: entrevistas sobre el desplazamiento por el mundo y la iluminación, nada sobre la cámara, y la opción «Pixel Perfect» de Sea of Stars descrita solo en guías y foros. Tampoco apareció ninguna charla ni artículo de Maddy Thorson sobre cámaras, y por eso Celeste se cita a partir de su código.39
En un teléfono: tocar para caminar, con una salida de emergencia
La versión móvil de Stardew Valley trae nueve esquemas de control, y el predeterminado es «Tap-to-move & Auto-Attack» (tocar para moverse y ataque automático): «Tap anywhere on screen and the farmer will walk to where you tapped.» (toca en cualquier parte de la pantalla y el granjero caminará hasta donde tocaste).40 Mantener el dedo apoyado «will cause the character to follow the touch» (hará que el personaje siga el toque), y la wiki advierte que el modo de seguimiento «is very literal, moving directly towards the finger without routing around blocking objects.» (es muy literal: va directo hacia el dedo sin rodear los objetos que bloquean el paso). El esquema de joystick invisible ocupa «the left half of the screen» (la mitad izquierda de la pantalla), con el centro allí donde toques. Y la wiki es honesta sobre el límite del esquema predeterminado: algunas tareas que exigen colocarse con cuidado «can not be completed using the default controls; temporarily switching to a control style with a movement joystick is necessary in such cases.» (no se pueden completar con los controles predeterminados; en esos casos hay que cambiar temporalmente a un estilo de control con joystick de movimiento).40 Los esquemas llegaron en una actualización de la que informó TouchArcade el 1 de noviembre de 2018, con un interruptor que vuelve a «the default tap-to-move and auto-attack controls.» (los controles predeterminados de tocar para moverse y ataque automático).41
El Pixel Remaster del primer Final Fantasy de Square Enix en iOS añadió una opción predeterminada de caminar o correr en su versión 1.2.0, fechada el 11 de marzo de 2025 en el historial de la app en la App Store (la serie se publica como apps separadas, y solo revisé esta): «In tap based movement mode the character controlled will always run as the default speed when moving.» (en el modo de movimiento por toque, el personaje controlado siempre correrá como velocidad predeterminada al moverse).42 La compatibilidad con mandos había llegado a las versiones móviles en una actualización que TouchArcade cubrió el 30 de enero de 2024.43 No encontré el esquema exacto de movimiento táctil documentado en ningún sitio más allá de esas notas, ni una descripción del movimiento de Terraria en móvil en la página de la wiki que consulté.39
Las Human Interface Guidelines de Apple dicen lo mismo desde el lado de la plataforma. Para juegos táctiles: «consider letting players tap objects to select them instead of adding a virtual selection button» (plantéate dejar que los jugadores toquen los objetos para seleccionarlos en lugar de añadir un botón virtual de selección); «For movement control, opt to show a virtual thumbstick wherever the player lands their thumb instead of a static thumbstick position» (para el control del movimiento, opta por mostrar un joystick virtual allí donde el jugador apoye el pulgar en lugar de en una posición fija); «Make sure frequently used controls are a minimum size of 44x44 pt» (asegúrate de que los controles de uso frecuente midan como mínimo 44x44 pt); «Always include visible and tactile press states» (incluye siempre estados de pulsación visibles y táctiles); y para caminar y esprintar, «consider combining the actions into a single control.» (plantéate combinar las acciones en un solo control). El registro de cambios de la página fecha esas prácticas de control táctil el 9 de junio de 2025.44 En la WWDC25, la sesión de Apple sobre controles táctiles planteó la premisa sin rodeos: «the vast majority of players won’t have a controller available.» (la gran mayoría de los jugadores no tendrá un mando a mano).45
Ninguna de estas fuentes dice que tocar para caminar sea la regla en los juegos para teléfono, y yo no lo afirmo. Lo que saco de ellas, como recomendación mía para Kiradex y no como hallazgo, es el esquema que Kiradex ya sigue a medias: tocar para caminar como opción predeterminada, tal como lo trae la versión móvil de Stardew; un joystick que aparece donde se apoya el pulgar, como aconsejan las HIG, para la precisión; y ninguna cruceta fija en pantalla.4044
5. El oficio, como reglas con números
Estas son las reglas contra las que está escrito el encargo de la sección 7, cada una vinculada a una medición de las anteriores. «Tick» significa un paso de simulación de 1/60 de segundo, que es como un fotograma de 59,73 hercios se convierte en algo que un iPhone puede sostener; a 16 ticks por celda, la caminata va a 3,75 celdas por segundo en lugar de 3,73.119
| Elemento | Regla | Fuente |
|---|---|---|
| Caminata | 1 pixel por tick en tiles de 16 pixeles: 16 ticks por tile, 3,75 tiles por segundo | Rojo, Cristal y Esmeralda caminan todos 16 pixeles en 16 fotogramas, 3,73 celdas por segundo |
| Carrera | 2 pixeles por tick, 8 ticks por tile; ninguna velocidad que no divida a 16 | la carrera y el surf de Esmeralda son 2, la bici 4; solo el 2-3-3 de la Bici Acrobática es irregular |
| Ciclo de caminata | una pisada por cada 16 pixeles a cualquier velocidad; mi propuesta para Kiradex es elegir el dibujo según la distancia recorrida, y para seis dibujos en 32 pixeles mostrar el dibujo floor(distance × 6 / 32) mod 6 |
Esmeralda cronometra dibujos y pasos en fotogramas, por separado, con duraciones que coinciden: su caminata es un ciclo de 32 fotogramas sobre dos celdas de 16 fotogramas, su carrera un ciclo de 16 fotogramas sobre dos celdas de 8 fotogramas |
| Paso | una vez empezado, termina; la entrada se lee en el tile | Esmeralda y Rojo leen la cruceta cuando el paso se completa |
| Giro | 8 ticks, unos 133 ms (los 8 fotogramas de Esmeralda son 134), desde parado; ninguno mientras se camina | WalkInPlaceFast de Esmeralda son 8; la rutina de giro de Cristal 6 fotogramas; la de Rojo 2 |
| Paso rechazado | se muestra, no se ignora: una caminata en el sitio de 32 ticks con un choque | WalkInPlaceSlow de Esmeralda son 32 |
| Cámara | fija al jugador: sin retraso, sin adelanto, movida en el mismo tick por los mismos pixeles | el objeto cámara de Esmeralda; el único adelanto que escribió Game Freak está desactivado |
| Borde del mapa | dibujar el exterior y mantener la cámara fija, o limitarla (ajuste al borde); nunca suavizar | los tiles de borde de Esmeralda; el ajuste al borde de Keren; los límites de sala de Celeste |
| Puerta | 4 dibujos de 5 ticks cada uno: 83 ms por dibujo, 333 ms para abrirse (los 84 y 335 de Esmeralda) | field_door.c de Esmeralda |
| Fundido | 9 niveles escalonados, uno cada 2 ticks, el último en el tick 16, 18 ticks (0,30 s) en total, de salida y de entrada; negro por defecto. Una adaptación, no una copia | el fundido normal de Esmeralda alcanza cada nivel en los mismos fotogramas pares en sus paletas de sprites, un fotograma después que en sus paletas de fondo, hace su última mezcla en el fotograma 16 y se vuelve inactivo en el fotograma 21; al entrar en una cueva funde a blanco, y al salir vuelve desde blanco; los 8 fotogramas de Cristal son el extremo rápido, los 32 de Rojo el lento |
| Ceremonia de entrada | 74 ticks, unos 1,23 s, sin entrada: abrir 20, paso hacia dentro 16, cerrar 20, fundido 18 | la entrada por puerta de Esmeralda: oscuro tras al menos 73 fotogramas, la carga del mapa no antes del fotograma 79 (1,32 s) |
| Sacudida | 1 pixel, 8 inversiones, separadas por 5 ticks (40 ticks, 0,67 s), solo para eventos con guion | la más común de las 24 sacudidas de Esmeralda |
| Frecuencia de fotogramas | simular a 60 haga lo que haga la pantalla; pedir 60, no 120 | la prioridad de Apple para juegos a 30 y 60; RealityKit suele renderizar a 60 |
| Háptica | confirmar eventos, no pasos; hacerla opcional | las HIG de Apple sobre la reproducción de háptica |
| Entrada | mi recomendación: tocar para caminar por defecto; un joystick flotante como opción de precisión; ninguna cruceta fija | el modo predeterminado de Stardew en móvil, el joystick flotante de las HIG; no es una regla que ninguno de los dos enuncie |
Fuentes de la tabla: las cuentas de caminata, animación y puerta de Esmeralda;1 sus temporizadores separados de dibujo y de paso;92 su cámara;123 su fundido y sus sacudidas;529 Rojo y Cristal;433 Keren y Celeste;2336 las pautas de Apple sobre ritmo de fotogramas, RealityKit y háptica;7846 las referencias de entrada.404244
Dos de esas reglas necesitan una frase cada una. La regla del ciclo de caminata es la más fácil de equivocar en un motor moderno, porque un sistema de animación cuenta sus propios segundos mientras que una caminata cuenta la distancia del mundo. Las portátiles mantenían las dos cosas juntas contándolas en los mismos fotogramas con duraciones elegidas para coincidir; en un teléfono, donde el tiempo de fotograma varía, mi propuesta es leer el dibujo a partir de la distancia recorrida, lo que da el mismo resultado sin un segundo reloj que mantener sincronizado. Y la regla de la frecuencia de fotogramas no es falta de ambición. Un mundo que se mueve un pixel entero por tick no tiene nada que mostrar entre ticks, así que una pantalla más rápida solo puede repetir la misma imagen, y la sección 6 muestra que eso es exactamente lo que hace.
6. La vía de Apple: ritmo de fotogramas, el reloj de RealityKit y la háptica
El primer artículo de esta serie expuso el motor sobre el que corre el mundo de Kiradex: una escena de RealityKit usada como renderizador 2D, una cámara ortográfica, el suelo como una sola malla de quads texturizados, los caminantes como quads, y la posición de cada sprite redondeada a unidades enteras del mundo en cada fotograma.22 Esta sección es la parte de la documentación de Apple que decide cómo se mueve ese mundo en el tiempo. Cada página que sigue se leyó tal como Apple la publica el 4 de octubre de 2026, y la disponibilidad indicada es la que declara cada página.
Ritmo de fotogramas en ProMotion
CADisplayLink.preferredFrameRateRange (iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0, visionOS 1.0) es una petición, no un ajuste.15 El consejo de la página es «Choose a frame rate range that your app can consistently maintain,» (elige un rango de frecuencia de fotogramas que tu app pueda mantener de forma constante) y describe qué hace el sistema con la petición: «The system typically provides a consistent frame rate by choosing one that’s a factor of the display’s maximum refresh rate.» (el sistema suele ofrecer una frecuencia constante eligiendo una que sea divisor de la frecuencia de actualización máxima de la pantalla). Por defecto, el rango es igual al máximo de la pantalla.15 El rango en sí es un CAFrameRateRange (iOS 15.0) con una frecuencia mínima, una máxima y una preferida.47
El artículo de Apple sobre ProMotion da los números. Las pantallas ProMotion cambian entre 24 y 120 hercios en el iPad Pro y entre 10 y 120 en los iPhone compatibles, y las frecuencias del iPhone son doce escalones: 120, 80, 60, 48, 40, 30, 24, 20, 16, 15, 12 y 10 hercios; las del iPad Pro son cinco de ellas.7 La lista de dispositivos nombra ahora el iPhone Air y «iPhone 17 and later» (iPhone 17 y posteriores) junto a «iPhone 13 Pro and later» (iPhone 13 Pro y posteriores).7 En el iPhone no ocurre nada por encima de 60 a menos que el Info.plist de la app ponga CADisableMinimumFrameDurationOnPhone (iOS 15.0) en true: «If you don’t enable this support, Core Animation won’t access higher frame rates (above 60Hz).» (si no activas esta compatibilidad, Core Animation no accederá a frecuencias más altas, por encima de 60 Hz).716 Dos frases del artículo importan a un juego más que todo lo demás. Una: «In iOS 15 and later, the system provides games with special priority to 30Hz and 60Hz refresh rates to ensure optimal performance,» (en iOS 15 y posteriores, el sistema da a los juegos prioridad especial a las frecuencias de 30 Hz y 60 Hz para asegurar un rendimiento óptimo) a la que se llega con CAFrameRateRange(minimum: 30, maximum: 60, preferred: 60).7 La otra: «Prepare your app to operate at any refresh rate, not just those it requests.» (prepara tu app para funcionar a cualquier frecuencia de actualización, no solo a las que pide).7 Y para todo lo que se anima: «Always use targetTimestamp to drive any animation, physics, or other time-related content provided in your CADisplayLink callback» (usa siempre targetTimestamp para dirigir cualquier animación, física u otro contenido dependiente del tiempo en tu callback de CADisplayLink) (targetTimestamp es de iOS 10.0).748
La sesión de la WWDC21 «Optimize for variable refresh rate displays» cubre tanto ProMotion en el iPad Pro como las pantallas Adaptive-Sync en el Mac.49 Para las pantallas Adaptive-Sync del Mac cambia la pauta anterior de Apple: en una pantalla de frecuencia fija, «we’ve previously recommended that you slow down your rendering to hit the next factor of the display’s fastest refresh rate» (antes recomendábamos ralentizar el renderizado hasta el siguiente divisor de la frecuencia de actualización más rápida de la pantalla); con Adaptive-Sync, «You should instead attempt to present frames at the highest rate your app can do so evenly.» (en cambio, deberías intentar presentar fotogramas a la frecuencia más alta a la que tu app pueda hacerlo de forma uniforme).49 La palabra que me llevo de ahí para un teléfono es evenly, uniforme.
El reloj de RealityKit
El mundo de Kiradex no tiene un display link propio. Avanza con el evento por fotograma de RealityKit, SceneEvents.Update (iOS 13.0), «An event invoked once per frame interval,» (un evento que se invoca una vez por intervalo de fotograma) cuyo deltaTime es «The elapsed time since the last update.» (el tiempo transcurrido desde la última actualización).5051 RealityView (iOS 18.0) documenta exactamente esa vía para el trabajo por fotograma, «you can use a System or directly subscribe to the engine’s SceneEvents.Update,» (puedes usar un System o suscribirte directamente al SceneEvents.Update del motor) y no ofrece ningún ajuste propio de frecuencia de fotogramas.52 El artículo de Apple sobre el rendimiento de RealityKit dice qué frecuencia esperar: «RealityKit typically limits the refresh rate,» (RealityKit suele limitar la frecuencia de actualización), que define como la frecuencia a la que el framework renderiza actualizaciones para la pantalla, «to 60 frames per second (fps).» (a 60 fotogramas por segundo).8 Si RealityView en un iPhone 18 Pro Max o en un iPhone Duo renderiza realmente a 60 o a 120 no es algo que yo haya medido, y la sección 7 convierte esa medición en la primera comprobación del encargo.
Por qué 120 hercios no aporta nada a una caminata de pixeles enteros
Esta es la aritmética, del mismo script de modelo que la sección 7 usa para el código actual de la app, aplicado esta vez a la propuesta: un mundo que avanza con un tick fijo de 60 hercios, un pixel por tick, con la pantalla mostrando lo que haya producido el último tick. En una pantalla de 60 hercios, cada pixel del mundo se muestra exactamente durante un refresco.6 En una pantalla de 120 hercios, a lo largo de un segundo, 59 de las 61 posiciones se mantienen exactamente dos refrescos y dos se mantienen uno.6 En una pantalla de 80 hercios, las duraciones alternan entre uno y dos refrescos, 42 posiciones mantenidas una vez y 19 dos veces.6 Según mi lectura, el caso de 120 hercios es el caso de 60 hercios dibujado dos veces, y el de 80 hercios es un ritmo irregular: un pixel que a veces se queda 12,5 milisegundos y a veces 25.
Así que la pregunta para un mundo de cuadrícula en un iPhone no es 120 hercios sino un ritmo uniforme a 60: un tick fijo de simulación de 60 hercios, desplazamientos enteros por tick, animaciones contadas en ticks, y el renderizador mostrando el último tick. Eso es también lo que hace que el movimiento sea independiente de la frecuencia que elija RealityKit. La sesión de la WWDC21 señala algo relacionado sobre los deltas de tiempo. Cuando un fotograma lento hace que el display link se salte una callback, el delta que hay que avanzar es «not 8ms, but rather 16ms» (no 8 ms, sino 16 ms); la sesión dice después que una app que «uses time delta to advance the state of your custom drawing» (usa el delta de tiempo para avanzar el estado de su dibujo personalizado) va a «slow down your custom drawing by one frame» (ralentizar su dibujo personalizado en un fotograma) cada vez que se salta una callback, lo que interpreto como una app que avanza los 8 milisegundos esperados en lugar del tiempo realmente transcurrido, y dice que la app «can instead keep track of a previous targetTimestamp so that you can advance the state correctly.» (puede, en cambio, llevar registro de un targetTimestamp anterior para poder avanzar el estado correctamente).49
Háptica
Core Haptics (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0, visionOS 1.0) construye un CHHapticPattern, «An object representing a haptic waveform,» (un objeto que representa una forma de onda háptica) a partir de diccionarios, de arrays de objetos CHHapticEvent o de un archivo AHAP.53 Los eventos son de dos tipos hápticos, hapticTransient y hapticContinuous; los transitorios son «brief impulses that occur at a specific point in time.» (impulsos breves que ocurren en un instante concreto).54 Cada uno acepta los parámetros hapticIntensity, hapticSharpness, attackTime, decayTime, releaseTime y sustained.55 Un CHHapticEngine los reproduce, y capabilitiesForHardware() dice si el dispositivo puede hacerlo.56
La vía más sencilla es UIImpactFeedbackGenerator (iOS 10.0), «A concrete feedback generator subclass that creates haptics to simulate physical impacts,» (una subclase concreta de generador de respuesta que crea háptica para simular impactos físicos) cuyos estilos describen «The mass of the objects in the collision» (la masa de los objetos que chocan) (light, medium, heavy, soft, rigid); impactOccurred(intensity:) es de iOS 13.0, y la página lista init(style:view:) en «Initializing the feedback generator» y init(style:) entre lo obsoleto.575859 prepare() solo reduce la latencia si tiene tiempo para actuar: «Calling prepare() and then immediately triggering feedback (without any time in between) does not improve latency,» (llamar a prepare() y disparar la respuesta inmediatamente, sin tiempo entre medias, no mejora la latencia) y el motor vuelve al reposo cuando «A short period of time passes (typically seconds).» (pasa un breve periodo de tiempo, normalmente segundos).60 En SwiftUI, sensoryFeedback(_:trigger:) (iOS 17.0) «Plays the specified feedback when the provided trigger value changes,» (reproduce el feedback indicado cuando cambia el valor de trigger proporcionado) incluido .impact(weight:intensity:).61
La página de las HIG sobre la reproducción de háptica es la mitad de diseño. «Avoid overusing haptics,»46 (evita abusar de la háptica) con la razón: «Often, the best haptic experience is one that people may not be conscious of, but miss when it’s turned off.» (a menudo, la mejor experiencia háptica es una de la que la gente no es consciente, pero que echa de menos cuando se apaga). «Make haptics optional.» (haz que la háptica sea opcional). Hacer coincidir «the intensity and sharpness of a haptic with the intensity and sharpness of the animation it accompanies.» (la intensidad y la nitidez de una háptica con la intensidad y la nitidez de la animación a la que acompaña).46 La nitidez puede transmitir una experiencia «that’s soft, rounded, or organic, or one that’s crisp, precise, or mechanical.» (suave, redondeada u orgánica, o bien nítida, precisa o mecánica).46 Y una háptica personalizada puede hacer que «a collision or a hit» (un choque o un golpe) se sienta muy distinto «from subtle experiences like the approach of footsteps or a looming danger.» (de experiencias sutiles como pasos que se acercan o un peligro inminente).46 Una caminata de 3,75 pasos por segundo durante minutos seguidos es el caso al que apunta la primera regla.1
Entrada
Las APIs de control táctil, en los términos de la sección 4. Touch Controller (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, visionOS 26.0) se resume en su página como «Integrate onscreen touch controls into your Metal-based games» (integra controles táctiles en pantalla en tus juegos basados en Metal): botones, crucetas, joysticks, aceleradores y paneles táctiles, expuestos a través de un GCController.62 Su TCDirectionPad (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0) puede configurarse «to behave as either a composite direction pad» (para comportarse como una cruceta compuesta) o «as four separate buttons.» (como cuatro botones separados).6263 El más antiguo GCVirtualController (iOS 15.0) es «A software emulation of a real controller that you configure specifically for your game.» (una emulación por software de un mando real que configuras específicamente para tu juego).64 Una dirección que probé, developer.apple.com/documentation/touchcontrols, devolvió un 404; la página del framework es touchcontroller.39
Lo que cuesta
Nada de esta sección se ha cronometrado en un teléfono. El tiempo de fotograma del mundo en el dispositivo, el coste de un acumulador de 60 hercios y el coste de mantener en marcha un motor háptico mientras el mundo está en pantalla están todos sin medir; las comprobaciones del encargo en la sección 7 están escritas para medir los dos primeros.
7. El encargo: qué construye Kiradex y qué comprobaciones debe superar
Dónde está hoy la caminata
Leí el código de caminata, cámara y transiciones del mundo en el repositorio de Kiradex el 4 de octubre de 2026, sin modificarlo, y escribí un modelo fotograma a fotograma de ese código. Todo lo que hay en esta subsección o bien se lee de ese código o bien lo calcula el modelo, y se indica cuál de las dos cosas es. El modelo hace cada una de las operaciones Float del código en 32 bits, en el orden del código, con el redondeo de Swift, a exactamente 1/60 o 1/120 de segundo por fotograma, y deja fuera los bordes del mapa, el pliegue del iPhone Duo y el desplazamiento de los pies, nada de lo cual cambia una caminata en línea recta en mitad del mapa. Es un modelo del código. No es una captura, y no se ha medido ningún teléfono.176
Lo que hace el código, según su lectura:
- La entrada es tocar para caminar, y nada más. El escenario convierte un toque en un punto del mundo y después toca a un coleccionista, el quiosco, o llama a
walk(to:). No hay arrastre, ni cruceta, ni ninguna llamada háptica en todo el target de la app: una búsqueda deUIImpactFeedbackGenerator,sensoryFeedbackyCHHapticno encuentra nada.17 - La ruta es una búsqueda de camino más corto en ocho direcciones, ordenada por el coste recorrido hasta el momento sin ninguna estimación de la distancia que falta, lo que la convierte en la búsqueda de Dijkstra y no en A*; las diagonales cuestan √2 y nunca cortan la esquina de una pared. Un paso diagonal mira de lado y divide su avance entre √2 después de aplicar el 1,6 de la carrera, así que en el modelo un paso diagonal tarda 22 fotogramas caminando y 14 corriendo a 60 hercios, frente a 15 y 10 de uno recto.176
- La velocidad es tiempo.
tilesPerSecondvale 4, que son 64 pixeles por segundo, y la caminata se convierte en carrera a 1,6 veces eso cuando la ruta tiene 6 pasos o más, así que una caminata en la app es de 1 a 5 tiles y todo lo que sea más largo se recorre corriendo. Cada fotograma sumadt × speed / lengthal avance de un paso, y cuando el avance llega a 1 el caminante salta al tile siguiente y el avance vuelve a 0, descartando lo que sobra.17 - El dibujo de la caminata se elige por tiempo. La columna es
cycle[Int(walker.clock * framesPerSecond) % count], conframesPerSeconda 8 y los ciclos de caminata y carrera de seis dibujos de la forja. El artículo de esta serie sobre personajes dio la misma cadencia, «the walk at eight frames a second» (la caminata a ocho fotogramas por segundo), y eso describe el código con exactitud.1735 - La cámara suaviza y luego redondea. En cada fotograma se mueve
(target - current) * min(1, dt * 6)hacia el jugador y después redondea ambos ejes a unidades enteras; se limita al mapa cuando el mapa es mayor que la vista. La frase del primer artículo, que cada sprite y la cámara «are rounded to whole world units each frame, after easing» (se redondean a unidades enteras del mundo en cada fotograma, después del suavizado), también es exacta.1722 - Puertas y warps. La puerta muestra de inmediato el dibujo entreabierto de la hoja y el abierto al cabo de 70 milisegundos, y retira la puerta 1,4 segundos después; el paso sobre el warp espera 220 milisegundos y luego cambia de lugar. El artículo sobre estructuras lo describió como una carencia conocida.1713
- Las transiciones son navegaciones push con su animación predeterminada; un cambio de piso sustituye el escenario por identidad sin fundido; salir llama a
dismiss(). El único velo de la app es la capa negra del visor de cartas, animada con.easeOut(duration: 0.25).17 - Ninguna petición de frecuencia de fotogramas. No hay
CADisplayLink,preferredFrameRateRangeniCADisableMinimumFrameDurationOnPhoneen la app ni en su archivo de proyecto; el mundo avanza conSceneEvents.Updatey eldeltaTimedel evento.17
Lo que el modelo dice que ese código hace en pantalla, con un tiempo de fotograma constante:
- Un salto de dos pixeles una vez por tile a 60 hercios. A 60 hercios, una caminata tarda 15 fotogramas por tile, 4,00 tiles por segundo, y los 16 pixeles de cada tile llegan como 14 fotogramas de 1 pixel y uno de 2: un salto de 2 pixeles por tile, cinco en una caminata de cinco tiles. Esmeralda mueve exactamente 1 pixel en cada fotograma.61
- Ceros y unos irregulares a 120 hercios. A 120 hercios la caminata tarda 30 fotogramas por tile y los 16 pixeles de cada tile llegan como 16 fotogramas de 1 pixel y 14 de ninguno, intercalados de forma irregular.6
- La carrera es más lenta que su constante. A 60 hercios la carrera tarda 10 fotogramas por tile, 6,00 tiles por segundo, cuando 4 por 1,6 serían 6,4, porque lo que sobra en cada tile se tira; los 16 pixeles de cada tile llegan como 4 fotogramas de 1 pixel y 6 de 2. A 120 hercios tarda 19 fotogramas por tile, 6,32 tiles por segundo, así que la velocidad de la carrera depende de la frecuencia de fotogramas.6
- Una cámara rezagada que nunca llega. Al caminar a 60 hercios, la cámara se queda cerca de un pixel más atrás con cada tile, de 5 pixeles al final del primer tile a 9 al final del quinto, la caminata más larga que hace la app, y la posición del jugador en pantalla cambia en 8 de los 73 fotogramas de la caminata posteriores al primero. En una carrera, que es cualquier ruta de 6 tiles o más, se queda 13 por detrás desde el segundo tile. A 120 hercios llega a 9 dentro del primer tile, caminando o corriendo, y ahí se mantiene. Cuando el jugador se detiene, la cámara se queda quieta a 4 pixeles del jugador a 60 hercios y a 9 a 120, y ahí permanece: en cuanto la distancia multiplicada por
min(1, dt × 6)baja de medio pixel, el redondeo devuelve la misma posición en cada fotograma. En qué lado se detiene depende de la dirección de la última caminata. En Esmeralda, tanto el retraso como el desfase en reposo son cero.6 - Los dibujos llevan su propio reloj. El ciclo de seis dibujos a 8 por segundo dura 0,75 segundos. Medido a partir de las posiciones del modelo entre los comienzos de ciclos sucesivos a 60 hercios, cubre 3,00 tiles caminando y 4,50 corriendo (4,80 a los 6,4 tiles por segundo nominales de la carrera, que el sobrante descartado nunca le deja alcanzar); el ciclo de dos pisadas de Esmeralda cubre 2,00 tiles. Según mi lectura, un ciclo que cubre la mitad más de terreno que sus pisadas se ve como pies que patinan, pero el modelo no mide dónde se apoya un pie en el suelo. El dibujo queda además en el filo de la navaja cada 15 fotogramas, a 60 y a 120 hercios, cuando el reloj multiplicado por 8 cae exactamente en un número entero, el límite entre dos dibujos: llevar el reloj en 32 bits, como hace la app, en lugar de en 64, cambia qué dibujo se muestra en 9 de los 11 fotogramas de ese tipo en 12 tiles de caminata a 60 hercios y en 14 de 23 a 120.6
- Un segundo toque a mitad de paso hace retroceder al caminante. Esto se lee del código, no se modela ni se captura:
walk(to:)pone el avance a 0 mientras el tile del caminante sigue siendo el origen del paso, así que el fotograma siguiente dibuja al caminante hasta 15 pixeles por detrás de donde estaba; los movimientos de otros coleccionistas que llegan del servidor hacen lo mismo.17
Pixeles por fotograma mostrado: el canon es uniforme, el código de hoy (un modelo, no una captura) no lo es, y un tick fijo es uniforme también a 120.621
La cámara suavizada y redondeada, según el modelo: se rezaga al caminar o correr y se queda corta cuando termina la caminata.621
Nada de esto es un juicio sobre cómo se siente en la mano, porque no se ha medido ningún teléfono. Los tiempos de fotograma reales fluctúan, lo que cambiará el patrón exacto de unos y doses. El retraso y el desfase en reposo también dependen del tiempo de fotograma, porque el paso de suavizado min(1, dt × 6) depende de él, y por eso el modelo da 4 pixeles en reposo a 60 hercios y 9 a 120. Según mi lectura, una fluctuación dentro del rango que muestra normalmente un teléfono cambiará su tamaño, no los eliminará; la condición es que los fotogramas duren menos de 1/6 de segundo, unos 167 milisegundos, porque con esa duración el factor de suavizado llega a 1 y la cámara aterriza sobre el jugador en un solo fotograma, así que un tirón lo bastante largo cierra la distancia en ese fotograma.176 El desajuste entre los dibujos y la caminata no depende de la frecuencia de fotogramas: un reloj de dibujos de 8 por segundo contra una caminata de 4 tiles por segundo da 3,00 tiles por ciclo a 60 hercios y más o menos lo mismo a 120. El de la carrera sí depende, 4,50 tiles por ciclo a 60 hercios y 4,75 a 120, porque depende de él el sobrante que descarta en cada tile.6
El encargo
Cada punto es un cambio en Kiradex/World/, la razón para hacerlo y una comprobación que puede verificar una línea de registro, un script o una captura. No se propone ningún recurso, nombre ni sonido de ninguno de los juegos: los números son mecánicas, y el arte, los sonidos y las palabras son de Kiradex.
1. Caminar con un tick, no con un reloj.
Cambio. Añadir un acumulador de 60 hercios a la actualización del rig. Cada callback suma su dt; si el acumulador contiene entonces más de 8 ticks de tiempo (133 milisegundos), el exceso se descarta y se escribe en el registro de movimiento como una línea dropped con sus milisegundos; después se ejecutan todos los ticks enteros que haya en el acumulador, cada uno restando un tick. El acumulador cuenta el tiempo en unidades enteras (nanosegundos, o el 1/60.000.000 de segundo del modelo), nunca en segundos de coma flotante: con un acumulador Double o Float, el tick 600 de una prueba de diez segundos cae un redondeo antes de su límite a algunas frecuencias y la cuenta sale 599. Esa es la regla del atraso: hasta 8 ticks de retraso se recuperan en la callback siguiente, y todo lo que pase de ahí se trata como una suspensión, de modo que el mundo se reanuda donde se detuvo en lugar de acelerar para ponerse al día. El ocho se elige para cubrir todas las frecuencias de la lista de Apple para los iPhone con ProMotion, hasta 10 hercios, que necesitan 6 ticks por callback.7 En el modelo, diez segundos de callbacks a cada una de las doce frecuencias ejecutan exactamente 600 ticks y no descartan nada, mientras que un tope de 4 ticks por callback ejecutaría 480 a 12 hercios y 400 a 10.6 Tras un tirón de un segundo a 60 hercios, la regla ejecuta 8 ticks en la callback siguiente y 1 en cada una de las posteriores, y registra 866,7 milisegundos descartados; el mismo tirón con el atraso conservado bajo un tope de 4 ejecuta 4 ticks en 19 callbacks seguidas, el avance rápido que la regla existe para evitar.6 Cada caminante lleva una cuenta de ticks del paso en lugar de un avance fraccionario, y su desplazamiento dibujado es esa cuenta multiplicada por sus pixeles por tick: enteros exactos, así que los caminantes ya no necesitan redondeo. La caminata es 1 pixel por tick, 16 ticks por tile; la carrera son 2 pixeles por tick, 8 ticks, y se mantiene la regla actual de que una ruta de 6 pasos o más es una carrera. Los pasos diagonales, que la búsqueda de rutas permite, conservan el √2 que el código ya pretende y su orden, con el ritmo de la carrera aplicado antes de la división entre √2: una diagonal caminando tarda 23 ticks (16√2 es más o menos 22,6, redondeado hacia arriba) y una diagonal corriendo 12 (8√2 es más o menos 11,3, redondeado hacia arriba), y cada una mueve 16 pixeles en cada eje en los ticks en los que cambia floor(16 × t / 23) o floor(16 × t / 12).17 Caminando, eso es 1 pixel en 16 de los 23 ticks y ninguno en los otros 7; corriendo, 1 pixel en 8 de los 12 ticks y 2 en 4, el único modo de andar irregular del encargo, igual que el de la Bici Acrobática es el único irregular de Esmeralda.1 No hay ningún sobrante que descartar.
Por qué. Las reglas de caminata y carrera de la sección 5; y el salto de 2 pixeles por tile del modelo a 60 hercios y los ceros y unos irregulares a 120.61
Comprobaciones. Un argumento de lanzamiento -motionLog registra el tick y la x, y del jugador en cada tick, y cada línea dropped. En la demo de orientación existente, que camina un tile en cada dirección, un script comprueba que cada tick de caminata mueve exactamente 1 pixel, que cada tile tarda 16 ticks y que ningún tick mueve 2. Una demo diagonal, con un paso diagonal caminando y otro corriendo en cada dirección, comprueba 23 ticks y 12, 16 pixeles en cada eje por paso, desplazamientos por eje de 0 o 1 pixel caminando y de 1 o 2 corriendo, y que una diagonal corriendo es más rápida que una caminando. Las pruebas unitarias alimentan el acumulador con secuencias inventadas de dt en unidades enteras exactas: diez segundos a cada una de las doce frecuencias del iPhone, de 120 a 10 hercios, ejecutan 600 ticks sin descartar nada, y un hueco de un segundo a 60 hercios ejecuta 8 ticks en la callback siguiente y después 1 por callback, con 866,7 milisegundos registrados como descartados. El mismo registro de movimiento en un iPhone 18 Pro Max y en la pantalla interior de un iPhone Duo da los mismos ticks por tile: la frecuencia de la pantalla no debe cambiar la caminata.
2. Elegir el dibujo de la caminata por distancia.
Cambio. Este punto es propuesta mía, no una copia: Esmeralda cronometra sus dibujos en fotogramas, ajustados a sus pasos, y no los lee a partir de la distancia.92 Mientras se camina, elegir la columna a partir del avance de la caminata contado en pasos, los pasos completados desde que empezó la caminata más los ticks del paso actual divididos por su duración: walkCycle[floor(steps × walkCycle.count / 2) % walkCycle.count], dos pisadas por cada dos pasos, y lo mismo para la carrera. En un paso recto eso es la distancia recorrida sobre 32 pixeles, floor(distancePx × 6 / 32) mod 6. En una diagonal cuenta el paso y no su longitud de √2, de modo que una pisada sigue cayendo al comienzo de cada paso con una zancada más larga, elección mía para dibujos hechos para zancadas rectas. Mostrar el dibujo de pie cuando termina la caminata, como hace Esmeralda. Dejar los relojes de reposo, parpadeo y emote como están. Un ciclo cronometrado por ticks también podría ajustarse a los pasos, como el de Esmeralda, pero seis dibujos en 32 ticks necesitarían duraciones desiguales de 5 y 6 ticks; contar el avance no necesita una segunda tabla y sirve para cualquier modo de andar que añada la app.
Por qué. Una pisada por cada 16 pixeles a cualquier velocidad, y una cadencia que no puede separarse del movimiento; en el modelo, el ciclo actual cubre 3,00 tiles caminando y 4,50 corriendo.16 Esto zanja además los «eight frames a second» (ocho fotogramas por segundo) del artículo sobre personajes: a 1 pixel por tick, seis dibujos en 32 pixeles cambian cada 5,33 pixeles, lo que son 11,25 dibujos por segundo, no 8.35
Comprobación. A partir del registro de movimiento más la columna mostrada: los dos dibujos de contacto (walk_a y walk_d en el ciclo de la forja) aparecen en los pixeles 0 y 16, con un margen de uno, de cada 32, a 60 y a 120 hercios; en la demo diagonal aparecen en el primer tick de cada paso.
3. Terminar el paso; mantener el toque; añadir un joystick flotante.
Cambio. Un toque a mitad de paso planifica desde el tile hacia el que se camina, no desde el origen, y el paso en curso termina primero; la cuenta del paso nunca se reinicia. Los movimientos de otros coleccionistas que llegan del servidor siguen la misma regla. Desde parado, un toque cuyo primer paso cambia la orientación gira durante 8 ticks antes de moverse. Un toque sobre una celda bloqueada junto al jugador, o sobre el tile que tiene delante, lo pone de cara a ella y reproduce un choque de 32 ticks con la háptica de rechazo del punto 6. Mantener el dedo apoyado hace que el jugador lo siga, como en el modo predeterminado de Stardew, pero con la ruta recalculada por la búsqueda de caminos existente cada vez que el dedo cruza un tile, ya que el seguimiento de Stardew no rodea obstáculos y el nuestro sí puede. Un joystick flotante de cuatro direcciones, desactivado por defecto, aparece donde se apoya el pulgar, construido sobre un gesto de arrastre de SwiftUI encima del mundo; todo lo que se dibuje mide al menos 44 por 44 puntos. El joystick de Touch Controller (iOS 26) sustituye al arrastre solo si una compilación de prueba demuestra que se dibuja encima de la RealityView: Apple describe el framework como pensado para «Metal-based games» (juegos basados en Metal), su sesión de la WWDC25 dice que «integrates directly with Metal» (se integra directamente con Metal), y el mundo de Kiradex lo dibuja RealityView sin ningún pase de renderizado propio.4562
Por qué. Las reglas de paso, giro, paso rechazado y entrada de la sección 5.14044624517
Comprobaciones. Una prueba de interfaz toca un tile 6 al este y, 120 milisegundos después, un tile 6 al norte; el registro de movimiento no muestra ningún tick en el que la x del jugador disminuya, y el tile del primer paso se completa antes del giro. Una prueba de interfaz toca la pared junto a la casa; el registro muestra un cambio de orientación y un choque de 32 ticks, sin cambio de posición. Con el joystick activado, un arrastre mantenido a la derecha durante 60 ticks desde el primer tick de movimiento completa tres tiles en el tick 48, empieza un cuarto mientras el joystick sigue pulsado y lo termina en el tick 64, después de soltarlo: el registro muestra al jugador 4 tiles al este, sobre un tile entero, con el paso que estaba en curso al soltar completado y sin ninguna parada entre tiles.
4. Fijar la cámara.
Cambio. Sustituir el suavizado por una cámara fijada en el mismo tick en que se mueve el jugador, solo a partir de pixeles enteros del mundo: camera = clamp(player + foldOffset, low, high). Cada término se mantiene entero, porque hoy solo el redondeo final hace entera la cámara: los límites y el desplazamiento del pliegue son fraccionarios. La posición del jugador es entera gracias al punto 1. El desplazamiento del pliegue, que follow calcula en puntos multiplicados por displayScale / pixelScale, se redondea a pixeles enteros del mundo una sola vez, cuando cambia el pliegue. Los límites del clamp se redondean hacia dentro, el inferior hacia arriba y el superior hacia abajo, porque la mitad del tamaño de la vista en pixeles del mundo no tiene por qué ser entera: fit divide los pixeles de pantalla de la vista entre un número entero de pixeles de pantalla por texel, así que una vista ilustrativa de 393 por 852 puntos a 3x obtiene 7, una vista de 168,43 pixeles del mundo de ancho y una mitad de ancho de 84,21, y los límites de la cámara pasan a ser 85 y el ancho del mapa menos 85, de modo que ningún fotograma muestra nada más allá del borde del mapa.176 Cuando el mapa es más pequeño que la vista, los límites se intercambian, como ya permite el clamp actual, y el mismo redondeo hacia dentro mantiene el mapa entero a la vista. Mantener el límite al mapa; la regla de la sección 5 permite limitar o dibujar el exterior, y la cámara de Kiradex sale del borde del mapa hacia el bosque solo cuando el Duo está medio abierto, con un margen de tiles enteros.17 Tratar el desplazamiento del pliegue como fijo hasta que cambie el pliegue; cuando cambie de a a b, hacerlo avanzar por a + round((b − a) × i / 8) para i de 0 a 8, un nivel cada 2 ticks, la cadencia del fundido, de modo que cada posición intermedia sea un pixel entero; un cambio de 0 a menos 37, por ejemplo, pasa por 0, −5, −9, −14, −19, −23, −28, −32 y −37.6 Mantener el paralaje del bosque, calculado a partir de la cámara fija y redondeado como hoy. Ninguna anticipación hacia el destino tocado: Game Freak construyó una y la dejó apagada, y el destino de un toque para caminar ya está en pantalla.12
Por qué. La regla de cámara de la sección 5, y el retraso del modelo, que crece a lo largo de una caminata hasta 9 pixeles en su quinto tile y es de 13 en una carrera, sus cambios de la posición del jugador en pantalla, y sus desfases en reposo de 4 y 9 pixeles.6
Comprobaciones. A partir del registro de movimiento, la cámara menos el jugador es constante en cada tick de una caminata por la plaza abierta (cero, o el desplazamiento del pliegue) y sigue igual después de que el jugador se detiene, y cada valor de la cámara es un número entero. Una prueba unitaria llama a fit con la vista de 393 por 852 puntos a 3x, lleva al jugador a los dos bordes de un mapa y comprueba posiciones enteras de la cámara que se detienen en 85 y en el ancho del mapa menos 85; la misma prueba cambia después el pliegue y comprueba que la cámara alcanza el nuevo desplazamiento a través de 9 posiciones de pixeles enteros, separadas por 2 ticks, y que ningún tick intermedio se sale de los límites. Para una captura, los dibujos de la caminata no sirven como marcador, porque los pies cambian de forma de un dibujo a otro; una compilación de depuración dibuja un marcador de un texel, de un color que no se usa en ningún otro sitio, en la posición de la entidad del jugador, y un script lo encuentra en cada fotograma de una caminata de 6 tiles por la plaza abierta: el mismo pixel de pantalla en todos los fotogramas. Cerca del borde del mapa el marcador se mueve y la cámara no, y siempre por pixeles enteros.
5. La puerta, el paso y el fundido, con el arte propio de Kiradex.
Cambio. Entrar por una puerta, que es un warp con una hoja de puerta. Hoy la puerta se abre después de que el caminante ha llegado a la celda de la puerta: el onStep del paso encuentra el warp en ese tile y llama ahí a openDoor.17 Esmeralda nunca deja al jugador de pie sobre una puerta cerrada: TryDoorWarp solo se activa cuando el jugador, en la celda de abajo, empuja hacia el norte contra una puerta, y Task_DoDoorWarp abre la puerta una celda más arriba y después fuerza el paso sobre ella.6525 El encargo retrasa el disparador una celda para que coincida:
- Cuando el siguiente paso de la ruta va sobre un warp de puerta, la caminata se detiene en la celda anterior, y la entrada empieza ahí, en el tick 0. Congelar la entrada y reproducir el sonido de la puerta, un sonido de Kiradex.
- Abrir la puerta en cuatro intervalos de 5 ticks, los cuatro dibujos de Esmeralda a sus cinco fotogramas cada uno (83 milisegundos por intervalo a 60 ticks por segundo, donde los cinco fotogramas de Esmeralda son 84; 20 ticks en total), en lugar del dibujo entreabierto inmediato y el abierto a los 70 milisegundos de hoy: cerrada, entreabierta, abierta, abierta. La hoja de puerta de Kiradex tiene tres dibujos, cerrada, entreabierta y abierta (el
door_sheet()de la forja dibuja tres, y la app la carga conSpriteSheet.bundled(name, columns: 3, rows: 1)), mientras que la puerta de Esmeralda es cerrada más tres, así que el dibujo abierto se mantiene durante el cuarto intervalo. Un cuarto dibujo de la forja, entre entreabierta y abierta, ocuparía ese intervalo; es arte opcional, no un requisito. Corregir el comentario que dice «four ticks» (cuatro ticks).2417 - Llevar al jugador con un paso forzado desde esa celda hasta la celda de la puerta, 16 ticks. Las puertas del pueblo están en la fila inferior de un edificio, con celdas del edificio a ambos lados y encima, y la ruta nunca corta la esquina de una pared, así que este paso es siempre hacia arriba desde la celda de abajo, como el de Esmeralda.17
- Ocultar al caminante y cerrar la puerta, con los intervalos al revés (abierta, abierta, entreabierta, cerrada), 20 ticks.
- Fundir un velo negro sobre el mundo en 9 niveles de opacidad escalonados (0, 2/16 y así hasta 16/16), el nivel
ien el tick2idel fundido, de modo que el último nivel cae en el tick 16 y se mantiene durante el tick 17: 18 ticks. Eso es una adaptación, elegida, no la temporización de Esmeralda. Las paletas de sprites de Esmeralda alcanzan cada nivel en los mismos fotogramas pares que este velo, sus paletas de fondo un fotograma antes, y tras la última mezcla en el fotograma 16 ejecuta cinco actualizaciones finales antes de volverse inactivo en el fotograma 21; un solo velo no tiene una segunda capa que vaya por detrás ni nada que terminar.5 Nunca animar la opacidad de la vista que contiene la propia RealityView; el visor de cartas le enseñó esa lección al primer artículo.22 - Hacer el push del destino con las animaciones desactivadas, y retirar el velo con los mismos 9 niveles.
- Llegar a la alfombrilla interior mirando hacia arriba. Salir es lo inverso: llegar a la celda de la puerta con la puerta abierta, un paso forzado hacia abajo de 16 ticks, la puerta se cierra, y después la entrada.
Los pisos, que hoy cambian el escenario sin fundido, reciben el mismo velo sin puerta y sin paso forzado. Salir de una habitación, que hoy llama a dismiss(), recibe el velo y el paso forzado hacia fuera.
Por qué. Las reglas de puerta, fundido y ceremonia de la sección 5; hoy el lugar cambia 220 milisegundos después del paso, la puerta muestra dos dibujos separados por 70 milisegundos y la pantalla se desliza de lado.1517
Comprobaciones. El registro de movimiento cuenta los ticks desde aquel en que la caminata se detiene debajo de la puerta. Muestra al jugador todavía en esa celda, y los intervalos de la puerta en los ticks 0, 5, 10 y 15 (cerrada, entreabierta, abierta, abierta); el paso forzado en los ticks 20 a 35, 1 pixel por tick, llegando a la celda de la puerta en el tick 35 y nunca antes; los intervalos de cierre en los ticks 36, 41, 46 y 51; el primer nivel del velo en el tick 56; y el push no antes del tick 74. Una caminata que termina en una celda de puerta sin esta secuencia no pasa la comprobación. El registro de movimiento anota también la opacidad del velo en cada tick: 9 valores distintos, cada uno mantenido 2 ticks, en los ticks 56 a 73 a la salida, y los mismos 9 a la entrada. Una grabación de pantalla lo confirma en una parte de la imagen que permanece quieta: el fotograma completo no sirve, porque el agua del suelo, en un mapa que la tenga, cambia de dibujo 4 veces por segundo y otros coleccionistas pueden pasar caminando, así que un script toma muestras de un trozo de muro de edificio elegido de antemano, sin ningún tile animado ni ningún caminante encima en la grabación, y cuenta 9 niveles distintos a la salida y 9 a la entrada, cada uno mantenido 2 fotogramas, con un margen de uno a 60 hercios.17 En una grabación de pantalla de un warp, el mundo nunca se traslada en horizontal: ningún deslizamiento de navegación.
6. Háptica: tres eventos y un interruptor.
Cambio. Un solo CHHapticEngine propiedad del mundo, que se arranca cuando aparece y se detiene cuando desaparece, con un interruptor de Háptica en los ajustes que está activado por defecto y respeta el ajuste de háptica del sistema. Los patrones, guardados como archivos AHAP en el bundle:
| Evento | Patrón | Por qué |
|---|---|---|
| Paso rechazado (el choque) | un hapticTransient, intensidad 0,4, nitidez 0,2 |
el impacto de las HIG es «a thud when two heavy objects collide» (un golpe sordo cuando chocan dos objetos pesados);46 suave, porque el arte es suave |
| La puerta se abre | un hapticTransient a 0,3 de intensidad y 0,6 de nitidez en cada intervalo que cambia la imagen al abrirse, el dibujo entreabierto en el tick 5 y el abierto en el tick 10 (83 y 167 ms) |
coincidir con la animación a la que acompaña |
| Carta alzada (la carta 3D) | un hapticTransient a 0,7 y 0,8 cuando llega arriba |
el único objeto real del mundo |
| Pasos | ninguno por defecto | 3,75 pasos por segundo durante minutos es el abuso contra el que advierten las HIG |
Los valores de intensidad y nitidez son puntos de partida míos, no mediciones, y son para ajustarlos en un dispositivo. UIImpactFeedbackGenerator(style: .soft, view:), con prepare() llamado cuando empieza una caminata, es la alternativa allí donde capabilitiesForHardware() indique que Core Haptics no está disponible.
Por qué. La regla de la háptica; las pautas de Apple citadas en la sección 6.46565760
Comprobación. Un argumento -hapticLog registra cada evento. Una entrada por puerta con guion registra exactamente 2 eventos de puerta, en los ticks 5 y 10 de la cuenta del punto 5, y ninguno por paso; con el interruptor apagado, ninguno en absoluto.
7. Frecuencia de fotogramas: 60, de forma uniforme.
Cambio. Ninguno en el Info.plist: no añadir CADisableMinimumFrameDurationOnPhone, porque el mundo no gana nada con 120. Mantener la RealityView; el tick del punto 1 hace que el movimiento sea independiente de la frecuencia a la que renderice. Si alguna vez se añade un display link, fijar CAFrameRateRange(minimum: 30, maximum: 60, preferred: 60) para obtener la prioridad de Apple para juegos.76
Comprobaciones. Registrar un histograma de SceneEvents.Update.deltaTime durante una caminata de 60 segundos en un iPhone 18 Pro Max y en un iPhone Duo, en las pantallas exterior e interior. El encargo supone que la moda es 16,7 milisegundos; si es 8,3, el acumulador del punto 1 mantiene igualmente la caminata en 16 ticks por tile, y el registro lo demuestra. Las líneas dropped del registro de movimiento: ninguna en una caminata normal, a ninguna de las dos frecuencias.
8. Sacudida, solo para un momento.
Cambio. Una sacudida de cámara de texels enteros, 1 texel, 8 inversiones, separadas por 5 ticks, usada para un único evento que se la gane, por ejemplo la revelación de una carta rara en la plaza, junto con la háptica de carta alzada. No para puertas, pasos ni llegadas.
Por qué. La más común de las 24 sacudidas de Esmeralda, y su contención.29
Comprobación. El registro de movimiento muestra el desplazamiento de la cámara alternando entre más y menos 1 texel en los ticks 0, 5 y así hasta 35, y de vuelta a 0 en el tick 40.
Fuera de este encargo
- Suavizado o anticipación de la cámara en la cuadrícula. No hay saltos que suavizar, y Game Freak lanzó su cámara de anticipación desactivada.1223
- 120 hercios para el mundo. El objetivo es un ritmo uniforme a 60; 120 muestra cada pixel dos veces.6
- Háptica en cada paso por defecto.46
- Una cruceta fija en pantalla. Mi recomendación es tocar para caminar más un joystick flotante opcional.44
- Cualquier sonido o melodía de puertas o warps de los juegos. Los sonidos son expresión, y Kiradex necesita los suyos.
- Bicis, surf, hielo y corrientes. Los caminantes de la app caminan y corren y nada más, y las velocidades de arriba quedan registradas para cuando eso cambie.17
La pregunta abierta: la temporización en un dispositivo
Cada cifra del modelo en esta sección supone 1/60 o 1/120 de segundo constantes entre fotogramas. Si RealityView en un iPhone 18 Pro Max o en un iPhone Duo renderiza a 60 o a 120, y cuánto fluctúa su deltaTime, está sin medir; el histograma del punto 7 es lo primero que hay que ejecutar, y el punto 1 está escrito para que la respuesta no cambie nada de la caminata a ninguna frecuencia de la lista de iPhone de Apple.687
Puntos clave
Si dibujas el arte
- Dibuja la caminata para una distancia. La caminata de Esmeralda son dos zancadas y dos posturas de pie a lo largo de 32 pixeles, mantenidas durante fotogramas que coinciden con sus pasos, y los modos de andar más rápidos reutilizan los dibujos con duraciones más cortas en lugar de añadir fotogramas, así que cae una pisada por cada 16 pixeles a cualquier velocidad.192
- Un ciclo de seis dibujos está bien, siempre que el motor lo reproduzca a lo largo de una distancia fija; reproducido a una frecuencia fija contra una caminata cronometrada por separado, su cadencia se desacompasa del movimiento, 3,00 tiles por ciclo caminando y 4,50 corriendo en el modelo del nuestro.6
- La puerta de Esmeralda es cerrada más tres dibujos, cada uno en pantalla durante 84 milisegundos. Una hoja de tres, cerrada, entreabierta y abierta, como la de Kiradex, llena los cuatro intervalos manteniendo el dibujo abierto durante dos de ellos, o la forja dibuja un cuarto; en cualquier caso, dibuja el estado abierto como algo que valga la pena ver.12417
Si construyes el motor
- Haz avanzar el mundo con un tick fijo y desplazamientos de pixeles enteros que dividan el tile, lee la entrada en el tile y deja que el renderizador muestre el último tick. Di qué pasa con el atraso: el nuestro recupera hasta 8 ticks y descarta el resto, registrado. Las tablas de pasos de Esmeralda son el modelo: todas suman 16.216
- Fija la cámara al jugador en el mismo tick, con límites y desplazamientos de pixeles enteros, para que nada necesite redondeo después. Suavizar y luego redondear deja la cámara rezagada detrás de un jugador que camina y, en el modelo de nuestro código, la detiene entre 4 y 9 pixeles antes hasta la siguiente caminata.36
- En el iPhone, no pidas 120 hercios para el movimiento de pixeles. Apple prioriza 30 y 60 para los juegos, RealityKit suele renderizar a 60, y una caminata de pixeles enteros a 120 solo mantiene cada pixel durante dos refrescos.786
- Funde por escalones, no con una rampa suave. El fundido de puerta de Esmeralda son nueve niveles separados por dos dieciseisavos, su última mezcla en el fotograma 17 y el fundido terminado en el 22; nuestro velo de 18 ticks es una adaptación de esa rampa, no una copia.5
- Nunca animes la opacidad de la vista de SwiftUI que contiene una RealityView; pon un velo encima.22
Si diseñas el bucle de juego
- Una entrada por puerta es en Esmeralda una ceremonia de al menos 1,32 segundos sin entrada del jugador, y el paso forzado sobre el umbral es lo que hace que se lea como entrar caminando.525
- Girar en el sitio durante 8 fotogramas permite al jugador ponerse de cara a algo sin moverse; un choque de 32 fotogramas le dice que un paso fue rechazado.1
- En un teléfono, mi recomendación: tocar para caminar como opción predeterminada, tal como lo trae la versión móvil de Stardew, un joystick flotante como alternativa de precisión, como aconsejan las HIG, y ninguna cruceta fija.4044
- La háptica confirma eventos, no pasos, y viene con un interruptor.46
Preguntas frecuentes
¿A qué velocidad camina el jugador en Pokémon?
En Rojo, Cristal y Esmeralda, un paso caminando recorre una celda de 16 pixeles en 16 fotogramas a 59,7275 fotogramas por segundo: 268 milisegundos por celda, 3,73 celdas por segundo. Esmeralda mueve 1 pixel en cada fotograma; Rojo y Cristal mueven 2 pixeles cada dos fotogramas. Correr y hacer surf en Esmeralda, y las bicis en Rojo y Cristal, llevan 8 fotogramas por celda, 7,47 celdas por segundo.1419
¿Cuántos fotogramas tiene un ciclo de caminata en Pokémon Esmeralda?
Cuatro entradas en 32 fotogramas: un dibujo de zancada durante 8 fotogramas, el dibujo de pie durante 8, la otra zancada durante 8, de pie durante 8. Eso son dos celdas de caminata, así que cada paso muestra una zancada y una postura de pie, y las piernas se alternan paso a paso. La carrera es un ciclo de 16 fotogramas sobre dos celdas de 8 fotogramas.192
¿La cámara de Pokémon Esmeralda se queda atrás respecto al jugador?
No. La cámara de Esmeralda copia la posición del jugador y desplaza el mapa los mismos pixeles en el mismo fotograma, así que el jugador se queda fijo en pantalla mientras el mundo se mueve. Tampoco se detiene en el borde de un mapa: el exterior se dibuja con los tiles de borde del diseño. En el código existe una cámara de anticipación para la bici, pero nunca se activa.231112
¿Cuánto dura la transición de puerta y fundido en Pokémon Esmeralda?
La puerta se abre en cuatro dibujos de cinco fotogramas (335 milisegundos), el jugador da un paso forzado de 16 fotogramas hacia dentro, la puerta se cierra en 20 fotogramas, y la pantalla se funde en nueve niveles, la última mezcla en el fotograma 17 del fundido y el fundido terminado en su fotograma 22: al menos 79 fotogramas, 1,32 segundos, antes de que el mapa siguiente pueda empezar a cargarse. Al entrar en una cueva, la pantalla funde a blanco en lugar de a negro, y al salir de una, vuelve desde blanco.152527
¿Debe un juego de pixel art funcionar a 120 Hz en un iPhone con ProMotion?
No para el movimiento de pixeles enteros. Un mundo que se mueve un pixel por cada tick de 60 hercios solo muestra cada pixel durante dos refrescos a 120 hercios, y con duraciones desiguales a 80. El artículo de Apple sobre ProMotion dice que los juegos reciben «special priority to 30Hz and 60Hz» (prioridad especial a 30 Hz y 60 Hz), una app de iPhone debe fijar CADisableMinimumFrameDurationOnPhone para superar los 60, y RealityKit suele renderizar a 60. Simula con un tick fijo de 60 hercios y deja que la pantalla sea lo que sea.67168
¿Por qué patinan los pies de mi sprite al caminar?
Normalmente porque los dibujos de la caminata van con un reloj y el movimiento con otro, con duraciones que no coinciden. El código actual de Kiradex reproduce un ciclo de seis dibujos a 8 dibujos por segundo mientras camina 4 tiles por segundo, así que un ciclo cubre 3 tiles en lugar de 2, como muestra un modelo del código. Las portátiles cuentan ambas cosas en fotogramas y hacen que los dibujos de cada paso duren lo mismo que el paso; elegir el dibujo a partir de la distancia recorrida, que es lo que propongo para Kiradex, consigue la misma coincidencia sin un segundo reloj. En cualquiera de los dos casos la cadencia no puede separarse del movimiento; que un pie se quede bien apoyado depende también de los propios dibujos.619
¿Debe un juego de pixeles para móvil usar una cruceta virtual?
No como opción predeterminada, según mi lectura de las fuentes. El modo predeterminado de Stardew Valley en móvil es tocar para moverse, con un joystick invisible entre sus otros esquemas para tareas que exigen precisión; las HIG de Apple recomiendan tocar los objetos directamente y un joystick que aparezca «wherever the player lands their thumb instead of a static thumbstick position.» (allí donde el jugador apoye el pulgar, en lugar de en una posición fija).4044
Relacionados en este sitio: Mundos de pixel art en el iPhone es la primera guía de esta serie, con la caminata de paso y postura de pie de Esmeralda, la receta de RealityKit sobre la que corre este mundo y la lección de opacidad del visor de cartas; Personas en pixel art: personajes y un creador en el iPhone es la segunda, con la caminata de seis dibujos cuya temporización este artículo pasa de un reloj a una distancia; Estructuras en pixel art: casas, salones e interiores en el iPhone es la tercera, con las puertas, los warps y los pisos cuya temporización mide la sección 2; iPhone Duo para desarrolladores y Prepara tu app para el iPhone Duo tratan las dos pantallas y el pliegue que la cámara del encargo deja libre; El modelo mental espacial de RealityKit explica el modelo de entidades y sistemas que hay detrás de SceneEvents.Update.
Fuentes
-
Medición del autor, 4 de octubre de 2026:
measure_gen3_motion.py, en la carpeta de evidencias del autor para este artículo, ejecutado sobrepokeemeraldde pret en el commit731ad5b; analiza las tablas de funciones de paso y las duraciones deInitMoveInPlaceensrc/event_object_movement.c, las tablas de animación ensrc/data/object_events/object_event_anims.h, la regla del contador de retardo ensrc/sprite.cy los fotogramas de puerta ensrc/field_door.c, y convierte los fotogramas a 59,7275 hercios. Salida guardada comomeasure_gen3_motion.out.txta su lado (velocidades de 3,73, 7,47, 9,95, 14,93 y 29,86 celdas por segundo; caminata en el sitio de 32, 16, 8 y 4 fotogramas;sAnim_GoSouthde 32 fotogramas; dibujos de puerta mantenidos 5 actualizaciones, 83,7 ms, 335 ms para cuatro). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/event_object_movement.c(sStep1FuncsasStep8Funcsy el comentario «Over the course of the step animation, these sum to 16 pixels (one full metatile)»;sStepTimesyNpcTakeStep, que indexa la tabla de pasos consTimer, una entrada por fotograma;SetStepAnimHandleAlternation, que fija la animación del modo de andar y la alternancia al empezar un paso;CameraObject_UpdateMove), commit731ad5b, consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/event_object_movement.c. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/overworld.c(el orden deOverworldBasic,RunTasks(); AnimateSprites(); CameraUpdate(); UpdateCameraPanning();, seguido más adelante en la misma función porUpdatePaletteFade();VBlankCB_Fieldllamando aTransferPlttBuffer;InitPlayerAvatarantes deInitCameraUpdateCallback(gPlayerAvatar.spriteId)) ysrc/sprite.c(AnimateSpritesejecuta las callbacks en orden de ranura), consultados el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/overworld.c y https://github.com/pret/pokeemerald/blob/master/src/sprite.c. La conclusión de que ocurre en el mismo fotograma es una lectura del código por parte del autor, no una ejecución. ↩↩↩↩↩↩↩ -
Medición del autor, 4 de octubre de 2026:
measure_gen12_motion.py, en la carpeta de evidencias del autor para este artículo, ejecutado sobrepokeredde pret end2704a6(home/overworld.asm,home/fade.asm) ypokecrystalen5beda23(engine/overworld/events.asm,engine/overworld/map_objects.asm,data/maps/setup_scripts.asm,engine/tilesets/timeofday_pals.asm). Salida guardada comomeasure_gen12_motion.out.txt(Rojo: 16 px en 16 fotogramas, bici 8 fotogramas, fundido de warp 32 fotogramas; Cristal: caminata de 8 actualizaciones de 2 px, bici 4 de 4, paso lento 16 de 1 a 1,87 celdas por segundo, fundido de puerta de 8 fotogramas en cada sentido). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Medición del autor, 5 de octubre de 2026:
measure_gen3_fade.py, en la carpeta de evidencias del autor para este artículo, un port en Python deBeginNormalPaletteFade,UpdatePaletteFadecon su indicador de transferencia pendiente,UpdateNormalPaletteFadeeIsSoftwarePaletteFadeFinishingdepokeemerald/src/palette.cde pret en731ad5b, ejecutado según el calendario de sus llamadores: la tarea de la puerta arranca el fundido dentro deRunTasks(Task_DoDoorWarp,src/field_screen_effect.c);BeginNormalPaletteFadeactualiza una vez, copia el búfer a la memoria de paletas y borra el indicador;OverworldBasic(src/overworld.c) vuelve a actualizar en el mismo fotograma; cada fotograma posterior actualiza una vez, y la transferencia de VBlank borra el indicador. Los fotogramas se cuentan desde el de la tarea como 0. Supone que no había ningún fundido en marcha; con lluvia, nieve, niebla, sombra o sequía activas,FadeScreenensrc/field_weather.ccopia el búfer teñido por el clima y después llama a la mismaBeginNormalPaletteFade(el manejador propio del clima para el fundido de salida esDoNothing), así que el calendario del fundido de salida se mantiene, mientras que el fundido de entrada pasa por el código del clima y no se simuló; el fundido de entrada tras un warp arranca desde la callback de carga del mapa, cuyo primer fotograma no se rastreó, así que se dan los dos casos. Salida guardada comomeasure_gen3_fade.out.txt(niveles de 0 a 16 en pasos de 2; primera mezcla visible en el fotograma 1; última mezcla, las paletas de sprites en 16, en el fotograma 16, 285 ms contando el fotograma 0; inactivo en el fotograma 21, 368 ms;FadeInFromWhitecon retardo 8 inactivo en el fotograma 85 u 86 contando desde el fotograma 0, es decir, tras 86 u 87 fotogramas, 1.440 o 1.457 ms; entrada por puerta: abrir 20, paso 16 y cerrar 20 fotogramas, la última mezcla del fundido tras al menos 73 fotogramas, 1,22 s, yWarpIntoMapno antes del fotograma 79, 1,32 s, ya queTask_WarpAndLoadMapespera a que el fundido se vuelva inactivo). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Modelo del autor, 5 de octubre de 2026:
measure_kiradex_motion.py, en la carpeta de evidencias del autor para este artículo, leetilesPerSecond(4),framesPerSecond(8),runPace(1,6), el umbral de carrera (6 pasos) y elmin(1, dt * 6)de la cámara deKiradex/World/PlazaRig.swift,centredeKiradex/World/TileMap.swift, y los cicloswalkyrunde seis dibujos descripts/forge/rig.py, y modelaadvance,place,animateyfollowfotograma a fotograma con cada operaciónFloathecha en 32 bits (float32de numpy) en el orden del código y con el redondeo de Swift, que se aleja de cero en la mitad, a exactamente 1/60 y 1/120 s, para una caminata de 5 tiles, el ritmo de caminata mantenido durante 12 tiles y una carrera de 12 tiles, cada una seguida de 3 s de pie; el límite del mapa, el pliegue y el desplazamiento de los pies se dejan fuera. Es un modelo del código, no una captura de un dispositivo. Salida guardada comomeasure_kiradex_motion.out.txt. Caminata a 60 Hz: 15 fotogramas por tile, cada tile 14 fotogramas de 1 px y uno de 2; distancia de la cámara de 5, 6, 7, 8 y 9 px al final de los cinco primeros tiles, 13 desde el noveno cuando se mantiene el ritmo de caminata; posición en pantalla cambiando en 8 de los 73 fotogramas en movimiento de una caminata de 5 tiles posteriores al primero (la caminata del modelo tiene 74 fotogramas en movimiento, y el último fotograma del último tile cuenta como de pie); desfase en reposo de 4 px. A 120 Hz: 30 fotogramas por tile, 16 de 1 px y 14 de 0; distancia 9; reposo 9. Carrera a 60 Hz: 10 fotogramas por tile, 6,00 tiles por segundo, 4 fotogramas de 1 px y 6 de 2 por tile; distancia 13 desde el segundo tile; reposo 4. Carrera a 120 Hz: 19 fotogramas por tile, 6,32 tiles por segundo; distancia 9; reposo 9. Recorrido del ciclo a partir de las posiciones simuladas entre comienzos sucesivos de un ciclo a 60 Hz: 3,00 tiles caminando, 4,50 corriendo (4,80 a los 6,4 tiles por segundo nominales); a 120 Hz, 3,00 y 2,94 caminando, 4,75 corriendo. Pasos diagonales en el código actual: 22 fotogramas caminando y 14 corriendo a 60 Hz, 43 y 27 a 120. Frente al mismo modelo ejecutado con el reloj, las posiciones y la cámara en doble precisión, todas las posiciones y valores de cámara quedan iguales y difieren 9 dibujos de caminata a 60 Hz y 14 a 120, todos en fotogramas en los que el reloj multiplicado por 8 es un número entero (11 fotogramas así en 12 tiles de caminata a 60 Hz, 23 a 120). La propuesta: un tick fijo de 60 Hz mantiene cada pixel durante 1 refresco a 60 Hz, 2 refrescos para 59 de 61 posiciones a 120 Hz, y 1 o 2 refrescos, 42 y 19 posiciones, a 80 Hz. El acumulador: diez segundos de callbacks a cada una de las doce frecuencias del iPhone de 120 a 10 Hz ejecutan 600 ticks con un límite de atraso de 8 ticks y nada descartado, frente a 480 a 12 Hz y 400 a 10 Hz con un tope de 4 ticks por callback; tras un tirón de 1 s a 60 Hz, la regla de 8 ticks ejecuta 8 ticks y después 1 por callback, descartando 866,7 ms, mientras que un tope de 4 ticks que conserva el atraso ejecuta 4 ticks en 19 callbacks consecutivas. La aritmética de la cámara:fiten una vista de 393 por 852 pt a 3x da 7 pixeles de pantalla por texel y una vista de 168,43 por 365,14 pixeles del mundo, mitad de ancho 84,21, límites redondeados hacia dentro de 85 y el ancho del mapa menos 85; un cambio de pliegue de 0 a −37 en 9 niveles pasa por 0, −5, −9, −14, −19, −23, −28, −32, −37. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, «Optimizing iPhone and iPad apps to support ProMotion displays» (el rango de 10 a 120 Hz del iPhone y sus doce frecuencias; la lista de dispositivos;
CADisableMinimumFrameDurationOnPhone; la prioridad para juegos a 30 y 60 Hz; «Prepare your app to operate at any refresh rate»;targetTimestamp), consultado el 4 de octubre de 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» a 60 fotogramas por segundo), consultado el 4 de octubre de 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) ysrc/sprite.c(animDelayCountercargado con la duración del fotograma menos uno, descontado porContinueAnim, y el fotograma siguiente tomado cuando llega a cero), consultados el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/data/object_events/object_event_anims.h y https://github.com/pret/pokeemerald/blob/master/src/sprite.c. ↩↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/field_player_avatar.c(PlayerWalkNormal,PlayerRun,PlayerWalkFastcon el comentario «same speed as running»,CheckMovementInputNotOnBikeyTURN_DIRECTION,PlayerTurnInPlace, el choque contra la pared), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/field_player_avatar.c. ↩↩↩↩↩ -
pret,
pokeemerald/src/fieldmap.c(GetBorderBlockAt: los metatiles de borde de 2 por 2 del diseño, marcados comoMAPGRID_IMPASSABLE), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/fieldmap.c. ↩↩↩ -
pret,
pokeemerald/src/field_camera.c(CameraUpdate;CameraPanningCB_PanAhead, que muevesVerticalCameraPande 2 en 2 hacia 72 o hacia menos 8 desde un reposo de 32, protegido porgUnusedBikeCameraAheadPanbacky con el comentario «this code is never reached»), consultado el 4 de octubre de 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 de octubre de 2026 (el fotograma de puerta de Esmeralda mantenido cinco actualizaciones, unos 84 milisegundos;
PlayerStepOutFromDoorde Rojo; la sacudida del ascensor de Esmeralda; la puerta de 70 milisegundos y el warp de 220 milisegundos de Kiradex listados entre las carencias), https://blakecrosley.com/blog/pixel-art-structures-on-iphone. ↩↩↩↩↩↩ -
Notas de investigación del autor para el artículo sobre estructuras, repositorio de Kiradex (privado),
docs/research/structures/01-structures-in-the-canon.md, 3 de octubre de 2026, que daban los fotogramas de puerta de Esmeralda como «4 ticks each (16 frames, about 0.27 s at 59.7 Hz)»; corregidas por la lectura deAnimateDoorFrameenfield_door.cque se da en este artículo. ↩↩ -
Apple Developer Documentation, «preferredFrameRateRange» (
CADisplayLink; iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0, visionOS 1.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/quartzcore/cadisplaylink/preferredframeraterange. ↩↩↩ -
Apple Developer Documentation, «CADisableMinimumFrameDurationOnPhone» (clave de Information Property List; iOS 15.0, iPadOS 15.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/bundleresources/information-property-list/cadisableminimumframedurationonphone. ↩↩↩
-
Lectura del autor del repositorio de Kiradex (privado) en el commit
b1b78b1, 4 de octubre de 2026, solo lectura:Kiradex/World/PlazaRig.swift(tilesPerSecond,framesPerSecond,runPace,walk(to:)y su regla de carrerasteps.count >= 6,advance, cuyo paso de avance esdt * Self.tilesPerSecond * (walker.running ? Self.runPace : 1) / length,animate,place,fit, que divide los pixeles de pantalla de la vista entre unpixelScaleentero,follow, que convierte los puntos del pliegue mediantedisplayScale / pixelScale, suaviza conmin(1, dt * 6)y redondea después, y cuyo clamp deja que la cámara pase del borde del mapa hacia el bosque solo cuando el Duo está medio abierto, enbeyondMargin, 12 tiles, mientras quebuildcoloca el bosque sea cual sea el pliegue,ripple, que cambia el dibujo del agua del suelo 4 veces por segundo en un mapa con agua,openDoory su comentario «at about Emerald’s four ticks a frame»),Kiradex/World/PlazaStage.swift(el manejo de toques, el retardo de warp de 220 milisegundos, la suscripción aSceneEvents.Update, el cambio de piso mediante.id),Kiradex/World/TileMap.swift(elpathde ocho direcciones, que toma el tile abierto con el menor coste hasta el momento, suma 1 o √2 por paso y no usa ninguna estimación de la distancia a la meta, y cuyos pasos diagonales se omiten salvo que las dos celdas laterales sean transitables),Kiradex/World/WorldMap.swift(la hoja de puerta de un warp, «closed, half open, open»),openDoorcargando esa hoja conSpriteSheet.bundled(name, columns: 3, rows: 1)y mostrando la columna 1 y después la columna 2,scripts/forge/kit.py(door_sheet(), «The three frames side by side, 48 × 32»),scripts/forge/town.pyyscripts/forge/buildings.py(cada warp de puerta del pueblo está en la celda de la puerta en la fila inferior de un edificio, y las celdas del edificio a la izquierda, a la derecha y encima de cada puerta están bloqueadas; comprobado para las siete puertas del pueblo en eltown.jsonpublicado),Kiradex/Views/Card/CardViewer.swift(el.easeOut(duration: 0.25)del velo) yscripts/forge/rig.py(los ciclos). La ausencia deUIImpactFeedbackGenerator,sensoryFeedback,CHHaptic,CADisplayLink,preferredFrameRateRangeyCADisableMinimumFrameDurationOnPhonees una búsqueda sobreKiradex/yproject.ymlel 4 de octubre de 2026, que no encontró ninguno; la misma búsqueda no encuentra ningúnMTKView,MTLRenderCommandEncoderniTouchControllerenKiradex/, así que el mundo no tiene un pase de renderizado propio. El retroceso ante un toque a mitad de paso se lee dewalk(to:), no se capturó. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
pret, clones superficiales de
pokered(commitd2704a6, 22 de septiembre de 2026),pokecrystal(5beda23, 29 de septiembre de 2026) ypokeemerald(731ad5b, 1 de octubre de 2026), las descompilaciones comunitarias de los juegos publicados, https://github.com/pret. ↩ -
Martin Korth, GBATEK, «LCD Dimensions and Timings» (280.896 ciclos y 16,743 ms por fotograma, «ca. 59.737 Hz»), consultado el 4 de octubre de 2026, https://problemkaputt.de/gbatek-lcd-dimensions-and-timings.htm; Pan Docs, «Rendering» («One frame: 70224 dots @ 59.7 fps»), consultado el 4 de octubre de 2026, https://gbdev.io/pandocs/Rendering.html. La cifra de 59,7275 es un cálculo del autor: 2^24 / 280.896, y 4.194.304 / 70.224. ↩↩↩↩↩
-
pret,
pokeemerald/src/bike.c(sMachBikeSpeedCallbacks,bikeFrameCountercon tope en 2,AcroBikeTransition_Movingllamando aPlayerRideWaterCurrent, y la única asignacióngUnusedBikeCameraAheadPanback = FALSE), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/bike.c. ↩↩↩ -
Figuras dibujadas por el script del autor
make_figures.py(consvgkit.py), 4 de octubre de 2026, solo a partir de datos en disco: las velocidades del canon demeasure_gen3_motion.out.txtymeasure_gen12_motion.out.txt; la caminata y la cámara de Kiradex del propiosimulate()demeasure_kiradex_motion.py(el primer segundo de una caminata de 5 tiles; la cámara para una caminata de 5 tiles a 60 y 120 Hz y una carrera de 12 tiles a 60 Hz), y el bucle de ticks de la propuesta del mismo script; el fundido de Esmeralda delrun()demeasure_gen3_fade.py, el port con el calendario de sus llamadores, y el fundido y la carga de mapa más temprana del gráfico de la puerta del mismo; las temporizaciones de puerta de Kiradex (70 ms, 1.400 ms, 220 ms) leídas dePlazaRig.swiftyPlazaStage.swiftenb1b78b1. No se usa arte de ningún juego. ↩↩↩↩↩ -
Blake Crosley, «Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew,» blakecrosley.com, 3 de octubre de 2026 (la caminata de Esmeralda «step, stand, step, stand at eight ticks each»; el «stand, step, stand, step-flipped» de Rojo; Celeste a 320 por 180 multiplicado por seis; el motor de RealityKit; posiciones «rounded to whole world units each frame, after easing»; la lección de opacidad del visor de cartas), 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 de mayo de 2015, una versión modificada de su charla en el Independent Games Summit, GDC 2015 (position-locking, edge-snapping, camera-window, lerp-smoothing, target-focus, dual-forward-focus; las citas), consultado el 4 de octubre de 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, que dibuja con el contador en 0 y avanza cuando el contador iguala el tiempo del fotograma), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/field_door.c. ↩↩↩↩ -
pret,
pokeemerald/src/field_screen_effect.c(los estados deTask_DoDoorWarp, el quinto llamando aWarpFadeOutScreeny pasando la tarea aTask_WarpAndLoadMap, que espera aPaletteFadeActive(), es decirgPaletteFade.active, y aBGMusicStopped()antes deWarpIntoMap;Task_ExitDoor,WarpFadeOutScreenllamando aGetMapPairFadeToType,WarpFadeInScreenllamando aGetMapPairFadeFromType, yFadeInFromWhiteconFadeScreen(FADE_FROM_WHITE, 8)), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/field_screen_effect.c. ↩↩↩↩↩↩↩↩↩↩ -
pret,
pokeemerald/src/palette.c(BeginNormalPaletteFadecondeltaY = 2, su propia llamada aUpdatePaletteFade, suCpuCopy32a la memoria de paletas ysPlttBufferTransferPending = FALSE;UpdatePaletteFade, que retorna de inmediato mientras ese indicador está activo y lo activa a partir degPaletteFade_selectedPalettesdespués de cada actualización;TransferPlttBuffer, que lo borra;UpdateNormalPaletteFade;IsSoftwarePaletteFadeFinishing), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/palette.c. ↩↩ -
pret,
pokeemerald/src/fldeff_flash.c(sTransitionTypescon sus 16 filas hacia y desdeMAP_TYPE_UNDERGROUND, cada una con un indicador de entrada, un indicador de salida y una rutina de transición;GetMapPairFadeToTypedevolviendo el indicador de entrada de una fila yGetMapPairFadeFromTypesu indicador de salida), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/fldeff_flash.c. ↩↩↩ -
pret,
pokeemerald/src/field_specials.c(ShakeCamera, que lee el paneo vertical, el paneo horizontal, el número de sacudidas y el retardo deVAR_0x8004aVAR_0x8007, y niega el paneo en cada sacudida), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/field_specials.c. ↩ -
Recuento del autor, 4 de octubre de 2026:
measure_gen3_shake.py, en la carpeta de evidencias del autor para este artículo, lee cada llamadaspecial ShakeCameray los cuatro valoressetvarque la preceden endata/**/*.incdepokeemeraldde pret en731ad5b. Salida guardada comomeasure_gen3_shake.out.txt(24 llamadas en 9 archivos, los ocho conjuntos de parámetros y sus recuentos de la tabla). ↩↩↩↩↩↩ -
pret,
pokered/home/overworld.asm(OverworldLoop,OverworldLoopLessDelay,wWalkCounter,AdvancePlayerSprite,DoBikeSpeedup, y el comentario del giro de 180 grados), commitd2704a6, consultado el 4 de octubre de 2026, https://github.com/pret/pokered/blob/master/home/overworld.asm. El comportamiento del giro se lee del código, no se ejecutó en un emulador. ↩↩↩↩↩↩ -
pret,
pokered/engine/overworld/movement.asm(UpdatePlayerSprite, el contador interno de la animación que avanza el dibujo al llegar a 4), consultado el 4 de octubre de 2026, https://github.com/pret/pokered/blob/master/engine/overworld/movement.asm. ↩↩ -
pret,
pokered/home/fade.asm(GBFadeOutToBlack) yhome/overworld.asm(PlayMapChangeSound,SFX_GO_INSIDEpara el tile de puerta$0b, y si noSFX_GO_OUTSIDE), consultado el 4 de octubre de 2026, https://github.com/pret/pokered/blob/master/home/fade.asm. ↩ -
pret,
pokecrystal/engine/overworld/events.asm(MaxOverworldDelay: db 2) yengine/overworld/map_objects.asm(StepVectors;StepFunction_Turn, cuyos.init1,.step1,.init2y.step2caen unos en otros, yObjectStep_AnonJumptable), commit5beda23, consultados el 4 de octubre de 2026, https://github.com/pret/pokecrystal/blob/master/engine/overworld/events.asm y https://github.com/pret/pokecrystal/blob/master/engine/overworld/map_objects.asm. Las tres actualizaciones del giro son una reproducción de la rutina instrucción por instrucción hecha por el autor, no una ejecución en un emulador. ↩↩↩↩↩ -
pret,
pokecrystal/data/maps/setup_scripts.asm(MapSetupScript_Doorempezando conFadeOutToWhite,MapSetupScript_Warpterminando conFadeInFromWhite) yengine/tilesets/timeofday_pals.asm, consultados el 4 de octubre de 2026, https://github.com/pret/pokecrystal/blob/master/data/maps/setup_scripts.asm y 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 de octubre de 2026 (la caminata de seis fotogramas; «the walk at eight frames a second» en la plaza; en «Desde la publicación», la figura de 26 pixeles sustituida en la compilación 34 de TestFlight por una figura de 30 pixeles en una celda de 32 por 40, que
scripts/forge/rig.pyenb1b78b1fija comoCELL_W, CELL_H = 32, 40), https://blakecrosley.com/blog/pixel-art-characters-on-iphone. ↩↩↩ -
Los desarrolladores de Celeste, el repositorio
NoelFB/Celesteen GitHub,Source/Player/Player.cs(la actualización de la cámara bajo «Camera (lerp by distance using delta-time)» y el getterCameraTarget), consultado el 4 de octubre de 2026, https://raw.githubusercontent.com/NoelFB/Celeste/master/Source/Player/Player.cs. ↩↩↩↩ -
Los desarrolladores de Celeste, repositorio
NoelFB/Celeste,README.md(los archivos de clase publicados «as a learning resource and for general interest»; la licencia MIT aplicada solo a ese código), consultado el 4 de octubre de 2026, https://raw.githubusercontent.com/NoelFB/Celeste/master/README.md. ↩ -
Stardew Valley Wiki, «Speed» (la velocidad base del jugador: 2 caminando, 5 corriendo, 6,6 a caballo, 7 tras una zanahoria), consultado el 4 de octubre de 2026, https://stardewvalleywiki.com/Speed. ↩
-
Notas de investigación del autor para este artículo, recopiladas el 4 de octubre de 2026, que registran lo que se buscó y no se encontró: una fuente primaria sobre las cámaras de Sea of Stars, Eastward o CrossCode; una charla o un artículo de Maddy Thorson sobre cámaras; el esquema táctil exacto de los Pixel Remaster; el movimiento de Terraria en móvil en https://terraria.wiki.gg/wiki/Mobile_version; y
developer.apple.com/documentation/touchcontrols, que devolvió un 404. ↩↩↩ -
Stardew Valley Wiki, «Mobile Controls» (los esquemas; «Tap-to-move & Auto-Attack» como predeterminado; las citas sobre tocar para moverse, seguir el toque, el joystick invisible y los límites de los controles predeterminados), consultado el 4 de octubre de 2026, https://stardewvalleywiki.com/Mobile_Controls. ↩↩↩↩↩↩↩
-
Jared Nelson, «’Stardew Valley’ is Getting A TON of New Control Options in the Next Update,» TouchArcade, 1 de noviembre de 2018, consultado el 4 de octubre de 2026, https://toucharcade.com/2018/11/01/stardew-valley-mobile-controls-update/. ↩
-
App Store, «FINAL FANTASY» de SQUARE ENIX, historial de versiones (versión 1.2.0, 03/11/2025, y la nota sobre el movimiento por toque), consultado el 4 de octubre de 2026, https://apps.apple.com/us/app/final-fantasy/id1492041278. ↩↩
-
Mikhail Madnani, TouchArcade, 30 de enero de 2024, sobre la actualización de Final Fantasy Pixel Remaster que llevó la compatibilidad con mandos a móvil, consultado el 4 de octubre de 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» (buenas prácticas de control táctil; entrada del registro de cambios del 9 de junio de 2025), consultado el 4 de octubre de 2026, https://developer.apple.com/design/human-interface-guidelines/game-controls. ↩↩↩↩↩↩↩
-
Apple, sesión 209 de la WWDC25 (el framework Touch Controls; «the vast majority of players won’t have a controller available»; «integrates directly with Metal»), transcripción consultada el 4 de octubre de 2026, https://developer.apple.com/videos/play/wwdc2025/209/. ↩↩↩
-
Apple Human Interface Guidelines, «Playing haptics» (buenas prácticas, háptica personalizada y la categoría de impacto de iOS; las citas), consultado el 4 de octubre de 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), consultado el 4 de octubre de 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), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/quartzcore/cadisplaylink/targettimestamp. ↩ -
Apple, sesión 10147 de la WWDC21, «Optimize for variable refresh rate displays» (las pantallas Adaptive-Sync en el Mac y ProMotion en el iPad Pro; las citas), transcripción consultada el 4 de octubre de 2026, https://developer.apple.com/videos/play/wwdc2021/10147/. ↩↩↩
-
Apple Developer Documentation, «SceneEvents.Update» (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/realitykit/sceneevents/update. ↩
-
Apple Developer Documentation, «deltaTime» (
SceneEvents.Update; iOS 13.0), consultado el 4 de octubre de 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; código por fotograma mediante un
SystemoSceneEvents.Update), consultado el 4 de octubre de 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), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/corehaptics/chhapticpattern; página del framework Core Haptics, https://developer.apple.com/documentation/corehaptics. ↩
-
Apple Developer Documentation, «CHHapticEvent» y «CHHapticEvent.EventType» (
hapticTransient,hapticContinuous; iOS 13.0), consultados el 4 de octubre de 2026, https://developer.apple.com/documentation/corehaptics/chhapticevent y https://developer.apple.com/documentation/corehaptics/chhapticevent/eventtype. ↩ -
Apple Developer Documentation, «CHHapticEvent.ParameterID» (iOS 13.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/corehaptics/chhapticevent/parameterid. ↩
-
Apple Developer Documentation, «CHHapticEngine» (iOS 13.0;
capabilitiesForHardware()), consultado el 4 de octubre de 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:)en «Initializing the feedback generator», einit(style:)entre lo obsoleto), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator. ↩↩ -
Apple Developer Documentation, «UIImpactFeedbackGenerator.FeedbackStyle» (iOS 10.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle. ↩
-
Apple Developer Documentation, «impactOccurred(intensity:)» (iOS 13.0, iPadOS 13.0, Mac Catalyst 13.1), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/impactoccurred(intensity:). ↩
-
Apple Developer Documentation, «prepare()» (
UIFeedbackGenerator; iOS 10.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/uikit/uifeedbackgenerator/prepare(). ↩↩ -
Apple Developer Documentation, «sensoryFeedback(:trigger:)» y «SensoryFeedback» (SwiftUI; iOS 17.0, iPadOS 17.0, Mac Catalyst 17.0, visionOS 26.0;
impact(weight:intensity:)), consultados el 4 de octubre de 2026, https://developer.apple.com/documentation/swiftui/view/sensoryfeedback(:trigger:) y 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), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/touchcontroller. ↩↩↩↩
-
Apple Developer Documentation, «TCDirectionPad» (iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/touchcontroller/tcdirectionpad. ↩
-
Apple Developer Documentation, «GCVirtualController» (Game Controller; iOS 15.0, iPadOS 15.0, Mac Catalyst 15.0), consultado el 4 de octubre de 2026, https://developer.apple.com/documentation/gamecontroller/gcvirtualcontroller. ↩
-
pret,
pokeemerald/src/field_control_avatar.c(TryDoorWarp, llamado con la celda que está delante del jugador mientras la dirección mantenida coincide con la orientación, que solo hace el warp cuando esa dirección es el norte y la celda es un warp de puerta), consultado el 4 de octubre de 2026, https://github.com/pret/pokeemerald/blob/master/src/field_control_avatar.c. ↩