Sistemas de Progressão em Jogos Explicados: XP, Skill Tree e Pacing

Entenda como funciona um sistema de progressão de jogo: curvas de XP, árvore de skills, desbloqueios e pacing, com código GDScript pronto para usar.
Sistemas de Progressão em Jogos Explicados: XP, Skill Tree e Pacing
Um sistema de progressão de jogo é o motivo de alguém jogar "só mais uma fase" às duas da manhã. É a estrutura que transforma tempo investido em poder, opções e identidade. Quando funciona, o jogador nem percebe que existe um sistema; quando falha, o jogo vira ou uma escadaria chata de grind ou um buffet onde tudo destrava cedo demais e não sobra motivo pra continuar.
A boa notícia: progressão não é mágica de designer veterano. São três peças que você consegue projetar e programar separadamente: a moeda de progresso (XP, na maioria dos casos), a estrutura de desbloqueio (níveis, árvore de skills, itens) e o ritmo em que tudo isso chega na mão do jogador (pacing). Esse artigo passa pelas três, com código GDScript do Godot 4 que roda como está.
O que um sistema de progressão precisa responder
Antes de abrir o editor, vale responder três perguntas no papel:
- O que o jogador faz pra progredir? Matar inimigos, completar quests, explorar, coletar. Isso define o que o seu jogo recompensa, e o jogador vai fazer mais daquilo que dá XP. Se só combate dá XP, ninguém explora.
- O que a progressão entrega? Números maiores (mais vida, mais dano), opções novas (habilidades, builds) ou acesso novo (áreas, modos). Os três são sensações diferentes.
- Em quanto tempo? Um roguelike entrega progresso em minutos. Um RPG, em dezenas de horas. O mesmo sistema com pacing errado pro gênero quebra o jogo inteiro.
A distinção entre número maior e opção nova é a que mais separa jogos bons de medianos. Subir de nível e ganhar +5 de vida é progressão vertical: você fica mais forte, mas joga igual. Destravar um dash ou um pulo duplo é progressão horizontal: o jogo muda de verdade. Os melhores sistemas misturam os dois, com o vertical dando a sensação constante de avanço e o horizontal marcando os momentos memoráveis.
A curva de XP: o coração do sistema de progressão
Quase todo jogo com níveis usa a mesma ideia: cada nível custa mais XP que o anterior. Se o custo fosse fixo, o jogador subiria de nível cada vez mais rápido conforme fica forte, e os níveis perderiam valor. A curva crescente compensa o fato de que inimigos de fim de jogo dão mais XP.
As duas famílias mais comuns:
Curva polinomial. O custo cresce com o nível elevado a um expoente, geralmente entre 1.5 e 2.5. É previsível e fácil de ajustar: expoente maior, fim de jogo mais lento.
Curva exponencial. O custo multiplica por um fator a cada nível (1.1x, 1.15x). Cresce devagar no começo e explode no final. Funciona bem em jogos onde o ganho de XP também escala agressivamente, como idle games.
Pra maioria dos projetos, a polinomial resolve. Em código:
extends Node
signal xp_changed(current_xp, xp_needed)
signal level_up(new_level)
var level := 1
var current_xp := 0
const BASE_XP := 100
const EXPONENT := 1.8
func xp_for_level(lvl: int) -> int:
# Custo pra ir do nível lvl pro lvl + 1.
return int(BASE_XP * pow(lvl, EXPONENT))
func add_xp(amount: int) -> void:
current_xp += amount
# while em vez de if: um ganho grande pode subir mais de um nível.
while current_xp >= xp_for_level(level):
current_xp -= xp_for_level(level)
level += 1
level_up.emit(level)
xp_changed.emit(current_xp, xp_for_level(level))
Com BASE_XP = 100 e expoente 1.8, o nível 2 custa 100 XP, o nível 5 custa cerca de 1.200 e o nível 10 passa de 6.000. Ajustar a curva inteira é mexer em duas constantes, e é por isso que vale centralizar a fórmula numa função em vez de espalhar valores mágicos pelo projeto.
Um detalhe de arquitetura: esse script funciona bem como autoload (singleton), porque XP é estado global do jogador. Os sinais xp_changed e level_up deixam a HUD, o som de level up e os efeitos visuais se conectarem sem o sistema de progressão saber que eles existem.
E uma regra prática de design que economiza retrabalho: faça os primeiros níveis chegarem rápido. O jogador precisa entender o loop de recompensa nos primeiros minutos. Se o primeiro level up demora meia hora, boa parte das pessoas larga antes de sentir o gostinho.
Árvore de skills: dados primeiro, interface depois
O erro clássico ao programar uma skill tree é começar pela interface, com cada botão da árvore carregando sua própria lógica. Aí adicionar uma skill nova vira cirurgia. O caminho são dados separados da apresentação: a árvore é uma estrutura de dados com pré-requisitos, e a interface só lê essa estrutura.
Uma árvore de skills, no fundo, é um grafo de dependências: cada skill tem um custo e uma lista de skills que precisam estar destravadas antes. Dá pra modelar com um dicionário simples:
extends Node
signal skill_unlocked(skill_id)
var skill_points := 0
var unlocked: Array[String] = []
var skills := {
"golpe_forte": {"cost": 1, "requires": []},
"investida": {"cost": 1, "requires": []},
"golpe_giratorio": {"cost": 2, "requires": ["golpe_forte"]},
"execucao": {"cost": 3, "requires": ["golpe_forte", "investida"]},
}
func can_unlock(id: String) -> bool:
if id in unlocked:
return false
var skill = skills[id]
if skill_points < skill["cost"]:
return false
for req in skill["requires"]:
if not req in unlocked:
return false
return true
func unlock(id: String) -> bool:
if not can_unlock(id):
return false
skill_points -= skills[id]["cost"]
unlocked.append(id)
skill_unlocked.emit(id)
return true
func has_skill(id: String) -> bool:
return id in unlocked
O resto do jogo só pergunta has_skill("investida") na hora de decidir se o input de dash faz alguma coisa. A interface da árvore percorre o dicionário, desenha um botão por skill e pinta de cinza o que can_unlock() recusar. Skill nova é uma linha no dicionário, sem tocar em lógica.
Em projeto maior, vale trocar o dicionário por Resources customizados (um .tres por skill), que dão editor visual e checagem de tipo. A estrutura lógica continua idêntica.
No design da árvore em si, duas armadilhas aparecem o tempo todo:
- Skill de imposto. Aquele nó obrigatório e chato ("+ 3% de dano") que só existe pra alcançar a skill boa. Uma ou outra passa, mas uma árvore cheia delas transforma escolha em burocracia.
- Escolha sem informação. Pedir que o jogador escolha entre três ramos na primeira hora, antes de entender o combate, não é profundidade, é chute. Ou deixe os primeiros pontos serem baratos de realocar, ou segure as bifurcações grandes pra quando o jogador já sabe o que gosta.
Desbloqueios: progressão além dos números
Nem toda progressão é barra de XP. Desbloqueio é qualquer conteúdo que existe no jogo mas ainda não está disponível: área nova, arma nova, modo de jogo, personagem, receita de craft. E desbloqueio bem feito segue uma lógica diferente da curva de XP: ele funciona melhor amarrado a marcos do que a acúmulo.
A diferença na prática: "destrave a besta ao derrotar o chefe da floresta" cria um momento. "Destrave a besta ao atingir 4.000 pontos de caça" cria uma tarefa. Acúmulo tem lugar (maestria de arma, por exemplo), mas os desbloqueios que estruturam o jogo rendem mais como marcos.
Metroidvanias são a aula definitiva do assunto: a progressão inteira é desbloqueio de habilidade que reabre o mapa. O pulo duplo não é só um poder, é uma chave. Quando você projeta um desbloqueio assim, a pergunta certa não é "o que o jogador ganha?", é "o que o jogador passa a poder fazer que antes não podia?". Se a resposta é "nada, só fica mais forte", é progressão vertical disfarçada.
Um padrão que uso pra manter desbloqueios organizados é tratá-los como flags centralizadas, no mesmo espírito da skill tree: um conjunto de IDs destravados, sinais pra avisar o resto do jogo, e o conteúdo perguntando "estou liberado?" em vez de cada porta do mapa carregar lógica própria.
Pacing: o ritmo que segura o jogador
Pacing é a parte invisível e a mais difícil de acertar, porque não tem fórmula, tem teste. É o intervalo entre recompensas ao longo do jogo inteiro. Os princípios que guiam o ajuste:
Comece denso, termine espaçado. Recompensas frequentes no início ensinam o loop e criam o hábito. Conforme o jogador investe no jogo, ele aceita intervalos maiores, porque cada recompensa pesa mais. O contrário (começo lento, final atropelado) é receita de abandono na primeira hora.
Alterne tamanhos de recompensa. Um fluxo constante de recompensas pequenas vira ruído; só recompensas grandes e raras vira deserto. O ritmo bom intercala: XP pinga sempre, level up a cada tantos minutos, skill nova de vez em quando, desbloqueio de área como evento.
Nunca deixe a barra parada. O jogador deve sempre enxergar o próximo objetivo de progressão a uma distância alcançável. Se ele olha a barra de XP e pensa "isso não anda", o sistema falhou, mesmo que a matemática esteja certa.
Cuidado com o grind como conserto. Se o teste mostra que o jogador chega forte demais no chefe, a tentação é encarecer a curva. Só que isso pune todo mundo com mais repetição. Antes de mexer na curva, olhe a fonte: talvez uma quest específica pague XP demais, ou um inimigo fácil demais seja farmável.
Pra ajustar pacing você precisa enxergar os números. Um log simples já muda o jogo durante o desenvolvimento:
# Conectado ao sinal level_up do sistema de XP.
func _on_level_up(new_level: int) -> void:
var minutes = Time.get_ticks_msec() / 60000.0
print("Nível %d aos %.1f minutos de jogo" % [new_level, minutes])
Rode uma sessão de teste, olhe os intervalos entre level ups e compare com a intenção. Se você queria um level up a cada 10 minutos no meio do jogo e o log mostra 25, não precisa de playtest formal pra saber onde mexer.
Por onde começar no seu projeto
Se o seu jogo ainda não tem progressão, a ordem que recomendo é a mais barata de iterar:
- Defina no papel as três respostas do início: o que dá progresso, o que a progressão entrega, em quanto tempo.
- Implemente só o XP com a curva polinomial e ligue numa barra na HUD. Jogue. Sinta o ritmo do pingar de XP antes de construir qualquer coisa em cima.
- Adicione níveis com uma recompensa vertical simples (vida, dano) e calibre a curva até os intervalos parecerem certos.
- Só então construa a skill tree ou os desbloqueios, que são as peças caras de produzir.
Progressão é um sistema que se ajusta jogando, não planilhando. A planilha te dá o ponto de partida; o playtest te diz a verdade. Comece pequeno, com uma curva e duas constantes, e deixe o sistema crescer junto com o jogo.


