Communitygithub.com

takumifukasawa/agent-skills

Gera listagens da Google Play Store para jogos: o GRÁFICO DE DESTAQUE promocional (1024x500) e CAPTURAS DE TELA de celular/tablet capturadas do jogo real rodando em Chromium headless, além do TEXTO da listagem (descrição curta/completa). Use quando o usuário pedir para 'criar imagens para loja / assets para loja / capturas de tela da Play Store / gráfico de destaque / descrição da loja / ストア画像 / スクショ(スクリーンショット)一式 / フィーチャーグラフィック / 簡単な説明 / 詳しい説明 / Play Console の説明文', ou para capturar muitas capturas de tela de jogabilidade para escolher. Também constrói uma PÁGINA SELETORA (um Artifact com caixas de seleção, comentários por seção e botões de cópia) para que o usuário marque o conjunto da loja — capturas de tela, gráfico de destaque, texto da listagem e candidatos a ícone do aplicativo também ('選定用の artifact', 'チェックして選びたい', 'アイコンも一緒に選びたい'). Funciona para jogos Phaser/PixiJS/Three.js/canvas puro servidos localmente. Irmão de `game-icon-from-screenshot` (esse faz o ícone quadrado do aplicativo; seus candidatos podem ir neste seletor). Não serve para arte promocional desenhada à mão/do zero.

O que é agent-skills?

Esta skill automatiza a produção de assets para a listagem de jogos na Google Play Store, capturando conteúdo diretamente do jogo em execução (sem maquetes). Ela gtrês entregáveis principais: (1) capturas de tela nos tamanhos exatos da Play Store para celular e tablet, (2) um gráfico de destaque de 1024x500 (banner promocional), e (3) o texto da listagem (descrição curta e completa). A skill usa Playwright + Chromium em modo headless para capturar o jogo em execução local (via `npm run dev`) e garante fidelidade visual — sem fundos verdes ou arte mockup. O fluxo de trabalho prioriza variedade: captura muitos quadros (fases, momentos de jogo, pausa, resultado) e oferece ao usuário uma página seletora interativa (Artifact) com checkboxes, comentários e botões de cópia para ele escolher o conjunto final. A página suporta grupos de imagens (seleção múltipla ou única), opções de texto com contagem de caracteres, e decisões salvas em banco de dados local (`picks/current`). Para evitar artefatos visuais, a skill usa supersampling (deviceScaleFactor=2 e redução de escala) em vez de upscale. Inclui também suporte para animação posada (via `pose-capture.cjs`) para jogos com pós-processamento WebGL (bloom) que travariam em headless, movendo o sujeito manualmente ao longo de curvas Bezier para gerar rastros de movimento realistas. As plataformas suportadas são jogos Phaser, PixiJS, Three.js ou canvas puro. Um script de contato (`contact_sheet.py`) gera folhas de contato numeradas para pré-seleção. A skill não se aplica a arte promocional desenhada à mão ou feita do zero.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/takumifukasawa/agent-skills/tree/HEAD/gamedev/game-store-assets

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Assets de loja para jogos a partir do jogo em execução

Produza imagens fiéis para a loja capturando o jogo real em execução (sem maquetes). O harness reutilizável está em scripts/; você fornece uma configuração PEQUENA e ESPECÍFICA DO JOGO (URL, verificação de prontidão, como posicionar/esconder/dirigir o jogo, texto do título).

Três entregáveis:

  1. Capturas de tela (scripts/screenshots.cjs) — lotes nos tamanhos exatos da loja.
  2. Gráfico de destaque — banner promocional de 1024x500: ou um quadro limpo do jogo ocupando toda a área (screenshots.cjs com um target paisagem) ou uma composição de quadro + título (scripts/feature_graphic.py). Pergunte qual; o padrão é quadro inteiro para jogos baseados em clima/atmosfera.
  3. Texto da listagem — descrição curta + completa: leia references/listing-text.md (limites, tom, o que não alegar, a pergunta do formulário "assets gerados por IA").

Para requisitos de tamanho da loja + predefinições, leia references/play-store-sizes.md.

Fluxo de trabalho: muitos candidatos → o usuário escolhe

O usuário escolhe; você fornece variedade e facilita a escolha.

  1. Capture muitos quadros por fase/nível/tema (ex.: 5 fases × 8–10). Use a progressão do jogo para variá-los também (estágios de atualização inicial / intermediário / final): quadros iniciais parecem vazios, os finais podem esconder o assunto.
  2. Pré-selecione ~15 com variedade (cada fase; momentos de passe/explosão; fundos ricos vs calmos). Olhe para eles primeiro com scripts/contact_sheet.py (numerado). Depois deixe o usuário escolher — na página seletora (abaixo) quando os Artifacts estiverem disponíveis, que é o que os usuários pediram depois de vê-la; caso contrário, envie a imagem da folha numerada e pegue os números.
  3. Recomende um conjunto, com uma justificativa (variação de cor / fase no conjunto, sem repetir o mesmo momento). Refaça as capturas conforme os comentários do usuário pedirem ("被写体をもう少し中央に") e adicione as refilmagens ao seletor como outro grupo compartilhando a mesma chave de escolha.
  4. Copie o conjunto final em uma pasta na ordem de upload (1-…png, 2-…), e registre a decisão (arquivos, texto escolhido na íntegra, opções rejeitadas e por quê, como foram capturadas) na documentação do projeto. Mantenha as imagens geradas fora do git, a menos que o projeto já rastreie assets da loja.

python3 ${CLAUDE_SKILL_DIR}/scripts/contact_sheet.py sheet.png a.png b.png … --cols 5 --thumb 300 (A técnica base de Playwright/canvas é compartilhada com game-icon-from-screenshot.)

Página seletora (Artifact): o usuário marca o conjunto

python3 ${CLAUDE_SKILL_DIR}/scripts/build_picker.py picker.json picker.html, depois publique picker.html com a ferramenta Artifact e capabilities: {"db": {}} (carregue as skills artifact-design / artifact-capabilities primeiro, pois a ferramenta Artifact exige). O formato da especificação está no cabeçalho de scripts/build_picker.py.

O que a página faz (tudo da especificação, nada específico do jogo no template):

  • Grupos de imagens — mode: "multi" (capturas de tela: ordem de marcação = ordem de upload, ← → para reordenar, max) ou "single" (gráfico de destaque, ícone do aplicativo). Vários grupos podem compartilhar uma chave pick (primeiros candidatos + refilmagens = uma escolha). Chips "おすすめ" de rec, ⤢ zoom, uma bandeja no topo mostrando o conjunto escolhido, e um botão "おすすめで埋める".
  • Grupos de texto — opções de rádio com o texto de submissão, uma tradução abaixo, contagem de caracteres contra limit, e um botão de cópia no canto superior direito de cada opção (copia apenas o texto de submissão).
  • Comentários como instruções — uma caixa de comentários por grupo (noteKey) mais uma geral "全体への指示". Os usuários escrevem instruções lá ("2 案目の最後の行はいらない", um rascunho em japonês para editar e traduzir) e dizem "保存しました".
  • Escolhas — pequenos rádios sim/não (texto do título no gráfico de destaque ou não).
  • Tudo é salvo em um documento do banco de dados (doc, padrão picks/current); leia com ArtifactData (action: "get", collection: "picks", doc_id: "current" para o padrão). As chaves de escolha / pick / nota compartilham esse documento, então o construtor recusa chaves conflitantes e ids de itens duplicados sempre que o usuário disser que salvou. Aja em cada nota não vazia, depois diga o que mudou. Para alterar a página, edite a especificação e reconstrua, depois publique o mesmo caminho de arquivo (mesma URL; as escolhas salvas sobrevivem porque os ids permanecem os mesmos). Após a primeira publicação, faça uma leitura de picks para confirmar as respostas da loja.

Ícones do aplicativo usam a mesma página: um seletor só de ícones (um grupo "single", aspect: "1/1", thumbWidth: 512) ou ícones como mais um grupo ao lado das imagens da loja. Faça os candidatos com game-icon-from-screenshot.

Pré-requisitos

  • Jogo rodando em uma URL local (npm run dev → ex.: http://localhost:5173).
  • Playwright + Chromium (resolvido automaticamente do node_modules do projeto, do cache do npx ou global; caso contrário, npx playwright install chromium).
  • Pillow (python3 -m pip install Pillow) para redimensionar/compor.
  • Um arquivo de fonte empacotado para o título do gráfico de destaque (um .ttf; fontes variáveis suportadas via eixos de peso).

Anti-aliasing: supersample, não upscale

Capturar no tamanho exato de saída parece serrilhado. Capture denso e depois reduza: defina superSample (deviceScaleFactor) = 2 e deixe o harness reduzir para o tamanho alvo. IMPORTANTE: faça a densidade com superSample, não usando um viewport muito maior — um canvas muito grande pode travar o loop de renderização/física headless (o jogo congela na inicialização). Mantenha o viewport em um tamanho onde o jogo realmente funcione (ex.: 1080x1920) e aumente superSample.

Capturas de tela

node ${CLAUDE_SKILL_DIR}/scripts/screenshots.cjs <config.(cjs|json)> [outDir]

  • Fotos posadas estáticas (shots): cada uma abre uma página nova, executa sua função pose (definir pontuação, posicionar o assunto, mostrar a sobreposição de resultado…), opcionalmente esconde o HUD e captura. Bom para telas de pronto/resultado.
  • Jogabilidade ao vivo (live): inicia o jogo, executa sua função keepAlive a cada verificação para manter o jogo ativo/dirigindo (um "piloto automático"), e captura quadros quando sua função pick diz que o quadro é bom. Quadros ao vivo mostram movimento real/rastros — é assim que você obtém fotos "realmente jogando", não estáticas. Capture muitos e deixe o usuário escolher.

Os campos fn são executados NA PÁGINA. Em uma config .cjs escreva funções reais (elas são serializadas com .toString(), então mantenha-as autocontidas — sem closures do Node). Em uma config .json escreva strings de função de seta.

  • Temporizado (timed): para jogos que podem jogar sozinhos (um parâmetro ?autoplay=, um modo demo/atração). Abre cada url por vez e captura a cada intervalMs — a maneira mais fácil de obter muitos quadros reais por fase.

Config (capturas de tela)

url, ready, target {width,height} (tamanho de saída + viewport), viewport (tamanho de captura quando deve ser diferente; os quadros são cortados para a proporção alvo, cropY ancora a faixa), superSample (2), launchArgs, executablePath, css (injetado; a maneira mais simples de esconder HUD para HUDs DOM), routes [{match, replace: [[from,to]…]}] (correções apenas na captura do código servido), waitFontsReady (true), hudHide (fn), shots [{name, pose?, hideHud?, settleMs?}], live {count, start, keepAlive, pick, hideHud, buildMs, pollMs, settleMs, maxTries, prefix}, timed {urls: [{prefix, url}], count, warmupMs, intervalMs}, outDir. A documentação completa dos campos está no cabeçalho de scripts/screenshots.cjs.

O Playwright deve ser resolvível: execute a partir de um diretório com ele em node_modules, ou NODE_PATH=<dir>/node_modules node ${CLAUDE_SKILL_DIR}/scripts/screenshots.cjs … (CommonJS). Se o navegador instalado for mais antigo que o pacote, o script usa o Chromium mais recente no cache ms-playwright.

Exemplo (WebGL / three.js, auto-jogável, HUD DOM)

{
  "target": {"width": 1080, "height": 1920},
  "viewport": {"width": 540, "height": 960},
  "superSample": 2,
  "launchArgs": ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"],
  "css": "body > div:not(#app) { display: none !important; }",
  "timed": {
    "urls": [{"prefix": "stage1", "url": "http://localhost:5173/?level=1&autoplay=1"},
             {"prefix": "stage2", "url": "http://localhost:5173/?level=2&autoplay=1"}],
    "count": 10, "warmupMs": 3000, "intervalMs": 3500
  }
}

Banner 1024x500 de um jogo apenas retrato (ele coloca letterbox em viewports largas, e seu mundo termina além da visão retrato): levante o limite de proporção apenas para a captura, capture um viewport mais quadrado em 2x, corte a faixa.

{
  "target": {"width": 1024, "height": 500},
  "viewport": {"width": 820, "height": 640},
  "superSample": 2,
  "launchArgs": ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"],
  "css": "body > div:not(#app) { display: none !important; }",
  "routes": [{"match": "GameConfig", "replace": [["maxAspect: 0.6", "maxAspect: 0"]]}],
  "timed": {"urls": [{"prefix": "banner", "url": "http://localhost:5173/?level=1&autoplay=1"}],
            "count": 6, "warmupMs": 3000, "intervalMs": 5000}
}

Exemplo (Phaser, retrato; o dev expõe window.__game)

Um exemplo trabalhado, de um jogo específico (um jogo de bola passando por um aro). Substitua pelos seus próprios nomes de cena/objeto, lógica de pose e condições de direção/escolha.

// phone.shots.cjs
const G = () => window.__game.scene.scenes[0]; // helper na página (embutido abaixo)
module.exports = {
  url: "http://localhost:5173/",
  target: { width: 1080, height: 1920 },
  superSample: 2,
  ready: () => !!(window.__game?.scene?.scenes?.[0]?.ring),
  hudHide: () => { const s = window.__game.scene.scenes[0]; ["scoreText","bestText","hintText"].forEach(k => s[k]?.setVisible(false)); },
  shots: [
    { name: "ready", hideHud: false },
    { name: "result", hideHud: true, settleMs: 1200, pose: () => {
        const s = window.__game.scene.scenes[0];
        s.matter.world.pause(); s.score = 18; s.highScore = 18; s.lastRunNewRecord = true; s.showResult();
    } },
  ],
  live: {
    count: 24, prefix: "play", pollMs: 45, buildMs: 700,
    startTap: true,            // toque confiável para começar (Phaser ignora eventos sintéticos)
    hideHud: false,            // manter a pontuação visível nas capturas de jogabilidade
    start: () => { window.__game.scene.scenes[0].goals = 6; }, // configuração extra (ex.: ativar variedade)
    keepAlive: () => { const s = window.__game.scene.scenes[0]; if (s.state === "playing" && s.ball.y > s.scale.height * 0.5) s.ball.setVelocityY(-13 * s.scaleFactor); },
    pick: () => { const s = window.__game.scene.scenes[0]; return s.state === "playing" && s.ball.body.velocity.y > 4; },
  },
};

Conjunto para tablet: mesma config com target: { width: 1440, height: 2560 }. (Você também pode upscalar as capturas de celular escolhidas para 1440x2560 para manter os quadros exatos escolhidos.)

Animação posada (scripts/pose-capture.cjs) — para jogos com pós-efeitos/bloom

O modo live assume que a física do jogo roda em headless. Muitas vezes não roda. Um pipeline de bloom / pós-efeito WebGL pode TRAVAR o loop de renderização+física headless, então o assunto congela na sua posição inicial (a velocidade é definida mas a posição nunca é integrada) — cada quadro "ao vivo" sai idêntico, o assunto preso no spawn. Isso pegou forte em um projeto real. Sintoma para verificar primeiro: meça o y do assunto em duas verificações; se nunca mudar, a física está travada.

Quando isso acontecer, não lute contra — dirija o assunto manualmente:

node ${CLAUDE_SKILL_DIR}/scripts/pose-capture.cjs <config.(cjs|json)> [outDir]

Cada captura pausa o mundo, depois anima o assunto ao longo de um caminho que VOCÊ define ao longo de N quadros (com qualquer emissor de partículas/rastro funcionando), para que um rastro de movimento real se construa independentemente da física. Config por captura: {name, setup, poseAt(t), steps, stepMs} — setup executa uma vez e coloca a cena no estado da captura (ex.: em um jogo de bola e portão: spawnar o portão, state="playing", world.pause(), trail.start(), esconder a dica de pronto, definir uma pontuação), poseAt(t) posiciona o assunto para t em [0..1].

O que faz um rastro posado parecer movimento real (o assunto do jogo exemplo cai sob gravidade; adapte para como o seu se move):

  • Caminho Bezier, não um lerp reto. Um caminho reto produz um rastro rígido e reto como uma régua; uma Bezier quadrática com um deslocamento lateral do ponto de controle dá o arco curvo natural que o jogo realmente produz. Varie a curva por captura.
  • Ease-in t*t (acelerando) parece gravidade.
  • Aumento moderado do emissor apenas para a captura (frequency maior + lifespan mais longo), porque a animação manual é mais lenta que uma queda rápida real e o emissor padrão deixa poucas partículas. Aumente MODERADAMENTE — um rastro exagerado não se parece com o jogo.

Gráfico de destaque

python3 ${CLAUDE_SKILL_DIR}/scripts/feature_graphic.py <config.json>

Compõe: fundo escuro → captura de tela real do jogo (esquerda, moldura neon) → título + tagline (direita, fonte empacotada) → linha de destaque. Renderizado em superSamplex e depois reduzido.

Config: out, size ([1024,500]), superSample (2), screenshot, font, title, tagline, bg, frameColor, titleColor, titleGlow, tagColor, accentColor, titleWeight, tagWeight.

Os valores de exemplo abaixo são de um jogo específico — substitua pelo seu próprio título, tagline, cores e fonte.

{
  "out": "store-assets/feature-graphic-1024x500.png",
  "screenshot": "store-assets/feature-shot.png",
  "font": "public/fonts/Orbitron.ttf",
  "title": "MEU JOGO", "tagline": "ARCADE DE UM TOQUE",
  "bg": "#0a0a1c", "frameColor": "#27e6ff",
  "titleColor": "#ffffff", "titleGlow": "#27e6ff", "tagColor": "#8fffe0", "accentColor": "#27e6ff",
  "titleWeight": 800, "tagWeight": 600
}

Lições (faça estas)

  • WebGL em headless precisa de SwiftShader. Sem --use-angle=swiftshader --enable-unsafe-swiftshader um canvas three.js fica preto. É lento: abra uma página por vez (5 páginas WebGL paralelas causaram timeout na chamada de captura de tela).
  • Esconder o HUD com CSS: não esconda o pai do canvas. Um div { visibility: hidden } genérico escondeu o canvas também (todo quadro idêntico, ~8 KB). Examine o DOM uma vez e exclua o wrapper do canvas por id. Arquivos idênticos e minúsculos = nada renderizado.
  • Jogos apenas retrato e banners paisagem. Um limite de proporção máxima coloca letterbox em um viewport largo (uma coluna estreita no meio). Levante-o por captura via routes; os arquivos do jogo são corrigidos em tempo real (substituições no código servido), sem modificar o código fonte. O jogo continua retrato em todos os outros lugares; o banner usa uma viewport mais quadrada.
  • Lag de física em capturas cronometradas. Física em tempo real ignora quadros quando a renderização atrasa — a cena avança em saltos, não suavemente. Defina warmupMs alto o suficiente para o SwiftShader se estabilizar (pelo menos 3 s). Este é o maior gargalo dentro do controle da skill.

Integração: assets da loja em um projeto novo

  1. O dev executa npm run dev e o jogo abre no navegador.
  2. Configure a skill: peça ao dev o URL local, os nomes dos objetos da cena, como posar para a tela de resultados, como iniciar uma nova corrida, como esconder o HUD e quais níveis jogar. Para isso, veja as cenas e variáveis globais do jogo nos arquivos-fonte (window.__game, scene.scenes[0], etc. — as exposições variam por framework).
  3. Capture os quadros (centenas), faça a pré-seleção para ~15 imagens variadas, depois publique o seletor. Deixe o dev escolher, guarde a escolha, produza os tamanhos da loja (phone + tablet) e o texto do anúncio.
  4. Se o gráfico de destaque existir, componha-o com a captura escolhida. Pergunte "texto do título no banner?" — isso é uma opção no seletor.
  5. Publique a saída final para o dev: uma pasta store-assets/ com phone/, tablet/ e arquivos de texto. Registre as decisões. Não adicione ao git a menos que o projeto já version esses arquivos.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Habilidades Relacionadas