2048小游戏实现

2048小游戏实现

将创建一个完整的2048小游戏,包含游戏核心逻辑和美观的用户界面。

设计思路

  • 4x4网格布局
  • 响应式设计,适配不同设备
  • 分数显示和最高分记录
  • 键盘控制(方向键)和触摸滑动支持
  • 游戏状态提示(胜利/失败)

完整代码实现

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2048小游戏</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }
        
        .container {
            max-width: 500px;
            width: 100%;
        }
        
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        
        h1 {
            color: #776e65;
            font-size: 60px;
            font-weight: 700;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
        }
        
        .scores-container {
            display: flex;
            gap: 10px;
        }
        
        .score-box {
            background: #bbada0;
            color: white;
            padding: 10px 15px;
            border-radius: 6px;
            text-align: center;
            min-width: 100px;
            position: relative;
        }
        
        .score-box:after {
            content: attr(data-label);
            display: block;
            font-size: 12px;
            font-weight: bold;
            text-transform: uppercase;
            color: #eee4da;
        }
        
        .score {
            font-size: 22px;
            font-weight: bold;
        }
        
        .game-intro {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        
        .game-explanation {
            color: #776e65;
            font-size: 16px;
            line-height: 1.4;
            max-width: 250px;
        }
        
        .restart-button {
            background: #8f7a66;
            color: #f9f6f2;
            border: none;
            border-radius: 6px;
            padding: 12px 20px;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.2s;
        }
        
        .restart-button:hover {
            background: #7c6b5a;
            transform: scale(1.05);
        }
        
        .game-container {
            background: #bbada0;
            border-radius: 6px;
            padding: 15px;
            position: relative;
            box-shadow: 0 10px 30px rgba(0,0,0,0.15);
        }
        
        .grid-container {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-gap: 10px;
            background: rgba(238, 228, 218, 0.35);
            border-radius: 6px;
            padding: 10px;
            position: relative;
            touch-action: none;
        }
        
        .grid-cell {
            background: rgba(238, 228, 218, 0.35);
            border-radius: 4px;
            height: 0;
            padding-bottom: 100%;
        }
        
        .tile-container {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            padding: 10px;
        }
        
        .tile {
            position: absolute;
            width: calc(25% - 12.5px);
            height: calc(25% - 12.5px);
            border-radius: 4px;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 35px;
            font-weight: bold;
            transition: all 0.15s ease;
            z-index: 10;
        }
        
        .tile-2 { background: #eee4da; color: #776e65; }
        .tile-4 { background: #ede0c8; color: #776e65; }
        .tile-8 { background: #f2b179; color: #f9f6f2; }
        .tile-16 { background: #f59563; color: #f9f6f2; }
        .tile-32 { background: #f67c5f; color: #f9f6f2; }
        .tile-64 { background: #f65e3b; color: #f9f6f2; }
        .tile-128 { background: #edcf72; color: #f9f6f2; font-size: 30px; }
        .tile-256 { background: #edcc61; color: #f9f6f2; font-size: 30px; }
        .tile-512 { background: #edc850; color: #f9f6f2; font-size: 30px; }
        .tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 25px; }
        .tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 25px; }
        .tile-super { background: #3c3a32; color: #f9f6f2; font-size: 20px; }
        
        .game-message {
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(238, 228, 218, 0.73);
            border-radius: 6px;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 100;
        }
        
        .game-message.game-won, 
        .game-message.game-over {
            display: flex;
        }
        
        .game-message p {
            font-size: 50px;
            font-weight: bold;
            color: #776e65;
            margin-bottom: 20px;
        }
        
        .instructions {
            margin-top: 30px;
            color: #776e65;
            text-align: center;
            line-height: 1.6;
            font-size: 16px;
        }
        
        @media (max-width: 520px) {
            .header {
                flex-direction: column;
                align-items: flex-start;
            }
            
            h1 {
                font-size: 50px;
                margin-bottom: 15px;
            }
            
            .game-intro {
                flex-direction: column;
                align-items: flex-start;
                gap: 15px;
            }
            
            .tile {
                font-size: 24px;
            }
            
            .tile-128, .tile-256, .tile-512 {
                font-size: 22px;
            }
            
            .tile-1024, .tile-2048 {
                font-size: 18px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>2048</h1>
            <div class="scores-container">
                <div class="score-box" data-label="分数">
                    <div class="score" id="score">0</div>
                </div>
                <div class="score-box" data-label="最高分">
                    <div class="score" id="best-score">0</div>
                </div>
            </div>
        </div>
        
        <div class="game-intro">
            <div class="game-explanation">
                合并相同数字的方块,得到2048方块!
            </div>
            <button class="restart-button" id="restart-button">新游戏</button>
        </div>
        
        <div class="game-container">
            <div class="grid-container">
                <!-- 4x4网格 -->
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                <div class="grid-cell"></div>
                
                <div class="tile-container" id="tile-container"></div>
                
                <div class="game-message" id="game-message">
                    <p id="message-text"></p>
                    <button class="restart-button" id="keep-playing-button">继续游戏</button>
                </div>
            </div>
        </div>
        
        <div class="instructions">
            <p><strong>游戏指南:</strong>使用键盘方向键(↑, ↓, ←, →)或触摸屏滑动移动方块。当两个相同数字的方块碰撞时,它们会合并成它们的和!</p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // 游戏常量
            const GRID_SIZE = 4;
            const CELL_SIZE = 20; // 百分比
            const CELL_GAP = 2;   // 百分比
            
            // 游戏状态
            let grid = [];
            let score = 0;
            let bestScore = localStorage.getItem('bestScore') || 0;
            let gameOver = false;
            let gameWon = false;
            
            // DOM元素
            const gridContainer = document.querySelector('.grid-container');
            const tileContainer = document.getElementById('tile-container');
            const scoreDisplay = document.getElementById('score');
            const bestScoreDisplay = document.getElementById('best-score');
            const restartButton = document.getElementById('restart-button');
            const gameMessage = document.getElementById('game-message');
            const messageText = document.getElementById('message-text');
            const keepPlayingButton = document.getElementById('keep-playing-button');
            
            // 初始化游戏
            function initGame() {
                // 重置游戏状态
                grid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
                tileContainer.innerHTML = '';
                score = 0;
                gameOver = false;
                gameWon = false;
                
                // 更新UI
                updateScore();
                gameMessage.className = 'game-message';
                
                // 添加初始方块
                addRandomTile();
                addRandomTile();
                
                // 渲染游戏板
                render();
            }
            
            // 添加随机方块
            function addRandomTile() {
                const emptyCells = [];
                
                // 查找所有空格子
                for (let row = 0; row < GRID_SIZE; row++) {
                    for (let col = 0; col < GRID_SIZE; col++) {
                        if (grid[row][col] === 0) {
                            emptyCells.push({ row, col });
                        }
                    }
                }
                
                // 如果有空格子,随机选择一个并添加2或4
                if (emptyCells.length > 0) {
                    const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                    grid[randomCell.row][randomCell.col] = Math.random() < 0.9 ? 2 : 4;
                }
            }
            
            // 渲染游戏板
            function render() {
                tileContainer.innerHTML = '';
                
                for (let row = 0; row < GRID_SIZE; row++) {
                    for (let col = 0; col < GRID_SIZE; col++) {
                        const value = grid[row][col];
                        if (value !== 0) {
                            const tile = document.createElement('div');
                            tile.className = `tile tile-${value}`;
                            tile.textContent = value;
                            
                            // 设置位置
                            tile.style.left = `${col * (CELL_SIZE + CELL_GAP) + CELL_GAP}%`;
                            tile.style.top = `${row * (CELL_SIZE + CELL_GAP) + CELL_GAP}%`;
                            
                            tileContainer.appendChild(tile);
                        }
                    }
                }
            }
            
            // 移动方块
            function move(direction) {
                if (gameOver) return;
                
                let moved = false;
                let newGrid = JSON.parse(JSON.stringify(grid));
                
                // 根据方向处理移动
                switch (direction) {
                    case 'up':
                        for (let col = 0; col < GRID_SIZE; col++) {
                            const column = [];
                            for (let row = 0; row < GRID_SIZE; row++) {
                                if (newGrid[row][col] !== 0) {
                                    column.push(newGrid[row][col]);
                                }
                            }
                            
                            // 合并相同数字
                            for (let i = 0; i < column.length - 1; i++) {
                                if (column[i] === column[i + 1]) {
                                    column[i] *= 2;
                                    score += column[i];
                                    column.splice(i + 1, 1);
                                }
                            }
                            
                            // 更新列
                            for (let row = 0; row < GRID_SIZE; row++) {
                                newGrid[row][col] = row < column.length ? column[row] : 0;
                            }
                        }
                        break;
                        
                    case 'down':
                        for (let col = 0; col < GRID_SIZE; col++) {
                            const column = [];
                            for (let row = GRID_SIZE - 1; row >= 0; row--) {
                                if (newGrid[row][col] !== 0) {
                                    column.push(newGrid[row][col]);
                                }
                            }
                            
                            // 合并相同数字
                            for (let i = 0; i < column.length - 1; i++) {
                                if (column[i] === column[i + 1]) {
                                    column[i] *= 2;
                                    score += column[i];
                                    column.splice(i + 1, 1);
                                }
                            }
                            
                            // 更新列
                            for (let row = GRID_SIZE - 1; row >= 0; row--) {
                                newGrid[row][col] = (GRID_SIZE - 1 - row) < column.length ? column[GRID_SIZE - 1 - row] : 0;
                            }
                        }
                        break;
                        
                    case 'left':
                        for (let row = 0; row < GRID_SIZE; row++) {
                            const rowData = [];
                            for (let col = 0; col < GRID_SIZE; col++) {
                                if (newGrid[row][col] !== 0) {
                                    rowData.push(newGrid[row][col]);
                                }
                            }
                            
                            // 合并相同数字
                            for (let i = 0; i < rowData.length - 1; i++) {
                                if (rowData[i] === rowData[i + 1]) {
                                    rowData[i] *= 2;
                                    score += rowData[i];
                                    rowData.splice(i + 1, 1);
                                }
                            }
                            
                            // 更新行
                            for (let col = 0; col < GRID_SIZE; col++) {
                                newGrid[row][col] = col < rowData.length ? rowData[col] : 0;
                            }
                        }
                        break;
                        
                    case 'right':
                        for (let row = 0; row < GRID_SIZE; row++) {
                            const rowData = [];
                            for (let col = GRID_SIZE - 1; col >= 0; col--) {
                                if (newGrid[row][col] !== 0) {
                                    rowData.push(newGrid[row][col]);
                                }
                            }
                            
                            // 合并相同数字
                            for (let i = 0; i < rowData.length - 1; i++) {
                                if (rowData[i] === rowData[i + 1]) {
                                    rowData[i] *= 2;
                                    score += rowData[i];
                                    rowData.splice(i + 1, 1);
                                }
                            }
                            
                            // 更新行
                            for (let col = GRID_SIZE - 1; col >= 0; col--) {
                                newGrid[row][col] = (GRID_SIZE - 1 - col) < rowData.length ? rowData[GRID_SIZE - 1 - col] : 0;
                            }
                        }
                        break;
                }
                
                // 检查是否有移动
                moved = JSON.stringify(grid) !== JSON.stringify(newGrid);
                
                if (moved) {
                    grid = newGrid;
                    addRandomTile();
                    updateScore();
                    render();
                    checkGameStatus();
                }
            }
            
            // 检查游戏状态
            function checkGameStatus() {
                // 检查是否达到2048
                for (let row = 0; row < GRID_SIZE; row++) {
                    for (let col = 0; col < GRID_SIZE; col++) {
                        if (grid[row][col] === 2048 && !gameWon) {
                            gameWon = true;
                            messageText.textContent = '恭喜你赢了!';
                            gameMessage.className = 'game-message game-won';
                        }
                    }
                }
                
                // 检查是否有空格子
                for (let row = 0; row < GRID_SIZE; row++) {
                    for (let col = 0; col < GRID_SIZE; col++) {
                        if (grid[row][col] === 0) {
                            return;
                        }
                    }
                }
                
                // 检查是否可以移动
                for (let row = 0; row < GRID_SIZE; row++) {
                    for (let col = 0; col < GRID_SIZE; col++) {
                        const value = grid[row][col];
                        // 检查右侧
                        if (col < GRID_SIZE - 1 && grid[row][col + 1] === value) {
                            return;
                        }
                        // 检查下方
                        if (row < GRID_SIZE - 1 && grid[row + 1][col] === value) {
                            return;
                        }
                    }
                }
                
                // 如果没有空格子且无法移动,游戏结束
                gameOver = true;
                messageText.textContent = '游戏结束!';
                gameMessage.className = 'game-message game-over';
            }
            
            // 更新分数
            function updateScore() {
                scoreDisplay.textContent = score;
                
                // 更新最高分
                if (score > bestScore) {
                    bestScore = score;
                    localStorage.setItem('bestScore', bestScore);
                }
                
                bestScoreDisplay.textContent = bestScore;
            }
            
            // 键盘控制
            document.addEventListener('keydown', (event) => {
                switch (event.key) {
                    case 'ArrowUp':
                        move('up');
                        break;
                    case 'ArrowDown':
                        move('down');
                        break;
                    case 'ArrowLeft':
                        move('left');
                        break;
                    case 'ArrowRight':
                        move('right');
                        break;
                }
            });
            
            // 触摸控制
            let touchStartX, touchStartY;
            
            gridContainer.addEventListener('touchstart', (event) => {
                touchStartX = event.touches[0].clientX;
                touchStartY = event.touches[0].clientY;
                event.preventDefault();
            });
            
            gridContainer.addEventListener('touchmove', (event) => {
                event.preventDefault();
            });
            
            gridContainer.addEventListener('touchend', (event) => {
                if (!touchStartX || !touchStartY) return;
                
                const touchEndX = event.changedTouches[0].clientX;
                const touchEndY = event.changedTouches[0].clientY;
                
                const dx = touchEndX - touchStartX;
                const dy = touchEndY - touchStartY;
                
                // 确定主要移动方向
                if (Math.abs(dx) > Math.abs(dy)) {
                    // 水平移动
                    if (dx > 0) {
                        move('right');
                    } else {
                        move('left');
                    }
                } else {
                    // 垂直移动
                    if (dy > 0) {
                        move('down');
                    } else {
                        move('up');
                    }
                }
                
                // 重置触摸起点
                touchStartX = null;
                touchStartY = null;
            });
            
            // 按钮事件
            restartButton.addEventListener('click', initGame);
            keepPlayingButton.addEventListener('click', () => {
                gameMessage.className = 'game-message';
            });
            
            // 初始化游戏
            initGame();
            bestScoreDisplay.textContent = bestScore;
        });
    </script>
</body>
</html>

功能说明

  1. 游戏核心功能

    • 4x4网格布局
    • 方块移动和合并逻辑
    • 随机生成新方块(90%概率生成2,10%概率生成4)
    • 分数计算(每次合并累加得分)
    • 最高分记录(使用localStorage保存)
  2. 游戏状态

    • 胜利条件:创建出2048方块
    • 失败条件:棋盘填满且无法继续合并
    • 游戏结束后可重新开始
  3. 控制方式

    • 键盘方向键(↑, ↓, ←, →)
    • 触摸屏滑动操作(支持移动设备)
  4. UI设计

    • 响应式布局,适配不同屏幕尺寸
    • 不同数字的方块有不同的颜色
    • 分数显示和最高分记录
    • 游戏状态提示(胜利/失败)

使用说明

  1. 使用键盘方向键或触摸屏滑动来移动方块
  2. 当两个相同数字的方块碰撞时,它们会合并成它们的和
  3. 每次移动后,会在空白位置随机生成一个新的方块(2或4)
  4. 游戏目标:创建出一个2048方块
  5. 当无法再移动方块时,游戏结束

这个2048小游戏包含了所有核心功能,界面美观,操作流畅,适合在桌面和移动设备上使用。

相关推荐
像风一样自由20202 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
浪裡遊3 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
独立开阀者_FwtCoder7 小时前
【Augment】 Augment技巧之 Rewrite Prompt(重写提示) 有神奇的魔法
前端·javascript·github
我想说一句7 小时前
事件机制与委托:从冒泡捕获到高效编程的奇妙之旅
前端·javascript
汤姆Tom7 小时前
JavaScript reduce()函数详解
javascript
小飞悟7 小时前
你以为 React 的事件很简单?错了,它暗藏玄机!
前端·javascript·面试
中微子7 小时前
JavaScript 事件机制:捕获、冒泡与事件委托详解
前端·javascript
蓝翔认证10级掘手7 小时前
🤯 家人们谁懂啊!我的摸鱼脚本它...它成精了!🚀
javascript
前端康师傅8 小时前
JavaScript 中你不知道的按位运算
前端·javascript