Como criar um jogo para o Catálogo de Games

Guia técnico completo para quem (ou qual IA) for gerar um jogo HTML5/JS para a plataforma. Os limites e opções aqui embaixo são lidos direto da configuração do servidor — se mudarem, esta página muda junto.

1. O ambiente onde o jogo roda

O jogo é servido dentro de um <iframe sandbox="allow-scripts"> sem allow-same-origin e sem allow-forms/allow-popups/allow-top-navigation. Na prática:

Resumindo: o jogo tem que ser 100% autocontido e falar com o mundo só pelo GameSDK.

2. Estrutura do arquivo .zip

3. GameSDK — a única forma de falar com a plataforma

Inclua antes do seu script principal:

<script src="/sdk/game-sdk.js?v=09c5ad64d2"></script>

O ?v=... é opcional mas recomendado — sem ele, o navegador de quem joga ainda funciona (o servidor nunca deixa isso ficar em cache velho de qualquer forma), mas com ele o arquivo pode ser cacheado com segurança em qualquer CDN na frente do site.

Isso expõe o objeto global GameSDK. Não precisa esperar nenhum evento de "pronto" — o SDK enfileira as chamadas até a página pai confirmar o handshake. Referência completa, método a método:

GameSDK.startSession()

Chame no início de cada partida — a plataforma passa a rastrear essa sessão internamente, não existe token pra você gerenciar. Retorna Promise<void>.

await GameSDK.startSession();

GameSDK.addScore(valor)

valor é um incremento (ex: pegou moeda = +10) — nunca o placar total. Sujeito a teto por evento e rate limit (ver seção 4). Devolve o total acumulado pelo servidor até agora.

var res = await GameSDK.addScore(10); console.log(res.accumulatedScore); // total real, some com o que você já mandou antes

GameSDK.finishSession(stats)

Encerra a partida atual. O placar final devolvido é sempre o que o servidor já acumulou — não dá pra passar um valor aqui. stats é opcional: um array de até 8 { label, value } só de exibição (nunca persistido, nunca usado pra calcular nada) — a plataforma monta uma tabelinha "Resultado final" com eles, ANTES do placar, na tela de fim de jogo.

var res = await GameSDK.finishSession([ { label: 'Ouro ganho', value: totalGold }, { label: 'Inimigos derrotados', value: kills }, { label: 'Ondas resistidas', value: wave }, ]); alert('Placar final: ' + res.score);

GameSDK.getLeaderboard()

Top 50 scores visíveis do jogo — o mesmo dado que aparece no ranking público da página do jogo.

var scores = await GameSDK.getLeaderboard(); // [{ player_name, score, created_at }, ...]

GameSDK.save(dados) / GameSDK.load()

Um slot de JSON livre por (jogo, usuário), até 256KB — organize múltiplos "slots" dentro do próprio objeto se precisar. load() devolve undefined se nunca salvou nada.

await GameSDK.save({ level: 3, coins: 120 }); var data = await GameSDK.load();

GameSDK.onPause(fn) / GameSDK.onResume(fn)

A plataforma chama isso quando o jogador sai da tela cheia sem querer (gesto do sistema, botão físico) — pause seu loop/áudio ali; onResume dispara quando ele volta. Registre uma vez, no início; não precisa (nem deve) ser chamado manualmente pelo seu código.

GameSDK.onPause(function () { engine.pause(); }); GameSDK.onResume(function () { engine.resume(); });

GameSDK.requireOrientation(orientation)

Declare 'landscape' ou 'portrait' se seu jogo só faz sentido numa orientação. No celular, a plataforma tenta travar de verdade (quando o navegador suporta) e, quando não dá, bloqueia a visão do jogo com um aviso pra girar o aparelho — sem que você precise tratar isso na mão. Chame uma vez, no início.

GameSDK.requireOrientation('landscape');

GameSDK.onMuteChange(fn)

O jogador pode ligar/desligar o som geral pela barra lateral da sandbox — a plataforma não tem áudio próprio pra mutar (quem toca som é o seu jogo), então só avisa: fn(true) = mutar, fn(false) = religar. Opcional — sem handler registrado, o toggle simplesmente não tem efeito no seu jogo.

GameSDK.onMuteChange(function (muted) { bgMusic.muted = muted; sfx.muted = muted; });

4. Regra de ouro do anti-cheat — nunca envie um placar pronto

Não existe GameSDK.setScore(numero) de propósito. O placar final salvo é sempre a soma que o próprio servidor acumulou, evento a evento, via GameSDK.addScore(valor). Não dá pra forjar o placar no DevTools chamando uma função com o número que quiser — essa função não existe.

5. Boas práticas de UI dentro do jogo

6. Metadados do cadastro (fora do zip)

Ícone, capa (nas duas variantes) e screenshots passam pelo mesmo reencode automático (PNG/JPG → WebP) citado na seção 2.

7. O que acontece depois do envio

  1. Validação automática (fila): zip-slip, extensões, antivírus, reencode de imagem — qualquer falha é logada com o motivo específico, visível pro dev.
  2. Se passar, o jogo fica pending aguardando aprovação manual de um admin.
  3. Só depois de aprovado ele aparece no catálogo público.

A conta que envia precisa ter e-mail confirmado e papel de dev (ou admin) — conta comum não publica jogo.

8. Exemplo mínimo completo

Zipe esse index.html sozinho (na raiz do zip) e já está pronto pra enviar pelo formulário. Cobre só o essencial (sessão + placar); os recursos opcionais (save/load, pause/resume, orientação, som) têm exemplo próprio na seção 3, junto da regra de cada um.

<!doctype html> <html lang="pt-br"> <head> <meta charset="utf-8"> <title>Meu Jogo</title> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <style> html, body { margin:0; height:100%; overflow:hidden; background:#111; touch-action:none; } #score { position:fixed; top:14px; left:14px; color:#fff; font:700 18px system-ui; } button { font:600 15px system-ui; padding:12px 24px; border-radius:8px; border:0; cursor:pointer; } </style> </head> <body> <div id="score">0 pontos</div> <button id="tap" style="position:fixed;bottom:24px;left:50%;transform:translateX(-50%)">Toque aqui</button> <script src="/sdk/game-sdk.js?v=09c5ad64d2"></script> <script> var scoreEl = document.getElementById('score'); var score = 0; var playing = false; async function start() { try { await GameSDK.startSession(); playing = true; } catch (err) { console.error('Erro ao iniciar sessão:', err.message); } } document.getElementById('tap').addEventListener('pointerdown', async function () { if (!playing) return; score += 1; scoreEl.textContent = score + ' pontos'; try { var res = await GameSDK.addScore(1); score = res.accumulatedScore; // servidor manda — sincroniza com o total real scoreEl.textContent = score + ' pontos'; } catch (err) { score -= 1; // evento rejeitado (ex: rate limit) — desfaz o incremento otimista scoreEl.textContent = score + ' pontos'; } }); start().then(function () { setTimeout(async function () { playing = false; var res = await GameSDK.finishSession(); alert('Fim de jogo! Placar final: ' + res.score); }, 30000); }); </script> </body> </html>

9. Erros comuns que rejeitam o jogo ou quebram em produção