A mesma nave em cada tela

Uma classificação diária só é justa se voar o mesmo trecho de caverna for igualmente difícil num celular pequeno e antigo, num tablet de 144 Hz e num navegador de desktop. Eis o que isso exigiu, e o que eu não consegui igualar.

Devlog · 20 de setembro de 2026 · todos os posts

O post sobre a caverna diária trata de tornar o mundo idêntico para cada jogador. Este trata da outra metade da justiça: fazer a nave se comportar de forma idêntica. Há duas formas separadas de um dispositivo mudar como o jogo se joga, e precisam de duas correções separadas. Uma é a frequência com que a tela atualiza. A outra é o tamanho da tela.

Problema um: a taxa de atualização

A nave tem uma posição e uma velocidade. A cada quadro, a gravidade ou o empuxo mudam a velocidade, e a velocidade muda a posição. A forma óbvia de escrever isso é:

vy += accel * dt;     // update velocity
py += vy * dt;        // then move by the NEW velocity

Parece inofensivo, mas está sutilmente errado. A segunda linha finge que a nave já passou o quadro inteiro viajando na sua velocidade de fim de quadro. Se a nave está acelerando, isso ultrapassa em metade de accel × dt² a cada quadro. O problema é que dt, a duração do quadro, é diferente em cada dispositivo, então o excesso também é diferente, e uma nave voava numa trajetória ligeiramente diferente em cada taxa de atualização.

Medi isso com um empuxo mantido por meio segundo. A resposta exata é 225 pixels. O integrador antigo dava 228 a 144 Hz, 232 a 60 Hz e 240 a 30 Hz. Três dispositivos, três distâncias diferentes, com a mesma entrada.

A correção é a regra do trapézio, que usa a média da velocidade antes e depois do quadro:

const vyPrev = vy;
vy += accel * dt;
py += (vyPrev + vy) * 0.5 * dt;   // average speed over the frame

Para uma aceleração constante isso é exato, seja qual for a duração do quadro. Reproduzir um mesmo padrão de entrada agora dá uma dispersão de 0,000 pixels entre 12 e 144 Hz. Antes era 4,1 pixels. Um teste verifica ambas as metades: que o novo integrador concorda em todas as taxas de atualização e que o antigo realmente divergia, então se alguém reverter isso, o teste falha em vez de a classificação derivar silenciosamente.

Duas ressalvas honestas

Isso não fechou a diferença de pontuação entre taxas de atualização. Eu esperava isso. Um piloto simulado ainda pontuava cerca de um terço a mais a 144 Hz do que a 60 Hz depois disso. Essa diferença não é física. É resolução de entrada: um polegar só pode mudar de pressionado para solto no limite de um quadro, então uma tela mais rápida realmente dá um controle mais fino. Nenhum integrador pode consertar isso, e um bot que alterna sua entrada a cada quadro exagera isso em comparação com um humano. A metade física agora é exata, e o resto é uma propriedade do hardware.

É um nerf bem leve a 60 Hz. Uma subida de meio segundo agora cobre 225 pixels em vez de 232. Deixei isso sem compensar. Se um teste em dispositivo real algum dia achasse a nave lenta, a alavanca é a constante de empuxo, cerca de 3% a mais restauraria a antiga manobra de meio segundo. Este é um bom momento para dizer que o empuxo já foi mudado duas vezes por feedback de jogadores, e essa história é o motivo pelo qual sou cauteloso ao mexer nele.

Problema dois: a altura da tela

Gravidade em pixels por segundo ao quadrado significa coisas bem diferentes num celular de 375 pontos de altura e num tablet de 520 pontos de altura. Se as constantes fossem as mesmas, a mesma subida cruzaria uma fração muito menor da tela no tablet, e o jogo pareceria mais pesado.

Então as constantes são definidas em relação a uma altura de referência de 440 pontos, a altura em paisagem do maior iPhone, onde a sensação do jogo foi ajustada e testada. Em qualquer outra tela, gravidade, empuxo e velocidade terminal são todos multiplicados por H / 440. Como a metade da folga do corredor também se escala com a altura, cada trajetória é geometricamente semelhante à de referência: a mesma fração do corredor cruzada por segundo, o mesmo tempo para ir de uma parede à outra, a mesma resposta percebida. Uma tela maior recebe uma aceleração mais acentuada em pixels, e uma menor uma mais suave, por design.

Isso vem com uma regra para todo código futuro: qualquer coisa que raciocine sobre movimento vertical deve ser baseada em proporções. Uma fração da velocidade terminal, uma fração da altura. Nunca uma constante bruta em pixels por segundo comparada com a velocidade da nave, porque esse número só está correto em um dispositivo. Se um literal for inevitável, ele é multiplicado pela mesma escala.

A versão web segue a mesma regra. A build do navegador limita seu canvas a 956 por 440, a mesma pegada daquele telefone de referência, então o jogo num navegador é uma cópia em pixels e física exata dele. Uma build web anterior tinha seu próprio multiplicador de sensação separado, que existia só para combater a moleza causada por um limite diferente, e foi removido assim que o limite ficou alinhado.

Problema três: a largura da tela

Altura é o eixo vertical. Largura é o horizontal, e é mais sutil. Obstáculos são espaçados em distâncias fixas no mundo. A caverna rola numa velocidade que inclui um fator para a largura da tela. Então numa tela mais larga os mesmos obstáculos passam mais rápido em termos de tela, e o tempo que você tem para reagir a cada um cai proporcionalmente à largura.

Isso era um vazamento real. Um tablet Android grande com cerca de 1280 pontos de largura enfrentava a caverna compartilhada cerca de 1,75 vezes mais rápido que um celular pequeno. Então a largura é limitada a 956 pontos em cada plataforma, a largura do iPhone mais largo em paisagem. Um dispositivo mais largo que isso ganha barras pretas à esquerda e à direita. É um no-op nos iPhones e limita só tablets Android grandes e navegadores de desktop.

O limite tem um resíduo que não corrigi. O raio de colisão do jogador se escala com a largura enquanto o corredor se escala com a altura, então num tablet mais quadrado a nave é um pouco menor em relação à folga do que num celular, cerca de 20% mais indulgente. Corrigir isso significa mudar a hitbox, o que é uma mudança de sensação que precisa do seu próprio teste de jogo, e erra na direção indulgente, então pode esperar.

Testar a justiça em vez de afirmá-la

Nada disso é visível jogando. Você não consegue sentir alguns pixels de desvio, e ninguém joga a mesma caverna em seis dispositivos ao mesmo tempo. Então cada afirmação tem um teste que a reproduz: o teste de física integra uma entrada fixa em taxas de atualização de 12 a 144 Hz, e um teste de caverna reproduz os geradores reais em seis tamanhos de dispositivo e compara o resultado byte a byte. Uma propriedade de justiça que você não testa já quebrou silenciosamente, na minha experiência.

Resumindo, o TUNL é justo entre telas de três formas separadas (taxa de atualização, altura e largura), cada uma com uma causa diferente, uma correção diferente e um resíduo diferente. Você pode ver o resultado por si mesmo, em qualquer dispositivo que tenha, na caverna de hoje.

Jogue a caverna de hoje