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>
相关推荐
maomeisanhuamao14 分钟前
软件/游戏提示:mfc42u.dll没有被指定在windows上运行如何解决?多种有效解决方法汇总分享
windows·游戏
看到请催我学习30 分钟前
js判断空对象
开发语言·前端·javascript·json
六卿33 分钟前
2、Three.js初步认识场景Scene、相机Camera、渲染器Renderer三要素
javascript·three.js·three
沉默是金~41 分钟前
Vue+Elementui el-tree树只能选择子节点并且支持检索
javascript·vue.js·elementui
0_11 小时前
让你的网页变的高大上:国际化Vue-i18n
前端·javascript·vue.js
西红柿炖前男友1 小时前
toolTip文字溢出如何解决?给你几个万能移植方案!
前端·javascript
哆啦美玲1 小时前
养成编程思维——栈与队列的运用
前端·javascript·算法
Eliauk__1 小时前
为什么 Vue 不建议用 index 做 key,为什么不建议用随机数做 key?
前端·javascript·面试
小黄编程快乐屋1 小时前
electron-updater软件自动检测更新 +无服务器本地测试
javascript·electron·serverless
王解2 小时前
ESLint:从 JSLint 到 JSHint,探索 JavaScript Linter 的进化历程 (1)
javascript·eslint