连连看,是一款经典的益智游戏,玩家需要连接两个相同的图标并消除它们。图标可以通过连线连接,只要图案相同且线条不被其他图标阻挡,就可以消除这些图标。游戏不仅考验玩家的观察力,还需要一定的空间想象能力。
实现连连看游戏时,我们需要处理图标的生成、连接规则、消除逻辑等多个功能。过去,要手动编写这些逻辑,涉及到连接算法、碰撞检测和UI布局等,可能会花费大量时间。但通过 Trae IDE,这一切变得轻松简单。只需要通过简单的指令,Trae 就能够自动帮我完成整个连连看游戏的开发。
💡 我的需求其实很简单
我的需求非常明确:制作一个连连看游戏,功能要求如下:
- 图标生成:生成一个包含多个图标的网格,玩家需要匹配相同的图标。
- 连接规则:玩家可以通过连接两个相同的图标来消除它们,连接路径不能被其他图标阻挡。
- 消除机制:每成功连接并消除一对图标,游戏会自动清除这两个图标,并刷新剩余图标。
- 简洁的UI:游戏界面要简洁,操作直观,玩家能够轻松进行连接和消除。
虽然规则简单,但涉及到图标的布局、连接检测、消除动画等,手动编写这些功能仍然需要不少工作。
✨ Trae 如何理解需求并生成代码?
我只需要在 Trae 中输入一句话:

"生成连连看游戏,玩家连接两个相同的图标,消除它们。"
Trae 自动解析并生成完整的连连看游戏代码,包括:
- 图标生成:游戏会随机生成多个图标,并将它们分布在网格上,玩家需要找到并连接相同的图标。
- 连接规则:玩家通过点击两个相同的图标,如果它们可以通过一条线连接(不被其他图标阻挡),这对图标就会被消除。
- 消除机制:每成功消除一对图标,游戏会自动更新网格,剩余的图标会重新排列,直到没有可以消除的图标为止。
- 简洁的UI设计:游戏界面设计简洁、直观,玩家可以轻松点击并连接图标,消除的效果和操作流畅自然。

通过 Trae,几秒钟内,我就拥有了一个完整的连连看游戏,带有图标生成、连接和消除等功能。
🧩 游戏操作简便,连接流畅
Trae 生成的连连看游戏操作非常简单。玩家只需要点击两个相同的图标,如果它们可以通过一条线连接,系统就会自动消除这对图标。每次消除后,剩余图标会重新排列,游戏界面更新非常流畅。
图标的消除动画也非常直观,每次成功连接一对图标,都会出现清除动画,增加了游戏的互动性和趣味性。玩家可以轻松上手,享受消除图标的乐趣。
🛠 游戏拓展,功能轻松加
尽管 Trae 生成的连连看游戏已经非常完备,但它支持轻松添加更多功能。例如:
- 难度设置:可以增加不同的难度设置,例如图标数量增加,网格大小调整等。
- 计时器功能:为游戏加入倒计时,增加一定的挑战性。
- 提示功能:可以为玩家提供一些提示,显示当前可以连接的图标对。
- 动画和音效:为消除动画加入音效,使游戏更加生动有趣。
这些新功能都可以通过简单的描述,Trae 会自动生成并将它们集成到现有的游戏中。
这就是连连看游戏开发的新体验
通过这次连连看游戏的开发,我深刻感受到 Trae 带来的便利。从图标的生成,到连接检测和消除机制,Trae 都能够自动帮我完成。这不仅大大节省了开发时间,也提升了我的工作效率,让我可以更加专注于游戏设计和创意扩展。
对于独立开发者或小团队来说,Trae 是一个非常高效的工具,能够帮助你快速完成游戏开发,专注于创意和功能拓展。
结语
如果你也想制作一个经典的连连看游戏,试试 Trae IDE,输入类似的需求:
"生成连连看游戏,玩家连接两个相同的图标,消除它们。"
几秒钟内,Trae 就会生成完整的连连看游戏代码,带有图标生成、连接和消除等功能。你可以直接将它嵌入到你的项目中,甚至根据需求继续扩展和优化。
快来体验一下 Trae,让你的连连看游戏开发变得更加轻松、富有创意!
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>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
.game-container {
position: relative;
width: 90%;
max-width: 800px;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
padding: 20px;
overflow: hidden;
}
.game-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.game-info {
display: flex;
gap: 20px;
}
.score-container, .time-container, .level-container {
background-color: #4a6fa5;
color: white;
padding: 10px 15px;
border-radius: 10px;
font-size: 18px;
font-weight: bold;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn {
background-color: #4a6fa5;
color: white;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn:hover {
background-color: #3a5a80;
transform: translateY(-2px);
}
.btn:active {
transform: translateY(0);
}
.game-board {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
gap: 5px;
margin: 0 auto;
width: 100%;
aspect-ratio: 1 / 1;
max-width: 600px;
}
.tile {
position: relative;
background-color: #dbe4f0;
border-radius: 8px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.tile:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
z-index: 1;
}
.tile.selected {
background-color: #ffcc80;
transform: scale(1.05);
box-shadow: 0 0 15px rgba(255, 204, 128, 0.8);
z-index: 2;
}
.tile.matched {
visibility: hidden;
opacity: 0;
transform: scale(0.1);
transition: all 0.5s ease;
}
.tile-content {
width: 80%;
height: 80%;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: #4a6fa5;
}
.connection-line {
position: absolute;
background-color: rgba(255, 204, 128, 0.8);
z-index: 3;
pointer-events: none;
}
.start-screen, .end-screen, .level-complete-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
.start-screen h1, .end-screen h1, .level-complete-screen h1 {
color: white;
font-size: 36px;
margin-bottom: 20px;
text-align: center;
}
.start-screen p, .end-screen p, .level-complete-screen p {
color: white;
font-size: 18px;
margin-bottom: 30px;
text-align: center;
max-width: 80%;
line-height: 1.5;
}
.final-score, .level-complete-score {
color: #ffcc80;
font-size: 32px;
font-weight: bold;
margin-bottom: 30px;
}
.hint-btn {
background-color: #6a8caf;
margin-left: 10px;
}
.hint-count {
display: inline-block;
background-color: white;
color: #4a6fa5;
width: 24px;
height: 24px;
border-radius: 50%;
text-align: center;
line-height: 24px;
margin-left: 5px;
}
.hint-highlight {
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 204, 128, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(255, 204, 128, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 204, 128, 0); }
}
.tile-match-animation {
animation: match-animation 0.5s;
}
@keyframes match-animation {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(0); }
}
.progress-container {
width: 100%;
height: 10px;
background-color: #dbe4f0;
border-radius: 5px;
margin-top: 10px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background-color: #4a6fa5;
width: 100%;
transition: width 1s linear;
}
@media (max-width: 768px) {
.game-container {
padding: 15px;
width: 95%;
}
.game-header {
flex-direction: column;
gap: 10px;
}
.game-info {
width: 100%;
justify-content: space-between;
}
.score-container, .time-container, .level-container {
font-size: 14px;
padding: 8px 12px;
}
.btn {
font-size: 14px;
padding: 8px 15px;
}
.tile-content {
font-size: 18px;
}
}
</style>
</head>
<body>
<div class="game-container">
<div class="game-header">
<div class="game-info">
<div class="score-container">得分: <span id="score">0</span></div>
<div class="time-container">时间: <span id="time">120</span>s</div>
<div class="level-container">关卡: <span id="level">1</span></div>
</div>
<div>
<button class="btn hint-btn" id="hint-btn">提示 <span class="hint-count" id="hint-count">3</span></button>
<button class="btn" id="restart-btn">重新开始</button>
</div>
</div>
<div class="progress-container">
<div class="progress-bar" id="progress-bar"></div>
</div>
<div class="game-board" id="game-board">
<!-- 游戏板块将由JS动态生成 -->
</div>
<div class="start-screen" id="start-screen">
<h1>连连看</h1>
<p>连接相同的图案,消除所有方块!<br>你可以连接的方块必须通过不超过三条直线相连。</p>
<button class="btn" id="start-btn">开始游戏</button>
</div>
<div class="end-screen" id="end-screen" style="display: none;">
<h1>游戏结束</h1>
<div class="final-score">最终得分: <span id="final-score">0</span></div>
<p>时间到了!再来一次,挑战更高分!</p>
<button class="btn" id="play-again-btn">再玩一次</button>
</div>
<div class="level-complete-screen" id="level-complete-screen" style="display: none;">
<h1>关卡完成!</h1>
<div class="level-complete-score">得分: <span id="level-complete-score">0</span></div>
<p>恭喜你完成了当前关卡!</p>
<button class="btn" id="next-level-btn">下一关</button>
</div>
</div>
<script>
// 获取DOM元素
const gameBoard = document.getElementById('game-board');
const scoreElement = document.getElementById('score');
const timeElement = document.getElementById('time');
const levelElement = document.getElementById('level');
const startScreen = document.getElementById('start-screen');
const endScreen = document.getElementById('end-screen');
const levelCompleteScreen = document.getElementById('level-complete-screen');
const finalScoreElement = document.getElementById('final-score');
const levelCompleteScoreElement = document.getElementById('level-complete-score');
const startBtn = document.getElementById('start-btn');
const restartBtn = document.getElementById('restart-btn');
const playAgainBtn = document.getElementById('play-again-btn');
const nextLevelBtn = document.getElementById('next-level-btn');
const hintBtn = document.getElementById('hint-btn');
const hintCountElement = document.getElementById('hint-count');
const progressBar = document.getElementById('progress-bar');
// 游戏变量
let score = 0;
let timeLeft = 120;
let level = 1;
let hintCount = 3;
let gameInterval;
let tiles = [];
let selectedTile = null;
let isPlaying = false;
let matchedPairs = 0;
let totalPairs = 0;
let connectionLines = [];
// 图标集合 (使用Emoji作为图标)
const icons = [
'🍎', '🍌', '🍒', '🍓', '🍊', '🍋', '🍍', '🥝',
'🍇', '🍉', '🥭', '🍑', '🥥', '🍐', '🥑', '🍈',
'🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼',
'🦁', '🐯', '🐨', '🐮', '🐷', '🐸', '🐵', '🐔'
];
// 初始化游戏
function initGame() {
// 清空游戏板
gameBoard.innerHTML = '';
clearConnectionLines();
// 重置游戏状态
score = 0;
timeLeft = 120;
hintCount = 3;
selectedTile = null;
matchedPairs = 0;
tiles = [];
// 更新显示
scoreElement.textContent = score;
timeElement.textContent = timeLeft;
levelElement.textContent = level;
hintCountElement.textContent = hintCount;
progressBar.style.width = '100%';
// 根据关卡设置游戏板大小
let rows, cols;
if (level === 1) {
rows = 6;
cols = 6;
} else if (level === 2) {
rows = 6;
cols = 8;
} else {
rows = 8;
cols = 8;
}
// 设置游戏板样式
gameBoard.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
gameBoard.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
// 创建配对的图标
const tileCount = rows * cols;
if (tileCount % 2 !== 0) {
console.error('瓦片数量必须是偶数');
return;
}
totalPairs = tileCount / 2;
const iconPairs = [];
// 选择足够的图标对
for (let i = 0; i < totalPairs; i++) {
const iconIndex = i % icons.length;
iconPairs.push(icons[iconIndex]);
iconPairs.push(icons[iconIndex]);
}
// 随机排列图标
shuffleArray(iconPairs);
// 创建瓦片
for (let i = 0; i < tileCount; i++) {
const tile = document.createElement('div');
tile.className = 'tile';
tile.dataset.index = i;
const tileContent = document.createElement('div');
tileContent.className = 'tile-content';
tileContent.textContent = iconPairs[i];
tile.appendChild(tileContent);
tile.addEventListener('click', handleTileClick);
gameBoard.appendChild(tile);
tiles.push({
element: tile,
icon: iconPairs[i],
row: Math.floor(i / cols),
col: i % cols,
matched: false
});
}
}
// 随机排列数组
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// 处理瓦片点击
function handleTileClick(e) {
if (!isPlaying) return;
const clickedTile = e.currentTarget;
const tileIndex = parseInt(clickedTile.dataset.index);
const tile = tiles[tileIndex];
// 如果已经匹配或者是同一个瓦片,则忽略
if (tile.matched || (selectedTile && selectedTile.element === clickedTile)) {
return;
}
// 如果已经选择了一个瓦片
if (selectedTile) {
// 检查是否匹配
if (selectedTile.icon === tile.icon) {
// 检查是否可以连接
const path = findPath(selectedTile, tile);
if (path) {
// 匹配成功
matchTiles(selectedTile, tile, path);
} else {
// 不能连接,取消选择
selectedTile.element.classList.remove('selected');
selectedTile = null;
// 选择新的瓦片
clickedTile.classList.add('selected');
selectedTile = tile;
}
} else {
// 不匹配,取消选择第一个瓦片
selectedTile.element.classList.remove('selected');
// 选择新的瓦片
clickedTile.classList.add('selected');
selectedTile = tile;
}
} else {
// 选择第一个瓦片
clickedTile.classList.add('selected');
selectedTile = tile;
}
}
// 匹配瓦片
function matchTiles(tile1, tile2, path) {
// 添加匹配动画
tile1.element.classList.add('tile-match-animation');
tile2.element.classList.add('tile-match-animation');
// 绘制连接线
drawConnectionLines(path);
// 延迟后移除瓦片
setTimeout(() => {
// 标记为已匹配
tile1.matched = true;
tile2.matched = true;
// 添加已匹配类
tile1.element.classList.add('matched');
tile2.element.classList.add('matched');
// 移除选中和动画类
tile1.element.classList.remove('selected', 'tile-match-animation');
tile2.element.classList.remove('selected', 'tile-match-animation');
// 清除连接线
clearConnectionLines();
// 更新分数
score += 10;
scoreElement.textContent = score;
// 重置选中的瓦片
selectedTile = null;
// 更新匹配对数
matchedPairs++;
// 检查是否完成关卡
if (matchedPairs === totalPairs) {
levelComplete();
}
}, 500);
}
// 寻找连接路径 (使用改进的BFS算法)
function findPath(tile1, tile2) {
// 创建一个表示游戏板的二维数组
const rows = Math.max(...tiles.map(t => t.row)) + 1;
const cols = Math.max(...tiles.map(t => t.col)) + 1;
const board = Array(rows).fill().map(() => Array(cols).fill(null));
// 填充游戏板
for (const tile of tiles) {
if (!tile.matched) {
board[tile.row][tile.col] = tile;
}
}
// 设置起点和终点
const start = { row: tile1.row, col: tile1.col };
const end = { row: tile2.row, col: tile2.col };
// 使用BFS寻找路径
const queue = [];
const visited = new Set();
const parent = new Map();
queue.push(start);
visited.add(`${start.row},${start.col}`);
while (queue.length > 0) {
const current = queue.shift();
// 如果到达终点
if (current.row === end.row && current.col === end.col) {
// 重建路径
const path = [];
let curr = `${end.row},${end.col}`;
while (curr) {
const [row, col] = curr.split(',').map(Number);
path.unshift({ row, col });
curr = parent.get(curr);
}
// 检查路径是否不超过三条直线
if (canConnectWithThreeLines(path)) {
return path;
}
return null;
}
// 尝试四个方向
const directions = [
{ dr: -1, dc: 0 }, // 上
{ dr: 1, dc: 0 }, // 下
{ dr: 0, dc: -1 }, // 左
{ dr: 0, dc: 1 } // 右
];
for (const dir of directions) {
let newRow = current.row + dir.dr;
let newCol = current.col + dir.dc;
// 继续在这个方向上移动,直到遇到障碍或边界
while (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
const key = `${newRow},${newCol}`;
// 如果是终点或空白,可以移动
if ((newRow === end.row && newCol === end.col) || board[newRow][newCol] === null) {
if (!visited.has(key)) {
queue.push({ row: newRow, col: newCol });
visited.add(key);
parent.set(key, `${current.row},${current.col}`);
}
}
// 如果遇到障碍(不是终点的瓦片),停止在这个方向上移动
if (board[newRow][newCol] !== null && !(newRow === end.row && newCol === end.col)) {
break;
}
// 继续在这个方向上移动
newRow += dir.dr;
newCol += dir.dc;
}
}
}
return null; // 没有找到路径
}
// 检查路径是否可以用不超过三条直线连接
function canConnectWithThreeLines(path) {
if (path.length <= 4) return true; // 最多3条线(4个点)
// 计算转弯次数
let turns = 0;
for (let i = 1; i < path.length - 1; i++) {
const prev = path[i-1];
const curr = path[i];
const next = path[i+1];
// 检查是否转弯
if ((prev.row === curr.row && curr.row !== next.row) ||
(prev.col === curr.col && curr.col !== next.col)) {
turns++;
}
if (turns > 2) return false; // 超过2次转弯(3条线)
}
return true;
}
// 绘制连接线
function drawConnectionLines(path) {
clearConnectionLines();
if (path.length < 2) return;
for (let i = 0; i < path.length - 1; i++) {
const start = path[i];
const end = path[i+1];
const line = document.createElement('div');
line.className = 'connection-line';
// 获取瓦片的位置和尺寸
const startTile = tiles.find(t => t.row === start.row && t.col === start.col);
const endTile = tiles.find(t => t.row === end.row && t.col === end.col);
if (!startTile || !endTile) continue;
const startRect = startTile.element.getBoundingClientRect();
const endRect = endTile.element.getBoundingClientRect();
const boardRect = gameBoard.getBoundingClientRect();
// 计算线的位置和尺寸
let left, top, width, height;
if (start.row === end.row) { // 水平线
const startX = startRect.left + startRect.width / 2 - boardRect.left;
const endX = endRect.left + endRect.width / 2 - boardRect.left;
left = Math.min(startX, endX);
top = startRect.top + startRect.height / 2 - boardRect.top;
width = Math.abs(endX - startX);
height = 3;
} else if (start.col === end.col) { // 垂直线
const startY = startRect.top + startRect.height / 2 - boardRect.top;
const endY = endRect.top + endRect.height / 2 - boardRect.top;
left = startRect.left + startRect.width / 2 - boardRect.left;
top = Math.min(startY, endY);
width = 3;
height = Math.abs(endY - startY);
}
// 设置线的样式
line.style.left = `${left}px`;
line.style.top = `${top}px`;
line.style.width = `${width}px`;
line.style.height = `${height}px`;
// 添加到游戏板
gameBoard.appendChild(line);
connectionLines.push(line);
}
}
// 清除连接线
function clearConnectionLines() {
connectionLines.forEach(line => {
if (line.parentNode) {
line.parentNode.removeChild(line);
}
});
connectionLines = [];
}
// 提示功能
function showHint() {
if (!isPlaying || hintCount <= 0) return;
// 减少提示次数
hintCount--;
hintCountElement.textContent = hintCount;
// 找到可以连接的一对瓦片
const availablePairs = findAvailablePair();
if (availablePairs) {
const [tile1, tile2] = availablePairs;
// 高亮显示这对瓦片
tile1.element.classList.add('hint-highlight');
tile2.element.classList.add('hint-highlight');
// 3秒后移除高亮
setTimeout(() => {
tile1.element.classList.remove('hint-highlight');
tile2.element.classList.remove('hint-highlight');
}, 3000);
}
}
// 找到一对可以连接的瓦片
function findAvailablePair() {
const availableTiles = tiles.filter(tile => !tile.matched);
for (let i = 0; i < availableTiles.length; i++) {
for (let j = i + 1; j < availableTiles.length; j++) {
const tile1 = availableTiles[i];
const tile2 = availableTiles[j];
if (tile1.icon === tile2.icon) {
const path = findPath(tile1, tile2);
if (path) {
return [tile1, tile2];
}
}
}
}
return null;
}
// 检查是否还有可连接的瓦片
function hasAvailableMoves() {
return findAvailablePair() !== null;
}
// 重新排列剩余瓦片
function reshuffleTiles() {
// 获取未匹配的瓦片
const unmatchedTiles = tiles.filter(tile => !tile.matched);
// 提取图标
const icons = unmatchedTiles.map(tile => tile.icon);
// 随机排列图标
shuffleArray(icons);
// 重新分配图标
for (let i = 0; i < unmatchedTiles.length; i++) {
unmatchedTiles[i].icon = icons[i];
unmatchedTiles[i].element.querySelector('.tile-content').textContent = icons[i];
}
}
// 开始游戏
function startGame() {
initGame();
isPlaying = true;
startScreen.style.display = 'none';
endScreen.style.display = 'none';
levelCompleteScreen.style.display = 'none';
// 开始计时器
gameInterval = setInterval(() => {
timeLeft--;
timeElement.textContent = timeLeft;
// 更新进度条
const percentage = (timeLeft / 120) * 100;
progressBar.style.width = `${percentage}%`;
if (timeLeft <= 0) {
endGame();
}
// 每30秒检查一次是否还有可连接的瓦片
if (timeLeft > 0 && timeLeft % 30 === 0) {
if (!hasAvailableMoves()) {
reshuffleTiles();
}
}
}, 1000);
}
// 结束游戏
function endGame() {
isPlaying = false;
clearInterval(gameInterval);
// 显示结束屏幕
finalScoreElement.textContent = score;
endScreen.style.display = 'flex';
}
// 关卡完成
function levelComplete() {
isPlaying = false;
clearInterval(gameInterval);
// 根据剩余时间增加分数
const timeBonus = timeLeft * 2;
score += timeBonus;
// 显示关卡完成屏幕
levelCompleteScoreElement.textContent = score;
levelCompleteScreen.style.display = 'flex';
}
// 下一关
function nextLevel() {
level++;
startGame();
}
// 事件监听
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
playAgainBtn.addEventListener('click', () => {
level = 1;
startGame();
});
nextLevelBtn.addEventListener('click', nextLevel);
hintBtn.addEventListener('click', showHint);
// 初始化游戏
initGame();
</script>
</body>
</html>