Voltar para o Blog
Quest Log

Como fazer um jogo .io estilo agar.io: mais difícil do que parece

Círculos coloridos de tamanhos diferentes em uma arena escura estilo agar.io, com um círculo maior prestes a engolir um menor e nomes flutuando sobre eles

Como fazer um jogo .io estilo agar.io: a mecânica é fácil, o multiplayer online não. Veja o caminho honesto com Godot 4, ENet e servidor autoritativo.

Todo mundo que começa em game dev esbarra na mesma ideia: fazer um jogo .io. Faz sentido na cabeça. O agar.io é um círculo que come bolinhas. O slither.io é uma cobrinha com skin bonita. O diep.io é um tanque que atira em formas geométricas. Arte simples, regra simples, roda no navegador, e ainda por cima esses jogos fizeram um sucesso absurdo. Parece o projeto perfeito para iniciante. Não é. E neste post eu vou te mostrar exatamente onde o buraco está, o que dá para fazer de verdade, e como prototipar a parte multiplayer na Godot 4 sem se afogar.

Aviso honesto de largada: você vai sair daqui sabendo por onde começar, mas também sabendo por que "clonar o agar.io" é um dos projetos mais enganosos que existem. A parte visível é fácil. A parte invisível é um dos problemas mais difíceis de toda a área: multiplayer online em tempo real.

O que define um jogo .io

Antes de falar de código, vale nomear o que faz um .io ser um .io, porque cada item dessa lista tem uma consequência técnica.

  • Entrada instantânea, sem cadastro. Você abre o site, digita um apelido e está jogando em cinco segundos. Sem conta, sem download, sem tela de loading longa.
  • Partida contínua. Não existe lobby esperando dez jogadores para começar. O mundo já está rodando, você entra no meio dele, como quem entra numa pista de dança. Quando você sai, o mundo continua sem você.
  • Crescer comendo os menores. A progressão inteira acontece dentro da partida. Você nasce pequeno, come quem é menor, foge de quem é maior, e o seu tamanho é o seu placar.
  • A morte zera tudo. Foi engolido? Recomeça do zero, agora mesmo, com um clique. Nada persiste entre as vidas. Essa crueldade é parte da graça.

Agora olhe de novo para essa lista com olhos de programador. "Partida contínua" significa um servidor que nunca desliga. "Entrada instantânea" significa que esse servidor aceita conexões novas a qualquer momento, no meio do caos. "Crescer comendo os menores" significa que dezenas de jogadores disputam o mesmo estado de mundo ao mesmo tempo, e alguém precisa decidir, com autoridade, quem comeu quem quando dois círculos se encostam com 80 milissegundos de atraso entre eles. O design mais simples do mundo está sentado em cima do problema mais difícil.

A parte fácil de verdade: a mecânica local

Sejamos justos com a intuição do iniciante: a mecânica local É fácil. Se o objetivo fosse um agar.io de um jogador só, contra bolinhas paradas, você faria em um fim de semana na Godot. O jogador é uma Area2D com um CollisionShape2D circular e um sprite. Ele segue o mouse, come o que encosta, cresce e desacelera conforme engorda:

extends Area2D

var raio: float = 16.0
var velocidade: float = 300.0

func _physics_process(delta: float) -> void:
    var direcao: Vector2 = (get_global_mouse_position() - global_position).normalized()
    global_position += direcao * velocidade * delta

func comer(valor: float) -> void:
    raio += valor
    scale = Vector2.ONE * (raio / 16.0)
    velocidade = maxf(80.0, 300.0 - raio)

Espalhe comida pelo mapa (a mesma Area2D, menor, com o sinal area_entered conectado), aumente a raio a cada colisão, dê um zoom out na Camera2D conforme o jogador cresce, e pronto: você tem a sensação central do agar.io rodando na sua máquina. É genuinamente um bom exercício, e se você nunca fez nada parecido, faça. Só não confunda isso com "fiz 90% de um jogo .io". Você fez uns 20%.

A parte difícil escondida: o multiplayer em tempo real

Aqui é onde o projeto de fim de semana vira um projeto de meses. Vou listar os monstros na ordem em que você tromba com eles.

Servidor autoritativo. Em um jogo competitivo online, o cliente nunca pode decidir o resultado. Se o código do jogador decide "eu comi fulano", qualquer pessoa que abra o console do navegador pode se declarar gigante e invencível. A regra é: o cliente envia intenções ("estou indo para a direita"), e o servidor, só ele, simula o mundo e responde com o que de fato aconteceu. Isso significa que a lógica do jogo roda no servidor, e o seu cliente bonito vira essencialmente um visualizador com input. É uma inversão mental grande para quem só fez jogo single player.

Sincronização de estado. O servidor sabe onde estão todos os círculos. Os clientes precisam saber também, muitas vezes por segundo, para desenhar a cena. Só que enviar tudo para todos o tempo todo não escala: com o mapa cheio, o tráfego explode. Jogos reais enviam só o que interessa a cada jogador (o que está perto da sua câmera) e com a menor frequência que ainda parece fluida.

Interpolação e lag. Os pacotes chegam em intervalos irregulares, e entre um e outro o seu cliente precisa desenhar alguma coisa. Se você só teleportar cada inimigo para a última posição recebida, o jogo vira um festival de engasgos. A solução clássica é interpolar: desenhar os outros jogadores levemente no passado, deslizando suavemente entre as duas últimas posições conhecidas. E para o seu próprio personagem, o oposto: predição local, mover na hora e corrigir depois se o servidor discordar. Cada uma dessas técnicas é um capítulo de estudo por si só.

Trapaça. Em um jogo onde tamanho é status, alguém VAI tentar trapacear na primeira semana. Servidor autoritativo resolve a maior parte, mas ainda sobram bots, speed hacks disfarçados de input legítimo e flood de conexões. Você não precisa resolver tudo no protótipo, mas precisa saber que o problema existe antes de colocar qualquer coisa pública no ar.

O custo de manter tudo ligado. Este é o monstro que ninguém te conta. Um jogo .io não tem "fechar a partida": o servidor fica ligado 24 horas por dia, 7 dias por semana, consumindo CPU, memória e banda, com dois jogadores online ou com duzentos. Não vou inventar números aqui porque eles variam demais com provedor e escala, mas já escrevi em detalhe sobre quanto custa manter um servidor de jogo online, e recomendo ler antes de sonhar com o lançamento. Spoiler do espírito da coisa: hobby que fica ligado a noite inteira vira boleto mensal.

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

Prototipando o jogo .io na Godot 4 com a MultiplayerAPI

Nada disso significa que você não deve tentar. Significa que você deve tentar com as ferramentas certas e o escopo certo. A Godot 4 tem uma API de rede de alto nível que cuida do transporte (a parte de sockets, pacotes e conexões) e te deixa focar na lógica. O peer padrão para começar é o ENetMultiplayerPeer, baseado em UDP, ideal para jogo de ação.

Subir um servidor e conectar um cliente é assim, e esse código é real, você pode colar em um projeto Godot 4 hoje:

extends Node

const PORTA: int = 9050
const MAX_JOGADORES: int = 16

func criar_servidor() -> void:
    var peer := ENetMultiplayerPeer.new()
    var erro: int = peer.create_server(PORTA, MAX_JOGADORES)
    if erro != OK:
        push_error("Nao consegui abrir o servidor na porta %d" % PORTA)
        return
    multiplayer.multiplayer_peer = peer

func conectar(ip: String) -> void:
    var peer := ENetMultiplayerPeer.new()
    var erro: int = peer.create_client(ip, PORTA)
    if erro != OK:
        push_error("Nao consegui conectar em %s" % ip)
        return
    multiplayer.multiplayer_peer = peer

Com a conexão viva, dois nodes fazem o trabalho pesado da sincronização. O MultiplayerSpawner replica a criação de cenas: quando o servidor instancia o círculo de um jogador novo, ele aparece automaticamente na tela de todo mundo. O MultiplayerSynchronizer, colocado como filho do círculo, fica espelhando pela rede as propriedades que você marcar no painel de replicação dele, tipicamente position e, no nosso caso, o raio. Você configura isso no editor, sem escrever o código de envio na mão.

Falta um conceito: autoridade. Cada node sincronizado tem um dono, e só o dono deveria alterá-lo. Um padrão comum é nomear o node do jogador com o id do peer e usar esse nome para definir a autoridade:

extends Area2D

func _enter_tree() -> void:
    set_multiplayer_authority(name.to_int())

func _physics_process(delta: float) -> void:
    if not is_multiplayer_authority():
        return
    # so o dono deste circulo processa o proprio input

Repare que esse padrão dá a autoridade do movimento ao próprio jogador, o que é ótimo para prototipar e ruim contra trapaça. A versão séria mantém a autoridade da simulação no servidor e usa RPCs para o cliente enviar só o input. Se os termos RPC e autoridade ainda são novos para você, comece pela nossa base de RPC no multiplayer da Godot, que constrói esses conceitos com calma e exemplos pequenos. E quando quiser transformar "digite o IP do host" em uma tela decente de entrar na partida, o passo seguinte é montar um lobby multiplayer na Godot, com lista de jogadores e sincronização de quem entra e sai.

O caminho honesto de escopo

Se eu fosse te dar um mapa, seria este, nesta ordem, sem pular etapa:

  1. Versão local, um jogador. O círculo, a comida, o crescimento, a câmera. Um fim de semana. Aqui você valida que a mecânica é divertida, e muita gente descobre que só comer bolinha sem outros humanos é um tédio, o que já é um aprendizado de design.
  2. Versão LAN, 2 a 4 jogadores. Um jogador hospeda com create_server, os outros conectam pelo IP da rede local. MultiplayerSpawner para os jogadores aparecerem, MultiplayerSynchronizer para posição e raio. Aqui você conhece os bugs clássicos de sincronização em ambiente controlado, sem lag de internet real.
  3. Online pequeno. Um servidor dedicado modesto na nuvem, exportado sem interface gráfica, amigos conectando de casa. Agora o lag é real e você vai sentir na pele por que interpolação existe. É também o primeiro mês em que a hospedagem custa dinheiro de verdade.
  4. Escala. E aqui a conversa muda de natureza. Cem jogadores simultâneos não é um problema de GDScript, é um problema de infraestrutura: quantas instâncias de servidor, como dividir o mapa entre elas, como balancear conexões, o que fazer quando uma instância cai. Os .io famosos não são um servidor, são frotas de servidores. Não existe atalho de engine para isso.

A maioria dos projetos deveria parar, com orgulho, entre as etapas 2 e 3. Um agar.io de LAN party com seus amigos é um projeto completo, terminado, que ensina mais sobre redes do que qualquer curso teórico, e que cabe no seu bolso.

O jogo .io é o exemplo perfeito de uma lição maior de game dev: a dificuldade de um jogo não está no que aparece na tela, está no que o sistema precisa garantir por baixo. Arte simples não é sinônimo de projeto simples. Se você quer fazer o seu, faça, mas faça na ordem certa: mecânica local primeiro, LAN depois, online pequeno em seguida, e a fantasia dos mil jogadores simultâneos guardada para quando você tiver time, dinheiro de servidor e cicatrizes de sincronização. Chegar lá é possível. Chegar lá no primeiro projeto, não. E saber disso antes de começar é o que separa quem termina um protótipo de quem abandona um sonho pela metade.

Perguntas frequentes

É difícil fazer um jogo .io como o agar.io?

A mecânica local é fácil: mover um círculo, comer bolinhas, crescer. Isso se faz em um fim de semana. O difícil é o multiplayer online em tempo real: servidor autoritativo, sincronização de estado, lag, proteção contra trapaça e um servidor ligado 24 horas por dia. É um problema de rede e infraestrutura, não só de código de jogo.

Qual engine usar para fazer um jogo .io?

Os .io clássicos rodam no navegador e foram feitos com JavaScript no cliente e Node.js ou similar no servidor. Para aprender os conceitos, a Godot 4 é uma ótima escolha: a MultiplayerAPI de alto nível com ENetMultiplayerPeer, MultiplayerSpawner e MultiplayerSynchronizer resolve a camada chata de transporte e deixa você focar na lógica.

Preciso de servidor dedicado para um jogo .io?

Para a versão final, sim. Um .io de verdade tem partida contínua, então precisa de um servidor autoritativo ligado o tempo todo, mesmo de madrugada, mesmo com dois jogadores online. Para aprender e prototipar, não: comece com um jogador hospedando na rede local e adie o servidor na nuvem até o jogo funcionar.

O que é servidor autoritativo e por que ele importa?

É o modelo em que o servidor é a única fonte da verdade: os clientes enviam intenções (direção do movimento) e o servidor decide o que aconteceu de fato, quem comeu quem, quem cresceu. Sem isso, qualquer jogador pode editar o próprio cliente e trapacear, e em um jogo onde tamanho é tudo, a trapaça mata o jogo em dias.

Quantos jogadores simultâneos um .io iniciante aguenta?

Depende menos do seu código e mais da infraestrutura. Com a Godot e ENet você prototipa bem com poucos jogadores em LAN. Dezenas já exigem otimizar o que trafega na rede. Centenas de jogadores simultâneos, como nos .io famosos, envolvem múltiplas instâncias de servidor, divisão de mapa e custo mensal real de hospedagem.