目录
功能概述
扫雷游戏是一款经典的单人小游戏,玩家通过点击格子来探索地图,避开爆炸的雷。游戏的基本功能包括:
- 多种地图尺寸:支持从10x10到30x30的不同尺寸地图。
- 多级难度:通过调整雷的数量,提供不同难度级别的游戏体验。
- 计时器:记录玩家完成游戏所用的时间。
- 右键标记:允许玩家标记可疑的格子。
- 自动打开空白区域:点击无雷的格子时,自动递归展开周围的周围无雷的格子。
代码结构
HTML结构
游戏界面由以下部分组成:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扫雷游戏</title>
<link rel="stylesheet" href="css/mine_clearance.css">
</head>
<body>
<nav class="navbar">
<ul>
<li><a href="/index.html">首页</a></li>
<li><a href="/mine_clearance.html">扫雷</a></li>
<li><a href="/other.html">其他游戏</a></li>
</ul>
</nav>
<div class="game-container">
<div class="settings">
<div class="select-mode">
<label for="mode">模式:</label>
<select id="mode">
<option value="10">10x10</option>
<option value="12">12x12</option>
<option value="16">16x16</option>
<option value="20">20x20</option>
<option value="24">24x24</option>
<option value="30">30x30</option>
</select>
</div>
<div class="select-difficulty">
<label for="difficulty">难度:</label>
<select id="difficulty">
<option value="1">1(模式值*0.1个雷)</option>
<option value="2">2(模式值*0.15个雷)</option>
<option value="3">3(模式值*0.2个雷)</option>
<option value="4">4(模式值*0.25个雷)</option>
<option value="5">5(模式值*0.3个雷)</option>
<option value="6">5(模式值*0.35个雷)</option>
<option value="7">7(模式值*0.4个雷)</option>
</select>
</div>
<div id="timer"></div>
<button id="generate">生成地图</button>
</div>
<div id="result" class="result"></div>
<div id="minefield"></div>
</div>
<script src="js/mine_clearance.js"></script>
</body>
</html>
JavaScript结构
JavaScript代码分为以下几个部分:
- 全局变量:存储当前地图数据、游戏状态和计时器等。
- Cell类:表示每个格子的属性,包括位置、是否为雷、是否已打开、周围雷数等。
- 地图生成:根据选定的模式和难度,生成地图并放置雷。
- 渲染函数:将地图数据转化为网页上的可见元素。
- 游戏逻辑:处理点击事件、右键标记、胜负判定等。
- 事件监听:绑定按钮点击事件,触发地图生成和游戏启动。
渲染机制
渲染函数
渲染函数renderMinefield
负责将地图数据转换为网页上的格子元素。
function renderMinefield() {
// 清空地图容器,确保每次渲染时都是全新的地图
minefield.innerHTML = '';
// 获取当前地图的大小(从 mode 的值中解析出数字)
const size = parseInt(mode.value);
// 设置 grid 布局和样式
// …………
// 遍历地图的每一行和每一列
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
// 获取当前格子的对象(从 currentMap 中获取)
const cell = currentMap[y][x];
// 创建一个新的 div 元素,作为地图上的一个格子
const cellElement = document.createElement('div');
// 为格子添加类名和数据属性
cellElement.className = 'cell unselectable'; // 默认类名,表示这是一个不可选的格子
cellElement.dataset.x = cell.x; // 存储格子的 x 坐标
cellElement.dataset.y = cell.y; // 存储格子的 y 坐标
// 如果格子未打开
if (!cell.open) {
// ……
} else {
// 如果格子已打开
// …………
// 如果当前格子是雷
// …………
}
// 绑定事件监听器(例如点击、右键标记等)
// 注意:事件监听器的绑定代码需要在这里添加
// …………
// 将创建的格子元素添加到地图容器中
minefield.appendChild(cellElement);
}
}
}
样式设计
通过CSS样式美化格子的外观,以下是CSS代码示例:
.cell {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: bold;
cursor: pointer;
background-color: #f0f0f0;
border: 1px solid #ccc;
transition: all 0.1s;
}
.cell.unselectable {
user-select: none;
}
.cell.closed {
background-color: #e8e8e8;
}
.cell.open {
background-color: #90EE90;
}
.cell.mine {
background-color: #ff4444;
}
游戏逻辑
地图生成
generateMap
函数负责根据选择的模式和难度生成地图,并放置雷。
function generateMap() {
const size = parseInt(mode.value);
const difficultyValue = difficulty.value;
const totalMines = calculateTotalMines(size, difficultyValue);
currentMap = createEmptyMap(size);
placeMines(currentMap, totalMines);
calculateMineCounts(currentMap);
renderMinefield();
gameActive = true;
timer = 0;
if (timerInterval) clearInterval(timerInterval);
timerInterval = startTimer();
}
雷的放置
placeMines
函数在空地图中随机放置雷。
function placeMines(map, totalMines) {
const size = map.length;
let minesPlaced = 0;
while (minesPlaced < totalMines) {
const x = Math.floor(Math.random() * size);
const y = Math.floor(Math.random() * size);
if (!map[y][x].mine) {
map[y][x].mine = true;
minesPlaced++;
}
}
}
周围雷数计算
calculateMineCounts
函数计算每个格子周围的雷数。
function calculateMineCounts(map) {
const size = map.length;
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
if (!map[y][x].mine) {
map[y][x].mineCount = countSurroundingMines(map, x, y);
}
}
}
}
点击处理
handleClick
函数处理左键点击事件,打开格子并检查周围的格子。
function handleClick(e, x, y) {
if (!gameActive) return;
if (!currentMap[y][x].open) {
currentMap[y][x].open = true;
const cellElement = document.querySelector(`[data-x="${x}"][data-y="${y}"]`);
if (cellElement) {
cellElement.classList.add('open');
cellElement.textContent = currentMap[y][x].mine ? '🏰' : (currentMap[y][x].mineCount > 0 ? currentMap[y][x].mineCount : '');
}
if (currentMap[y][x].mine) {
gameActive = false;
revealAllMines();
showResult("你输了!");
} else {
checkSurroundings(x, y, true);
}
}
checkWin();
}
胜负判定
checkWin
函数检查是否所有非雷格子都已被打开,决定胜负。
function checkWin() {
let win = true;
for (let row of currentMap) {
for (let cell of row) {
if (!cell.mine && !cell.open) {
win = false;
break;
}
}
}
if (win && gameActive) {
gameActive = false;
if (timerInterval) clearInterval(timerInterval);
showResult("你赢了!");
}
}
事件处理
左键点击
左键点击打开格子,并根据格子内容(雷或空白)执行不同的逻辑。
cellElement.addEventListener('click', (e) => handleClick(e, cell.x, cell.y));
右键标记
右键点击在格子上添加或移除旗帜标记。
cellElement.addEventListener('contextmenu', (e) => {
e.preventDefault();
handleRightClick(e, cell.x, cell.y);
});
function handleRightClick(e, x, y) {
if (!gameActive) return;
const cellElement = document.querySelector(`[data-x="${x}"][data-y="${y}"]`);
if (cellElement) {
cellElement.classList.toggle('flagged');
}
}
效果展示
扫雷
其他游戏
通过iframe展示的3个链接的小游戏,点击选中的小游戏,游戏区域会自动展示该游戏界面.