连连看,是一款经典的益智游戏,玩家需要连接两个相同的图标并消除它们。图标可以通过连线连接,只要图案相同且线条不被其他图标阻挡,就可以消除这些图标。游戏不仅考验玩家的观察力,还需要一定的空间想象能力。
实现连连看游戏时,我们需要处理图标的生成、连接规则、消除逻辑等多个功能。过去,要手动编写这些逻辑,涉及到连接算法、碰撞检测和UI布局等,可能会花费大量时间。但通过 Trae IDE,这一切变得轻松简单。只需要通过简单的指令,Trae 就能够自动帮我完成整个连连看游戏的开发。
💡 我的需求其实很简单
我的需求非常明确:制作一个连连看游戏,功能要求如下:
- 图标生成:生成一个包含多个图标的网格,玩家需要匹配相同的图标。连接规则:玩家可以通过连接两个相同的图标来消除它们,连接路径不能被其他图标阻挡。消除机制:每成功连接并消除一对图标,游戏会自动清除这两个图标,并刷新剩余图标。简洁的UI:游戏界面要简洁,操作直观,玩家能够轻松进行连接和消除。
虽然规则简单,但涉及到图标的布局、连接检测、消除动画等,手动编写这些功能仍然需要不少工作。
✨ Trae 如何理解需求并生成代码?
我只需要在 Trae 中输入一句话:
“生成连连看游戏,玩家连接两个相同的图标,消除它们。”
Trae 自动解析并生成完整的连连看游戏代码,包括:
- 图标生成:游戏会随机生成多个图标,并将它们分布在网格上,玩家需要找到并连接相同的图标。连接规则:玩家通过点击两个相同的图标,如果它们可以通过一条线连接(不被其他图标阻挡),这对图标就会被消除。消除机制:每成功消除一对图标,游戏会自动更新网格,剩余的图标会重新排列,直到没有可以消除的图标为止。简洁的UI设计:游戏界面设计简洁、直观,玩家可以轻松点击并连接图标,消除的效果和操作流畅自然。
通过 Trae,几秒钟内,我就拥有了一个完整的连连看游戏,带有图标生成、连接和消除等功能。
🧩 游戏操作简便,连接流畅
Trae 生成的连连看游戏操作非常简单。玩家只需要点击两个相同的图标,如果它们可以通过一条线连接,系统就会自动消除这对图标。每次消除后,剩余图标会重新排列,游戏界面更新非常流畅。
图标的消除动画也非常直观,每次成功连接一对图标,都会出现清除动画,增加了游戏的互动性和趣味性。玩家可以轻松上手,享受消除图标的乐趣。
🛠 游戏拓展,功能轻松加
尽管 Trae 生成的连连看游戏已经非常完备,但它支持轻松添加更多功能。例如:
- 难度设置:可以增加不同的难度设置,例如图标数量增加,网格大小调整等。计时器功能:为游戏加入倒计时,增加一定的挑战性。提示功能:可以为玩家提供一些提示,显示当前可以连接的图标对。动画和音效:为消除动画加入音效,使游戏更加生动有趣。
这些新功能都可以通过简单的描述,Trae 会自动生成并将它们集成到现有的游戏中。
这就是连连看游戏开发的新体验
通过这次连连看游戏的开发,我深刻感受到 Trae 带来的便利。从图标的生成,到连接检测和消除机制,Trae 都能够自动帮我完成。这不仅大大节省了开发时间,也提升了我的工作效率,让我可以更加专注于游戏设计和创意扩展。
对于独立开发者或小团队来说,Trae 是一个非常高效的工具,能够帮助你快速完成游戏开发,专注于创意和功能拓展。
结语
如果你也想制作一个经典的连连看游戏,试试 Trae IDE,输入类似的需求:
“生成连连看游戏,玩家连接两个相同的图标,消除它们。”
几秒钟内,Trae 就会生成完整的连连看游戏代码,带有图标生成、连接和消除等功能。你可以直接将它嵌入到你的项目中,甚至根据需求继续扩展和优化。
快来体验一下 Trae,让你的连连看游戏开发变得更加轻松、富有创意!
<!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>
