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:
| Dificuldade | Intervalo base | Meteoros por vez | Meteoros por segundo |
|---|---|---|---|
| 1 | 1,20 s | 1 | 0,78 |
| 2 | 0,85 s | 1 | 1,08 |
| 4 | 0,60 s | 1 | 1,48 |
| 8 | 0,42 s | 1,25 | 2,50 |
| 16 | 0,30 s | 1,45 | 3,87 |
| 24 | 0,24 s | 1,45 | 4,53 |
| 36 ou mais | 0,20 s (teto) | 1,45 | 5,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:
| Momento | Dificuldade | Intervalo base | Meteoros por segundo | Velocidade |
|---|---|---|---|---|
| Tier I, começo | 1,0 | 1,20 s | 0,78 | 1,0× |
| Tier I, 1.000 pontos | 1,7 | 0,92 s | 1,00 | 1,1× |
| Tier II, 2.000 pontos | 4,3 | 0,58 s | 1,92 | 1,7× |
| Tier II, 5.000 pontos | 8,1 | 0,42 s | 2,92 | 2,4× |
| Tier III, 5.000 pontos | 13,5 | 0,33 s | 3,61 | 3,5× |
| Tier III, 10.000 pontos | 24,0 | 0,24 s | 4,53 | 3,8× |
| Tier III, 15.715 pontos | 36,0 | 0,20 s | 5,27 | 3,8× |
| Recorde: 29.901 pontos, Tier III | 65,8 | 0,20 s | 5,27 | 3,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:
- 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.
- 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.
- 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.
- 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
dtdo 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.
