Dificuldade progressiva em JavaScript se programa com três peças: um número que mede o quanto o jogador avançou, funções que traduzem esse número em parâmetros do jogo — de quanto em quanto tempo nasce um inimigo, a que velocidade ele vem, quanta vida tem — e um teto para cada parâmetro. Sem a primeira peça, cada sistema cresce no próprio ritmo e a curva vira um acaso; sem a última, o jogo fica impossível em algum ponto.

Este tutorial monta essas peças com o código do Navistron, um jogo de nave em canvas e JavaScript puro em que meteoros caem do topo da tela. No começo cai menos de um meteoro por segundo; no limite, mais de cinco, e quase quatro vezes mais rápidos. As fórmulas abaixo são as do arquivo do jogo, conferidas em 04/10/2026, e todas as contas foram rodadas em script. O lado de quem joga está em como a dificuldade aumenta no Navistron; aqui o foco é como escrever isso no seu próprio jogo.

1. Um único número de dificuldade

O primeiro passo é ter uma função que responde, a qualquer momento, à pergunta "quão difícil está agora?". No Navistron ela combina dois eixos: o tier da nave, que sobe em degraus a cada 5 boosts coletados, e a pontuação, que cresce de forma contínua.

const TIER_DIFF = [1, 1.8, 3.0, 4.8, 7.0, 10.0, 14.0];
function getDiff() { return TIER_DIFF[Math.min(powerTier, TIER_DIFF.length - 1)] * (1 + score * 0.0007); }

A estrutura é multiplicativa: o tier define a base e a pontuação multiplica. No Tier I, com 0 pontos, a dificuldade é 1; no Tier III, com 10 mil pontos, é 3 × 8 = 24. O fator 0,0007 por ponto significa +70% a cada mil pontos, sobre a base do tier. Somar em vez de multiplicar deixaria o tier com peso cada vez menor ao longo da partida; multiplicando, quem sobe de tier mais cedo enfrenta uma curva inteira mais íngreme, e é isso que dá peso a cada boost coletado.

A função também não guarda estado: lê o tier e a pontuação do momento e devolve um número, então pode ser chamada em qualquer lugar — spawn, velocidade, vida dos inimigos, mísseis — e testada isoladamente. No Navistron o tier só sobe durante a partida (a função tierUp() é chamada a cada 5 boosts e nunca desce), e a pontuação e o tier voltam a zero quando uma nova partida começa.

2. Intervalo de spawn: dividir pela raiz da dificuldade

O ritmo em que os inimigos nascem é o parâmetro que o jogador mais sente. No Navistron, um temporizador conta para trás com o tempo real do quadro e, ao zerar, cria meteoros e sorteia o próximo intervalo:

meteorTimer -= dt;
meteorTimer = Math.max(0.2, 1.2 / Math.sqrt(diff)) + Math.random() * 0.15;

Cada parte da segunda linha tem um papel:

  • 1.2 / Math.sqrt(diff): o intervalo base cai com a raiz da dificuldade. Quadruplicar a dificuldade divide o intervalo por dois, não por quatro: a curva muda rápido no começo e depois desacelera, o que dá tempo de adaptação a cada novo ritmo.
  • Math.max(0.2, ...): o teto de ritmo. Nenhuma dificuldade faz nascer meteoro a menos de 0,2 segundo do anterior. É ele que transforma "fica difícil" em "fica difícil até aqui".
  • Math.random() * 0.15: um sorteio de 0 a 0,15 segundo somado ao intervalo, 0,075 em média. Sem ele, os meteoros cairiam num metrônomo, e o jogador passaria a jogar o ritmo em vez de olhar para a tela.

Repare também no meteorTimer -= dt: o temporizador desconta o tempo real de cada quadro (dt, em segundos), e não um quadro por vez. Assim a chuva tem o mesmo ritmo a 60 e a 144 quadros por segundo — o tema do artigo sobre performance em jogos web com delta time.

3. Mais de um inimigo por vez, em degraus

Encurtar o intervalo tem limite, então o Navistron usa uma segunda alavanca: a chance de nascerem dois meteoros de uma vez.

const count = diff > 8 ? (Math.random() < .45 ? 2 : 1) : diff > 4 ? (Math.random() < .25 ? 2 : 1) : 1;

Até a dificuldade 4 nasce sempre um; acima de 4, dois em 25% das vezes; acima de 8, em 45%. Em média, 1; 1,25; e 1,45 meteoro por vez. O efeito é diferente de só acelerar: em vez de um fluxo mais denso e regular, aparecem picos de pressão, quando dois chegam juntos. Degraus assim são fáceis de ajustar, porque cada um mexe numa faixa só da curva.

Juntando as duas alavancas, a taxa média de meteoros por segundo é a média de meteoros por vez dividida pelo intervalo médio, com o sorteio incluído:

DificuldadeIntervalo baseMeteoros por vezMeteoros por segundo
11,20 s10,78
20,85 s11,08
40,60 s11,48
80,42 s1,252,50
160,30 s1,453,87
240,24 s1,454,53
36 ou mais0,20 s (teto)1,455,27

4. Velocidade com teto

A velocidade dos meteoros cresce de forma linear, mas com limite:

const speed = ((big ? 60 : 90) + Math.random() * (big ? 60 : 110)) * Math.min(1 + (diff - 1) * 0.20, 3.8);

A primeira parte sorteia a velocidade base (meteoros grandes são mais lentos); a segunda multiplica por um fator que sobe 0,2 a cada ponto de dificuldade e para em 3,8. O termo linear só funciona por causa do teto: sem ele, na dificuldade 65,8 — a do recorde do jogo, na tabela abaixo — os meteoros cairiam 14 vezes mais rápido que no começo. Com o teto, o fator chega a 3,8 na dificuldade 15 e fica ali.

Vale anotar em que ponto cada teto é alcançado em cada tier, porque é ali que a sensação do jogo muda. No Tier III do Navistron, a velocidade chega ao máximo aos 5.715 pontos e o ritmo de spawn, aos 15.715. No Tier II, aos 10.477 e aos 27.143.

5. A curva inteira numa tabela

Com as fórmulas prontas, a curva cabe numa tabela. Valores para alguns momentos de uma partida do Navistron:

MomentoDificuldadeIntervalo baseMeteoros por segundoVelocidade
Tier I, começo1,01,20 s0,781,0×
Tier I, 1.000 pontos1,70,92 s1,001,1×
Tier II, 2.000 pontos4,30,58 s1,921,7×
Tier II, 5.000 pontos8,10,42 s2,922,4×
Tier III, 5.000 pontos13,50,33 s3,613,5×
Tier III, 10.000 pontos24,00,24 s4,533,8×
Tier III, 15.715 pontos36,00,20 s5,273,8×
Recorde: 29.901 pontos, Tier III65,80,20 s5,273,8×

Três leituras saem dessa tabela. A chuva fica cerca de 6,7 vezes mais densa do começo ao limite. A velocidade bate no teto antes do ritmo de spawn, então o fim de jogo é "mais meteoros na mesma velocidade" — o que o artigo sobre o que muda depois dos 15 mil pontos mostra do ponto de vista de quem joga. E a mesma pontuação vale curvas diferentes conforme o tier: aos 5 mil pontos, quem está no Tier III enfrenta 3,6 meteoros por segundo; no Tier II, 2,9.

A última linha é real: o recorde do jogo, 29.901 pontos do piloto GUI em 29/09/2026, terminou no Tier III, com a dificuldade em 65,8 — bem dentro do teto. O ranking e a telemetria pública mostram as partidas que chegaram lá, e as 50 melhores partidas do jogo estão analisadas em outro artigo.

6. Como testar a curva antes de jogar

Jogar para testar a curva é lento e enviesado: quem programou o jogo joga melhor do que quase todo mundo. Antes, imprima a curva: copie as fórmulas para um script (Node ou o console do navegador), percorra uma grade de tiers e pontuações e mostre, para cada ponto, a dificuldade, o intervalo, a taxa média e a velocidade. As tabelas deste artigo saíram assim.

O que procurar na saída:

  1. Onde cada teto começa. Se um teto chega cedo demais, o resto da partida fica plano; se nunca chega, o jogo não tem fim jogável.
  2. Saltos entre tiers. Subir de tier multiplica a dificuldade de uma vez — do Tier II para o III, por 1,67 na mesma pontuação. Saltos são bons para marcar conquista, mas precisam caber no reflexo humano.
  3. A taxa média, não o pior caso. Com sorteio, o intervalo varia; a conta que importa para a sensação é a média de longo prazo: meteoros por vez divididos pelo intervalo médio.
  4. A curva contra os dados. Depois de publicar, compare com partidas reais. Se ninguém passa de uma faixa, a curva pode estar dura demais ali.

7. Erros comuns ao programar dificuldade progressiva

  • Crescer sem teto. Parâmetro que cresce para sempre vira um muro. Dê a cada um um limite explícito, como o 0,2 do spawn e o 3,8 da velocidade.
  • Contar quadros em vez de tempo. Um temporizador que desconta 1 por quadro deixa o jogo mais difícil em telas de 144 Hz. Desconte o dt do quadro.
  • Intervalo fixo, sem sorteio. Ritmo perfeito vira previsível; uma pequena variação aleatória mantém a atenção na tela.
  • Cada sistema com a sua fórmula. Se o spawn usa o tempo e a velocidade usa a pontuação, ninguém prevê a curva. Um número central de dificuldade resolve isso.
  • Esquecer de reiniciar o estado. Pontuação, tier e temporizadores voltam a zero na nova partida; senão a segunda começa mais difícil que a primeira.

Se o seu jogo ainda está no começo, o artigo sobre como criar um jogo 2D em JavaScript com canvas monta o laço de jogo, a colisão e as partículas em que essas fórmulas se encaixam. E para ver a curva do Navistron funcionando, é só jogar uma partida: grátis, no navegador, sem login. Os bastidores e os números do jogo saem todo dia no Instagram do Navistron.

FAQ

O que é dificuldade progressiva em jogos?

É o aumento gradual do desafio conforme o jogador avança: mais inimigos, inimigos mais rápidos ou mais resistentes. Em código, uma forma simples é um número de dificuldade calculado a partir do progresso e usado por todos os sistemas do jogo.

Por que usar raiz quadrada no intervalo de spawn?

Porque ela achata a curva: quadruplicar a dificuldade só divide o intervalo por dois. O jogo acelera rápido no começo e devagar depois, o que dá tempo de adaptação e evita que o ritmo dispare nas fases avançadas.

Qual deve ser o teto de dificuldade de um jogo?

Não existe um número universal. O teto deve ficar perto do limite do reflexo humano e ser testado com jogadores reais. No Navistron, o intervalo base do spawn para em 0,2 segundo e a velocidade em 3,8 vezes a inicial.

A dificuldade deve depender do tempo ou da pontuação?

Depende do jogo. Amarrar a dificuldade ao progresso faz o desafio crescer junto com quem joga bem; o tempo puro sobe igual para todos. O Navistron combina o tier da nave, que sobe com os boosts, e a pontuação da partida.

Como deixar o spawn de inimigos menos previsível?

Some um pequeno valor aleatório ao intervalo e varie a quantidade por vez com probabilidade. No Navistron, cada intervalo ganha de 0 a 0,15 segundo sorteado, e acima da dificuldade 8 dois meteoros nascem juntos em 45% das vezes.