ふくふくHukuhuku Inc.
EP.05LLM Gamedev 11分公開: 2026-05-26

pygame で学ぶ LLM 開発の全工程 ── 仕様 → 実装 → 配布まで Claude と

pygame を使って Claude / ChatGPT と一緒にゲーム 1 本を作る全工程。仕様策定の対話、実装の生成、テスト、PyInstaller での配布まで。教育・プロトタイプの王道環境。

#pygame#Python#Claude Code#教育#プロトタイプ
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

EP.05 は を使って「 開発の全工程」を 1 セッションで通すサンプル EP。Python が書ければ誰でも始められる、教育・プロトタイプの王道環境です。 / と相性が抜群で、仕様 → 実装 → 配布まで対話で進めます。

1. なぜ pygame で学ぶか

  • 最小学習コスト: Python + pygame の 2 つだけ覚えれば動く
  • LLM 生成精度が圧倒的: 20 年以上安定 API、Claude/ChatGPT の学習データが豊富
  • 1 ファイル完結: シーン / リソース / コードが 1 つの .py に書ける (小規模なら)
  • Python 資産との連携: NumPy / Pillow / OpenCV / sklearn 等と直結
  • 配布: PyInstaller (デスクトップ) or pygbag (Web) で出せる

2. 環境構築

pygame-ce のインストール
Bash
# venv 推奨python3 -m venv .venvsource .venv/bin/activate
pip install pygame-cepython -c "import pygame; print(pygame.version.ver)"  # 確認

3. 仕様策定の対話 (Claude と)

Claude への最初のプロンプト例
Text
pygame で「シンプルな避けゲー」を作りたい。仕様:- 画面: 800x600- プレイヤー: 矩形、矢印キーで上下左右移動、画面外に出られない- 敵: 上から落ちてくる円、ランダム位置、徐々に増える- スコア: 生存時間 (秒)- 衝突したらゲームオーバー、Enter キーで再スタート- 1 ファイル完結 (main.py のみ)- 60 FPS、見やすい配色
まず仕様を確認させて欲しい。質問があれば聞いて、なければコードを出して。
「確認させて」 と書く効用

LLM にいきなりコードを書かせない。仕様の曖昧点 (敵の出現頻度 / 衝突判定の精度 / 配色) を質問させると、後の手戻りが減る。Claude / GPT は質問が上手いので「質問があれば」 と添えるだけで仕様精度が上がる。

4. Claude が生成する main.py

100 行で動く避けゲー
Python
import pygameimport randomimport sys
# 初期化pygame.init()WIDTH, HEIGHT = 800, 600screen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption("避けゲー")clock = pygame.time.Clock()font = pygame.font.Font(None, 36)
# 配色BG = (20, 20, 30)PLAYER_COLOR = (100, 200, 255)ENEMY_COLOR = (255, 80, 80)TEXT_COLOR = (255, 255, 255)

class Player:    def __init__(self):        self.rect = pygame.Rect(WIDTH // 2 - 20, HEIGHT - 60, 40, 40)        self.speed = 5
    def update(self, keys):        if keys[pygame.K_LEFT]: self.rect.x -= self.speed        if keys[pygame.K_RIGHT]: self.rect.x += self.speed        if keys[pygame.K_UP]: self.rect.y -= self.speed        if keys[pygame.K_DOWN]: self.rect.y += self.speed        self.rect.clamp_ip(screen.get_rect())
    def draw(self):        pygame.draw.rect(screen, PLAYER_COLOR, self.rect, border_radius=6)

class Enemy:    def __init__(self, speed):        self.radius = random.randint(10, 25)        self.x = random.randint(self.radius, WIDTH - self.radius)        self.y = -self.radius        self.speed = speed
    def update(self):        self.y += self.speed
    def draw(self):        pygame.draw.circle(screen, ENEMY_COLOR, (self.x, int(self.y)), self.radius)
    def collides_with(self, rect: pygame.Rect) -> bool:        # 円矩形衝突 (簡易)        cx, cy = max(rect.left, min(self.x, rect.right)), max(rect.top, min(self.y, rect.bottom))        return (cx - self.x) ** 2 + (cy - self.y) ** 2 <= self.radius ** 2

def game_loop():    player = Player()    enemies: list[Enemy] = []    spawn_timer = 0    start_time = pygame.time.get_ticks()    game_over = False
    while True:        for event in pygame.event.get():            if event.type == pygame.QUIT:                pygame.quit()                sys.exit()            if game_over and event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:                return
        if not game_over:            keys = pygame.key.get_pressed()            player.update(keys)
            elapsed = (pygame.time.get_ticks() - start_time) / 1000            spawn_interval = max(10, 40 - int(elapsed))            spawn_timer += 1            if spawn_timer >= spawn_interval:                spawn_timer = 0                enemies.append(Enemy(speed=2 + elapsed * 0.05))
            for e in enemies[:]:                e.update()                if e.y > HEIGHT + 30:                    enemies.remove(e)                elif e.collides_with(player.rect):                    game_over = True
        # 描画        screen.fill(BG)        player.draw()        for e in enemies:            e.draw()        score = int((pygame.time.get_ticks() - start_time) / 1000)        screen.blit(font.render(f"Score: {score}", True, TEXT_COLOR), (10, 10))        if game_over:            text = font.render("Game Over - Enter to restart", True, TEXT_COLOR)            screen.blit(text, text.get_rect(center=(WIDTH // 2, HEIGHT // 2)))
        pygame.display.flip()        clock.tick(60)

if __name__ == "__main__":    while True:        game_loop()

5. デバッグの対話

実行 → エラーログを に貼る が基本。 は traceback がシンプルなので が解釈しやすい。「動くけど挙動がおかしい」 系は、症状を 1-2 行で説明 すれば直してくれる (例: 「ゲームオーバー後に Enter を押しても再スタートしない」)。

6. テスト

test_collision.py ── ロジック部のユニットテスト
Python
import pytestimport pygamefrom main import Enemy
pygame.init()  # Rect を使うため
@pytest.fixturedef player_rect():    return pygame.Rect(100, 100, 40, 40)
def test_enemy_collides_when_overlapping(player_rect):    e = Enemy(speed=2)    e.x, e.y, e.radius = 110, 110, 15    assert e.collides_with(player_rect)
def test_enemy_does_not_collide_when_far(player_rect):    e = Enemy(speed=2)    e.x, e.y, e.radius = 500, 500, 15    assert not e.collides_with(player_rect)

7. PyInstaller での配布

macOS / Windows / Linux で実行ファイル化
Bash
pip install pyinstaller
# 1 ファイル exe (起動遅め、配布 1 ファイル)pyinstaller --onefile --windowed main.py
# ディレクトリ配布 (起動速い、複数ファイル)pyinstaller --windowed main.py
# 出力: dist/main (or dist/main.app on Mac, dist/main.exe on Win)# 各 OS でビルドする (クロスコンパイル不可)

8. Web 配信: pygbag (pygame for Web)

pygbag でブラウザで動かす
Bash
pip install pygbagpygbag main.py# → http://localhost:8000 でテスト# → build/ ディレクトリの中身を任意の Web サーバーに置けば配信完了# → itch.io にも HTML5 ゲームとして上げられる

9. pygame で作る価値があるもの / ないもの

向く向かない
教育・学習用商用 AAA
プロトタイプ・ゲームジャムモバイル配信
アルゴリズム可視化高度な 3D
シミュレーション・ボードゲームMMO / 大規模ネットワーク
LLM 連動の実験リアルタイム物理大規模

10. 次の話

EP.06 は Bevy (Rust) × LLM ── ECS (Entity Component System) パターンと Rust の型安全性が LLM 生成にどう効くか。LLM hallucination を compile error で弾けるのが Bevy の強み。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp