当前位置: 首页 > web >正文

html5:从零构建经典游戏-扫雷游戏

扫雷是Windows系统自带的经典游戏,陪伴了许多人的童年。

本文将详细解析一个用HTML、CSS和JavaScript实现的扫雷游戏代码,带你了解其背后的实现原理。

游戏概述

这个扫雷游戏实现包含以下核心功能:

  • 10×10的游戏棋盘

  • 15个随机分布的地雷

  • 左键点击揭开格子

  • 右键点击标记地雷

  • 自动展开空白区域

  • 胜利/失败判定

HTML结构

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>扫雷游戏</title><!-- CSS样式 -->
</head>
<body><h1>扫雷游戏</h1><div id="game-board"></div><!-- JavaScript代码 -->
</body>
</html>

结构非常简单,只有一个标题和游戏板容器。所有游戏逻辑都通过JavaScript动态生成。

CSS样式设计

CSS部分定义了游戏的外观:

.cell {width: 30px;height: 30px;border: 1px solid #ccc;/* 其他样式 */
}
.cell.revealed {background-color: #fff;
}
.cell.mine {background-color: #ff4444;
}
.cell.flagged {background-color: #ffcc00;
}
  • 基本格子样式:灰色背景,浅灰色边框

  • 已揭开格子:白色背景

  • 地雷格子:红色背景

  • 标记格子:黄色背景

JavaScript游戏逻辑

1. 游戏初始化

javascript

const BOARD_SIZE = 10;
const MINE_COUNT = 15;
let board = [];
let gameOver = false;

定义了游戏常量:棋盘大小和地雷数量,以及游戏状态变量。

2. 创建游戏板

createBoard()函数负责初始化游戏:

  1. 设置CSS Grid布局

  2. 创建格子DOM元素并添加到游戏板

  3. 初始化游戏数据结构

  4. 放置地雷并计算数字

javascript

function createBoard() {// 设置Grid布局gameBoard.style.gridTemplateColumns = `repeat(${BOARD_SIZE}, 30px)`;// 创建格子for (let row = 0; row < BOARD_SIZE; row++) {board[row] = [];for (let col = 0; col < BOARD_SIZE; col++) {// 创建DOM元素const cell = document.createElement('div');// 添加事件监听cell.addEventListener('click', handleCellClick);cell.addEventListener('contextmenu', handleRightClick);// 初始化游戏数据board[row][col] = { mine: false, revealed: false, flagged: false, count: 0 };}}placeMines();calculateNumbers();
}

3. 随机放置地雷

placeMines()函数随机放置指定数量的地雷:

javascript

function placeMines() {let minesPlaced = 0;while (minesPlaced < MINE_COUNT) {const row = Math.floor(Math.random() * BOARD_SIZE);const col = Math.floor(Math.random() * BOARD_SIZE);if (!board[row][col].mine) {board[row][col].mine = true;minesPlaced++;}}
}

使用while循环确保放置足够数量的地雷,避免重复放置。

4. 计算周围地雷数

calculateNumbers()为每个非地雷格子计算周围地雷数量:

javascript

function calculateNumbers() {for (let row = 0; row < BOARD_SIZE; row++) {for (let col = 0; col < BOARD_SIZE; col++) {if (board[row][col].mine) continue;let count = 0;// 检查周围8个格子for (let i = -1; i <= 1; i++) {for (let j = -1; j <= 1; j++) {const newRow = row + i;const newCol = col + j;// 边界检查if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE && board[newRow][newCol].mine) {count++;}}}board[row][col].count = count;}}
}

5. 处理玩家点击

左键点击处理函数handleCellClick

javascript

function handleCellClick(event) {if (gameOver) return;const cell = event.target;const row = parseInt(cell.dataset.row);const col = parseInt(cell.dataset.col);revealCell(row, col);
}

右键点击处理函数handleRightClick用于标记地雷:

javascript

function handleRightClick(event) {event.preventDefault(); // 阻止上下文菜单if (gameOver) return;const cell = event.target;const row = parseInt(cell.dataset.row);const col = parseInt(cell.dataset.col);if (!board[row][col].revealed) {board[row][col].flagged = !board[row][col].flagged;cell.classList.toggle('flagged', board[row][col].flagged);}
}

6. 揭开格子逻辑

revealCell()是游戏核心函数,处理格子揭开逻辑:

javascript

function revealCell(row, col) {// 已揭开或已标记则返回if (board[row][col].revealed || board[row][col].flagged) return;const cell = document.querySelector(`.cell[data-row='${row}'][data-col='${col}']`);board[row][col].revealed = true;cell.classList.add('revealed');// 踩到地雷if (board[row][col].mine) {cell.classList.add('mine');gameOver = true;alert('游戏结束!你踩到了地雷!');return;}// 显示周围地雷数cell.textContent = board[row][col].count || '';// 如果是空白格子,自动展开周围if (board[row][col].count === 0) {for (let i = -1; i <= 1; i++) {for (let j = -1; j <= 1; j++) {const newRow = row + i;const newCol = col + j;// 边界检查if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE) {revealCell(newRow, newCol);}}}}checkWin();
}

7. 胜利判定

checkWin()检查玩家是否已揭开所有安全格子:

javascript

function checkWin() {let unrevealedSafeCells = 0;for (let row = 0; row < BOARD_SIZE; row++) {for (let col = 0; col < BOARD_SIZE; col++) {if (!board[row][col].mine && !board[row][col].revealed) {unrevealedSafeCells++;}}}if (unrevealedSafeCells === 0) {gameOver = true;alert('恭喜你,你赢了!');}
}

游戏特点

  1. 递归展开:当点击空白格子时,会自动展开相邻的所有空白格子

  2. 即时反馈:点击地雷立即结束游戏,显示失败

  3. 标记功能:右键点击可以标记疑似地雷的格子

  4. 响应式设计:使用CSS Grid布局,适应不同屏幕大小

总结

这个扫雷游戏实现展示了如何使用基本的Web技术创建经典游戏。

通过合理的数据结构和算法设计,实现了游戏的核心逻辑。代码结构清晰,适合初学者学习和扩展。

html代码

如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>扫雷游戏</title><style>body {font-family: Arial, sans-serif;text-align: center;}#game-board {display: grid;margin: 20px auto;border: 2px solid #333;}.cell {width: 30px;height: 30px;border: 1px solid #ccc;text-align: center;line-height: 30px;cursor: pointer;background-color: #eee;}.cell.revealed {background-color: #fff;cursor: default;}.cell.mine {background-color: #ff4444;}.cell.flagged {background-color: #ffcc00;}</style>
</head>
<body><h1>扫雷游戏</h1><div id="game-board"></div><script>const BOARD_SIZE = 10;const MINE_COUNT = 15;let board = [];let gameOver = false;function createBoard() {const gameBoard = document.getElementById('game-board');gameBoard.style.gridTemplateColumns = `repeat(${BOARD_SIZE}, 30px)`;for (let row = 0; row < BOARD_SIZE; row++) {board[row] = [];for (let col = 0; col < BOARD_SIZE; col++) {const cell = document.createElement('div');cell.classList.add('cell');cell.dataset.row = row;cell.dataset.col = col;cell.addEventListener('click', handleCellClick);cell.addEventListener('contextmenu', handleRightClick);gameBoard.appendChild(cell);board[row][col] = { mine: false, revealed: false, flagged: false, count: 0 };}}placeMines();calculateNumbers();}function placeMines() {let minesPlaced = 0;while (minesPlaced < MINE_COUNT) {const row = Math.floor(Math.random() * BOARD_SIZE);const col = Math.floor(Math.random() * BOARD_SIZE);if (!board[row][col].mine) {board[row][col].mine = true;minesPlaced++;}}}function calculateNumbers() {for (let row = 0; row < BOARD_SIZE; row++) {for (let col = 0; col < BOARD_SIZE; col++) {if (board[row][col].mine) continue;let count = 0;for (let i = -1; i <= 1; i++) {for (let j = -1; j <= 1; j++) {const newRow = row + i;const newCol = col + j;if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE && board[newRow][newCol].mine) {count++;}}}board[row][col].count = count;}}}function handleCellClick(event) {if (gameOver) return;const cell = event.target;const row = parseInt(cell.dataset.row);const col = parseInt(cell.dataset.col);revealCell(row, col);}function handleRightClick(event) {event.preventDefault();if (gameOver) return;const cell = event.target;const row = parseInt(cell.dataset.row);const col = parseInt(cell.dataset.col);if (!board[row][col].revealed) {board[row][col].flagged = !board[row][col].flagged;cell.classList.toggle('flagged', board[row][col].flagged);}}function revealCell(row, col) {if (board[row][col].revealed || board[row][col].flagged) return;const cell = document.querySelector(`.cell[data-row='${row}'][data-col='${col}']`);board[row][col].revealed = true;cell.classList.add('revealed');if (board[row][col].mine) {cell.classList.add('mine');gameOver = true;alert('游戏结束!你踩到了地雷!');return;}cell.textContent = board[row][col].count || '';if (board[row][col].count === 0) {for (let i = -1; i <= 1; i++) {for (let j = -1; j <= 1; j++) {const newRow = row + i;const newCol = col + j;if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE) {revealCell(newRow, newCol);}}}}checkWin();}function checkWin() {let unrevealedSafeCells = 0;for (let row = 0; row < BOARD_SIZE; row++) {for (let col = 0; col < BOARD_SIZE; col++) {if (!board[row][col].mine && !board[row][col].revealed) {unrevealedSafeCells++;}}}if (unrevealedSafeCells === 0) {gameOver = true;alert('恭喜你,你赢了!');}}createBoard();</script>
</body>
</html>

http://www.xdnf.cn/news/2380.html

相关文章:

  • Git 使用教程
  • 矩阵系统源码搭建热门音乐功能板块开发,支持OEM
  • Redux-Saga vs Redux-Thunk
  • 【漫话机器学习系列】227.信息检索与数据挖掘中的常用加权技术(TF-IDF)
  • 【nvm管理多个 Node.js 版本】
  • LLM开发——基于DeepSeek R1 和 Qwen 构建智能检索增强生成系统
  • 博物馆除湿控湿保卫战:M-5J1R 电解除湿科技如何重塑文物守护的未来
  • Azure Devops - 尝试一下在Pipeline中使用Self-hosted Windows agent
  • Rust游戏开发全栈指南:从理论到实践的革新之路
  • 蓝桥杯 1. 确定字符串是否包含唯一字符
  • Pycharm(十七)生成器
  • Python----深度学习(基于DNN的吃鸡预测)
  • SHCTF-REVERSE
  • 数据集下载(AER 和causaldata R包)
  • AI音频核爆!Kimi开源“六边形战士”Kimi-Audio,ChatGPT语音版?
  • ZeroGrasp:零样本形状重建助力机器人抓取
  • 使用 MediaPipe 和 OpenCV 快速生成人脸掩膜(Face Mask)
  • 后端响应巨量数据,如何优化性能?
  • [GXYCTF2019]Ping Ping Ping
  • Monorepo、Lerna、Yarn Workspaces、pnpm Workspaces 用法
  • 深入解析 npm 与 Yarn:Node.js 包管理工具对比与选型指南
  • 全栈量子跃迁:当Shor算法破解RSA时,我们如何用晶格密码重构数字世界的信任基岩?
  • MySQL:13.用户管理
  • Flutter 泛型 泛型方法 泛型类 泛型接口
  • HarmonyOS Next~鸿蒙系统UI创新实践:原生精致理念下的设计革命
  • flask uri 怎么统一加前缀
  • zynq7035的arm一秒钟最多可以支持触发多少次中断
  • 【合新通信】---Mini单路光模块(Mini SFF/USOT)
  • [Lc_week] 447 | 155 | Q1 | hash | pair {}调用
  • 【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务