見出し画像

【ChatGPT o1-mini】のプログラミング能力の向上。簡単なプロンプトでアステロイドゲームを作った(遊べます)。


はじめに

13日に発表されたChatGPTの新モデル、大型のo1-previewと小型のo1-mini。
プログラミング能力は、小型のo1-miniの方が高いということで、実際に試してみます。

プロンプト

以下のようなシンプルなプロンプトを入力して、ゲームを作ります。
果たして、動くものはできるでしょうか?

HTMLファイルのシングルページアプリケーションのゲームを作ります。アステロイドゲームを作ってください。

アステロイドゲームをシングルページアプリケーション(HTMLファイル)として作成するためのサンプルコードを以下に提供します。このゲームは、HTML5の`<canvas>`要素とJavaScriptを使用して実装されています。

HTMLファイル

以下のコードをコピーして、例えば`asteroid_game.html`という名前で保存してください。その後、ブラウザで開くことでゲームをプレイできます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>アステロイドゲーム</title>
    <style>
        body {
            margin: 0;
            background: #000;
            overflow: hidden;
            color: #fff;
            font-family: Arial, sans-serif;
        }
        #gameCanvas {
            display: block;
            margin: 0 auto;
            background: #000;
        }
        #score {
            position: absolute;
            top: 10px;
            left: 50%;
            transform: translateX(-50%);
            font-size: 20px;
        }
        #gameOver {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 40px;
            display: none;
        }
        #restart {
            position: absolute;
            top: 60%;
            left: 50%;
            transform: translateX(-50%);
            padding: 10px 20px;
            font-size: 20px;
            display: none;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="score">スコア: 0</div>
    <canvas id="gameCanvas" width="800" height="600"></canvas>
    <div id="gameOver">ゲームオーバー</div>
    <button id="restart">再スタート</button>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const scoreElement = document.getElementById('score');
        const gameOverElement = document.getElementById('gameOver');
        const restartButton = document.getElementById('restart');

        const WIDTH = canvas.width;
        const HEIGHT = canvas.height;

        let score = 0;
        let animationId;
        let gameOver = false;

        // プレイヤーの設定
        const player = {
            x: WIDTH / 2,
            y: HEIGHT / 2,
            radius: 20,
            angle: 0,
            rotation: 0,
            speed: 0,
            velocity: { x: 0, y: 0 },
            thrust: 0.1,
            friction: 0.99,
            draw: function() {
                ctx.save();
                ctx.translate(this.x, this.y);
                ctx.rotate(this.angle);
                ctx.beginPath();
                ctx.moveTo(20, 0);
                ctx.lineTo(-10, 10);
                ctx.lineTo(-10, -10);
                ctx.closePath();
                ctx.strokeStyle = 'white';
                ctx.stroke();
                ctx.restore();
            },
            update: function() {
                this.angle += this.rotation;
                this.velocity.x *= this.friction;
                this.velocity.y *= this.friction;
                this.velocity.x += Math.cos(this.angle) * this.speed;
                this.velocity.y += Math.sin(this.angle) * this.speed;
                this.x += this.velocity.x;
                this.y += this.velocity.y;

                // 画面端を反対側に移動
                if (this.x > WIDTH + this.radius) this.x = -this.radius;
                if (this.x < -this.radius) this.x = WIDTH + this.radius;
                if (this.y > HEIGHT + this.radius) this.y = -this.radius;
                if (this.y < -this.radius) this.y = HEIGHT + this.radius;
            }
        };

        // アステロイドの設定
        const asteroids = [];
        const asteroidCount = 5;

        function createAsteroid(x, y, radius, level) {
            const asteroid = {
                x: x,
                y: y,
                radius: radius,
                speed: Math.random() * 1.5 + 0.5,
                angle: Math.random() * Math.PI * 2,
                velocity: {
                    x: Math.cos(Math.random() * Math.PI * 2) * (Math.random() * 1.5 + 0.5),
                    y: Math.sin(Math.random() * Math.PI * 2) * (Math.random() * 1.5 + 0.5)
                },
                level: level || 0,
                draw: function() {
                    ctx.save();
                    ctx.translate(this.x, this.y);
                    ctx.beginPath();
                    for (let i = 0; i < 10; i++) {
                        const angle = (Math.PI * 2 / 10) * i;
                        const r = this.radius + Math.random() * 10 - 5;
                        ctx.lineTo(r * Math.cos(angle), r * Math.sin(angle));
                    }
                    ctx.closePath();
                    ctx.strokeStyle = 'white';
                    ctx.stroke();
                    ctx.restore();
                },
                update: function() {
                    this.x += this.velocity.x;
                    this.y += this.velocity.y;

                    // 画面端を反対側に移動
                    if (this.x > WIDTH + this.radius) this.x = -this.radius;
                    if (this.x < -this.radius) this.x = WIDTH + this.radius;
                    if (this.y > HEIGHT + this.radius) this.y = -this.radius;
                    if (this.y < -this.radius) this.y = HEIGHT + this.radius;
                }
            };
            asteroids.push(asteroid);
        }

        // 初期アステロイド生成
        for (let i = 0; i < asteroidCount; i++) {
            let x, y;
            do {
                x = Math.random() * WIDTH;
                y = Math.random() * HEIGHT;
            } while (Math.hypot(player.x - x, player.y - y) < 100);
            createAsteroid(x, y, 50);
        }

        // キー入力の処理
        const keys = {};
        document.addEventListener('keydown', (e) => {
            keys[e.code] = true;
        });
        document.addEventListener('keyup', (e) => {
            keys[e.code] = false;
        });

        // 弾丸の設定
        const bullets = [];
        const bulletSpeed = 5;
        const bulletLimit = 10;

        function shootBullet() {
            if (bullets.length < bulletLimit) {
                bullets.push({
                    x: player.x,
                    y: player.y,
                    velocity: {
                        x: Math.cos(player.angle) * bulletSpeed + player.velocity.x,
                        y: Math.sin(player.angle) * bulletSpeed + player.velocity.y
                    },
                    radius: 2,
                    lifespan: 60,
                    draw: function() {
                        ctx.beginPath();
                        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
                        ctx.fillStyle = 'white';
                        ctx.fill();
                    },
                    update: function() {
                        this.x += this.velocity.x;
                        this.y += this.velocity.y;
                        this.lifespan--;
                    }
                });
            }
        }

        // 衝突判定
        function checkCollision(obj1, obj2) {
            const dist = Math.hypot(obj1.x - obj2.x, obj1.y - obj2.y);
            return dist < obj1.radius + obj2.radius;
        }

        // ゲームループ
        function gameLoop() {
            ctx.clearRect(0, 0, WIDTH, HEIGHT);

            // プレイヤーの操作
            if (keys['ArrowLeft']) {
                player.rotation = -0.05;
            } else if (keys['ArrowRight']) {
                player.rotation = 0.05;
            } else {
                player.rotation = 0;
            }

            if (keys['ArrowUp']) {
                player.speed = player.thrust;
            } else {
                player.speed = 0;
            }

            if (keys['Space']) {
                shootBullet();
                keys['Space'] = false; // 連射防止
            }

            player.update();
            player.draw();

            // アステロイドの更新と描画
            asteroids.forEach((asteroid, index) => {
                asteroid.update();
                asteroid.draw();

                // プレイヤーとの衝突判定
                if (checkCollision(player, asteroid)) {
                    endGame();
                }

                // 弾丸との衝突判定
                bullets.forEach((bullet, bIndex) => {
                    if (checkCollision(bullet, asteroid)) {
                        bullets.splice(bIndex, 1);
                        asteroids.splice(index, 1);
                        score += 100;
                        scoreElement.textContent = `スコア: ${score}`;

                        // 小さいアステロイドを生成
                        if (asteroid.radius > 20) {
                            for (let i = 0; i < 2; i++) {
                                createAsteroid(asteroid.x, asteroid.y, asteroid.radius / 2, asteroid.level + 1);
                            }
                        }
                    }
                });
            });

            // 弾丸の更新と描画
            bullets.forEach((bullet, index) => {
                bullet.update();
                bullet.draw();
                if (bullet.lifespan <= 0) {
                    bullets.splice(index, 1);
                }
            });

            // 次のフレーム
            if (!gameOver) {
                animationId = requestAnimationFrame(gameLoop);
            }
        }

        // ゲーム終了
        function endGame() {
            gameOver = true;
            cancelAnimationFrame(animationId);
            gameOverElement.style.display = 'block';
            restartButton.style.display = 'block';
        }

        // 再スタート
        restartButton.addEventListener('click', () => {
            // 初期化
            asteroids.length = 0;
            bullets.length = 0;
            score = 0;
            scoreElement.textContent = `スコア: ${score}`;
            player.x = WIDTH / 2;
            player.y = HEIGHT / 2;
            player.velocity.x = 0;
            player.velocity.y = 0;
            gameOver = false;
            gameOverElement.style.display = 'none';
            restartButton.style.display = 'none';

            // アステロイド再生成
            for (let i = 0; i < asteroidCount; i++) {
                let x, y;
                do {
                    x = Math.random() * WIDTH;
                    y = Math.random() * HEIGHT;
                } while (Math.hypot(player.x - x, player.y - y) < 100);
                createAsteroid(x, y, 50);
            }

            // ゲーム開始
            animationId = requestAnimationFrame(gameLoop);
        });

        // ゲーム開始
        animationId = requestAnimationFrame(gameLoop);
    </script>
</body>
</html>

ゲーム画面

三角形の機体を操作します。

ゲームの操作方法

  • 回転: 左矢印キー (`ArrowLeft`) と右矢印キー (`ArrowRight`) を使用して宇宙船を回転させます。

  • 推進: 上矢印キー (`ArrowUp`) を押して宇宙船を前進させます。

  • 射撃: スペースキー (`Space`) を押して弾丸を発射します。

コードの説明

  1. HTML構造:

    • `<canvas>`要素はゲームの描画領域です。

    • スコア表示やゲームオーバーのメッセージ、再スタートボタンを表示するための要素も含まれています。

  2. スタイル:

    • ゲームの背景を黒に設定し、キャンバスを中央に配置しています。

    • スコアやゲームオーバーのメッセージ、再スタートボタンのスタイルも定義しています。

  3. JavaScript:

    • プレイヤー: 宇宙船の位置、速度、回転などを管理します。`draw`メソッドで宇宙船を描画し、`update`メソッドで位置と速度を更新します。

    • アステロイド: ランダムな位置と速度でアステロイドを生成し、画面上を移動させます。衝突判定も行います。

    • 弾丸: 宇宙船から発射され、一定の寿命で消滅します。アステロイドと衝突するとアステロイドを分割または破壊します。

    • ゲームループ: `requestAnimationFrame`を使用して、毎フレームゲームの状態を更新し、描画します。

    • 衝突判定: プレイヤーとアステロイド、弾丸とアステロイドの衝突を検出します。

    • ゲームオーバー: 宇宙船がアステロイドと衝突した場合にゲームを終了し、再スタートボタンを表示します。

    • 再スタート: ゲームオーバー後に再度ゲームを開始できるように初期化します。

カスタマイズ

この基本的なアステロイドゲームを基に、以下のような機能を追加・カスタマイズすることができます。

  • 音楽と効果音: ゲームに音楽や効果音を追加して、より没入感のある体験に。

  • スコアボード: 高得点を記録し、表示する機能。

  • 複数のレベル: アステロイドの数や速度をレベルに応じて増加させる。

  • パワーアップ: シールドや追加弾薬などのパワーアップアイテムを追加。

  • グラフィックスの向上: より詳細な宇宙船やアステロイドのデザイン。

注意事項

  • パフォーマンス: 多数のアステロイドや弾丸を同時に処理すると、ブラウザのパフォーマンスに影響を与える可能性があります。適切に最適化してください。

  • モバイル対応: このコードはデスクトップ向けに設計されています。モバイルデバイスでプレイ可能にするためには、タッチ操作のサポートや画面サイズの調整が必要です。

ぜひ、このコードを基にして、自分だけのアステロイドゲームを作成してみてください!

以下のファイルをダウンロードして、ファイルをクリックすると実際に遊べます!

o1-mini版アステロイドゲームのファイル

弊社サーバにもアップしておきました。こちらをクリックしていただくとブラウザで遊べます。

比較対象

同じプロンプトでGPT-4oで作成したアステロイドゲームのファイル
GPT-4oで作ったこのゲームは、機体も動きませんので、不合格です。

同じく、Webアプリケーション制作に特化したGPTsが出力したアステロイドゲーム
このGPTsで作ったゲームは、ちゃんと遊べますが、o1-miniで作ったゲームの方が面白いし、オープニングやエンディングもできていて、ゲームとしての完成度は高いです。

まとめ

以上、o1-miniのプログラミング能力を試す実験としてのゲーム制作でした。
結論としては、少なくとも今回試したゲーム制作においては、o1-miniは、GPT-4oよりプログラミング能力が上がっています。

【重版決定】AIアプリをプログラミング不要で開発する書籍

GPTsを自分で作って世界に公開したい方はこちら!

この記事が気に入ったらサポートをしてみませんか?