Game Dev 08 de agosto de 2026• 7 min de leitura• Rafael Guimarães

GGPong: Da Inteligência Artificial Preditiva ao Multiplayer Peer-to-Peer

Bastidores do GGPong: implementação de inteligência preditiva para a CPU, sincronização de física de rebate no Host e suporte a WebRTC.

GGPong: Da Inteligência Artificial Preditiva ao Multiplayer Peer-to-Peer

GG Pong por dentro: a CPU que segue a bola e o multiplayer peer-to-peer com PeerJS

Pong é o jogo perfeito para aprender a fazer jogos. As regras cabem numa frase, a tela tem só três objetos, e mesmo assim ele toca em quase tudo que importa: movimento, colisão, pontuação, um adversário controlado pelo computador e, se você quiser ir além, jogo em rede.

O GG Pong é a versão do portal. Este artigo abre o código e explica as decisões por trás de cada parte: como a bola ganha ângulo, por que a CPU erra de propósito e como duas pessoas jogam uma contra a outra pela internet sem que exista um servidor de jogo no meio.

O que tem em uma partida

O menu tem dois caminhos: Single Player (vs CPU) e Multiplayer Lobby.

  • Você controla a paleta da esquerda. No teclado, com as setas para cima e para baixo. No celular ou com o mouse, tocando na metade de cima da tela para subir e na metade de baixo para descer.
  • A paleta da direita é a CPU ou o outro jogador.
  • Quem fizer 10 pontos primeiro vence. Aparece o nome do vencedor e um botão para começar de novo.
  • Há botões de pausa e de voltar ao menu na própria tela.

O jogo é feito com Phaser 3, numa tela de 1024 por 768 que se ajusta ao tamanho da janela. O visual é proposital: retângulos brancos, uma linha central cinza e o placar em fonte grande, como no original.

A física da bola

Movimento independente da taxa de quadros

O Phaser chama a função update a cada quadro e informa quanto tempo passou desde o anterior. O jogo converte esse tempo para segundos e multiplica todas as velocidades por ele:

update(time, deltaMs) {
  const dt = deltaMs / 1000;           // segundos desde o último quadro
  ball.x += ball.vx * dt;
  ball.y += ball.vy * dt;
}

Com isso as velocidades ficam em pixels por segundo, não por quadro. A bola atravessa a tela no mesmo tempo num monitor de 60 Hz e num de 144 Hz. Sem esse cuidado, quem tem uma tela mais rápida jogaria uma partida mais rápida.

O saque

Depois de cada ponto, a bola volta ao centro e sai numa direção sorteada, com velocidade horizontal de 400 px/s e vertical de 200 px/s:

resetBall() {
  ball.x = width / 2;
  ball.y = height / 2;
  ball.vx = (Math.random() > 0.5 ? 1 : -1) * BALL_SPEED_START;        // 400
  ball.vy = (Math.random() > 0.5 ? 1 : -1) * BALL_SPEED_START * 0.5;  // 200
}

Sortear o lado evita que quem acabou de perder ponto receba sempre o mesmo saque.

O ângulo da rebatida

Este é o detalhe que transforma Pong em um jogo de habilidade. Se a bola sempre voltasse no mesmo ângulo, não haveria o que fazer além de ficar na frente dela. No GG Pong, o ponto da paleta onde a bola bate decide para onde ela vai:

// Paleta com 100 px de altura: offset vai de -1 (topo) a +1 (base)
const offset = (paddle.y - ball.y) / 50;
ball.vy = -offset * 350;
ball.vx = Math.abs(ball.vx) * 1.05;   // e acelera 5%
  • Bater no centro devolve a bola quase reta.
  • Bater perto da ponta de cima manda a bola para cima, com até 350 px/s de velocidade vertical.
  • Bater perto da ponta de baixo manda para baixo.

Cada rebatida também aumenta a velocidade horizontal em 5%. É uma regra simples que dá ritmo à partida: as trocas começam calmas e vão ficando tensas até alguém errar. Depois de dez rebatidas seguidas, a bola já está cerca de 60% mais rápida do que no saque.

Colisões

As paletas medem 20 por 100 pixels e a bola é um quadrado de 20 por 20. Como tudo é retângulo, a colisão é um teste de sobreposição entre dois retângulos, que o Phaser oferece pronto (Phaser.Geom.Intersects.RectangleToRectangle). A bola também quica nas bordas de cima e de baixo, invertendo a velocidade vertical.

A CPU: imperfeita de propósito

A forma mais fácil de fazer uma CPU imbatível em Pong é colocar a paleta exatamente na altura da bola em todos os quadros. Ela nunca erra, e o jogo fica chato em segundos. O trabalho de quem faz o adversário é o contrário: fazer a CPU perder de um jeito que pareça justo.

O GG Pong faz isso com três regras:

const diff = ball.y - cpu.y;
const DEAD_ZONE = 35;                    // px
const CPU_SPEED = PADDLE_SPEED * 0.5;    // 300 px/s, metade do jogador

if (Math.abs(diff) > DEAD_ZONE) {
  cpu.y += Math.sign(diff) * CPU_SPEED * dt;
}
  1. Ela segue a altura atual da bola, não o lugar onde a bola vai chegar. Quando a bola vem inclinada, a CPU corre atrás dela em vez de esperá-la no ponto certo.
  2. Ela se move na metade da velocidade do jogador. Você anda a 600 px/s, ela a 300. Uma bola rápida, rebatida perto da ponta, pode ser simplesmente mais rápida que ela.
  3. Ela tem uma zona morta de 35 pixels. Se a bola está a menos de 35 px do centro da paleta, a CPU não se mexe. Isso evita que a paleta trema a cada quadro e cria uma pequena folga que você pode explorar.

O resultado é um adversário que devolve as bolas fáceis e perde nas difíceis. Para ganhar dele, a estratégia é usar o ângulo: rebater perto da ponta da paleta, mandando a bola para longe de onde a CPU está.

Ideia para uma próxima versão: prever a trajetória

Uma CPU mais forte poderia calcular onde a bola vai cruzar a linha da paleta, incluindo os quiques nas paredes, e ir esperá-la lá. Isso não está no jogo hoje. Seria algo assim:

// Não implementado no GG Pong: exemplo de como seria
function predictY(ball, targetX, height) {
  const t = (targetX - ball.x) / ball.vx;   // tempo até chegar
  let y = ball.y + ball.vy * t;
  const span = height * 2;
  y = ((y % span) + span) % span;           // "desdobra" os quiques
  return y > height ? span - y : y;
}

Com essa previsão, a dificuldade passaria a ser controlada somando um erro sorteado ao ponto previsto: quanto maior o erro, mais fácil a CPU. É um caminho natural para ter níveis de dificuldade, que hoje o jogo não tem.

Multiplayer sem servidor de jogo

Jogar Pong pela internet tem um problema clássico: os dois jogadores precisam ver a mesma bola no mesmo lugar. A solução comum é ter um servidor que calcula a partida e manda o resultado para os dois. O GG Pong evita esse servidor com uma conexão peer-to-peer: os dois navegadores conversam diretamente.

As três peças

  1. WebRTC, a tecnologia dos navegadores que permite a conexão direta entre dois computadores. O jogo usa a biblioteca PeerJS, que simplifica o WebRTC.
  2. Um servidor de sinalização, que só apresenta um navegador ao outro. Depois que a conexão direta está estabelecida, ele sai do caminho. O GG Pong usa o servidor público do PeerJS e um servidor STUN do Google, que ajuda cada navegador a descobrir seu endereço na internet.
  3. Uma lista de salas no Firebase Firestore, para que um jogador encontre o outro.

Criar e entrar numa sala

Quem cria a sala escolhe um nome (até 15 caracteres, o jogo sugere algo como "Strawberry"). O jogo grava no Firestore o nome da sala e o identificador PeerJS de quem a criou, com o status "esperando".

Quem quer entrar vê a lista de salas disponíveis e clica em uma. O jogo lê o identificador gravado na sala e conecta direto no navegador de quem a criou. Ninguém precisa copiar ou digitar código nenhum.

Para a lista não ficar cheia de salas abandonadas, quem criou a sala manda um sinal de vida a cada 10 segundos. Salas sem sinal há mais de 30 segundos somem da lista.

Quem manda na bola

Com dois navegadores calculando a física ao mesmo tempo, os resultados acabariam diferentes. Por isso só um deles calcula:

  • Quem criou a sala (host) fica com a paleta da esquerda, calcula toda a física e, a cada quadro, envia o estado completo da partida: bola, paletas e placar.
  • Quem entrou (cliente) fica com a paleta da direita e só envia a altura da própria paleta, e só quando ela se move.
// No host, a cada quadro
peer.send({ type: 'update', state: gameState });

// No cliente, quando a paleta se move
peer.send({ type: 'input', y: myPaddle.y });

Esse modelo é simples e evita trapaça e divergência: só existe uma bola de verdade, a do host. O custo é que o cliente vê a partida com o atraso da conexão. Entre dois jogadores na mesma cidade isso é quase imperceptível. Em conexões lentas, a bola do cliente pode dar pequenos saltos, porque ele mostra a última posição recebida, sem suavizar entre uma atualização e outra.

Resumo

  • Phaser 3, tela de 1024 por 768, velocidades em pixels por segundo
  • Partida até 10 pontos; a bola acelera 5% a cada rebatida
  • O ângulo de saída depende de onde a bola bate na paleta
  • CPU que segue a bola na metade da sua velocidade, com zona morta de 35 px
  • Multiplayer P2P com PeerJS, salas listadas no Firestore e física calculada só no host

Jogar o GG Pong

Continue lendo

RG

Rafael Guimarães

Criador do Guima Games Portal & Game Developer

Ver mais artigos