scenario-side-view-game-kit について
横スクロールゲーム向けの画像アセット制作スキルです。画像・動画生成とローカルの Python スクリプトを組み合わせ、ピクセルアニメーションのスプライトシートやパララックス用のレイヤーを作成します。
主な機能
- 01キャラクターのポーズ画像と動画を生成
- 02共通パレットのスプライトシートに加工
- 03ポーズシートからアクションフレームを抽出
- 04背景、中景、足場などのレイヤーを作成
- 05テクスチャのタイリングと足場端を処理
活用シーン
- 01横スクロールゲームの移動・攻撃アニメーション制作
- 02パララックス用のレベルレイヤー作成
- 03生成アセットをゲーム用ファイルに加工
MCP 対応
Scenario のツールを使い、モデルの選択、生成ジョブの実行・管理、アセットのアップロードとダウンロードを行います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-side-view-game-kitインストール後、Claude Code で/scenario-side-view-game-kitを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/processing-settings.mdprompt-templates.mdscripts/process_env.pyreframe_stills.pyside_sprites.pysprite_cycles.py詳細
シナリオ横スクロールゲームキット
概要
横スクロールゲームには、1回の生成では用意できない2種類の画像アセットが必要です。ひとつは同じ足元のラインで滑らかにアニメーションするピクセルスプライト、もうひとつはパララックス用に重ねても、継ぎ目なく繰り返しても使える、ペイント調のレベルレイヤーです。この手法は、1回のエージェントセッションで制作されたブラウザー向け横スクロールゲーム(主人公1人、地上の敵2体、5レベル)から生まれました。
- キャラクター。 マゼンタ一色の背景に2つのポーズを描いたテキスト画像生成の静止画(主人公:待機とジャンプ、敵:待機と攻撃の構え)を、頭上に余白を設けて再構成します。続いて、各サイクルにつき3秒の画像から動画へのクリップを1本作成し、キャラクターが純粋な横向きの右向きで「トレッドミルのように」走る、または歩くようにします。ローカルスクリプトでマゼンタをキーイングし、ループまたは単発の区間を切り出して、キャラクターの各サイクルを共通のキャンバスとパレットにピクセル化します。落下、壁への張り付き、急降下、ダッシュは1枚のポーズシートから作成します。向きは片方だけ作り、ゲーム内で反転させます。
- レベル。 レベルごとに「精細な高解像度ピクセルアート」のレンダリングを5枚作成します(背景、中景、壁のテクスチャ、プラットフォームの帯、小道具)。それぞれをローカルで WebP レイヤーにします。
仕上げをローカルで行うのは意図的です。キーイング、ループ検索、共通パレット、継ぎ目のないクロスフェードにはディスク上のピクセルが必要であり、成果物はゲーム用ファイルだからです。完成したストリップもプラットフォームに置きたい場合は、upload_asset を使ってコレクションにアップロードしてください。
接続、スコープ、基本のループについては scenario スキルを参照してください。フレームツール、GIF プレビュー、エンジン向けスライスについては scenario-sprite-animation を参照してください。ピクセルのクリーンアップやその他の静止画については scenario-game-assets を参照してください。クリップモデルの種類については scenario-video を参照してください。音楽と効果音はこの画像スキルの対象外です。ここに記載された関連スキルが利用可能なスキルに見つからない場合は、ユーザーにインストールを依頼してください(npx skills add scenario-labs/skills --skill <name>)。無人で進める場合は、ツールのスキーマに基づいて作業し、不足している機能について明記してください。
エージェントはスクリプトをローカルで実行します(Python 3、Pillow、NumPy。side_sprites.py には ffmpeg も必要です)。
reframe_stills.py はクリップの最初のフレームを作成し、
side_sprites.py はストリップを作成し、
sprite_cycles.py をインポートします(直接実行しないでください)。また、
process_env.py はレベルレイヤーを仕上げます。スロット付きのプロンプトは
references/prompt-templates.md を参照してください。スクリプトのフラグを変更する前、または --report がループに問題ありと示した場合は、
references/processing-settings.md を読んでください。
クイックリファレンス
| 手順 | 方法 | 残す条件 |
|---|---|---|
| モデル | 静止画、ポーズシート、レイヤーには capability: "txt2img" を指定して recommend を実行し、クリップには features: ["endImage"] を指定して "img2video" を実行します。ユーザーの言葉を prompt として渡し、その後 model_schema_get を実行します | 各スキーマのフィールド名、列挙値、上限を把握していること |
| リフレームとアップロード | reframe_stills.py:960 四方の 58%を算出し、足元を 82%に配置します。静止フレームには upload_asset と upload_asset_complete を実行し、主人公の空中スピン用のアクションフレームにも実行します | 武器を振り上げたり宙返りしたりするための頭上の余白があること |
| ポーズシート | テキストから画像を生成し、選んだ静止画を参照画像として使用して、マゼンタの背景に 4 つのポーズを横並びにします | スプライトシートと同じデザインとカラーパレットであること |
| 価格と待機 | 個別のペイロードごとに dry_run: true を指定し、その後、並行実行数の上限を守りながら複数回に分けて wait: false で実行し、jobs_wait を呼び出します。完了するまで pending_job_ids を指定して再度呼び出します | 費用を使う前に合計額をユーザーに伝えていること |
| スプライトシート | side_sprites.py --report を実行し、その後 --report なしで実行します | ループの継ぎ目(待機状態は除く)がベースラインの 0.6 倍未満で、単色のブロックがないこと |
| レベルレイヤー | レベルごとに 5 回レンダリングし、レベル 1 から始めます | 暗い下部中央、何も置かない中景の中央、互いに離れた小道具があること |
| レベルの仕上げ | process_env.py --env env --out assets --levels N | テクスチャタイルがあり、足場の縁が平坦で、小道具の数が一致していること |
| 納品 | スプライトシートとポーズ画像はロスレス WebP と <name>_meta.json(セル、足元のアンカー、fps)で納品し、レイヤーは非可逆 WebP で納品します | キャラクターごとにアンカーが 1 つあり、端にマゼンタが残っていないこと |
以下のペイロード名(startImage、endImage、referenceImages、background、quality、
numOutputs、cfgScale)は、ビルドのスキーマの例です。選択したモデルの
model_schema_get に記載されているフィールドを使用してください。画像モデルには、強いレイアウト遵守、
参照画像入力、透明背景オプション、テンプレートの 1536x1024 が必要です。スキーマにそのいずれかがない場合(ピクセルアート特化モデルではサイズ上限が大幅に小さいことがあります)は、recommend がそのような上限付きモデルから選ぶよう求めている場合でも、次に順位の高いモデルを選んでください。
作業例
ユーザーが「横スクロールゲーム用のアートを作ってください。レイピアを持ったキツネの騎士の主人公、つるはしを持ったモグラの鉱夫の敵、そしてキノコの森とクリスタル鉱山の 2 つのレベルです」と依頼したとします。すべての呼び出しで team_id と project_id を渡してください(それらの選び方については scenario スキルで説明しています)。
-
ヒーローの静止画。
recommend { "capability": "txt2img", "prompt": "<the user's request>; needs reference image input, transparent output" }を実行し、候補のmodel_schema_getを取得して、 2ポーズテンプレート(左: 右向きに立って構える姿勢、右:右向きにジャンプしている姿勢)の料金をmodel_run { "model_id": "<pick>", "dry_run": true, "parameters": { "prompt": "...", "width": 1536, "height": 1024, "quality": "high", "background": "opaque", "numOutputs": 2 } }で見積もります。 ユーザーに料金を伝えてから実行し、処理中として返された場合はjob_idを指定してjobs_waitを実行します。 2枚ともasset_displayで表示し、両方のポーズがモデルに収まり、真横向きで(すべてのクリップで体が斜め向きになってしまうため)、ピンクや紫が一切含まれないものを選びます。それをasset_downloadし、curl -Lでfox_two_pose.pngに保存します。 -
最初のフレーム。
python3 scripts/reframe_stills.py fox_two_pose.png --name fox --out framesを実行すると、frames/fox_rest.png(左のポーズ。地上でのすべてのクリップの最初のフレーム)とframes/fox_action.png(ジャンプ)が作成されます。両方を確認してから、upload_asset { "file_name": "fox_rest.png", "content_type": "image/png", "kind": "image", "file_size": <bytes> }を実行し、パーツをPUTし、upload_asset_complete { "upload_id": "<id>" }を実行します。アクションフレームも同様にアップロードしますが、これは空中スピンでのみ使用します。 -
ヒーローのクリップ。
recommend { "capability": "img2video", "features": ["endImage"], "prompt": "<the user's request>; square clip, no audio" }を実行し、 続けてmodel_schema_getを取得します。 モーションテンプレートから、サイクルごとに1つのペイロードを使います。ビルドのスキーマは次のとおりです。run: { "prompt": "<run sentence> <facing and tail>", "negativePrompt": "<negative>, slowing down, stopping, standing still", "startImage": "<fox_rest id>", "duration": "3", "aspectRatio": "1:1", "generateAudio": false } atk1: { "prompt": "<attack: a fast rapier thrust ... returns to the starting ready pose> <facing and tail>", "negativePrompt": "<negative>", "startImage": "<fox_rest id>", "endImage": "<fox_rest id>", "duration": "3", "aspectRatio": "1:1", "generateAudio": false }待機と被ダメージは攻撃と同じようにピン留めします。空中スピンでは、アクションIDを両方のフレームに指定します。異なるペイロードごとに
dry_run: trueで料金を見積もり(最初のフレームのみ、ピン留め、ガイダンスを変更)、合計をユーザーに伝えます。チームの同時実行上限内で、"wait": falseを指定してクリップを数回に分けて起動します(scenarioスキルのparallel-custom-jobsの行を参照してください。429で起動が拒否された場合、ジョブは作成されていません。そのため、次の回に再度起動し、取り下げないでください)。jobs_wait { "job_ids": [...] }を1回実行して結果を収集します。処理中のジョブがある間は、返されたpending_job_idsをjob_idsに指定して再度実行します(job_getはポーリングに使わないでください)。それぞれをasset_downloadし(動画ではformatを省略)、clips/fox_<cycle>.mp4として保存します。<cycle>にはステップ5の--cyclesで使う名前(run、idle、atk1、spin、hurt)をそのまま指定します。各クリップをローカルのコンタクトシートで確認します(動画のフレームはasset_displayでは表示されないため、ffmpeg -i clips/fox_run.mp4 -vf "fps=6,scale=240:-1,tile=6x3" fox_run_sheet.pngを実行します)。カメラに向いたり、カメラから背を向けたりするもの、または移動するものだけを再実行します。 -
ポーズシート。
referenceImages: ["<chosen still id>"]と ポーズシートテンプレート(転倒、壁への張り付き、突進、ダッシュ)を指定して、テキストから画像を生成します。料金を見積もってユーザーに伝え、実行してからfox_poses.pngをダウンロードします。 -
ストリップ。
python3 scripts/side_sprites.py --clips clips --name fox --cycles run:loop:8,idle:loop:8,atk1:attack:7,spin:attack:8,hurt:attack:5 --report python3 scripts/side_sprites.py --clips clips --name fox --height 56 --palette 28 \ --cycles run:loop:8,idle:loop:8,atk1:attack:7,spin:attack:8,hurt:attack:5 \ --poses fox_poses.png --jump frames/fox_action.png --webp --out spritesストリップを4倍の大きさで、ニアレストネイバー法を使って表示します。斬撃の軌跡が白いブロックにキーイングされてしまった場合は、
--drop atk1=<index>(0始まり)と--forceを指定して再実行します。 -
敵。 同じ手順で、2ポーズの静止画を作ります(待機姿勢と、デザインの一貫性を保った攻撃前の構え)。リフレームして、
mole_rest.pngのみアップロードします。歩行は最初のフレームのみを使い、武装した敵の歩行時の文言を指定します。スキーマに項目があれば、cfgScale: 0.7など、低めのガイダンスを指定します。攻撃と被ダメージはピン留めします。side_sprites.py --clips clips --name mole --height 72 --cycles walk:loop:8,attack:attack:8,hurt:attack:5 --reportを実行します。 歩行がループ不良と判定された場合は、再実行する前に--range walk=12:48を指定します。 -
レベルレイヤー。 キノコの森用に、5 つの 環境テンプレートを埋めます (背景とテクスチャは不透明、その他は透明)。それぞれ異なる設定で
dry_runを実行し、 合計をユーザーに伝えてから実行し、env/bg1.png、env/mid1.png、env/tex1.png、env/ledge1.png、env/props1.pngとしてダウンロードします。その後、python3 scripts/process_env.py --env env --out assets --levels 1を実行します。クリスタル鉱山を生成する前に、 キツネを配置したレベル 1 をゲームに入れます。そのレベルだけを処理するには--levels 2,を使います (単独の2は個数を表し、レベル 1 も再処理します)。再レンダリング後は--forceを付けます。 -
確認。 各ストリップを
<name>_meta.jsonの fps で再生し、テクスチャを壁にタイル状に並べ、 長いプラットフォームと短いプラットフォームを描きます。
よくある間違い
- 走りや歩きの最後のフレームを固定する。 短い移動クリップの両端で静止画を固定すると、クリップの時間が加速と減速に費やされます。走りと歩きは最初のフレームだけを固定し、アイドルとすべてのワンショットでは最初=最後に固定して、静止状態に戻るようにします。
- 武器を使いながら歩く敵。 最初の武器を持った歩行アニメーションでは、歩みの途中で武器を振り、ベースラインの 1.44 倍の速度で閉じました。「腕は動かさず……武器を持ち上げたり、振ったり、回したりしない」と指示し、武器に関する語句をネガティブプロンプトに入れて、ガイダンスを 0.7 にしたところ、0.42 倍で閉じました。
- エフェクトがブロック状になったフレームを残す。 白い斬撃の軌跡と着地時の砂ぼこりが、キーイングによって塊になりました。
--dropを使えば、共有キャンバスやパレットを変えずにサンプリングされたフレームを削除できます。 - 出来の悪いループをすべて再生成して費用をかける。 重い足取りの歩行アニメーションは 1.03 倍で閉じました。範囲を広げても同じ区間が見つかり、レビュー後に採用されました。範囲を広げてループを確認し、目に見えて引っかかる場合にだけ費用をかけましょう。
- ポーズシートを列で分割する。 ポーズは横方向に重なります。連結成分で分割し(
--posesが行います)、間隔を広く取るよう指定しましょう。 - テクスチャのプロンプトに「シームレスにタイル化可能」と書けばよいと考える。 レンダリング結果は安定してタイル化できません。再レンダリングする代わりに、
process_env.pyで反対側の端をクロスフェードします。 - プラットフォームのストリップを引き伸ばす。 左端、繰り返す中央部分、右端を描けば、どの長さでも彫刻された端を保てます。
- レトロ風のレベルアート。 小さなレトロ調の絵は最初のプレイテストで不合格になりました。代わりに 2 倍サイズで描いた 1920x1152 の詳細なレイヤーを使い、スプライトはニアレストネイバーでの拡大縮小を維持しました。残りのレベルを作る前に、ゲーム内でレベル 1 を検証しましょう。
- キャラクターにピンクや紫を使う。 マゼンタが強い色はすべてキーイングで除去されます。「キャラクターにピンク、紫、マゼンタを一切使わない」と指定し、どれかが見られる結果は不採用にします。武器を素早く振ると、紫のモーションブラーの色合いが加わり、振っている途中でキーイングされることがあります。これは
--dropでは修正できません。フレームを削除すると攻撃そのものが失われるためです。その結果は不採用にするか、プロンプトに「モーションブラーなし、武器の色を維持」と指定し、ネガティブプロンプトに「武器の紫またはマゼンタの色合い」を加えて再実行します(ある検証実行ではこれで色合いが消えましたが、着弾フレームの 1 枚には引き続き--dropが必要でした)。 - ブロックされた戦闘クリップの表現を変える。 突きやつるはしを振る動作が
moderation_blockedになることがあります。フィルターはプロバイダー側のものなので、エラーを読み、動作を弱める前に、scenario-moderationに従って変更していないペイロードを別のプロバイダーのモデルで実行します(同じrecommendを使い、endImage機能も維持します)。 - 劣化するスプライトストリップ。 非可逆 WebP では共有パレットの色が変わります。非可逆圧縮で出荷するのは HD レイヤーだけです。