top of page
MSL

もう「作れません」とは言わせない。AIに"丸投げ"で、Webサイトにゲームを実装した全記録

「自社のWebサイト、もう少し訪問者に楽しんでもらえないかな…」


ある週末、ふとそんなことを思いました。例えば、ちょっとした息抜きになるような、懐かしのブロック崩しゲームがあったら面白いかもしれない。


しかし、多くの人はプログラミングの専門家ではありません。以前なら「エンジニアに相談して、見積もりを取って、要件を伝えて…」と、考えただけで気が遠くなるような道のりでした。時間もコストもかかります。


でも、今は違います。私たちには、超優秀なAIアシスタント「Google Gemini」がいます。

「もしかして、AIに頼めば一瞬で作ってくれるのでは?」

そんな淡い期待を胸に、週末のAIプログラミングに挑戦してみることにしました。


AIアシスタントへの最初の"お願い"

早速Geminiに、こんな風に話しかけてみました。まるで作ってほしいものを部下に説明するような感覚です。

「Webページに埋め込める、シンプルなブロック崩しゲームを作ってくれないかな?スコアは、今のプレイ中のハイスコアが分かればOK。あと、見た目はカラフルで楽しい感じにしてほしい!」

専門的な言葉は使っていません。ポイントは「何がしたいか」を具体的に伝えることだけ。 すると、どうでしょう。


数秒後、Geminiは「はい、承知いたしました」とでも言うかのように、ずらっとプログラムのコードを書き出したのです。まるで魔法を見ているようでした。


「あ、ここ修正して」と追加で"お願い"

出来上がったコードを早速自分のテストページに設置してみると、ゲームはきちんと動きます。しかし、少しだけ気になる点が。

  • キーボードでの操作が、なんだかスムーズじゃない。

  • ゲームオーバーになったら、そこで終わり。もう一度プレイできない。

これでは、せっかく訪れてくれたユーザーをがっかりさせてしまいます。

昔なら、ここからの修正依頼がまた大変な作業でした。しかし、AIアシスタントなら話は別です。私はGeminiに、こうフィードバックを送りました。

「ありがとう!動いたよ。ただ、キーボードの操作がうまくいかないみたい。それと、ゲームオーバーになったら、もう一度遊べるように修正してほしいな。」

この、まるで同僚に「ちょっと修正お願い!」と頼むような気軽さ。するとGeminiは、再び完璧な修正コードを提案してくれました。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ブロック崩しゲーム</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            text-align: center;
            margin: 0;
            padding: 20px;
            background-color: #f0f0f0;
            /* ユーザーがキー入力でページをスクロールさせないように */
            overflow: hidden; 
        }
        h1 {
            font-size: 24px;
            color: #333;
        }
        #gameContainer {
            display: inline-block;
            border: 2px solid #333;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        canvas {
            display: block;
            background: #fff;
            cursor: pointer; /* クリック可能であることを示すカーソル */
        }
        #infoPanel {
            display: flex;
            justify-content: space-around;
            padding: 10px;
            background-color: #333;
            color: #fff;
            font-size: 16px;
        }
        #instructions {
            margin-top: 15px;
            color: #555;
            font-size: 14px;
        }
    </style>
</head>
<body>

    <div id="gameContainer">
        <h1>ブロック崩し</h1>
        <canvas id="gameCanvas" width="480" height="360"></canvas>
        <div id="infoPanel">
            <div>SCORE: <span id="score">0</span></div>
            <div>HIGH SCORE: <span id="highScore">0</span></div>
        </div>
    </div>
    <div id="instructions">
        操作: マウス移動 または ← → キー<br>
        リスタート: ゲームオーバー後に画面をクリック
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        // ゲームの状態管理
        let gameState = "start"; // "start", "playing", "gameOver", "gameClear"

        // ゲームの変数
        let ballRadius = 10;
        let x, y, dx, dy;

        // パドルの変数
        let paddleHeight = 10;
        let paddleWidth = 75;
        let paddleX;

        // キーボード操作用の変数
        let rightPressed = false;
        let leftPressed = false;
        
        // ブロックの変数
        const brickRowCount = 5;
        const brickColumnCount = 7;
        const brickWidth = 55;
        const brickHeight = 20;
        const brickPadding = 10;
        const brickOffsetTop = 30;
        const brickOffsetLeft = 30;
        const brickColors = ["#e53935", "#d81b60", "#8e24aa", "#5e35b1", "#3949ab", "#1e88e5", "#039be5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e"];
        let bricks = [];

        // スコア
        let score = 0;
        let highScore = 0;

        // DOM要素
        const scoreDisplay = document.getElementById('score');
        const highScoreDisplay = document.getElementById('highScore');

        // イベントリスナー
        document.addEventListener("keydown", keyDownHandler, false);
        document.addEventListener("keyup", keyUpHandler, false);
        document.addEventListener("mousemove", mouseMoveHandler, false);
        canvas.addEventListener("click", clickHandler, false);

        function keyDownHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") rightPressed = true;
            else if(e.key == "Left" || e.key == "ArrowLeft") leftPressed = true;
        }

        function keyUpHandler(e) {
            if(e.key == "Right" || e.key == "ArrowRight") rightPressed = false;
            else if(e.key == "Left" || e.key == "ArrowLeft") leftPressed = false;
        }
        
        function mouseMoveHandler(e) {
            let relativeX = e.clientX - canvas.getBoundingClientRect().left;
            if(relativeX > 0 && relativeX < canvas.width) {
                paddleX = relativeX - paddleWidth / 2;
                if (paddleX < 0) paddleX = 0;
                if (paddleX + paddleWidth > canvas.width) paddleX = canvas.width - paddleWidth;
            }
        }
        
        function clickHandler() {
            if (gameState === "gameOver" || gameState === "gameClear" || gameState === "start") {
                resetGame();
                gameState = "playing";
            }
        }

        function resetGame() {
            // ボールとパドルの位置をリセット
            x = canvas.width / 2;
            y = canvas.height - 30;
            dx = 3;
            dy = -3;
            paddleX = (canvas.width - paddleWidth) / 2;
            
            // スコアをリセット
            score = 0;
            scoreDisplay.textContent = score;

            // ブロックをリセット
            for(let c = 0; c < brickColumnCount; c++) {
                bricks[c] = [];
                for(let r = 0; r < brickRowCount; r++) {
                    bricks[c][r] = { x: 0, y: 0, status: 1, color: brickColors[(c + r * brickColumnCount) % brickColors.length] };
                }
            }
        }
        
        function collisionDetection() {
            for(let c = 0; c < brickColumnCount; c++) {
                for(let r = 0; r < brickRowCount; r++) {
                    let b = bricks[c][r];
                    if(b.status == 1) {
                        if(x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
                            dy = -dy;
                            b.status = 0;
                            score += 10;
                            scoreDisplay.textContent = score;

                            if (isGameClear()) {
                                gameState = "gameClear";
                                updateHighScore();
                            }
                        }
                    }
                }
            }
        }
        
        function isGameClear() {
            for(let c=0; c<brickColumnCount; c++) {
                for(let r=0; r<brickRowCount; r++) {
                    if(bricks[c][r].status === 1) return false;
                }
            }
            return true;
        }

        function updateHighScore() {
            if (score > highScore) {
                highScore = score;
                highScoreDisplay.textContent = highScore;
            }
        }
        
        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, ballRadius, 0, Math.PI*2);
            ctx.fillStyle = "#333";
            ctx.fill();
            ctx.closePath();
        }

        function drawPaddle() {
            ctx.beginPath();
            ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
            ctx.fillStyle = "#0095DD";
            ctx.fill();
            ctx.closePath();
        }

        function drawBricks() {
            for(let c = 0; c < brickColumnCount; c++) {
                for(let r = 0; r < brickRowCount; r++) {
                    if(bricks[c][r].status == 1) {
                        let brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
                        let brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
                        bricks[c][r].x = brickX;
                        bricks[c][r].y = brickY;
                        ctx.beginPath();
                        ctx.rect(brickX, brickY, brickWidth, brickHeight);
                        ctx.fillStyle = bricks[c][r].color;
                        ctx.fill();
                        ctx.closePath();
                    }
                }
            }
        }
        
        function drawMessage(message, subMessage) {
             ctx.font = "32px Arial";
             ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
             ctx.textAlign = "center";
             ctx.fillText(message, canvas.width/2, canvas.height/2 - 20);
             ctx.font = "16px Arial";
             ctx.fillText(subMessage, canvas.width/2, canvas.height/2 + 20);
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawBricks();
            drawPaddle();
            drawBall();
            
            if (gameState === "playing") {
                collisionDetection();

                // パドル移動
                if(rightPressed && paddleX < canvas.width - paddleWidth) paddleX += 7;
                else if(leftPressed && paddleX > 0) paddleX -= 7;

                // ボールの移動と衝突判定
                if(x + dx > canvas.width - ballRadius || x + dx < ballRadius) dx = -dx;
                if(y + dy < ballRadius) dy = -dy;
                else if(y + dy > canvas.height - ballRadius) {
                    if(x > paddleX && x < paddleX + paddleWidth) {
                        dy = -dy;
                    } else {
                        gameState = "gameOver";
                        updateHighScore();
                    }
                }
                x += dx;
                y += dy;
            }

            if (gameState === "start") {
                 drawMessage("ブロック崩し", "画面をクリックしてスタート");
            } else if (gameState === "gameOver") {
                 drawMessage("GAME OVER", "クリックしてリトライ");
            } else if (gameState === "gameClear") {
                 drawMessage("GAME CLEAR!", "クリックしてもう一度プレイ");
            }

            requestAnimationFrame(draw);
        }

        // 初期化してゲームループを開始
        resetGame();
        draw();
    </script>

</body>
</html>

このコードを貼り付けるだけで、私のWebサイトには、思い描いていた通りのブロック崩しゲームが完成してしまいました。




アイデアさえあれば、誰でも「開発者」になれる時代

今回の体験で、確信したことがあります。


これからの時代に求められるのは、必ずしもプログラミングを書く技術だけではない。「何を、どのように作れば面白いか、役に立つか」というアイデアを考え、AIに的確に伝える能力こそが、新しい武器になるのだと。


もちろん、世界を変えるような全く新しいサービスには、今もこれからもプロのエンジニアの力が必要です。しかし、「社内アンケート用の簡単なツールが欲しい」「商品説明に、動きのあるシミュレーターを入れたい」といった、過去に前例のあるプログラムであれば、AIは非常に強力なパートナーになります。


「エンジニアがいないから」「予算がないから」と諦めていた、あなたの頭の中にある「あったらいいな」。それを形にするのに、もう特別なスキルは必要ありません。


ぜひ、あなたもAIアシスタントに、ビジネスのアイデアを打ち明けてみませんか? 思ったよりずっと簡単に、未来が動き出すかもしれませんよ。

Komentarze


(C) 株式会社マーケティングサイエンスラボ

(C) MSL,2020-2025

bottom of page