牌語備忘録 -pygo

あくまでもメモです。なるべくオフィシャルの情報を参照してください。

牌語備忘録 -pygo

ChatGPT-4o と Claude で『Pyxel で作る小学生向け単純シューティングゲーム制作』のチュートリアルみいたいなのを作ってみたメモ

前置き

  • 小4になる次男から「プログラミング教えてほしい」「ゲームを作ってみたい」とのご希望をいただいた
  • ちょうど生成AIで何かしたいと思っていたので試しに調べたりしてみた
  • ChatGPT-4o やってみたら良さげだったので、ついでに気になっていた Claude でもやってみることにした

『Pyxel で作る小学生向け単純シューティングゲーム制作』チュートリアル

ChatGPT-4o を利用して作成

このリンク先の記事を参考にして、ちょい足しして調整したメッセージからできたものをこれまた動くように調整しつつ、ドキュメントを自分でまとめて作成してみた。

あなたはソフトウェア開発のエキスパートで、pyxelでとてもシンプルな2Dゲームを開発したいと考えています。

これはゲームの説明です。
- プレイヤーキャラクターは画面の中央にいます。
- プレイヤーキャラクターは矢印キーで右か左に曲がることができます。
- 敵キャラクターはランダムでプレイヤーキャラクターと同じY軸ライン上の左右からプレイヤーキャラクターに近づいてきます。
- プレイヤーキャラクターはZキーで前方に弾を撃つことができます。
- 弾が敵キャラに当たると敵キャラは消滅します。
- 敵キャラがプレイヤーキャラに触れるとゲームオーバー。

ステップ・バイ・ステップで考えてコーディングを始めてください。後ほど、さまざまな画像を提供します。今のところ、キャラクターには色の違う正方形を使うだけでいいです。
flake8 の lint に沿ってコーディングしてください。
  • 調整しつつ30分でそれっぽい動くものが出来上がった
  • コード修正とチュートリアルの手順をまとめたドキュメントを書くのにプラス30分くらい

最終的なコード

import pyxel
import random


class Bullet:
    def __init__(self, x, y, direction):
        self.x = x
        self.y = y
        self.vx = direction * 2

    def update(self):
        self.x += self.vx

    def draw(self):
        pyxel.rect(self.x, self.y - 1, 2, 2, 7)


class Enemy:
    def __init__(self, from_left, y):
        self.y = y
        self.size = 8
        if from_left:
            self.x = 0 - self.size
            self.vx = 1
        else:
            self.x = pyxel.width + self.size
            self.vx = -1

    def update(self):
        self.x += self.vx

    def draw(self):
        pyxel.rect(self.x, self.y - self.size // 2, self.size, self.size, 8)

    def is_out_of_screen(self):
        return self.x < -self.size or self.x > pyxel.width + self.size


class Player:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.direction = 1
        self.bullets = []

    def update(self):
        if pyxel.btn(pyxel.KEY_LEFT):
            self.direction = -1
        elif pyxel.btn(pyxel.KEY_RIGHT):
            self.direction = 1

        if pyxel.btnp(pyxel.KEY_Z):
            self.bullets.append(Bullet(self.x, self.y, self.direction))

        for bullet in self.bullets:
            bullet.update()

        self.bullets = [
            b for b in self.bullets if 0 <= b.x <= pyxel.width
        ]

    def draw(self):
        size = 8
        pyxel.rect(self.x - size // 2, self.y - size // 2, size, size, 11)
        tip_x = self.x + self.direction * 6
        tip_y = self.y
        pyxel.pset(tip_x, tip_y, 7)

        for bullet in self.bullets:
            bullet.draw()


class App:
    def __init__(self):
        pyxel.init(160, 120)
        self.reset()
        pyxel.run(self.update, self.draw)

    def reset(self):
        self.player = Player(80, 60)
        self.enemies = []
        self.frame_count = 0
        self.game_over = False
        self.score = 0  # スコア初期化

    def update(self):
        if self.game_over:
            if pyxel.btnp(pyxel.KEY_R):
                self.reset()
            return

        self.player.update()

        for enemy in self.enemies:
            enemy.update()

        if self.frame_count % 30 == 0:
            if random.random() < 0.5:
                from_left = random.choice([True, False])
                self.enemies.append(Enemy(from_left, self.player.y))

        # 弾と敵の当たり判定 + スコア加算
        new_enemies = []
        for enemy in self.enemies:
            hit = False
            for bullet in self.player.bullets:
                if abs(bullet.x - enemy.x) < 4 and abs(bullet.y - enemy.y) < 4:
                    hit = True
                    self.score += 1
                    break
            if not hit:
                new_enemies.append(enemy)
        self.enemies = [
            e for e in new_enemies if not e.is_out_of_screen()
        ]

        # 敵とプレイヤーの衝突
        for enemy in self.enemies:
            if abs(enemy.x - self.player.x) < 6 and abs(enemy.y - self.player.y) < 6:
                self.game_over = True

        self.frame_count += 1

    def draw(self):
        pyxel.cls(0)
        self.player.draw()
        for enemy in self.enemies:
            enemy.draw()

        pyxel.text(5, 5, f"SCORE: {self.score}", 7)

        if self.game_over:
            pyxel.text(58, 50, "GAME OVER", 8)
            pyxel.text(45, 60, "Press R to Restart", 7)


if __name__ == "__main__":
    App()

画面

www.youtube.com

Claude を利用して作成

デフォルト?の「レッスンやカリキュラムを設計する」で下記のように聞かれたので返答したら、一気に python のコードと5回のレッスンカリキュラムのドキュメントも作成された。すごい。

どのような科目や分野のレッスン・カリキュラムをお考えですか?(例:言語学習、プログラミング、音楽など) 対象となる学習者の年齢層やレベルはどのようなものでしょうか?

  • pyxel で単純なシューティング 2D ゲームを作成するチュートリアル
  • 対象は小学4年生のプログラミング初心者レベル

一部動かなかったり、不要なスペースなどあったので flake8 でエラーにならないよう整えてもらったりした。
ここまでで10分強でできてなかなか衝撃的。 そこからコードをチェックしつつドキュメントを微調整してたら1時間以上すぎてしまったけど。

最終的なコード




画面

(実際は効果音が鳴っているが動画では無音になってしまった...)

www.youtube.com

比較と感想

  • それぞれのゲームの挙動 -ChatGPT 版
    • 敵が左右から出てくる
    • プレイヤーは真ん中にいて動けないが左右に弾を打てる  - 想像してたシューティングゲームと違う挙動
    • Claude 版
      • 敵が上部に表示され、左右に動き下に向かって弾を打ってくる
      • プレイヤーは下部に表示され左右に動けて、上に弾を打つ
      • 効果音付き
      • 想像してた単純なシューティングゲームの体をしている
  • 最初はChatGPT-4oですごいと思ったけど、Claude はそれを超えてすごかった印象
  • ただどちらもそれっぽい嘘つくw
  • 便利すぎるのでそのうち課金しそう
  • 気が向いたらコードとチュートリアルのドキュメントを github に置くかもしれない
  • これ書いてから気づいたのだけど Pyxel の書籍が今年1月に出てた

https://amzn.to/4i9MIvT