Communitygithub.com

takumifukasawa/agent-skills

Captura el video REAL de un juego web/canvas en ejecución y conviértelo en un VIDEO PUBLICITARIO terminado (ej. Meta / TikTok, 800x1000 o 1080x1920, 15-30 s, con sonido). Graba en Chromium (canvas.captureStream + MediaRecorder -> ffmpeg) con el sonido WebAudio del juego y una línea de tiempo por fotograma, luego edita una lista de cortes (JSON) para crear el anuncio: cambios de velocidad, fundidos, una pista musical, cortes y pases de puerta que caen en el ritmo, con normalización de volumen. También cubre cómo hacer que la grabación sea reproducible (con semilla y paso fijo), un piloto automático similar al humano que siempre supera el juego, y scripts de nivel exclusivos para grabación. Úsalo cuando el usuario pida "capturar video de juego / grabar el juego / hacer un video publicitario / 広告動画を作りたい / 広告用の動画 / プレイ動画を撮る / ゲームプレイをキャプチャ / mp4 で録画 / カットをつないで / 音楽に合わせて / 倍速にして / 人間っぽい自動操作 / 録画用のステージ", o quiera material para editar después (After Effects, etc.). Funciona para juegos Phaser/PixiJS/Three.js/canvas puro servidos localmente. Es complemento de `game-store-assets` (capturas de pantalla + gráfico destacado) y `game-icon-from-screenshot` (icono cuadrado de aplicación).

¿Qué es agent-skills?

Esta habilidad te permite capturar el video real de un juego web basado en canvas (Phaser, PixiJS, Three.js o canvas puro) y convertirlo en un anuncio publicitario listo para plataformas como Meta o TikTok. Utiliza `canvas.captureStream` junto con MediaRecorder en Chromium para grabar el búfer de renderizado del juego, incluyendo su sonido WebAudio, y genera una línea de tiempo por fotograma que detalla eventos como la puntuación, el ritmo y los pases de obstáculos. Luego, un editor de listas de cortes (JSON) permite ensamblar el metraje en un anuncio final con cambios de velocidad, fundidos, una pista musical de fondo sincronizada con el ritmo, y normalización de volumen (LUFS). La habilidad también incluye herramientas para hacer que la grabación sea reproducible mediante semillas y paso fijo, un piloto automático que simula un comportamiento humano y siempre completa el nivel, y scripts de nivel exclusivos para grabación que muestran las mecánicas del juego en un ritmo atractivo. Es compatible con Node.js y Playwright (Chromium), y requiere ffmpeg en el PATH. Los resultados son archivos mp4 listos para su publicación, con la calidad necesaria para anuncios profesionales.

Compatible con✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/takumifukasawa/agent-skills/tree/HEAD/gamedev/game-gameplay-video

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Video de juego y video publicitario desde el juego en ejecución

Dos capas:

  1. Capturar el juego real en ejecución a mp4 (scripts/capture-video.cjs) — imagen, opcionalmente su sonido, y una línea de tiempo por fotograma de lo que sucedió.
  2. Editar las tomas en el anuncio (scripts/ad-edit.cjs) a partir de una lista de cortes, o entregar los clips sin editar a un editor no lineal (NLE).

Entre ellas están las cosas que hacen que el metraje sea lo suficientemente bueno para un anuncio: una ejecución reproducible, un piloto automático similar al humano que siempre supera el nivel, y (opcionalmente) un script de nivel exclusivo para grabación para que el escenario muestre sus trucos con un buen ritmo.

npm run dev                                                     # el juego, ej. http://localhost:5173
node ${CLAUDE_SKILL_DIR}/scripts/capture-video.cjs game.capture.cjs 40 auto out take1
node ${CLAUDE_SKILL_DIR}/scripts/ad-edit.cjs ads/a.json out                  # lista de cortes -> out/<list.out>.mp4
node ${CLAUDE_SKILL_DIR}/scripts/probe-trajectory.cjs probe.cjs 30 t.json    # ajustar el piloto automático (headless, rápido)
python3 ${CLAUDE_SKILL_DIR}/scripts/plot-trajectory.py t.png t.json

Abre cada renderizado para el usuario (open <file> en macOS) tan pronto como se escriba; revisar es el bucle, así que no hagas que tengan que pegar rutas.

1. Captura (scripts/capture-video.cjs)

canvas.captureStream(fps) graba el búfer de respaldo del canvas, no la ventana, ni el DOM. Por lo tanto:

  • Sin cursor / sin HUD gratis. Las superposiciones del DOM (puntuación, botones, cursor de dedo de desarrollo) nunca aparecen en el video. Una imagen limpia es lo que suele querer un anuncio; añade texto en la edición.
  • La resolución de captura es independiente de la ventana. Aumenta el tamaño de renderizado interno del motor al tamaño del anuncio (o al doble y deja que la edición reduzca la escala — el sobremuestreo supera al antialiasing del motor) mientras la ventana se mantiene pequeña (ver Desacoplamiento).
  • Sonido (audio: true): cada conexión a los altavoces de un AudioContext se bifurca a un MediaStreamDestination y se graba con su propio MediaRecorder; ffmpeg los multiplexa. Desbloquea el AudioContext del juego en beforeRecord (Chromium se ejecuta con --autoplay-policy=no-user-gesture-required).
  • Línea de tiempo (timeline: () => ({state, score, beat, ...})): se muestrea en cada fotograma de la página y se escribe como <name>.timeline.json ({t, ...} en cada cambio). A partir de esto se corta: cuándo se pasó cada puerta, cuándo se superó el nivel, dónde está cada pulso de la música. Expón el pulso musical del juego como un entero beat si lo tiene.
  • Con o sin ventana (headed/headless). Headed es el valor predeterminado seguro. Headless funciona y no roba el foco (headless: true añade banderas de GPU) — pero verifica el conteo de fotogramas de una toma corta primero (ver Problemas).

Configuración

camposignificado
url (oblig.)URL del juego en ejecución, con su propia consulta de captura (semilla, paso fijo, script…)
ready (oblig.)() => boolean en la página
targettamaño de captura {width,height} (1080×1920 por defecto; ej. 1600×2000 para un anuncio de 800×1000)
displayHeightaltura del canvas en pantalla (760)
canvasSelectorel canvas del juego (canvas)
setRenderSize / setDisplaySizeen la página; aumentar el tamaño de renderizado del motor / reducir CSS y reasignar entrada
beforeRecorden la página, una vez antes de grabar (desbloquear audio, ocultar superposición de depuración…)
autopiloten la página, una vez justo después de iniciar la grabación, en modo auto
audio, timeline, headless, settle (ms)ver arriba
bitrate, fps, seconds, outDir, nameanulaciones (CLI: config seconds mode outDir name)

Las funciones se serializan (toString), así que mantenlas autocontenidas. Salidas: <name>.mp4 (H.264/AAC, para editar), <name>.webm (+ .audio.webm, los maestros — consérvalos), <name>.timeline.json.

Desacoplar la resolución de captura de la ventana

Por motor (la ventana se mantiene pequeña; captureStream graba el búfer de respaldo):

  • Phaser: g.scale.resize(w, h) y luego reinicia la escena (los juegos almacenan en caché un scaleFactor en create()); para juego manual también g.canvas.style.width/height + g.scale.refresh() para que los toques se asignen correctamente.
  • Three.js: renderer.setPixelRatio(w / cssW) y luego el redimensionamiento propio del juego con el tamaño CSS (para que la relación de aspecto de la cámara y los búferes de postFX lo sigan). Un juego con relación de aspecto limitada a vertical (con letterboxing hasta una relación máxima) debe tener el límite eliminado para una relación de anuncio más ancha como 4:5.
  • PixiJS: renderer.resize(w, h) + estilo del canvas.

Modos

  • manual — juegas la ventana a mano (solo headed). Lo más auténtico.
  • auto — el autopilot de la configuración conduce. Para anuncios, haz que parezca humano y que nunca falle — consulta references/autopilot.md.

2. Hacer la ejecución reproducible (vale la pena antes de ajustar nada)

Ajustar un corte, un piloto automático o un nivel significa regrabar muchas veces; si cada toma se desarrolla de manera diferente, nunca se puede converger. Añade dos interruptores exclusivos de captura al juego (parámetros de consulta; el juego distribuido los ignora):

  • seed — la aleatoriedad del juego (disposición de obstáculos y las elecciones del piloto automático) a partir de un PRNG con semilla en lugar de Math.random. Deja la aleatoriedad puramente visual intacta.
  • fixed step — simular en pasos fijos de 1/60 s con un acumulador (renderizar una vez por fotograma, ejecutar 0..n pasos). Los tiempos de fotograma reales fluctúan; un paso fijo hace que la simulación sea idéntica independientemente de la velocidad de fotogramas (60 Hz, 120 Hz, un fotograma perdido bajo una carga pesada de captura).

Entonces la misma URL es la misma ejecución — en el navegador para que el usuario la verifique, en la sonda y en la captura. Elige una buena ejecución por semilla (barre algunas con la sonda y elige según criterios medibles), no rehaciendo capturas al azar. Detalles y problemas: references/recording-scripts.md.

3. Edición (scripts/ad-edit.cjs)

Una lista de cortes (JSON) → un mp4 en el tamaño del anuncio (reducción de escala lanczos), CRF 17, AAC, volumen normalizado (por defecto −14 LUFS, a lo que normalizan las redes sociales). Campos principales:

{
  "out": "ad-a", "size": [800, 1000], "speed": 1.111,
  "fade": 0, "fadeOut": 0, "audioFadeIn": 0.3, "audioFadeOut": 0.8,
  "cuts": [{"take": "stage1", "from": 20.3, "to": 24.6, "passAt": 2}, {"take": "stage3", "from": 37.2, "to": 41.2}],
  "music": {"take": "music4", "snap": 4, "gates": true, "speedRange": [1, 1.25], "passAt": 2, "align": 4}
}
  • speed es la velocidad de reproducción. "Time stretch 85%" de After Effects es velocidad 1/0.85. El sonido mantiene su tono (atempo). Acelerado → se escribe a 60 fps (fotogramas descartados); ralentizado → a 60·velocidad fps (sin fotogramas repetidos).
  • music coloca UNA pista continua (una toma grabada con los efectos de sonido del juego silenciados) debajo de los cortes directos — el montaje entonces tiene una sola canción en lugar de una canción nueva por corte. snap recorta los cortes a compases enteros; gates da a cada corte su propia velocidad e inicio para que sus pases de puerta caigan en los pulsos de la pista; passAt coloca el primer pase N pulsos después del corte; align alinea la pista con un corte de la misma toma (su finalización y la cadencia de la canción coinciden).

Lo que hizo que los cortes se sintieran "en" o "fuera" de ritmo, y la receta de la pista musical: references/ad-editing.md.

4. Scripts de nivel exclusivos para grabación (opcional)

Cuando el escenario es demasiado uniforme para un anuncio, añade una disposición solo para captura: agujeros/obstáculos colocados por pulso con alturas y trucos explícitos, más puntos de ruta invisibles que solo el piloto automático sigue (un movimiento adicional sin obstáculos extra). Ponle ritmo para que un nuevo truco aparezca cada ~5 s y el primero se alcance en ~5 s. Consulta references/recording-scripts.md.

Problemas / lo que no funcionó (conserva el "por qué")

  • Un solo MediaRecorder con pistas de video + audio se atascó justo después del encabezado webm (decenas de bytes, ffmpeg "Error al analizar el encabezado EBML"). Dos grabadores iniciados en el mismo tick, multiplexados por ffmpeg, funcionan. Un "Error al analizar el encabezado del paquete Opus" de ffmpeg es el último fragmento parcial y es inofensivo.
  • Ctrl-C dejó un webm pero no mp4. Los manejadores de SIGINT/SIGTERM/SIGHUP propios de Playwright cierran el navegador y salen primero; inicia con handleSIG*: false y usa un process.on("SIGINT") persistente (un listener once se elimina a sí mismo y Node muere con la segunda señal).
  • Headless congeló el sujeto al inicio en un juego de Phaser con mucho bloom; en un juego de Three.js headless funcionó bien una vez que se habilitó la GPU (--use-angle=metal|d3d11|vulkan --enable-gpu --ignore-gpu-blocklist). Cuenta los fotogramas de una toma corta (ffprobe -count_frames) — ~60/s significa que es real.
  • Ventana sobredimensionada. No escalar la ventana; aumentar el tamaño de renderizado interno.
  • Redimensionar sin reiniciar → escala de juego incorrecta (scaleFactor en caché).
  • H.264 rechazó dimensiones impares — scale=trunc(iw/2)*2:trunc(ih/2)*2.
  • Consultar el juego desde Node cada 100 ms dio tiempos de paso de ±0.1 s — inútil para cortar al ritmo. Muestrea en la página cada fotograma (timeline).
  • Iniciar el piloto automático en un momento diferente dio una ejecución diferente con la misma semilla (cambia cómo se consume el PRNG compartido en relación con la disposición). Inícialo de la misma manera en la sonda y en la captura, y vuelve a probar después de cada cambio de script/piloto automático.
  • Un rectángulo negro ocupó la pantalla a mitad de la toma, solo en la resolución de captura. Sprites de puntos divididos por la profundidad de vista (size / -mv.z, colas de estela xy / w); una partícula que cruzaba el plano de la cámara produjo un sprite cuadrado de tamaño infinito/NaN, y el historial temporal del bloom mantuvo el NaN para siempre. Soluciona ambos extremos: salta puntos en el plano de la cámara y limita los tamaños en los sombreadores; haz que la cadena de bloom reemplace valores no finitos (isnan/isinf → 0, clamp) en su paso brillante y al mezclar el historial. Diagnostica leyendo cada búfer de postprocesamiento (readRenderTargetPixels) y contando half-floats con exponente 0x7c00.
  • Perder una toma al detenerse temprano — los fragmentos se transmiten al disco cada segundo; el maestro webm sobrevive a una transcodificación interrumpida:
    ffmpeg -y -i take.webm -i take.audio.webm -r 60 -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
      -c:v libx264 -preset slow -crf 16 -pix_fmt yuv420p -c:a aac -movflags +faststart take.mp4
    

Reutilizable vs específico del juego

  • Reutilizable (esta habilidad): el andamiaje de captura (bifurcación de sonido, línea de tiempo, desacoplamiento de redimensionamiento, manejo de detención, transcodificación), el editor de lista de cortes (velocidad, fundidos, pista musical, puertas bloqueadas al ritmo, volumen), la sonda/gráfico de trayectoria y los métodos en references/.
  • Específico del juego (tu configuración / tu juego): URL y consulta, ready, enganches de redimensionamiento, el muestreador de línea de tiempo, el piloto automático en sí, los interruptores de semilla/paso fijo, los scripts de nivel.

Individual skills in this repo

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

Skills relacionados