HTML5+JavaScript实现消消乐游戏

HTML5+JavaScript实现消消乐游戏

点击两个相邻的方块来交换它们位置。

如果交换后形成三个或更多相同图案的方块连成一线,这些方块会被消除。

消除后,上方的方块会下落填补空缺,顶部会生成新的方块。

每消除一个方块得10分。例如,如果一次消除了4个方块,玩家将得到40分。

运行效果如下图:

源码如下:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>基础消消乐游戏 - Emoji版</title>
    <style>
        body {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
            font-family: Arial, sans-serif;
        }
        #gameContainer {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        canvas {
            border: 2px solid #000;
            margin-bottom: 10px;
        }
        #scoreDisplay {
            font-size: 24px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <div id="scoreDisplay">分数: 0</div>
        <canvas id="gameCanvas" width="400" height="400"></canvas>
    </div>

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

        const GRID_SIZE = 8;
        const CELL_SIZE = canvas.width / GRID_SIZE;
        const EMOJIS = ['☮', '⚜', '♾ ', '☯', '⚛', '✳'];

        let grid = [];
        let selectedCell = null;
        let score = 0;

        function initGrid() {
            for (let i = 0; i < GRID_SIZE; i++) {
                grid[i] = [];
                for (let j = 0; j < GRID_SIZE; j++) {
                    grid[i][j] = EMOJIS[Math.floor(Math.random() * EMOJIS.length)];
                }
            }
        }

        function drawGrid() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.font = `${CELL_SIZE * 0.8}px Arial`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';

            for (let i = 0; i < GRID_SIZE; i++) {
                for (let j = 0; j < GRID_SIZE; j++) {
                    ctx.fillText(
                        grid[i][j], 
                        i * CELL_SIZE + CELL_SIZE / 2, 
                        j * CELL_SIZE + CELL_SIZE / 2
                    );
                }
            }

            if (selectedCell) {
                ctx.strokeStyle = 'black';
                ctx.lineWidth = 2;
                ctx.strokeRect(
                    selectedCell.x * CELL_SIZE, 
                    selectedCell.y * CELL_SIZE, 
                    CELL_SIZE, 
                    CELL_SIZE
                );
            }
        }

        function checkMatches() {
            let matched = [];
            
            // 检查水平匹配
            for (let j = 0; j < GRID_SIZE; j++) {
                let streak = 1;
                for (let i = 1; i < GRID_SIZE; i++) {
                    if (grid[i][j] === grid[i-1][j]) {
                        streak++;
                    } else {
                        if (streak >= 3) {
                            for (let k = i - streak; k < i; k++) {
                                matched.push({x: k, y: j});
                            }
                        }
                        streak = 1;
                    }
                }
                if (streak >= 3) {
                    for (let k = GRID_SIZE - streak; k < GRID_SIZE; k++) {
                        matched.push({x: k, y: j});
                    }
                }
            }
            
            // 检查垂直匹配
            for (let i = 0; i < GRID_SIZE; i++) {
                let streak = 1;
                for (let j = 1; j < GRID_SIZE; j++) {
                    if (grid[i][j] === grid[i][j-1]) {
                        streak++;
                    } else {
                        if (streak >= 3) {
                            for (let k = j - streak; k < j; k++) {
                                matched.push({x: i, y: k});
                            }
                        }
                        streak = 1;
                    }
                }
                if (streak >= 3) {
                    for (let k = GRID_SIZE - streak; k < GRID_SIZE; k++) {
                        matched.push({x: i, y: k});
                    }
                }
            }
            
            return matched;
        }

        function removeMatches(matches) {
            matches.forEach(cell => {
                grid[cell.x][cell.y] = null;
            });
            //updateScore(matches.length);
        }

        function updateScore(matchCount) {
            score += matchCount * 10;
            scoreDisplay.textContent = `分数: ${score}`;
        }

        function fillBlanks() {
            for (let i = 0; i < GRID_SIZE; i++) {
                let blanks = 0;
                for (let j = GRID_SIZE - 1; j >= 0; j--) {
                    if (!grid[i][j]) {
                        blanks++;
                    } else if (blanks > 0) {
                        grid[i][j + blanks] = grid[i][j];
                        grid[i][j] = null;
                    }
                }
                for (let j = 0; j < blanks; j++) {
                    grid[i][j] = EMOJIS[Math.floor(Math.random() * EMOJIS.length)];
                }
            }
        }

        function swapCells(cell1, cell2) {
            const temp = grid[cell1.x][cell1.y];
            grid[cell1.x][cell1.y] = grid[cell2.x][cell2.y];
            grid[cell2.x][cell2.y] = temp;
        }

        canvas.addEventListener('click', (event) => {
            const rect = canvas.getBoundingClientRect();
            const x = Math.floor((event.clientX - rect.left) / CELL_SIZE);
            const y = Math.floor((event.clientY - rect.top) / CELL_SIZE);

            if (selectedCell) {
                if ((Math.abs(selectedCell.x - x) === 1 && selectedCell.y === y) ||
                    (Math.abs(selectedCell.y - y) === 1 && selectedCell.x === x)) {
                    swapCells(selectedCell, {x, y});
                    let matches = checkMatches();
                    if (matches.length === 0) {
                        swapCells(selectedCell, {x, y});
                    } else {
                        let totalMatches = 0;
                        while (matches.length > 0) {
                            totalMatches += matches.length;
                            removeMatches(matches);
                            fillBlanks();
                            matches = checkMatches();
                        }
                        updateScore(totalMatches);  // 在所有匹配处理完后更新分数
                    }
                }
                selectedCell = null;
            } else {
                selectedCell = {x, y};
            }
            drawGrid();
        });

        function gameLoop() {
            drawGrid();
            requestAnimationFrame(gameLoop);
        }

        initGrid();
        gameLoop();
    </script>
</body>
</html>
相关推荐
Amy.Wang37 分钟前
前端如何实现电子签名
前端·javascript·html5
海天胜景39 分钟前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼39 分钟前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
百锦再44 分钟前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref
jingling5551 小时前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架
FogLetter1 小时前
图片懒加载:让网页飞起来的魔法技巧 ✨
前端·javascript·css
拾光拾趣录1 小时前
JavaScript 加载对浏览器渲染的影响
前端·javascript·浏览器
点金石游戏出海2 小时前
每周资讯 | Krafton斥资750亿日元收购日本动画公司ADK;《崩坏:星穹铁道》新版本首日登顶iOS畅销榜
游戏·ios·业界资讯·apple·崩坏星穹铁道
wsdchong之小马过河2 小时前
2025《烈焰之刃》游戏攻略
游戏
浪裡遊3 小时前
Sass详解:功能特性、常用方法与最佳实践
开发语言·前端·javascript·css·vue.js·rust·sass