Communitygithub.com

게임 플레이 영상 캡처 및 광고 영상 제작

실행 중인 웹/캔버스 게임의 실제 게임플레이 비디오를 캡처하여 완성된 광고 영상(예: Meta/TikTok, 800x1000 또는 1080x1920, 15-30초, 사운드 포함)으로 변환합니다. Chromium에서 canvas.captureStream + MediaRecorder -> ffmpeg로 녹화하며, 게임의 WebAudio 사운드와 프레임별 타임라인을 포함합니다. 그런 다음 컷 리스트(JSON)로 광고를 편집합니다: 속도 변경, 페이드, 배경 음악, 비트에 맞춘 컷 및 게이트 통과, 음량 정규화. 또한 재현 가능한 푸티지(시드 고정, 고정 스텝), 항상 클리어하는 사람 같은 자동 조종, 녹화 전용 레벨 스크립트를 다룹니다.

게임 플레이 영상 캡처 및 광고 영상 제작란 무엇인가요?

이 스킬은 실행 중인 웹 게임(Phaser/PixiJS/Three.js/일반 캔버스)에서 실제 게임플레이 영상을 캡처하고, 이를 광고용 영상으로 편집하는 완전한 파이프라인을 제공합니다. Chromium의 canvas.captureStream을 사용해 캔버스 백킹 버퍼를 직접 녹화하므로 커서나 HUD 없이 깨끗한 영상을 얻을 수 있으며, WebAudio 사운드를 함께 녹음합니다. 또한 프레임별 타임라인을 생성하여 각 게이트 통과 시간, 레벨 클리어, 음악 비트 등을 기록합니다. `ad-edit` 스크립트는 JSON 컷 리스트를 받아 속도 변경, 페이드, 배경 음악, 비트에 동기화된 컷 및 음량 정규화를 적용합니다. 게임에 시드 및 고정 스텝을 추가하여 녹화를 완전히 재현 가능하게 만들고, 사람처럼 보이는 자동 조종으로 항상 클리어하도록 보장하며, 녹화 전용 레벨 스크립트를 통해 더 흥미로운 광고 영상을 제작할 수 있습니다. `game-store-assets`(스토어 스크린샷 + 피처 그래픽) 및 `game-icon-from-screenshot`(정사각형 앱 아이콘)과 함께 사용됩니다.

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

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

실행 중인 게임에서 게임플레이 영상 및 광고 영상 제작

두 가지 계층:

  1. 캡처: 실제 실행 중인 게임을 mp4로 녹화 (scripts/capture-video.cjs) — 화면, 선택적으로 사운드, 그리고 각 프레임의 타임라인.
  2. 편집: 컷 리스트를 기반으로 광고 영상으로 편집 (scripts/ad-edit.cjs) — 또는 원본 클립을 NLE(비선형 편집기)에 넘깁니다.

이 두 단계 사이에는 광고에 적합한 푸티지를 만드는 요소들이 있습니다: 재현 가능한 실행, 사람처럼 보이는 자동 조종(항상 클리어), 그리고 (선택적으로) 녹화 전용 레벨 스크립트로 스테이지가 좋은 리듬으로 특징을 보여주도록 합니다.

npm run dev                                                     # 게임 실행, 예: 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                  # 컷 리스트 -> out/<list.out>.mp4
node ${CLAUDE_SKILL_DIR}/scripts/probe-trajectory.cjs probe.cjs 30 t.json    # 자동 조종 튜닝 (헤드리스, 빠름)
python3 ${CLAUDE_SKILL_DIR}/scripts/plot-trajectory.py t.png t.json

각 렌더링 결과가 생성되면 즉시 사용자에게 열어줍니다 (macOS에서 open <file>) — 검토가 반복 과정의 핵심이므로 경로를 직접 입력하게 하지 마세요.

1. 캡처 (scripts/capture-video.cjs)

canvas.captureStream(fps)는 캔버스의 백킹 버퍼를 녹화합니다 — 창이나 DOM이 아닙니다. 따라서:

  • 커서 / HUD가 자동으로 제외됩니다. DOM 오버레이(점수, 버튼, 개발자용 손가락 커서)는 비디오에 나타나지 않습니다. 광고에는 보통 깨끗한 화면이 좋습니다; 텍스트는 편집 단계에서 추가하세요.
  • 캡처 해상도는 창과 독립적입니다. 엔진의 내부 렌더링 크기를 광고 크기로 높이거나 — 2배로 해서 편집 시 다운스케일(슈퍼샘플링이 엔진의 안티앨리어싱보다 나은 결과) — 창은 작게 유지합니다(분리 참고).
  • 사운드 (audio: true): AudioContext의 스피커에 연결된 모든 연결을 MediaStreamDestination으로 분기하여 자체 MediaRecorder로 녹음하고, ffmpeg가 믹싱합니다. beforeRecord에서 게임의 AudioContext를 잠금 해제하세요 (Chromium은 --autoplay-policy=no-user-gesture-required로 실행).
  • 타임라인 (timeline: () => ({state, score, beat, ...})): 페이지에서 매 프레임마다 샘플링하여 <name>.timeline.json ({t, ...}) 파일로 기록합니다. 이 파일을 기준으로 컷을 만듭니다: 각 게이트 통과 시간, 레벨 클리어 시간, 모든 음악 비트 위치. 게임에 음악 비트가 있다면 정수 beat로 노출하세요.
  • 헤드드 또는 헤드리스. 헤드드가 안전한 기본값입니다. 헤드리스도 작동하며 포커스를 빼앗지 않습니다 (headless: true는 GPU 플래그를 추가) — 하지만 먼저 짧은 테이크의 프레임 수를 확인하세요 (문제점 참고).

설정

필드의미
url (필수)실행 중인 게임 URL, 게임 자체 캡처 쿼리(시드, 고정 스텝, 스크립트…) 포함
ready (필수)페이지 내 () => boolean
target캡처 크기 {width,height} (기본값 1080×1920; 예: 800×1000 광고용 1600×2000)
displayHeight화면상 캔버스 높이 (760)
canvasSelector게임 캔버스 (canvas)
setRenderSize / setDisplaySize페이지 내; 엔진의 렌더링 크기 증가 / CSS 축소 + 입력 재매핑
beforeRecord페이지 내, 녹화 직전 한 번 (오디오 잠금 해제, 디버그 오버레이 숨기기…)
autopilot페이지 내, 녹화 시작 직후 한 번, auto 모드에서
audio, timeline, headless, settle (ms)위 참고
bitrate, fps, seconds, outDir, name재정의 (CLI: config seconds mode outDir name)

함수는 직렬화(toString)되므로 자체 포함형으로 유지하세요. 출력: <name>.mp4 (H.264/AAC, 편집용), <name>.webm (+ .audio.webm, 마스터 — 보관), <name>.timeline.json.

캡처 해상도와 창 분리

엔진별 (창은 작게 유지; captureStream은 백킹 버퍼 녹화):

  • Phaser: g.scale.resize(w, h) 후 씬 재시작 (게임은 create()에서 scaleFactor를 캐시); 수동 플레이를 위해 g.canvas.style.width/height + g.scale.refresh()로 탭 매핑 보정.
  • Three.js: renderer.setPixelRatio(w / cssW) 후 CSS 크기로 게임 자체 리사이즈 (카메라 종횡비와 포스트FX 버퍼가 따라가도록). 세로비가 제한된 게임(최대 종횡비로 레터박스)은 4:5 같은 더 넓은 광고 종횡비를 위해 제한을 해제해야 함.
  • PixiJS: renderer.resize(w, h) + 캔버스 스타일.

모드

  • manual — 창에서 직접 플레이 (헤드드 전용). 가장 자연스러움.
  • auto — 설정의 autopilot이 구동. 광고의 경우 사람처럼 보이고 절대 놓치지 않아야 함 — references/autopilot.md 참고.

2. 실행 재현 가능하게 만들기 (튜닝 전에 가치 있음)

컷, 자동 조종 또는 레벨을 튜닝하려면 여러 번 재녹화해야 합니다; 매번 다른 결과가 나오면 수렴할 수 없습니다. 게임에 캡처 전용 스위치 두 개를 추가하세요 (쿼리 파라미터; 출시된 게임은 무시):

  • 시드 — 게임플레이 난수(장애물 배치 그리고 자동 조종의 선택)를 Math.random 대신 시드 기반 PRNG로 제어. 순수 시각적 난수는 그대로 둡니다.
  • 고정 스텝 — 누산기를 사용하여 고정 1/60초 스텝으로 시뮬레이션 (프레임당 한 번 렌더링, 0..n 스텝 실행). 실제 프레임 시간은 지터가 있지만, 고정 스텝은 시뮬레이션을 프레임 속도(60 Hz, 120 Hz, 무거운 캡처에서 드롭된 프레임)와 관계없이 동일하게 만듭니다.

그러면 동일한 URL이 동일한 실행을 의미합니다 — 사용자가 브라우저에서 확인할 때, 프로브에서, 그리고 캡처에서 모두 동일합니다. 좋은 실행은 시드로 선택하세요 (프로브로 몇 개를 스위핑하여 측정 가능한 기준으로 선택), 캡처를 다시 돌리지 말고. 자세한 내용과 주의점: references/recording-scripts.md.

3. 편집 (scripts/ad-edit.cjs)

컷 리스트(JSON) → 광고 크기의 mp4 (lanczos 다운스케일), CRF 17, AAC, 음량 정규화 (기본값 −14 LUFS, 소셜 피드가 정규화하는 값). 주요 필드:

{
  "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는 재생 속도. After Effects의 "time stretch 85%"는 속도 1/0.85. 사운드는 피치 유지 (atempo). 빨라짐 → 60 fps로 기록 (프레임 드롭); 느려짐 → 60·speed fps로 기록 (프레임 반복 없음).
  • music은 하나의 연속적인 배경 음악을 배치 (게임 효과음이 음소거된 테이크로 녹화)하여 하드 컷 아래에 깔기 — 몽타주에 컷마다 다른 노래 대신 하나의 노래가 사용됩니다. snap은 컷을 전체 마디로 자름; gates는 각 컷에 자체 속도와 시작을 부여하여 게이트 통과가 배경 음악의 비트에 맞도록 함; passAt은 첫 번째 통과를 컷 시작 후 N 비트에 배치; align은 배경 음악을 동일한 테이크의 컷과 정렬 (클리어와 노래의 케이던스가 일치).

컷이 "맞거나" "안 맞는" 느낌, 그리고 배경 음악 레시피: references/ad-editing.md.

4. 녹화 전용 레벨 스크립트 (선택 사항)

스테이지 자체가 광고에 너무 단조로울 때, 캡처 전용 레이아웃을 추가하세요: 비트별로 배치된 구멍/장애물 (명시적 높이와 특징 포함), 그리고 자동 조종만 통과하는 보이지 않는 웨이포인트 (추가 장애물 없이 추가 조작). 약 5초마다 새로운 특징이 등장하고 첫 번째 특징은 약 5초 내에 도달하도록 속도를 조절하세요. references/recording-scripts.md 참고.

문제점 / 작동하지 않은 것 ("이유" 유지)

  • 비디오 + 오디오 트랙이 있는 하나의 MediaRecorder가 webm 헤더 직후 중단됨 (수십 바이트, ffmpeg "EBML header parsing failed"). 같은 틱에 시작된 두 개의 레코더를 ffmpeg로 믹싱하면 작동합니다. ffmpeg의 후행 "Error parsing Opus packet header"는 마지막 부분적 청크로 무해합니다.
  • Ctrl-C로 webm은 남고 mp4는 없음. Playwright 자체 SIGINT/SIGTERM/SIGHUP 핸들러가 브라우저를 닫고 먼저 종료 — handleSIG*: false로 실행하고, 영구 process.on("SIGINT") 사용 (once 리스너는 스스로 제거하고 Node는 두 번째 신호에서 종료).
  • 헤드리스가 스폰 시 주제를 멈춤 — 하나의 블룸이 많은 Phaser 게임에서 발생; Three.js 게임에서는 GPU가 활성화되면 (--use-angle=metal|d3d11|vulkan --enable-gpu --ignore-gpu-blocklist) 헤드리스가 정상 작동. 짧은 테이크의 프레임 수 계산 (ffprobe -count_frames) — ~60/s면 실제임.
  • 과도하게 큰 창. 창을 키우지 말고 내부 렌더링 크기를 높이세요.
  • 재시작 없이 리사이즈 → 잘못된 게임플레이 스케일 (캐시된 scaleFactor).
  • H.264가 홀수 해상도를 거부 — scale=trunc(iw/2)*2:trunc(ih/2)*2.
  • Node에서 100ms마다 게임 폴링 → 통과 시간 ±0.1초 — 비트에 맞춰 컷하기에 부적합. 페이지에서 매 프레임 샘플링 (timeline).
  • 자동 조종을 다른 시점에 시작하면 같은 시드라도 다른 실행 (공유 PRNG가 레이아웃에 대해 소비되는 방식 변경). 프로브와 캡처에서 동일한 방식으로 시작하고, 스크립트/자동 조종 변경 후 항상 다시 프로브.
  • 검은 사각형이 중간에 화면을 덮음, 캡처 해상도에서만. 포인트 스프라이트가 뷰 깊이로 나누어짐 (size / -mv.z, 스트릭 꼬리 xy / w); 카메라 평면을 교차하는 입자가 무한대/NaN 크기의 정사각형 스프라이트를 생성하고, 블룸의 시간적 히스토리가 NaN을 계속 유지. 양쪽 수정: 카메라 평면의 포인트를 건너뛰고 셰이더에서 크기를 클램프; 블룸 체인이 밝은 패스와 히스토리 혼합 시 비유한 값을 대체 (isnan/isinf → 0, 클램프). 각 포스트 프로세스 버퍼를 읽어 (readRenderTargetPixels) 0x7c00 지수 하프플로트를 세어 진단.
  • 조기 중단으로 테이크 손실 — 청크가 매초 디스크로 스트리밍; webm 마스터는 중단된 트랜스코드에서도 생존:
    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
    

재사용 가능 vs 게임별

  • 재사용 가능 (이 스킬): 캡처 하네스 (사운드 분기, 타임라인, 리사이즈 분리, 중단 처리, 트랜스코드), 컷 리스트 편집기 (속도, 페이드, 배경 음악, 비트 고정 게이트, 음량), 궤적 프로브/플롯, 그리고 references/의 방법들.
  • 게임별 (설정 / 게임): URL 및 쿼리, ready, 리사이즈 훅, 타임라인 샘플러, 자동 조종 자체, 시드 / 고정 스텝 스위치, 레벨 스크립트.

Individual skills in this repo

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

관련 스킬