目录
-
- 最终效果
- 前言
- 游戏介绍
- 技术架构
- HTML结构设计
- CSS样式实现
-
- [1. 整体风格](#1. 整体风格)
- [2. 背景装饰动画](#2. 背景装饰动画)
- [3. 棋盘与棋子样式](#3. 棋盘与棋子样式)
- [4. 消除动画](#4. 消除动画)
- [5. 提示闪烁效果](#5. 提示闪烁效果)
- [6. 按钮渐变与悬停效果](#6. 按钮渐变与悬停效果)
- [7. 响应式设计](#7. 响应式设计)
- JavaScript核心逻辑
-
- [1. 游戏配置与状态管理](#1. 游戏配置与状态管理)
- [2. 棋盘初始化](#2. 棋盘初始化)
- [3. 渲染棋盘](#3. 渲染棋盘)
- [4. 点击处理流程](#4. 点击处理流程)
- 连连看算法详解
- 游戏状态管理
- UI交互设计
- 最终实现场景
- 技术总结
- 使用说明
- 结语
- 参考资源
最终效果
前言
中秋佳节,月圆人团圆。月饼、玉兔、明月、灯笼------这些传统元素构成了中秋节独特的文化符号。为了让更多人在数字时代也能感受到传统节日的魅力,我设计并实现了这款"中秋连连看"小游戏。
连连看作为一款经典的益智游戏,规则简单却不失趣味性,特别适合在节日期间与家人朋友共同娱乐。本项目将传统的连连看玩法与中秋元素相结合,通过纯前端技术(HTML + CSS + JavaScript)实现了一个功能完整、可实际游玩的小游戏。
游戏特色
- 完全可玩:不是Demo,是真正可以玩的游戏
- 中秋主题:月饼、玉兔等8种中秋元素图案
- 多种难度:简单(8×8)、普通(10×10)、困难(12×12)三种模式
- 智能提示:卡住时可使用提示功能
- 重排功能:当无法继续时自动或手动重排
- 计时系统:限时挑战增加紧张感
- 关卡系统:无限关卡,挑战你的极限
- 精美界面:渐变背景、动画效果、响应式设计
游戏介绍
基本规则:
- 点击两个相同的图案进行配对
- 两个图案之间的连接路径不能超过2个转折点
- 连接路径上不能有其他图案阻挡
- 成功配对后图案消除,获得分数
- 在规定时间内消除所有图案即可过关
得分规则:
- 基础分:每对10分
- 连击奖励:连续消除有额外加分(1.5倍递增)
- 时间奖励:剩余时间×2作为额外奖励
道具系统:
- 💡 提示:显示一对可消除的图案(3次)
- 🔄 重排:重新打乱剩余图案位置(3次)
游戏元素
本游戏使用8种中秋主题的Emoji图案:
- 🥮 月饼
- 🐰 玉兔
- 🌕 满月
- 🏮 灯笼
- 🌙 月牙
- 🎋 竹子
- 🌾 稻穗
- 🍂 枫叶
技术架构
技术栈
- HTML5:结构化标记语言
- CSS3:样式、动画、渐变、响应式设计
- JavaScript ES6+:游戏逻辑、算法实现
- Canvas API:绘制连接线
项目结构
zhongqiu/
├── lianliankan.html # 主HTML文件
├── lianliankan.css # 样式文件
├── lianliankan.js # 游戏逻辑
└── 连连看游戏技术文章.md # 技术文档
核心模块划分
游戏系统
├── 游戏初始化模块
│ ├── 配置管理
│ ├── 状态初始化
│ └── 事件绑定
│
├── 棋盘管理模块
│ ├── 棋盘生成
│ ├── 图案分配
│ ├── 渲染更新
│ └── 重排功能
│
├── 交互处理模块
│ ├── 点击事件
│ ├── 选择逻辑
│ ├── 匹配判定
│ └── 消除动画
│
├── 算法核心模块
│ ├── 路径查找
│ ├── 直线判定
│ ├── 一次转折
│ ├── 两次转折
│ └── 有效移动检测
│
├── 游戏控制模块
│ ├── 计时系统
│ ├── 计分系统
│ ├── 关卡管理
│ ├── 暂停/继续
│ └── 结束判定
│
└── UI展示模块
├── 界面更新
├── 消息提示
├── 弹窗管理
├── 进度显示
└── Canvas绘图
HTML结构设计
整体布局
HTML采用语义化标签,清晰地划分了游戏的各个功能区域:
html
<body>
<!-- 背景装饰层 -->
<div class="bg-decoration">
月亮、星星、灯笼等装饰元素
</div>
<!-- 主游戏容器 -->
<div class="game-container">
<!-- 标题区 -->
<header class="game-header"></header>
<!-- 信息栏 -->
<div class="game-info"></div>
<!-- 控制按钮 -->
<div class="game-controls"></div>
<!-- 游戏棋盘 -->
<div class="game-board-container">
<div class="game-board"></div>
<canvas class="line-canvas"></canvas>
</div>
<!-- 进度条 -->
<div class="progress-container"></div>
<!-- 消息提示 -->
<div class="game-message"></div>
</div>
<!-- 各种弹窗 -->
<div class="modal" id="startModal"></div>
<div class="modal" id="pauseModal"></div>
<div class="modal" id="endModal"></div>
</body>
关键设计点
- 背景装饰层:使用固定定位,不影响游戏区域的交互
- 游戏棋盘:采用CSS Grid布局,灵活适应不同难度的网格大小
- Canvas画布:覆盖在棋盘上方,用于绘制连接线
- 弹窗系统:统一的modal样式,通过显隐控制不同场景
数据属性设计
每个棋子使用data-*
属性存储坐标:
html
<div class="tile" data-row="3" data-col="5">🥮</div>
这样便于在JavaScript中快速定位和操作。
CSS样式实现
1. 整体风格
采用现代化、扁平化的设计风格,配合中秋主题的配色方案:
主色调:
- 背景:深紫色渐变(#1a237e → #4a148c)
- 主要元素:白色半透明卡片(backdrop-filter毛玻璃效果)
- 强调色:红色(#d32f2f)、金黄色(#ffd700)
css
body {
background: linear-gradient(135deg,
#1a237e 0%,
#311b92 50%,
#4a148c 100%);
}
.game-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
}
2. 背景装饰动画
月亮呼吸效果:
css
.moon-bg {
animation: moonPulse 8s ease-in-out infinite;
}
@keyframes moonPulse {
0%, 100% {
transform: scale(1);
opacity: 0.8;
}
50% {
transform: scale(1.05);
opacity: 1;
}
}
星空移动效果:
使用径向渐变创建星星,配合background-position动画:
css
.stars-bg {
background-image:
radial-gradient(2px 2px at 20% 30%, white, transparent),
radial-gradient(2px 2px at 60% 70%, white, transparent),
/* ... 更多星星 */;
animation: starsMove 120s linear infinite;
}
灯笼摆动效果:
css
@keyframes lanternSwing {
0%, 100% { transform: rotate(-5deg); }
50% { transform: rotate(5deg); }
}
3. 棋盘与棋子样式
Grid布局:
css
.game-board {
display: grid;
gap: 5px;
/* 通过JavaScript动态设置行列数 */
}
棋子设计:
css
.tile {
width: 60px;
height: 60px;
background: white;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
}
/* 悬停效果 */
.tile:hover {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}
/* 选中状态 */
.tile.selected {
background: linear-gradient(135deg, #ffd700, #ffed4e);
transform: scale(1.1);
box-shadow: 0 0 20px rgba(255, 215, 0, 0.6);
}
4. 消除动画
css
@keyframes matchAnimation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.3) rotate(10deg);
opacity: 0.8;
}
100% {
transform: scale(0);
opacity: 0;
}
}
这个动画分三个阶段:
- 保持原状
- 放大并旋转
- 缩小消失
5. 提示闪烁效果
css
@keyframes hintBlink {
0%, 100% {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
50% {
box-shadow: 0 0 25px rgba(76, 175, 80, 0.8);
transform: scale(1.1);
}
}
6. 按钮渐变与悬停效果
css
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
}
7. 响应式设计
css
@media (max-width: 768px) {
.tile {
width: 45px;
height: 45px;
font-size: 1.5rem;
}
}
@media (max-width: 480px) {
.tile {
width: 35px;
height: 35px;
font-size: 1.2rem;
}
}
JavaScript核心逻辑
1. 游戏配置与状态管理
配置对象:
javascript
const GAME_CONFIG = {
tiles: ['🥮', '🐰', '🌕', '🏮', '🌙', '🎋', '🌾', '🍂'],
easy: { rows: 8, cols: 8, time: 240 },
normal: { rows: 10, cols: 10, time: 180 },
hard: { rows: 12, cols: 12, time: 120 },
pointsPerMatch: 10,
comboMultiplier: 1.5
};
状态对象:
javascript
let gameState = {
board: [], // 二维数组存储棋盘
rows: 0, // 行数
cols: 0, // 列数
level: 1, // 当前关卡
score: 0, // 分数
time: 180, // 剩余时间
hints: 3, // 提示次数
shuffles: 3, // 重排次数
selectedTile: null, // 当前选中的棋子
isPlaying: false, // 是否正在游戏
isPaused: false, // 是否暂停
timer: null, // 计时器
combo: 0, // 连击数
totalPairs: 0, // 总对数
matchedPairs: 0, // 已匹配对数
difficulty: 'normal' // 难度
};
2. 棋盘初始化
生成算法:
javascript
function initBoard() {
const { rows, cols } = gameState;
const totalCells = rows * cols;
gameState.totalPairs = totalCells / 2;
// 1. 创建图案对
const tiles = [];
const tilesPerType = totalCells / GAME_CONFIG.tiles.length;
GAME_CONFIG.tiles.forEach(tile => {
for (let i = 0; i < tilesPerType / 2; i++) {
tiles.push(tile, tile); // 成对添加
}
});
// 2. 打乱图案
shuffleArray(tiles);
// 3. 填充二维数组
gameState.board = [];
for (let i = 0; i < rows; i++) {
gameState.board[i] = [];
for (let j = 0; j < cols; j++) {
gameState.board[i][j] = tiles[i * cols + j];
}
}
renderBoard();
}
关键点:
- 确保每种图案有偶数个(成对)
- 使用Fisher-Yates算法打乱数组
- 按行列顺序填充二维数组
3. 渲染棋盘
javascript
function renderBoard() {
const { rows, cols, board } = gameState;
// 设置Grid布局
elements.gameBoard.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
elements.gameBoard.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
// 清空并重新创建
elements.gameBoard.innerHTML = '';
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const tile = document.createElement('div');
tile.className = 'tile';
tile.textContent = board[i][j];
tile.dataset.row = i;
tile.dataset.col = j;
tile.addEventListener('click', () => handleTileClick(i, j));
elements.gameBoard.appendChild(tile);
}
}
// 设置Canvas大小
const boardRect = elements.gameBoard.getBoundingClientRect();
elements.lineCanvas.width = boardRect.width;
elements.lineCanvas.height = boardRect.height;
}
4. 点击处理流程
javascript
function handleTileClick(row, col) {
// 1. 游戏状态检查
if (!gameState.isPlaying || gameState.isPaused) return;
const clickedTile = getTileElement(row, col);
if (!clickedTile || clickedTile.classList.contains('empty')) return;
// 2. 点击同一个棋子,取消选择
if (gameState.selectedTile &&
gameState.selectedTile.row === row &&
gameState.selectedTile.col === col) {
deselectTile();
return;
}
// 3. 第一次点击,选中棋子
if (!gameState.selectedTile) {
selectTile(row, col);
return;
}
// 4. 第二次点击,尝试匹配
const firstTile = gameState.selectedTile;
// 检查图案是否相同
if (gameState.board[firstTile.row][firstTile.col] !==
gameState.board[row][col]) {
deselectTile();
selectTile(row, col);
showMessage('图案不匹配', 'error');
return;
}
// 检查是否可连接
const path = findPath(firstTile.row, firstTile.col, row, col);
if (path) {
matchTiles(firstTile.row, firstTile.col, row, col, path);
} else {
deselectTile();
selectTile(row, col);
showMessage('无法连接', 'error');
}
}
流程图:
点击棋子
↓
检查游戏状态 → 暂停/结束 → 返回
↓
检查是否为空 → 是 → 返回
↓
已选同一个 → 是 → 取消选择 → 返回
↓
第一次选择 → 是 → 标记选中 → 返回
↓
第二次选择
↓
图案相同? → 否 → 重新选择
↓ 是
可以连接? → 否 → 显示提示 + 重新选择
↓ 是
执行消除 → 更新状态 → 检查胜利条件
连连看算法详解
这是游戏的核心,也是最复杂的部分。连连看的规则是:两个相同图案之间的连接路径最多只能有2个转折点。
算法分类
- 直线连接(0个转折)
- 一次转折连接(1个转折)
- 两次转折连接(2个转折)
1. 直线连接
两点在同一行或同一列,且之间无障碍物。
javascript
function canConnectDirect(row1, col1, row2, col2) {
// 同一行
if (row1 === row2) {
const minCol = Math.min(col1, col2);
const maxCol = Math.max(col1, col2);
for (let col = minCol + 1; col < maxCol; col++) {
if (gameState.board[row1][col] !== null) {
return false; // 有障碍
}
}
return true;
}
// 同一列
if (col1 === col2) {
const minRow = Math.min(row1, row2);
const maxRow = Math.max(row1, row2);
for (let row = minRow + 1; row < maxRow; row++) {
if (gameState.board[row][col1] !== null) {
return false; // 有障碍
}
}
return true;
}
return false;
}
示意图:
横向直线:
🥮 . . . 🥮 ✓ 可连接
🥮 . 🐰 . 🥮 ✗ 中间有障碍
纵向直线:
🥮
.
.
🥮 ✓ 可连接
2. 一次转折连接
经过一个转折点连接两点。转折点有两种可能:
- (row1, col2):先横后竖
- (row2, col1):先竖后横
javascript
function canConnectWithOneTurn(row1, col1, row2, col2) {
// 转折点1: (row1, col2)
if (isEmpty(row1, col2) || (row1 === row2 && col1 === col2)) {
if (canConnectDirect(row1, col1, row1, col2) &&
canConnectDirect(row1, col2, row2, col2)) {
return [[row1, col1], [row1, col2], [row2, col2]];
}
}
// 转折点2: (row2, col1)
if (isEmpty(row2, col1) || (row1 === row2 && col1 === col2)) {
if (canConnectDirect(row1, col1, row2, col1) &&
canConnectDirect(row2, col1, row2, col2)) {
return [[row1, col1], [row2, col1], [row2, col2]];
}
}
return null;
}
示意图:
转折点 (row1, col2):
🥮 → → ↓
↓
🥮
转折点 (row2, col1):
🥮
↓
↓
→ → → 🥮
3. 两次转折连接
需要经过两个转折点。策略:
- 尝试在每一行寻找可行的中转点
- 尝试在每一列寻找可行的中转点
javascript
function canConnectWithTwoTurns(row1, col1, row2, col2) {
const { rows, cols } = gameState;
// 通过行扩展 (寻找横向中转线)
for (let col = 0; col < cols; col++) {
if (col === col1 || col === col2) continue;
if ((isEmpty(row1, col) || col === col2) &&
(isEmpty(row2, col) || col === col1)) {
if (canConnectDirect(row1, col1, row1, col) &&
canConnectDirect(row1, col, row2, col) &&
canConnectDirect(row2, col, row2, col2)) {
return [[row1, col1], [row1, col], [row2, col], [row2, col2]];
}
}
}
// 通过列扩展 (寻找纵向中转线)
for (let row = 0; row < rows; row++) {
if (row === row1 || row === row2) continue;
if ((isEmpty(row, col1) || row === row2) &&
(isEmpty(row, col2) || row === row1)) {
if (canConnectDirect(row1, col1, row, col1) &&
canConnectDirect(row, col1, row, col2) &&
canConnectDirect(row, col2, row2, col2)) {
return [[row1, col1], [row, col1], [row, col2], [row2, col2]];
}
}
}
return null;
}
示意图:
横向中转:
🥮 → → → col
↓
↓
🥮 ← col
纵向中转:
🥮
↓
↓
row → → → 🥮
路径查找总控函数
javascript
function findPath(row1, col1, row2, col2) {
// 1. 尝试直线连接
if (canConnectDirect(row1, col1, row2, col2)) {
return [[row1, col1], [row2, col2]];
}
// 2. 尝试一次转折
const oneTurn = canConnectWithOneTurn(row1, col1, row2, col2);
if (oneTurn) return oneTurn;
// 3. 尝试两次转折
const twoTurns = canConnectWithTwoTurns(row1, col1, row2, col2);
if (twoTurns) return twoTurns;
// 4. 无法连接
return null;
}
算法复杂度分析
- 直线连接:O(n),n为行数或列数
- 一次转折:O(n),最多检查2个转折点
- 两次转折:O(n²),需要遍历所有可能的中转线
- 总体:O(n²),可接受的时间复杂度
游戏状态管理
计时系统
javascript
function startTimer() {
if (gameState.timer) clearInterval(gameState.timer);
gameState.timer = setInterval(() => {
if (gameState.isPaused) return;
gameState.time--;
updateUI();
// 时间警告
if (gameState.time === 30) {
showMessage('⚠️ 只剩30秒了!', 'error');
}
// 时间到
if (gameState.time <= 0) {
loseGame();
}
}, 1000);
}
设计考虑:
- 暂停时不计时(通过检查isPaused标志)
- 最后30秒警告提醒
- 时间归零触发失败
计分系统
基础分数:
javascript
const basePoints = GAME_CONFIG.pointsPerMatch; // 10分
连击加成:
javascript
gameState.combo++; // 每次成功匹配增加连击数
let points = basePoints;
if (gameState.combo > 1) {
// 1.5倍递增:10, 15, 22, 33, 49, ...
points = Math.floor(
basePoints * Math.pow(GAME_CONFIG.comboMultiplier, gameState.combo - 1)
);
}
时间奖励:
javascript
const timeBonus = gameState.time * 2; // 剩余每秒2分
gameState.score += timeBonus;
匹配逻辑
javascript
function matchTiles(row1, col1, row2, col2, path) {
// 1. 绘制连接线
drawPath(path);
// 2. 增加连击
gameState.combo++;
// 3. 计算分数
let points = GAME_CONFIG.pointsPerMatch;
if (gameState.combo > 1) {
points = Math.floor(
points * Math.pow(GAME_CONFIG.comboMultiplier, gameState.combo - 1)
);
showMessage(`连击 x${gameState.combo}! +${points}分`, 'combo');
} else {
showMessage(`匹配成功! +${points}分`, 'success');
}
gameState.score += points;
gameState.matchedPairs++;
// 4. 延迟后移除棋子(显示动画)
setTimeout(() => {
removeTiles(row1, col1, row2, col2);
updateUI();
// 5. 检查胜利/失败条件
checkGameEnd();
}, 500);
}
胜利与失败判定
javascript
function checkGameEnd() {
// 全部消除 → 胜利
if (gameState.matchedPairs >= gameState.totalPairs) {
setTimeout(() => winGame(), 500);
return;
}
// 无有效移动 → 自动重排
if (!hasValidMoves()) {
showMessage('没有可消除的图案了!自动重排...', 'error');
setTimeout(() => shuffleBoard(), 1500);
}
}
// 时间到 → 失败
if (gameState.time <= 0) {
loseGame();
}
UI交互设计
Canvas绘制连接线
javascript
function drawPath(path) {
const canvas = elements.lineCanvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
const tileSize = 60 + 5; // 棋子大小 + 间距
const offset = tileSize / 2 + 20; // 棋盘padding
ctx.strokeStyle = '#4caf50'; // 绿色
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
// 连接路径上的所有点
for (let i = 0; i < path.length; i++) {
const [row, col] = path[i];
const x = col * tileSize + offset;
const y = row * tileSize + offset;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// 500ms后清除
setTimeout(() => clearCanvas(), 500);
}
提示功能
javascript
function useHint() {
if (gameState.hints <= 0) {
showMessage('没有提示次数了!', 'error');
return;
}
// 查找一对可消除的图案
const validMove = findValidMove();
if (validMove) {
gameState.hints--;
const [row1, col1, row2, col2] = validMove;
// 添加闪烁效果
const tile1 = getTileElement(row1, col1);
const tile2 = getTileElement(row2, col2);
if (tile1) tile1.classList.add('hint');
if (tile2) tile2.classList.add('hint');
// 3秒后移除效果
setTimeout(() => {
if (tile1) tile1.classList.remove('hint');
if (tile2) tile2.classList.remove('hint');
}, 3000);
updateUI();
showMessage('💡 已显示提示', 'success');
} else {
showMessage('没有可消除的图案!', 'error');
}
}
查找有效移动:
javascript
function findValidMove() {
const { rows, cols, board } = gameState;
// 双重循环遍历所有可能的配对
for (let i1 = 0; i1 < rows; i1++) {
for (let j1 = 0; j1 < cols; j1++) {
if (board[i1][j1] === null) continue;
for (let i2 = 0; i2 < rows; i2++) {
for (let j2 = 0; j2 < cols; j2++) {
if (board[i2][j2] === null) continue;
if (i1 === i2 && j1 === j2) continue;
// 图案相同且可连接
if (board[i1][j1] === board[i2][j2]) {
if (findPath(i1, j1, i2, j2)) {
return [i1, j1, i2, j2];
}
}
}
}
}
}
return null;
}
重排功能
javascript
function shuffleBoard() {
// 1. 收集所有非空棋子
const tiles = [];
const { rows, cols, board } = gameState;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] !== null) {
tiles.push(board[i][j]);
}
}
}
// 2. 打乱
shuffleArray(tiles);
// 3. 重新分配到非空位置
let index = 0;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] !== null) {
board[i][j] = tiles[index++];
}
}
}
// 4. 重新渲染
renderBoard();
updateUI();
showMessage('🔄 棋盘已重排!', 'success');
}
弹窗管理
javascript
function showModal(modalId) {
document.getElementById(modalId).classList.remove('hidden');
}
function hideModal(modalId) {
document.getElementById(modalId).classList.add('hidden');
}
三种弹窗:
- 开始弹窗:游戏介绍、难度选择
- 暂停弹窗:暂停时显示
- 结束弹窗:胜利/失败统计
消息提示系统
javascript
function showMessage(message, type = '') {
elements.gameMessage.textContent = message;
elements.gameMessage.className = 'game-message ' + type;
// 2秒后自动清除
setTimeout(() => {
elements.gameMessage.textContent = '';
elements.gameMessage.className = 'game-message';
}, 2000);
}
消息类型:
success
:绿色,成功操作error
:红色,错误提示combo
:橙色,连击提示
最终实现场景
游戏启动流程
-
打开页面
- 显示精美的背景(月亮、星星、灯笼)
- 弹出开始弹窗
-
选择难度
- 简单:8×8,240秒
- 普通:10×10,180秒
- 困难:12×12,120秒
-
点击开始
- 生成随机棋盘
- 启动倒计时
- 按钮变为可用状态
游戏进行中
正常游玩:
[查看棋盘] → [点击第一个棋子] → [高亮显示]
↓
[点击第二个棋子]
↓
[判断是否匹配]
├─ 匹配且可连接 → [绘制路径] → [播放动画] → [消除] → [加分]
└─ 不匹配/无法连接 → [提示错误] → [重新选择]
使用道具:
- 点击"提示"按钮 → 两个可消除的棋子闪烁
- 点击"重排"按钮 → 剩余棋子重新打乱
- 点击"暂停"按钮 → 弹出暂停窗口,计时暂停
连击系统:
第1对:+10分
第2对:+15分(连击x2)
第3对:+22分(连击x3)
第4对:+33分(连击x4)
...
游戏结束
胜利条件:
- 在规定时间内消除所有棋子
胜利界面:
🎉 恭喜过关!
最终得分:1250
剩余时间:45秒
时间奖励:+90分
当前关卡:3
[下一关] [返回主菜单]
失败条件:
- 时间归零
失败界面:
😢 时间到!
最终得分:680
已消除:28/50对
完成度:56%
[重新挑战] [返回主菜单]
关卡递进
每通过一关:
- 关卡数 +1
- 保持之前的总分
- 时间重置
- 道具次数重置
实际游玩体验
第1关(普通难度):
- 10×10的棋盘,100个棋子(50对)
- 8种图案,每种12-13个
- 180秒倒计时
- 新手较容易,图案分布均匀
第5关:
- 同样10×10,但因为熟练度提升,玩得更快
- 连击次数增多,高分段出现
- 策略性增强(先消边缘还是中间?)
高难度挑战:
- 12×12的棋盘,144个棋子(72对)
- 只有120秒
- 极度考验观察力和反应速度
- 提示和重排要谨慎使用
技术总结
关键技术点
-
数据结构
- 二维数组存储棋盘状态
- 对象管理游戏状态
- 数组存储路径信息
-
算法实现
- Fisher-Yates洗牌算法
- 路径搜索算法(DFS思想)
- 有效移动检测
-
DOM操作
- 动态创建元素
- Grid布局应用
- 事件监听与处理
-
Canvas绘图
- 2D上下文操作
- 路径绘制
- 动画清除
-
CSS动画
- Keyframes定义
- Transform变换
- Transition过渡
-
游戏设计
- 状态机管理
- 计时系统
- 计分机制
- UI反馈
使用说明
快速开始
-
下载文件
bash# 确保以下三个文件在同一目录 - lianliankan.html - lianliankan.css - lianliankan.js
-
打开游戏
-
直接在浏览器中打开
lianliankan.html
-
或使用本地服务器:
bashpython -m http.server 8000 # 访问 http://localhost:8000/lianliankan.html
-
-
开始游玩
- 阅读游戏说明
- 选择难度
- 点击"开始游戏"
操作指南
鼠标操作:
- 点击棋子选择
- 再次点击可消除的图案配对
- 点击控制按钮使用功能
按钮说明:
- 🎮 开始游戏:开始新游戏
- ⏸️ 暂停:暂停当前游戏
- 💡 提示:显示一对可消除的图案
- 🔄 重排:重新打乱剩余图案
- 🔄 重新开始:返回主菜单
结语
用代码绘制节日,用技术传承文化。愿每一位开发者都能在创作中感受到编程的乐趣,在分享中传递技术的温度。
最好祝大家中秋快乐!Happy Mid-Autumn Festival! 🌕✨