1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeでファミコンの縦スクロールSTGを作った話 ── Vibe Codingで8ビットの壁を越える

1
Last updated at Posted at 2026-02-15

TL;DR

  • Claude Codeに「ファミコン縦スクロールアクションゲームの仕様書を作って」と依頼
  • 仕様内容を確認(自分で見て確認)
  • 「この仕様書に基づいてファミコン向けSTGを作って」と依頼
  • CC65(C言語)+ 6502アセンブリのハイブリッド構成で約1,500行
  • NES固有のバグ(スタックポインタ未初期化、スプライト0ヒット不発、VBlank時間超過)をAIと対話しながら解決
  • AIによるコードレビューで5件のバグを追加発見
  • 所要時間:約3時間(環境構築からプレイ可能なROMまで)

はじめに

「ファミコンのゲームを作りたい」── そう思ったことがある開発者は少なくないはずです。しかし現実には、6502アセンブリ、PPUレジスタ、VBlankタイミング、スプライト0ヒットなど、レトロゲーム開発特有の厄介な制約が立ちはだかります。

本記事では、Claude Code(Vibe Coding) を使って、ファミコン向け縦スクロールSTG「STAR ASCEND」をゼロから開発した過程を紹介します。要求仕様書を渡すところから、ビルド・デバッグ・レビュー・コミットまで、ほぼ全てをAIとの対話で進めた記録です。

完成したもの

Adobe Express - nes.gif

STAR ASCEND ── 1面構成の縦スクロールSTG

  • スプライト0ヒットによるラスター分割(固定ステータスバー+スクロールゲーム画面)
  • 3種の敵(スカウト / ファイター / ボマー)+ドクロ型ボス戦艦
  • パワーアップ(3段階)、ボム(画面フラッシュ+全弾消去)
  • スコア / ハイスコア / ライフ表示
  • ROM容量:40,976バイト(PRG 32KB + CHR 8KB + iNESヘッダ 16B)

開発環境

ツール 用途 インストール
CC65 6502向けCコンパイラ brew install cc65
Mesen NESエミュレータ GitHub Releasesから.appをDL
Claude Code AI開発アシスタント 公式サイト

Mesenのインストールでハマった点

MesenのmacOSのIntel版を入れてしまい。Apple Silicon版に入れ直しました。

# ビルド後の起動
open -a Mesen star_ascend.nes

開発の流れ

1. 要求仕様書を渡す

まずゲームの仕様書(famicom_vscroll_stg_spec.md)を書いてClaude Codeに読み込ませました。

Claude への指示:「この仕様書に基づいてファミコン向けSTGを作って」

仕様書に含めた内容:

  • ゲームの基本仕様(縦スクロールSTG、1面構成)
  • メモリマップ(iNESヘッダ、PRG/CHR配置)
  • PPU制御の方針(スプライト0ヒットでステータスバー分割)
  • 敵の出現テーブル定義

2. アセンブリからCへの移行

最初は6502アセンブリで全体が生成されましたが、1,000行を超えた時点でメンテナンスが困難に。CC65のC言語に切り替えました。

star_ascend/
├── star_ascend.c    # ゲームロジック(約1,270行 / C言語)
├── crt0.s           # スタートアップ、NMI、コントローラ(約270行 / 6502 asm)
├── chr_data.s       # タイルデータ BG 256 + SPR 256(6502 asm)
├── nes.cfg          # リンカ設定
└── Makefile

ポイント: NMIハンドラ・スプライト0待ち・コントローラ読取りはアセンブリのまま残し、ゲームロジックだけCで書くハイブリッド構成です。VBlankのタイミングがシビアな処理はCのオーバーヘッドで破綻するリスクがあるため、Claude自身がこの構成を提案しました。

3. ビルド → 実行

make        # cc65 → ca65 → ld65 → star_ascend.nes (40,976 bytes)
make run    # Mesenで起動

ビルドは数秒。修正→ビルド→エミュレータ確認のサイクルが速いので、Vibe Codingとの相性が良いです。

デバッグの記録 ── NES固有のバグとの戦い

Vibe Codingでファミコンゲームを作ると、AIが生成したコードにNES固有のハードウェアバグが潜みます。修正過程を3件紹介します。

:bug: Bug 1: グレースクリーン(何も表示されない)

症状: ROMを起動しても画面が灰色のまま。

原因: CC65のソフトウェアスタックポインタspが未初期化。

crt0.sのリセットハンドラでRAMを$00でクリアした後、ゼロページにあるspもゼロに。CC65のC関数は呼び出しごとにspを操作するため、全てのC関数でゼロページが破壊されていました。

; 修正: RAMクリア後にspをRAM末尾に初期化
lda #<(__RAM_START__ + __RAM_SIZE__)
sta sp
lda #>(__RAM_START__ + __RAM_SIZE__)
sta sp+1

jsr copydata    ; ROM → RAM 初期値コピー
jsr initlib     ; CC65ランタイム初期化
jmp _main

:warning: CC65 + NESの必須知識: spの初期化を忘れると何も動きません。リンカ設定(nes.cfg)でdefine = yesを指定し、__RAM_START__/__RAM_SIZE__シンボルを公開する必要もあります。

:bug: Bug 2: STARTボタンでフリーズ

症状: タイトル画面は正常。STARTを押すとスコアと星が見えるが、完全フリーズ。

原因: スプライト0ヒットが永久に発生しない。問題は2つ。

問題①: ステージ準備中(STATE_STAGING)でoam_clear()が全スプライトを隠し、スプライト0が再配置されなかった。STATE_PLAYに遷移した瞬間、スプライト0ヒット待ちが無限ループに。

問題②: スプライト0タイルのピクセル位置とBGタイルのピクセル位置が重ならなかった。スプライト0ヒットは「両方の不透明ピクセルが同じ座標で重なる」ことが条件です。

// 修正: ステージ準備中もスプライト0を必ず配置
static void update_staging(void)
{
    if (--stage_timer == 0) {
        game_state = STATE_PLAY;
    }
    oam_clear();
    oam_spr(248, 15, TILE_SPR0, 0x20);  // ← これが無いとフリーズ
}

さらにCHR-ROMに専用のBGマーカータイル(左上1ピクセルだけ描画)を追加し、スプライト0と同じ画面座標に配置しました。

:bug: Bug 3: ボム使用後にスコア表示が崩壊

症状: ボムを使った後しばらくすると、SCOREの文字が化ける。

原因: NMIハンドラ内でunsigned longの除算を7回実行していた。

NESのVBlank時間は約2,273サイクルしかありません。CC65の32ビット除算は1回で数百サイクル消費するため、7回 × 除算 = VBlank時間を大幅超過。PPUアドレス設定の途中で描画期間に突入し、スコア表示が壊れていました。

// 修正: 計算をメインループ、PPU転送をNMIに分離

// メインループ側(重い処理はここ)
static void prepare_score_display(void) {
    score_to_tiles(score_val, score_tiles);    // 32bit除算 × 7
    score_to_tiles(hiscore_val, hiscore_tiles);
    score_dirty = 1;
}

// NMI側(PPU転送だけ = 軽い)
static void update_score_display(void) {
    if (!score_dirty) return;
    score_dirty = 0;
    ppu_addr(0x2007);
    for (i = 0; i < 7; ++i) PPUDATA = score_tiles[i];
}

:bulb: NES開発の鉄則: NMI内では「PPUレジスタへの書き込み」以外の処理を極力避ける。重い計算は事前にメインループで済ませておく。

コードレビュー ── AIが自分のコードをレビューする

ゲームが動くようになった段階で、Claude Codeに「レビューして」と依頼しました。全ソースを読み直し、以下の結果が返ってきました。

発見されたバグ(5件)

# 重要度 内容
1 ボスをボムで倒してもSTATE_CLEARに遷移しない → 空のステージに閉じ込められる
2 ハイスコアのPPUアドレスが1バイトずれ($2012$2013
3 コピーライト年が "2027" 表示(タイルID $22$21
4 プレイヤー移動時の境界クランプ漏れ(速度4で最大3px超過)
5 ライフ表示がゲーム中に更新されない

発見された潜在的問題(2件)

  • NMIハンドラがC関数を呼ぶため、フレーム落ち時にゼロページの一時変数が破壊される可能性
  • SFX優先度がなく、長い効果音が短い効果音で上書きされる

レビューの精度について

バグ #1(ボスをボムで倒すとハマる)は秀逸でした。 check_collisions()でのボス撃破ではSTATE_CLEARを設定しているのに、use_bomb()では忘れている。同じ処理が2箇所にあって片方で漏れているパターンを、コード全体を俯瞰して検出しています。人間のレビューでも見落としやすいバグです。

一方、バグ #3(コピーライト年) は人間なら画面を見た瞬間に気づきます。AIは実行画面を見られないので、タイルIDテーブルを逆引きして数値的に検証しています。網羅的ですが、「目で見て確認する」を代替するものではありません。

レビュー結果を「y」で承認すると、5件全てを即座に修正 → ビルド → コミットまで対話だけで完了しました。

ボスキャラクターの制作

「ボスのキャラクターを極悪人の宇宙船をイメージしたものにして」と依頼。

Claudeはドクロモチーフの戦艦「Skull Battlecruiser」を提案。6タイル(8×8 × 6)を左右反転で3×3に配置する構成です。

[翼TL]  [スカルTC]  [翼TL反転]    ← ドクロの顔(目と歯がオレンジに光る)
[翼ML]  [コアMC]    [翼ML反転]    ← 船体(リアクターが赤く光る)
[翼BL]  [エンジンBC] [翼BL反転]   ← 武器ポッド+エンジン排気

NESの制約(1パレット4色、8×8タイル単位)の中で、ダークレッドの船体にオレンジに光るドクロの目という配色を実現。CHRデータの2ビットプレーン形式を直接エンコードしてくれるので、ドット絵エディタなしでタイルが作れます。

所要時間

フェーズ 時間
環境構築(CC65 + Mesen) 15分
仕様書作成 20分
初期実装(アセンブリ版) 30分
C言語への移植 20分
グレースクリーンのデバッグ 15分
フリーズバグのデバッグ 20分
スコア表示バグのデバッグ 10分
速度調整・ボスデザイン 15分
レビュー+バグ修正 15分
仕様書更新・記事下書き 20分
合計 約3時間

1面構成のシンプルなゲームですが、ラスター分割、敵出現テーブル、ボム演出、スコアシステムなど、STGの基本要素は一通り入っています。全て手作業ならNES開発経験者でも数日、未経験者なら数週間かかる内容です。

感想

Vibe Codingの強み:知識の壁を越える

NES開発で最もつらいのは、PPUの動作仕様やVBlankタイミングなど 「知らなければ絶対に解決できない」系の問題 です。Claude Codeはこれらの知識を持っていて、バグの症状から原因を推定し、適切な修正を提示できます。

CC65のsp初期化問題は「CC65ランタイムの内部挙動」を知らないとたどり着けません。従来ならドキュメントを読み漁って数時間かかるところを、コードレビューの中で指摘してもらえました。

Vibe Codingの限界:目と手は人間の仕事

AIはエミュレータの画面を見られません。 スクリーンショットを渡せば状況は理解できますが、「操作してみて違和感がある」レベルのフィードバックは人間の役割です。実際、「動きが遅い」という感覚から速度を2倍にする判断は、プレイした自分にしかできませんでした。

スコア表示の崩壊のようなタイミング依存のバグも、コード上は正しく見えます。エミュレータのデバッグ機能(PPU Viewer等)と組み合わせて初めて追い詰められる類のものです。

総評

ファミコン開発は「古い」が「単純」ではありません。むしろ、極端なリソース制約の中で工夫する面白さがあります。Vibe Codingはその工夫の試行回数を劇的に増やしてくれるツールです。

2026年に、1983年のハードウェアのためのコードをAIと書く。これはこれで一つの未来の形かもしれません。

リポジトリ

git clone https://github.com/SamAkada/star_ascend.git
cd star_ascend
make          # star_ascend.nes をビルド
make run      # Mesenで起動

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?