GGBoard: como fizemos um jogo de tabuleiro 3D multiplayer que roda no navegador
Jogo de tabuleiro é reunião: todo mundo em volta da mesa, esperando a sua vez, torcendo pelo dado. O GGBoard tenta levar isso para o navegador. Cada jogador entra de onde estiver, escolhe um personagem e anda por um tabuleiro 3D cheio de casas que mandam voltar, pular a vez ou tentar a sorte no dado.
Este artigo explica as três partes que fazem o jogo funcionar: como o tabuleiro é descrito, como os jogadores ficam sincronizados sem um servidor de jogo e como o mundo 3D é montado. No fim, o editor que usamos para criar tabuleiros novos.
Como se joga
- Você escolhe um nome e um personagem. Há sete modelos 3D, de tanques a um baú.
- Quem cria a sala recebe um código e um link de convite para mandar aos amigos. A página do jogo fala em até 8 jogadores.
- Na sua vez, você clica em Rolar Dado e seu personagem anda casa por casa, com um pulinho a cada passo.
- Algumas casas têm efeitos: avançar ou voltar para outra casa, voltar ao início, perder a próxima vez, jogar de novo ou rolar o dado mais uma vez para decidir o caminho.
- Em encruzilhadas, o jogo pergunta qual caminho você quer seguir.
- Vence quem chegar primeiro à chegada.
Um painel lateral mostra a vez de quem, em que casa cada um está e um histórico das últimas jogadas.
Nomes de jogadores: o que vale testar
O nome escolhido no início acompanha o jogador na sala. Em uma partida com amigos, isso parece um detalhe; em uma interface multiplayer, o apelido precisa continuar reconhecível no painel lateral e no histórico, inclusive em telas pequenas.
É possível experimentar letras diferentes em um nome de exibição, mas há uma distinção técnica: a fonte tipográfica definida no layout não é a mesma coisa que os caracteres Unicode digitados ou colados pelo jogador. Alguns símbolos podem não ter um glifo disponível em determinado aparelho; outros ocupam mais espaço do que parecem. Isso não significa que o GGBoard tenha esses problemas hoje — é uma proposta de verificação para futuras versões.
Um teste editorial e de experiência do usuário pode comparar quatro exemplos no fluxo de entrada, no painel da sala e no histórico:
AnaeJoão: nomes comuns, incluindo acento da língua portuguesa;Joãoem composições Unicode diferentes: conferir se grafias visualmente iguais são tratadas de modo consistente;★Luna★: observar se os símbolos ficam legíveis sem apertar os elementos da interface;- um nome com caractere invisível: verificar se a identificação não se torna ambígua para os demais jogadores.
Para montar amostras de teste, uma coleção de símbolos para copiar pode fornecer exemplos como ★, ♡ e ✦; copiar um caractere não garante que o jogo o aceite ou apresente corretamente. Em ambientes que exigem identificação única, também vale considerar o alerta do Unicode Consortium sobre caracteres visualmente confundíveis.
A proposta não é aumentar a quantidade de estilos no jogo, mas definir, documentar e testar quais caracteres fazem sentido para o campo de nome, preservando desempenho, clareza e acessibilidade.
O tabuleiro é um arquivo de dados
A decisão mais importante do GGBoard foi separar o jogo do tabuleiro. O código não sabe nada sobre semáforos, patos ou obras na pista. Ele só sabe ler um arquivo JSON que descreve as casas e o que cada uma faz.
O tabuleiro padrão, o Mapa Urbano, é um arquivo com uma imagem de fundo de 1200 por 900 e uma lista de 90 casas. Cada casa tem uma posição, um tipo e as casas para onde se pode ir a partir dela:
{
"id": 14,
"x": 412, "y": 238,
"type": "special",
"connections": [15],
"message": "Semáforo fechado! Fique uma rodada sem jogar.",
"action": { "type": "SKIP_TURN" }
}(Exemplo ilustrativo do formato; os campos são os que o jogo lê.)
Os tipos de casa são cinco:
| Tipo | O que é |
|---|---|
start | a saída |
normal | uma casa comum |
special | uma casa com efeito |
roundabout | uma rotatória, ponto em que o caminho se divide |
end | a chegada |
E as casas especiais usam um conjunto pequeno de ações:
| Ação | Efeito |
|---|---|
MOVE | leva o jogador direto para outra casa |
MOVE_TO_START | volta para o início |
SKIP_TURN | perde a próxima vez |
EXTRA_TURN | joga de novo |
FORCE_STOP | para o movimento ali, mesmo que o dado mande andar mais |
CONDITIONAL_DICE | rola o dado de novo; o resultado par ou ímpar decide o que acontece |
EVEN_ODD_BIFURCATION | rola o dado; par vai para um caminho, ímpar para outro |
No Mapa Urbano, das 90 casas, 20 são especiais e 12 são rotatórias. Os temas vêm da cidade: semáforo, viatura de polícia, faixa de pedestres, patos atravessando, obras, uma árvore caída, trem e posto de gasolina.
Por que isso importa
Com o tabuleiro em dados, criar um tabuleiro novo não exige mexer no código. Basta outra imagem e outro arquivo JSON. Quem cria a sala pode inclusive carregar um arquivo de tabuleiro próprio antes de começar a partida.
Isso também deixa as regras fáceis de testar: sete ações bem definidas, combinadas de formas diferentes, rendem muitos tabuleiros possíveis.
Multiplayer sem servidor de jogo
O GGBoard não tem um servidor que calcula a partida. Os navegadores dos jogadores se conectam diretamente, por WebRTC, usando a biblioteca PeerJS.
O host manda
Um dos jogadores é o host: quem criou a sala. O código da sala é, na verdade, o identificador do navegador dele na rede do PeerJS. Quem recebe o link conecta direto nesse navegador.
O host guarda o estado oficial da partida: onde está cada jogador, de quem é a vez, quem está preso. Os outros jogadores não mexem no estado; eles só fazem pedidos:
// Um jogador que não é o host, quando clica em "Rolar Dado"
host.send({ type: 'ROLL_DICE_REQUEST' });
// No host
if (state.activePlayerId === senderId &&
state.gameState === 'playing' &&
!state.isMoving) {
rollDice();
broadcast({ type: 'GAME_STATE_SYNC', state: serialize() });
}O host confere três coisas antes de aceitar: é mesmo a vez de quem pediu, a partida está em andamento e ninguém está se movendo naquele momento. Um pedido fora de hora é simplesmente ignorado. A escolha de caminho numa encruzilhada passa pela mesma checagem: o host só aceita um caminho que esteja entre as opções daquela casa.
Depois de cada mudança, o host manda o estado completo para todos. Assim, mesmo que alguém perca uma mensagem, a próxima corrige tudo.
Quem cai, volta
Se um jogador perde a conexão no meio da partida, o host o marca como desconectado e pula a vez dele, para que o jogo não trave esperando. Quando ele volta, reassume o lugar. Também dá para entrar numa partida que já começou.
O custo do peer-to-peer
Sem servidor de jogo, o GGBoard não tem custo de servidor por partida, e a latência entre amigos costuma ser baixa. O preço é que a partida depende do host: se quem criou a sala fechar a aba, a sala fecha para todos. Para um jogo de tabuleiro, em que as partidas são entre conhecidos e por turnos, essa troca vale a pena.
O mundo em 3D
O GGBoard usa Three.js para desenhar o tabuleiro em 3D. A arte do tabuleiro é uma imagem estendida no chão; por cima dela ficam as casas, como círculos, e as ligações entre elas, como linhas semitransparentes.
Luz e sombra
São duas luzes: uma ambiente, que clareia tudo por igual, e uma direcional, que funciona como o sol e projeta sombras. As sombras usam um mapa de 2048 por 2048 com bordas suaves. Elas parecem um detalhe, mas são o que faz os personagens parecerem apoiados no tabuleiro, e não flutuando sobre ele.
O jogo também limita a resolução em telas de altíssima densidade a 2× para não pesar em placas de vídeo mais simples.
Os personagens
Os sete personagens são modelos OBJ com materiais MTL e texturas PNG, arquivos entre 36 KB e 174 KB. No saguão, o personagem escolhido gira devagar numa prévia 3D.
O movimento casa a casa leva 450 ms por casa. A posição vai de uma casa à outra em linha reta, e a altura segue uma curva de seno, o que cria o pulinho:
// t vai de 0 a 1 durante o passo
piece.position.lerpVectors(from, to, t);
piece.position.y = 3 + 35 * Math.sin(t * Math.PI);Uma linha de código e o peão deixa de deslizar para "pular" de casa em casa, como uma peça de verdade.
As câmeras
A tecla V alterna entre uma câmera em perspectiva, que segue o jogo de perto, e uma ortográfica vista de cima, que mostra o tabuleiro inteiro sem distorção. Um segundo botão escolhe o que a câmera acompanha: o seu personagem, quem está jogando agora, a câmera livre ou qualquer outro jogador. Arrastar o mouse gira a câmera em volta do ponto que ela está olhando.
Seguir automaticamente quem está jogando é o que torna a espera interessante: mesmo fora da sua vez, você vê o peão do amigo cair numa casa de "volte ao início".
O dado
O dado é um número de 1 a 6 sorteado pelo host. Na tela, uma caixa troca de número 16 vezes, a cada 75 ms, antes de parar no resultado. Como o sorteio acontece só no host, ninguém consegue "escolher" o próprio dado.
O editor de tabuleiros
Montar 90 casas à mão, uma por uma, seria lento. Por isso o GGBoard tem um editor, uma página separada, que trabalha em 2D sobre a imagem do tabuleiro:
- Você carrega a imagem de fundo.
- O botão Auto-Detectar Casas varre os pixels da imagem procurando círculos claros e cria uma casa em cada um.
- Com as ferramentas Adicionar, Conectar, Remover e Editar, você ajusta o que a detecção errou, liga as casas na ordem do caminho e define o tipo e a ação de cada uma.
- As setas do teclado movem a casa selecionada 5 pixels, ou 1 com Shift, para alinhar no lugar certo.
- No fim, o editor exporta o arquivo JSON do tabuleiro, pronto para o jogo carregar.
A detecção automática é o que torna o editor prático: a arte do tabuleiro já tem as casas desenhadas, e o editor só precisa descobrir onde elas estão.
Resumo
- Tabuleiro descrito em JSON: 5 tipos de casa e 7 ações; o Mapa Urbano tem 90 casas
- Multiplayer peer-to-peer com PeerJS; o host valida os pedidos e envia o estado a todos
- Three.js com luz direcional e sombras suaves, câmeras em perspectiva e de cima
- Peões que pulam casa a casa com uma curva de seno
- Editor com detecção automática de casas na imagem do tabuleiro
Texto técnico original: Rafael Guimarães dos Santos. Colaboração editorial nesta atualização: Sophie Ale.
