2048小游戏实现
2048小游戏实现
将创建一个完整的2048小游戏,包含游戏核心逻辑和美观的用户界面。
设计思路
- 4x4网格布局
- 响应式设计,适配不同设备
- 分数显示和最高分记录
- 键盘控制(方向键)和触摸滑动支持
- 游戏状态提示(胜利/失败)
完整代码实现
<!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;// 更新UIupdateScore();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或4if (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() {// 检查是否达到2048for (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>
功能说明
-
游戏核心功能:
- 4x4网格布局
- 方块移动和合并逻辑
- 随机生成新方块(90%概率生成2,10%概率生成4)
- 分数计算(每次合并累加得分)
- 最高分记录(使用localStorage保存)
-
游戏状态:
- 胜利条件:创建出2048方块
- 失败条件:棋盘填满且无法继续合并
- 游戏结束后可重新开始
-
控制方式:
- 键盘方向键(↑, ↓, ←, →)
- 触摸屏滑动操作(支持移动设备)
-
UI设计:
- 响应式布局,适配不同屏幕尺寸
- 不同数字的方块有不同的颜色
- 分数显示和最高分记录
- 游戏状态提示(胜利/失败)
使用说明
- 使用键盘方向键或触摸屏滑动来移动方块
- 当两个相同数字的方块碰撞时,它们会合并成它们的和
- 每次移动后,会在空白位置随机生成一个新的方块(2或4)
- 游戏目标:创建出一个2048方块
- 当无法再移动方块时,游戏结束
这个2048小游戏包含了所有核心功能,界面美观,操作流畅,适合在桌面和移动设备上使用。