ClaudeでLINEツムツムのような落ち物パズルを作ってみた【AI生成ゲーム実験 Vol.2】★誰でも作れる作成用プロンプトとコピペで遊べるhtmlソースコードを公開!

2025年7月9日水曜日

オリジナルゲーム ゲーム開発 プロンプト 無料ゲーム

t f B! P L

 こんにちは、AM0500です。

今回は、AI「Claude(claude 4 sonnet)」に指示を出して作ってみた、LINEツムツムのような?落ち物パズルゲームをご紹介します。ソースコードも紹介していますので是非作成を試してみてくださいね。

🔗 実際にプレイしてみる

▼ ブラウザでそのまま遊べます(PC推奨):
👉 LINEツムツムをイメージしたゲームをプレイする (外部ページに移動します)

作成に使用したツールはこちら
👉 使用しているAI Claude(claude 4 sonnet)はこちら


🎮 ゲーム画面(スクリーンショット)


画像
画像

📝 一言コメント

LINEのツムツムというゲームをAIが認識できるか。そしてどんなゲームになるのかを試した作品になります。本家とはちょっと違う感じにはなっていますが、思ったよりシンプルだけどおもしろいゲームになりました。
ちなみに200コンボくらいまでコンボ繋がりました。意外とアリです。




🧠 使用したClaudeへのプロンプト

以下のような指示で、今回のゲームが生成されました。

LINEツムツムのように、爽快感のある消しもの落ちパズルゲームを作ってください。

このようなプロンプトで作成されたゲームになります。
説明文を読む限り、LINEツムツムというゲームを認識している感じでした。


🕹️ Claudeが返してくれた説明文

ゲームの特徴:
🎮 基本システム

同じ色のツムを3つ以上つなげてクリックで消去
8×12のグリッドで6色のかわいいツム
重力でツムが落下し、新しいツムが補充される

✨ 爽快感要素

連鎖システム:消した後に自動的に新しいマッチを探して連続消去
コンボシステム:連続で消すとスコアにボーナス
滑らかなアニメーション(落下、消去、選択エフェクト)
スコアエフェクト:点数が飛び出すアニメーション

🎨 視覚効果

グラデーション背景とグラスモーフィズムデザイン
選択時のグロー効果
消去時のスケールアニメーション
絵文字を使った可愛いツムデザイン

🎯 ゲームプレイ

戦略的思考:大きなグループを作ってより多くのポイント獲得
連鎖を狙って高得点を目指す
消せる組み合わせがなくなるとゲームオーバー

操作は簡単で、同じ色のツムが3つ以上つながっている部分をクリックするだけです。連鎖が発生すると自動的に消去が続き、非常に爽快感があります!ぜひプレイしてみてください!


💡 ソースコード(HTML)

ソースコード全体は少し長いです。これを一度に作成してくれます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ツムツム風パズルゲーム</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%);
            font-family: 'Comic Sans MS', cursive;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        
        .game-container {
            background: rgba(255, 255, 255, 0.9);
            border-radius: 20px;
            padding: 20px;
            backdrop-filter: blur(10px);
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
            border: 3px solid #fff;
        }
        
        .game-info {
            display: flex;
            justify-content: space-between;
            margin-bottom: 15px;
            font-size: 18px;
            font-weight: bold;
            color: #333;
        }
        
        .score-info {
            background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
            color: white;
            padding: 10px 15px;
            border-radius: 15px;
            box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
        }
        
        .combo-info {
            background: linear-gradient(45deg, #4ecdc4, #44a08d);
            color: white;
            padding: 10px 15px;
            border-radius: 15px;
            box-shadow: 0 4px 15px rgba(78, 205, 196, 0.3);
        }
        
        canvas {
            border: 3px solid #fff;
            border-radius: 15px;
            background: #f8f9fa;
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
            cursor: pointer;
        }
        
        .instructions {
            text-align: center;
            margin-top: 15px;
            color: #666;
            font-size: 14px;
        }
        
        .game-over {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.9);
            color: white;
            padding: 30px;
            border-radius: 20px;
            text-align: center;
            font-size: 24px;
            display: none;
            z-index: 1000;
        }
        
        .restart-btn {
            background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
            color: white;
            border: none;
            padding: 15px 30px;
            border-radius: 25px;
            margin-top: 15px;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
            box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
            transition: transform 0.2s;
        }
        
        .restart-btn:hover {
            transform: scale(1.05);
        }
        
        .effect-text {
            position: absolute;
            font-size: 20px;
            font-weight: bold;
            color: #ff6b6b;
            pointer-events: none;
            z-index: 100;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="game-info">
            <div class="score-info">
                スコア: <span id="score">0</span>
            </div>
            <div class="combo-info">
                コンボ: <span id="combo">0</span>
            </div>
        </div>
        <canvas id="gameCanvas" width="400" height="600"></canvas>
        <div class="instructions">
            同じ色のツムを3つ以上つなげてクリック!
        </div>
        <div class="game-over" id="gameOver">
            <div id="gameOverText">ゲームオーバー</div>
            <div style="font-size: 16px; margin: 10px 0;">
                最終スコア: <span id="finalScore">0</span>
            </div>
            <button class="restart-btn" onclick="restartGame()">もう一度プレイ</button>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const scoreElement = document.getElementById('score');
        const comboElement = document.getElementById('combo');
        const gameOverElement = document.getElementById('gameOver');
        const finalScoreElement = document.getElementById('finalScore');

        // ゲーム設定
        const GRID_WIDTH = 8;
        const GRID_HEIGHT = 12;
        const TSUM_SIZE = 45;
        const COLORS = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dda0dd'];
        const EMOJI_TSUMS = ['🐭', '🐱', '🐶', '🐰', '🐸', '🐷'];

        // ゲーム状態
        let gameState = {
            score: 0,
            combo: 0,
            grid: [],
            selectedTsums: [],
            isAnimating: false,
            gameRunning: true
        };

        // ツムのクラス
        class Tsum {
            constructor(x, y, color, emoji) {
                this.x = x;
                this.y = y;
                this.color = color;
                this.emoji = emoji;
                this.scale = 1;
                this.targetY = y;
                this.velocity = 0;
                this.isSelected = false;
                this.isDisappearing = false;
                this.disappearProgress = 0;
            }

            draw() {
                ctx.save();
                
                const centerX = this.x * TSUM_SIZE + TSUM_SIZE / 2;
                const centerY = this.y * TSUM_SIZE + TSUM_SIZE / 2;
                
                // 消える効果
                if (this.isDisappearing) {
                    ctx.globalAlpha = 1 - this.disappearProgress;
                    this.scale = 1 + this.disappearProgress * 0.5;
                }
                
                // 選択時の効果
                if (this.isSelected) {
                    ctx.shadowColor = this.color;
                    ctx.shadowBlur = 20;
                    this.scale = 1.1;
                }
                
                ctx.translate(centerX, centerY);
                ctx.scale(this.scale, this.scale);
                
                // ツムの背景円
                ctx.beginPath();
                ctx.arc(0, 0, TSUM_SIZE * 0.4, 0, Math.PI * 2);
                ctx.fillStyle = this.color;
                ctx.fill();
                
                // 外枠
                ctx.strokeStyle = '#fff';
                ctx.lineWidth = 3;
                ctx.stroke();
                
                // 絵文字
                ctx.font = `${TSUM_SIZE * 0.5}px serif`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillStyle = '#fff';
                ctx.fillText(this.emoji, 0, 0);
                
                ctx.restore();
            }

            update() {
                if (this.isDisappearing) {
                    this.disappearProgress += 0.1;
                    return this.disappearProgress >= 1;
                }
                
                // 落下アニメーション
                if (this.y < this.targetY) {
                    this.velocity += 0.5;
                    this.y += this.velocity;
                    if (this.y >= this.targetY) {
                        this.y = this.targetY;
                        this.velocity = 0;
                    }
                }
                
                // スケールを元に戻す
                if (this.scale > 1 && !this.isSelected) {
                    this.scale -= 0.05;
                    if (this.scale < 1) this.scale = 1;
                }
                
                return false;
            }
        }

        // グリッドを初期化
        function initGrid() {
            gameState.grid = [];
            for (let y = 0; y < GRID_HEIGHT; y++) {
                gameState.grid[y] = [];
                for (let x = 0; x < GRID_WIDTH; x++) {
                    const colorIndex = Math.floor(Math.random() * COLORS.length);
                    gameState.grid[y][x] = new Tsum(x, y, COLORS[colorIndex], EMOJI_TSUMS[colorIndex]);
                }
            }
        }

        // マウスクリック処理
        canvas.addEventListener('click', (e) => {
            if (!gameState.gameRunning || gameState.isAnimating) return;
            
            const rect = canvas.getBoundingClientRect();
            const x = Math.floor((e.clientX - rect.left) / TSUM_SIZE);
            const y = Math.floor((e.clientY - rect.top) / TSUM_SIZE);
            
            if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT) {
                selectConnectedTsums(x, y);
            }
        });

        // 接続されたツムを選択
        function selectConnectedTsums(startX, startY) {
            const targetTsum = gameState.grid[startY][startX];
            if (!targetTsum) return;
            
            // 既存の選択をクリア
            clearSelection();
            
            const visited = new Set();
            const connected = [];
            
            function dfs(x, y) {
                const key = `${x},${y}`;
                if (visited.has(key)) return;
                if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return;
                
                const tsum = gameState.grid[y][x];
                if (!tsum || tsum.color !== targetTsum.color) return;
                
                visited.add(key);
                connected.push(tsum);
                
                // 隣接するセルを探索
                dfs(x + 1, y);
                dfs(x - 1, y);
                dfs(x, y + 1);
                dfs(x, y - 1);
            }
            
            dfs(startX, startY);
            
            if (connected.length >= 3) {
                gameState.selectedTsums = connected;
                connected.forEach(tsum => tsum.isSelected = true);
                
                // 少し遅延してから消去
                setTimeout(() => {
                    removeTsums();
                }, 300);
            }
        }

        // 選択をクリア
        function clearSelection() {
            gameState.selectedTsums.forEach(tsum => tsum.isSelected = false);
            gameState.selectedTsums = [];
        }

        // ツムを消去
        function removeTsums() {
            if (gameState.selectedTsums.length === 0) return;
            
            gameState.isAnimating = true;
            
            // スコア計算
            const count = gameState.selectedTsums.length;
            const baseScore = count * 10;
            const comboBonus = gameState.combo * 5;
            const totalScore = baseScore + comboBonus;
            
            gameState.score += totalScore;
            gameState.combo++;
            
            // エフェクトテキストを表示
            showScoreEffect(totalScore, count);
            
            // ツムを消去アニメーション開始
            gameState.selectedTsums.forEach(tsum => {
                tsum.isDisappearing = true;
                tsum.isSelected = false;
            });
            
            setTimeout(() => {
                // グリッドからツムを削除
                gameState.selectedTsums.forEach(tsum => {
                    const x = tsum.x;
                    const y = tsum.y;
                    gameState.grid[y][x] = null;
                });
                
                gameState.selectedTsums = [];
                
                // ツムを落下させる
                dropTsums();
                
                // 新しいツムを補充
                setTimeout(() => {
                    fillEmptySpaces();
                    gameState.isAnimating = false;
                    
                    // 自動的に消せる組み合わせをチェック
                    setTimeout(() => {
                        checkForAutoMatches();
                    }, 500);
                }, 300);
            }, 500);
        }

        // ツムを落下させる
        function dropTsums() {
            for (let x = 0; x < GRID_WIDTH; x++) {
                // 下から上へ空きを詰める
                let writeIndex = GRID_HEIGHT - 1;
                for (let y = GRID_HEIGHT - 1; y >= 0; y--) {
                    if (gameState.grid[y][x]) {
                        if (writeIndex !== y) {
                            gameState.grid[writeIndex][x] = gameState.grid[y][x];
                            gameState.grid[writeIndex][x].targetY = writeIndex;
                            gameState.grid[y][x] = null;
                        }
                        writeIndex--;
                    }
                }
            }
        }

        // 空きスペースを補充
        function fillEmptySpaces() {
            for (let x = 0; x < GRID_WIDTH; x++) {
                for (let y = 0; y < GRID_HEIGHT; y++) {
                    if (!gameState.grid[y][x]) {
                        const colorIndex = Math.floor(Math.random() * COLORS.length);
                        gameState.grid[y][x] = new Tsum(x, -1, COLORS[colorIndex], EMOJI_TSUMS[colorIndex]);
                        gameState.grid[y][x].targetY = y;
                    }
                }
            }
        }

        // 自動マッチをチェック
        function checkForAutoMatches() {
            let foundMatch = false;
            
            for (let y = 0; y < GRID_HEIGHT && !foundMatch; y++) {
                for (let x = 0; x < GRID_WIDTH && !foundMatch; x++) {
                    const tsum = gameState.grid[y][x];
                    if (tsum) {
                        const connected = getConnectedTsums(x, y, tsum.color);
                        if (connected.length >= 3) {
                            gameState.selectedTsums = connected;
                            connected.forEach(t => t.isSelected = true);
                            setTimeout(() => removeTsums(), 300);
                            foundMatch = true;
                        }
                    }
                }
            }
            
            if (!foundMatch) {
                gameState.combo = 0;
                checkGameOver();
            }
        }

        // 接続されたツムを取得
        function getConnectedTsums(startX, startY, color) {
            const visited = new Set();
            const connected = [];
            
            function dfs(x, y) {
                const key = `${x},${y}`;
                if (visited.has(key)) return;
                if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return;
                
                const tsum = gameState.grid[y][x];
                if (!tsum || tsum.color !== color) return;
                
                visited.add(key);
                connected.push(tsum);
                
                dfs(x + 1, y);
                dfs(x - 1, y);
                dfs(x, y + 1);
                dfs(x, y - 1);
            }
            
            dfs(startX, startY);
            return connected;
        }

        // スコアエフェクトを表示
        function showScoreEffect(score, count) {
            const effectDiv = document.createElement('div');
            effectDiv.className = 'effect-text';
            effectDiv.textContent = `+${score}`;
            effectDiv.style.left = Math.random() * 200 + 200 + 'px';
            effectDiv.style.top = Math.random() * 100 + 150 + 'px';
            
            document.body.appendChild(effectDiv);
            
            // アニメーション
            let opacity = 1;
            let y = 0;
            const animate = () => {
                opacity -= 0.02;
                y -= 2;
                effectDiv.style.opacity = opacity;
                effectDiv.style.transform = `translateY(${y}px)`;
                
                if (opacity > 0) {
                    requestAnimationFrame(animate);
                } else {
                    document.body.removeChild(effectDiv);
                }
            };
            animate();
        }

        // ゲームオーバーチェック
        function checkGameOver() {
            let hasValidMove = false;
            
            for (let y = 0; y < GRID_HEIGHT && !hasValidMove; y++) {
                for (let x = 0; x < GRID_WIDTH && !hasValidMove; x++) {
                    const tsum = gameState.grid[y][x];
                    if (tsum) {
                        const connected = getConnectedTsums(x, y, tsum.color);
                        if (connected.length >= 3) {
                            hasValidMove = true;
                        }
                    }
                }
            }
            
            if (!hasValidMove) {
                gameState.gameRunning = false;
                finalScoreElement.textContent = gameState.score;
                gameOverElement.style.display = 'block';
            }
        }

        // ゲームを再開
        function restartGame() {
            gameState.score = 0;
            gameState.combo = 0;
            gameState.gameRunning = true;
            gameState.isAnimating = false;
            gameOverElement.style.display = 'none';
            clearSelection();
            initGrid();
        }

        // 描画
        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // グリッドの背景
            ctx.fillStyle = '#f8f9fa';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // ツムを描画
            for (let y = 0; y < GRID_HEIGHT; y++) {
                for (let x = 0; x < GRID_WIDTH; x++) {
                    const tsum = gameState.grid[y][x];
                    if (tsum) {
                        tsum.draw();
                    }
                }
            }
        }

        // 更新
        function update() {
            let needsUpdate = false;
            
            for (let y = 0; y < GRID_HEIGHT; y++) {
                for (let x = 0; x < GRID_WIDTH; x++) {
                    const tsum = gameState.grid[y][x];
                    if (tsum) {
                        const shouldRemove = tsum.update();
                        if (shouldRemove) {
                            gameState.grid[y][x] = null;
                        }
                        needsUpdate = true;
                    }
                }
            }
            
            return needsUpdate;
        }

        // UI更新
        function updateUI() {
            scoreElement.textContent = gameState.score;
            comboElement.textContent = gameState.combo;
        }

        // メインゲームループ
        function gameLoop() {
            update();
            draw();
            updateUI();
            requestAnimationFrame(gameLoop);
        }

        // ゲーム開始
        initGrid();
        gameLoop();
    </script>
</body>
</html>

🔔 補足・注意事項

このコードや説明は、Claudeのバージョンや環境によって出力内容が異なる可能性があります。
同じ命令をしても、まったく違うコードが返ってくることもあるので、再現性は参考程度にご覧ください。


🔔 リンク

👉 AM0500 作品一覧
👉 AM0500 GAME LAB

このブログを検索

アクションゲームメーカーでタイトル画面からゲーム本編へ切り替える基本手順(画面遷移のやり方)

 こんにちは、AM0500です。 今回は、アクションゲームメーカー(AGM)で タイトル画面から実際のゲームシーンへ移動させる手順 をまとめます。 AGMでは、シーンの移動を個別のプログラムで書くのではなく、 「SceneTransition」という専用の画面 で一括管理します。...

最近の投稿

記事まとめ

自己紹介

自分の写真
飼い猫のモイちゃんに朝5時に起こされる生活が続いたのでAM0500と名付けました。モイちゃんへの愛でゲーム制作にも挑戦しはじめて、素人ながらも、モイちゃんを主役にしたゲームを作り上げた時の喜びは忘れられません。そこから趣味でゲーム開発を始めました。勉強中の身ですがよろしくお願いします。

QooQ