Voltar para o Blog
Quest Log

Placar Online no Godot: Como Fazer um Ranking de Pontuação de Verdade

Tela de jogo mostrando uma tabela de ranking com nomes de jogadores e pontuações em ordem

Como fazer um placar online (ranking online) no Godot: recorde local vs backend, envio de score via HTTPRequest, montar a UI da lista e evitar trapaça.

Placar Online no Godot: Como Fazer um Ranking de Pontuação de Verdade

Colocar um placar online no seu jogo parece um detalhe pequeno, mas ele muda o comportamento de quem joga. Um número que só você vê é uma coisa. Um ranking onde o seu nome aparece acima (ou abaixo) do de outra pessoa é outra bem diferente. Neste post a gente vê como fazer isso no Godot de um jeito honesto: o que dá para resolver no próprio dispositivo, o que obriga a ter um servidor, quais backends um iniciante consegue usar sem virar administrador de infraestrutura, e o alerta que quase todo tutorial esquece de dar, que é o problema de trapaça.

Vou avisar logo de cara: para muitos jogos, você não precisa de placar online nenhum. Um recorde salvo no aparelho já entrega quase toda a sensação de progresso. Só siga para o online se a competição entre jogadores for parte do que torna o jogo divertido, porque a partir daí o custo sobe.

Recorde local versus placar online: não são a mesma coisa

Essa é a confusão que trava mais gente no começo, então vamos separar bem.

O recorde local é uma pontuação salva no próprio dispositivo do jogador. Você grava um arquivo (com ConfigFile ou um save), lê ele quando o jogo abre e mostra na tela. Ninguém além daquele jogador vê aquele número. É simples, funciona offline, não custa nada e não depende de conexão. Se você ainda não fez isso, o caminho é aprender a salvar recorde e pontuação localmente no Godot antes de pensar em servidor. Boa parte dos jogos para na etapa local, e está tudo certo.

O placar online, ou ranking online, é diferente por definição: as pontuações ficam num servidor compartilhado, fora do aparelho. Quando um jogador termina uma partida, o jogo envia o score dele para esse servidor. Quando alguém quer ver a tabela, o jogo pede a lista dos melhores para o mesmo servidor. O ponto central é que todos os jogadores disputam a mesma tabela. Por isso o online obriga a ter um backend. Não tem como fazer "placar entre jogadores" salvando só no dispositivo de cada um: os aparelhos não se falam sozinhos.

Resumindo a decisão:

  • Só o próprio jogador precisa ver a pontuação: recorde local resolve.
  • Jogadores diferentes precisam disputar a mesma lista: aí sim, placar online com backend.

Quando o placar online faz sentido

Placar online funciona bem em jogo arcade de pontuação alta, em jogo casual de "bata seu amigo", em desafios semanais. Ele existe por um motivo prático além do ego: competição segura os jogadores. Se você quer entender por que uma tabela de ranking ajuda a trazer gente de volta, vale ler como a retenção de jogadores funciona, porque o placar é uma das alavancas clássicas de retorno.

Agora, o custo. Placar online traz trabalho que o local não tem: você vai lidar com conexão instável (o envio pode falhar), com nomes ofensivos que alguém vai digitar, com trapaça (mais sobre isso adiante) e, dependendo do volume, com conta paga no backend. Nada disso é impossível, mas é honesto colocar na balança antes de começar.

Opções de backend para iniciante

Você não precisa escrever um servidor do zero. Existem opções reais, e cada uma tem uma troca diferente.

SilentWolf. É um serviço feito especificamente para Godot, com plugin pronto e funções de placar, contas de jogador e mais. Para quem quer o caminho mais curto até um ranking funcionando, é a escolha natural: você instala o plugin, configura uma chave e chama as funções dele. Tem plano gratuito com limite e planos pagos conforme cresce.

Firebase. É a plataforma de backend do Google. Não é feita para Godot, então você conversa com ela via HTTPS (a API REST do Firestore, por exemplo) usando o HTTPRequest. É mais genérica e mais poderosa, mas também exige entender um pouco mais de como estruturar os dados e as regras de segurança.

API própria simples. Se você já sabe programar um pouco de backend, dá para subir uma API mínima (em qualquer linguagem) que recebe um score e devolve a lista ordenada. É mais trabalho, mas você controla tudo, inclusive a validação contra trapaça, que é onde as opções prontas às vezes deixam a desejar.

Para todas elas, do lado do Godot, o node que faz a conversa com a internet é o mesmo: o HTTPRequest.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

O fluxo geral no Godot

Independente do backend, o desenho é sempre esse:

  1. O jogador termina a partida e o jogo tem um score final.
  2. O jogo envia esse score (com o nome do jogador) para o servidor, via HTTPS.
  3. Para exibir a tabela, o jogo pede ao servidor a lista dos melhores.
  4. O servidor responde, normalmente em JSON, com nomes e pontuações.
  5. O jogo lê esse JSON e monta a UI da lista na tela.

O HTTPRequest no Godot trabalha de forma assíncrona: você dispara a requisição e ela avisa depois, por um sinal, quando a resposta chegou. Isso é bom, porque o jogo não trava esperando a internet. Mas exige que você conecte o sinal request_completed antes de enviar.

Enviar a pontuação com HTTPRequest

Aqui vai um exemplo tipado de envio de um score. Repare que estou mandando os dados como JSON no corpo de um POST, que é o formato que a maioria dos backends espera. A URL e a estrutura exata dependem do serviço que você escolher; o conceito é o mesmo.

extends Node

@onready var http: HTTPRequest = $HTTPRequest

const URL_PLACAR: String = "https://seu-backend.exemplo/scores"

func _ready() -> void:
    http.request_completed.connect(_ao_receber_resposta)

func enviar_score(nome: String, valor: int) -> void:
    var corpo: Dictionary = {
        "nome": nome,
        "pontuacao": valor
    }
    var json: String = JSON.stringify(corpo)
    var cabecalhos: PackedStringArray = ["Content-Type: application/json"]
    var erro: int = http.request(URL_PLACAR, cabecalhos, HTTPClient.METHOD_POST, json)
    if erro != OK:
        push_error("Falha ao iniciar o envio do score")

func _ao_receber_resposta(resultado: int, codigo: int, cabecalhos: PackedStringArray, corpo: PackedByteArray) -> void:
    if resultado != HTTPRequest.RESULT_SUCCESS or codigo != 200:
        push_warning("Envio do score nao foi aceito pelo servidor")
        return
    print("Score registrado no placar online")

Alguns pontos que valem atenção nesse trecho. O request() só inicia a requisição; a resposta real chega no _ao_receber_resposta, por isso a checagem de sucesso mora lá. E note que eu verifico tanto o resultado (a conexão em si funcionou) quanto o codigo HTTP (o servidor aceitou). É comum a conexão dar certo mas o servidor recusar por algum motivo, então tratar os dois separadamente evita bug silencioso.

Buscar a lista e montar a UI

Para exibir o ranking, você faz um GET, recebe o JSON com a lista e constrói as linhas na tela. Um jeito limpo é ter uma cena pequena para "uma linha do ranking" (posição, nome e pontuação) e instanciar ela dentro de um VBoxContainer. Assim a lista cresce sozinha conforme os dados chegam.

extends VBoxContainer

@onready var http: HTTPRequest = $HTTPRequest

func _ready() -> void:
    http.request_completed.connect(_ao_receber_lista)
    _pedir_ranking()

func _pedir_ranking() -> void:
    var erro: int = http.request("https://seu-backend.exemplo/scores/top")
    if erro != OK:
        push_error("Falha ao pedir o ranking")

func _ao_receber_lista(resultado: int, codigo: int, cabecalhos: PackedStringArray, corpo: PackedByteArray) -> void:
    if resultado != HTTPRequest.RESULT_SUCCESS or codigo != 200:
        push_warning("Nao consegui carregar o ranking")
        return
    var texto: String = corpo.get_string_from_utf8()
    var dados: Variant = JSON.parse_string(texto)
    if not dados is Array:
        push_warning("Resposta do ranking em formato inesperado")
        return
    _montar_lista(dados)

func _montar_lista(lista: Array) -> void:
    for filho in get_children():
        if filho is Label:
            filho.queue_free()
    var posicao: int = 1
    for item in lista:
        var nome: String = str(item.get("nome", "???"))
        var pontos: int = int(item.get("pontuacao", 0))
        var linha: Label = Label.new()
        linha.text = "%d. %s - %d" % [posicao, nome, pontos]
        add_child(linha)
        posicao += 1

Aqui eu montei a lista com Label simples só para o exemplo ficar curto e legível. Num jogo de verdade, você provavelmente vai preferir uma cena estilizada por linha, com fonte, cor e alinhamento, mas a lógica de percorrer o JSON e criar um node por item é exatamente essa. Note também que eu limpo os filhos antigos antes de montar de novo, senão a lista duplicaria toda vez que atualizasse.

Se você quer que o próprio recorde do jogador (o local) e o placar online conversem, por exemplo enviar para o online só quando bate o recorde pessoal, o ponto de partida é ter um sistema de save e load completo já organizado. Aí o local vira a fonte da verdade no dispositivo e o online recebe só quando faz sentido.

O alerta que quase ninguém dá: trapaça

Esta é a parte mais importante e a menos glamourosa. Placar online é fácil de burlar quando você confia no cliente.

Pensa no que os exemplos acima fazem: o jogo, rodando na máquina do jogador, envia um número dizendo "minha pontuação foi 999999". O servidor recebe e guarda. O problema é que o jogador controla a máquina dele. Ele pode interceptar a requisição, editar o número e reenviar, sem nem tocar no jogo. Ou seja: se a sua única defesa é o código dentro do jogo, ela não existe de verdade, porque o atacante roda o jogo, não você.

A regra de ouro é: nunca confie no cliente. Toda validação que importa precisa acontecer no servidor, onde o jogador não manda. Na prática isso significa algumas coisas:

  • Valide a plausibilidade no servidor. Se o máximo teórico do seu jogo é 5000 pontos, o servidor deve recusar 999999. Se leva no mínimo dois minutos para pontuar mil, um envio a cada dez segundos é suspeito.
  • Limite os envios por jogador. Rate limiting evita que alguém floode a tabela.
  • Não embuta segredos no build público. Chave de API dentro do jogo distribuído pode ser extraída. Segredo de verdade fica no servidor.
  • Para jogos competitivos sérios, revalide a partida. O nível máximo é o servidor recalcular ou conferir um resumo do que aconteceu na partida, não só aceitar o número final. Isso é bem mais trabalho e a maioria dos jogos pequenos não precisa chegar lá.

Vou ser direto sobre o que isso significa para você. Serviços como SilentWolf e Firebase facilitam guardar e listar pontuações, mas eles não sabem as regras do seu jogo, então a validação de plausibilidade forte é responsabilidade sua. Se o seu placar for só por diversão, entre amigos, talvez você aceite que ele seja burlável e siga em frente. Não tem problema nenhum nisso, desde que seja uma escolha consciente. O que não vale é achar que enviar o score por HTTPS já torna o ranking "seguro". HTTPS protege os dados no caminho, não protege contra o jogador que mente sobre a própria pontuação.

Fechando

Colocar um placar online no Godot é, na mecânica, simples: um HTTPRequest para enviar o score, outro para buscar a lista, e um pouco de código para montar a UI a partir do JSON. O que separa um ranking de verdade de um brinquedo é a parte que não aparece na tela: escolher um backend que dê conta (SilentWolf para o caminho curto, Firebase para mais controle, API própria para controle total) e tratar trapaça validando do lado do servidor.

E vale repetir, porque é fácil esquecer no meio da empolgação: para muitos jogos, um recorde local já basta. Ele é offline, gratuito, impossível de burlar entre estranhos (porque não há estranhos) e resolve a maior parte da sensação de progresso. Suba para o online quando a disputa entre jogadores for parte da graça, e quando você estiver disposto a pagar o custo de infraestrutura e de combate à trapaça que vem junto.

Perguntas frequentes

Como fazer um ranking online no Godot?

Você precisa de um backend que guarde as pontuações fora do dispositivo. No jogo, usa o node HTTPRequest para enviar o score por HTTPS e para pedir a lista dos melhores. Depois monta a UI lendo o JSON recebido. Opções prontas para iniciante incluem o SilentWolf (feito para Godot) e o Firebase.

Preciso de um servidor para ter placar online?

Sim. Placar online por definição significa que as pontuações ficam num servidor compartilhado, não no dispositivo de cada jogador. Você não precisa escrever esse servidor do zero: serviços como SilentWolf e Firebase fazem esse papel. Sem backend, só dá para ter recorde local.

Qual a diferença entre recorde local e placar online?

O recorde local fica salvo no aparelho do próprio jogador (num arquivo com ConfigFile, por exemplo) e só ele vê. O placar online guarda as pontuações num servidor, então todos os jogadores disputam a mesma tabela. Local é simples e offline; online exige backend e conexão.

Como impedir trapaça no placar online?

A regra é nunca confiar no cliente. Se o jogo só manda o número final, é fácil forjar. A defesa real é validar no servidor: recalcular ou checar a plausibilidade da pontuação, limitar quantos envios por jogador e usar uma chave secreta que não vai embutida no build público. Nenhuma proteção só no lado do cliente resolve.

SilentWolf é gratuito para Godot?

O SilentWolf tem um plano gratuito com limite de uso, e planos pagos quando o volume cresce. Ele foi feito para Godot e cobre placar, contas de jogador e outras funções, então é um bom ponto de partida para quem não quer montar backend próprio.

Vale a pena fazer placar online no meu jogo?

Depende. Para muitos jogos, um recorde local já basta e evita o custo de backend, contas e moderação. Placar online faz sentido quando a competição entre jogadores é parte da graça e você aceita lidar com trapaça e infraestrutura.