HTML5+JavaScript实现连连看游戏之二
以前一篇,见 https://blog.csdn.net/cnds123/article/details/144220548
连连看游戏连接规则:
只能连接相同图案(或图标、字符)的方块。
连线路径必须是由直线段组成的,最多可以有两个拐角。
连线路径必须是空的,不能穿过其他方块。
当两个相同图案的方块被有效连接时,它们会从游戏板上消失。当所有方块都被消除时,玩家获胜。
现在再发布一个,可以自定义 行列数、可用图片数(实际是使用Font Awesome图标库以便跨平台兼容,通过CDN加载图标库)
同一局游戏中所有图标使用相同颜色,不同局颜色会变化。
游戏就可以在行数和列数乘积为偶数时才允许开始,如果行数和列数的乘积为奇数,则不允许游戏开始,并且可以通过弹出提示信息来告知用户。
点击"开始"按钮后立即生成布局并开始计时。
运行界面
下面代码若要在手机上玩,可以在 <meta charset="UTF-8"> 之后添加一行
<meta name="viewport" content="width=device-width, initial-scale=1.0">
源码如下:
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>连连看游戏(跨平台版)</title><!-- 引入Font Awesome图标库 --><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"><style>body {font-family: Arial, sans-serif;display: flex;flex-direction: column;align-items: center;background-color: #f0f0f0;}#controls {margin: 20px 0;text-align: center;}input, button {margin: 0 10px;padding: 5px;}button {background-color: #4CAF50;color: white;border: none;cursor: pointer;transition: background-color 0.3s;}button:hover {background-color: #45a049;}#timebar {height: 30px;width: 600px;background-color: #ddd;margin-bottom: 20px;position: relative;}#timebar-inner {height: 100%;width: 100%;background-color: green;transition: width 1s linear;}#timeleft {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);color: white;font-weight: bold;z-index: 1;}#container {background-color: white;padding: 20px;border-radius: 10px;box-shadow: 0 0 10px rgba(0,0,0,0.1);display: flex;justify-content: center;align-items: center;}table {border-collapse: collapse;}td {border: 1px solid #ddd;text-align: center;vertical-align: middle;cursor: pointer;transition: background-color 0.3s;width: 40px;height: 40px;font-family: "Font Awesome 5 Free";font-weight: 900;}td:hover {background-color: #f5f5f5;}td i {font-size: 24px;}</style>
</head>
<body><div id="controls">行数<input id="setrow" type="number" value="9" min="2" max="20">列数<input id="setcol" type="number" value="16" min="2" max="40">图片数<input id="setpic" type="number" value="12" min="1" max="26">时间<input id="settime" type="number" value="120" min="10" max="1200">秒<button onclick="SetupGame();">设置</button><button onclick="StartGame();" id="startButton">开始</button></div><div id="timebar"><div id="timebar-inner"></div><div id="timeleft"></div></div><div id="container"></div><script>// 图标配置(扩展更多图标请访问:https://fontawesome.com/icons)const icons = ['fa-heart', 'fa-star', 'fa-circle', 'fa-square','fa-check', 'fa-times', 'fa-bell', 'fa-flag','fa-cloud', 'fa-sun', 'fa-moon', 'fa-leaf','fa-gem', 'fa-car', 'fa-tree', 'fa-coffee','fa-bolt', 'fa-rocket', 'fa-key', 'fa-bug','fa-cat', 'fa-dog', 'fa-fish', 'fa-horse'];// 图标配置(仅定义类型)const iconClasses = ['fa-heart', 'fa-star', 'fa-circle', 'fa-square','fa-check', 'fa-times', 'fa-bell', 'fa-flag','fa-cloud', 'fa-sun', 'fa-moon', 'fa-leaf'];let currentColor = ""; // 全局当前颜色let RowMax = 11;let ColMax = 18;let PicMax = 12;let TimeMax = 120;let st;let TmpStr = "";let TmpObj = null;let TmpTime = 0;let TmpInt = 0;let gameMatrix;let gameStarted = false;let P = new Array(4);for(let i=0; i<4; i++) P[i] = {x:0, y:0};function SetupGame(){clearInterval(st);// 参数校验const rowVal = Math.min(20, Math.max(2, parseInt(document.getElementById("setrow").value)));const colVal = Math.min(40, Math.max(2, parseInt(document.getElementById("setcol").value)));RowMax = rowVal + 2;ColMax = colVal + 2;// 检查行数与列数的乘积是否为偶数if ((rowVal * colVal) % 2 !== 0) {alert("行数和列数的乘积必须为偶数,请调整行数或列数。");return; // 终止函数执行}PicMax = Math.min(icons.length, Math.max(1, parseInt(document.getElementById("setpic").value)));TimeMax = Math.min(1200, Math.max(10, parseInt(document.getElementById("settime").value)));generateNewGame();document.getElementById("timeleft").innerHTML = "点击开始按钮开始游戏";document.getElementById("timebar-inner").style.width = "100%";document.getElementById("timebar-inner").style.backgroundColor = "#4CAF50";document.getElementById("startButton").disabled = false;gameStarted = false;}function getRandomColor() {return `hsl(${Math.random() * 360}, 70%, 60%)`; // 随机HSL颜色}function generateNewGame() {gameMatrix = Array(RowMax).fill().map(() => Array(ColMax).fill(0));// 生成全局统一颜色currentColor = `hsl(${Math.random()*360}, 70%, 60%)`; // 随机HSL颜色// 生成配对逻辑(仅存储图标类型索引)const totalCells = (RowMax-2)*(ColMax-2);const pairs = [];for(let i=0; i<totalCells/2; i++){const type = i % PicMax; // 确保成对生成pairs.push(type, type);}// 洗牌算法for(let i = pairs.length -1; i > 0; i--){const j = Math.floor(Math.random() * (i+1));[pairs[i], pairs[j]] = [pairs[j], pairs[i]];}// 构建表格(所有图标使用相同颜色)TmpStr = "<table>";let pairIndex = 0;for(let i=0; i<RowMax; i++){TmpStr += "<tr>";for(let j=0; j<ColMax; j++){TmpStr += `<td onclick="CheckP(this,${i},${j});">`;if(i === 0 || j === 0 || i === RowMax-1 || j === ColMax-1){gameMatrix[i][j] = null; // 边界保持为空} else {const type = pairs[pairIndex++];gameMatrix[i][j] = type; // 存储类型索引// 所有图标使用同一颜色TmpStr += `<i class="fas ${iconClasses[type]}" style="color:${currentColor}"></i>`;}TmpStr += "</td>";}TmpStr += "</tr>";}TmpStr += "</table>";document.getElementById("container").innerHTML = TmpStr;TmpInt = totalCells/2;}// 开始游戏function StartGame(){gameStarted = true; // 标记游戏已开始TmpTime = TimeMax; // 重置时间document.getElementById("timeleft").innerHTML = TmpTime; // 显示剩余时间document.getElementById("timebar-inner").style.width = "100%";document.getElementById("timebar-inner").style.backgroundColor = "green";st = setInterval(ShowTime, 1000); // 开始倒计时// 禁用"开始"按钮document.getElementById("startButton").disabled = true;}// X方向连线。(有起点,无终点)function LineX(x, y, xt){for(let i=x; i!=xt; (x<xt? i++: i--) ){if(gameMatrix[i][y]){return false;}}return true;}// Y方向连线。(有起点,无终点)function LineY(x, y, yt){for(let i=y; i!=yt; (y<yt? i++: i--) ){if(gameMatrix[x][i]){return false;}} return true;}// 2个点被3条线连接function LinkP(P1,P2){// P1在P2下方,交换P1、P2if(P1.x>P2.x){[P1, P2] = [P2, P1];}// P1下方1点(x+1)先横向再纵向是否可连接。(因为起点P1不为空,所以检测其下方一点)if( LineX((P1.x+1), P1.y, P2.x) && LineY(P2.x, P1.y, P2.y) ) return true;// P1先向上侧连接,再检测该点再横向再纵向是否可连接P2。for(let j=(P1.y-1); j>=0; j--){if(gameMatrix[P1.x][j]) break;if( LineX((P1.x+1), j, P2.x) && LineY(P2.x, j, P2.y) ) return true;}// P1先向下侧连接,再检测该点再横向再纵向是否可连接P2。for(let j=(P1.y+1); j<ColMax; j++){if(gameMatrix[P1.x][j]) break;if( LineX((P1.x+1), j, P2.x) && LineY(P2.x, j, P2.y) ) return true;}// P1在P2左侧,交换P1、P2if(P1.y>P2.y){[P1, P2] = [P2, P1];}if( LineY(P1.x, (P1.y+1), P2.y) && LineX(P1.x, P2.y, P2.x) ) return true;for(let j=(P1.x-1); j>=0; j--){if(gameMatrix[j][P1.y]) break;if( LineY(j, (P1.y+1), P2.y) && LineX(j, P2.y, P2.x) ) return true;}for(let j=(P1.x+1); j<RowMax; j++){if(gameMatrix[j][P1.y]) break;if( LineY(j, (P1.y+1), P2.y) && LineX(j, P2.y, P2.x) ) return true;}return false;}// 单击检测该点(仅需比较类型)function CheckP(o,x,y){if (!gameStarted) return;if(gameMatrix[x][y] !== null){ if(null==TmpObj){ TmpObj = o;TmpObj.style.border = "2px solid blue";P[0].x = x;P[0].y = y;}else if(o!=TmpObj){TmpObj.style.border = "";P[1].x = x;P[1].y = y;// 仅比较类型索引if(gameMatrix[P[0].x][P[0].y] === gameMatrix[P[1].x][P[1].y]){if(LinkP(P[0],P[1])){gameMatrix[P[0].x][P[0].y] = null;gameMatrix[P[1].x][P[1].y] = null;TmpObj.innerHTML = "";o.innerHTML = "";TmpTime++;TmpInt--;if(!TmpInt){clearInterval(st);document.getElementById("timeleft").innerHTML = "";document.getElementById("timebar-inner").style.backgroundColor = "white";alert("恭喜完成!");document.getElementById("startButton").disabled = false;gameStarted = false;}}}TmpObj = null;}} else {if(TmpObj) TmpObj.style.border = "";TmpObj = null;}}function ShowTime(){TmpTime--; // 时间减1// 更新时间显示document.getElementById("timeleft").innerHTML = TmpTime;let percentage = Math.floor(100*TmpTime/TimeMax);document.getElementById("timebar-inner").style.width = percentage + "%";if(percentage <= 25){document.getElementById("timebar-inner").style.backgroundColor = "red";}else if(percentage <= 50){document.getElementById("timebar-inner").style.backgroundColor = "yellow";}if(!TmpTime){ // 剩余时间为0clearInterval(st); // 清除倒计时document.getElementById("timeleft").innerHTML = "";document.getElementById("timebar-inner").style.backgroundColor = "white";alert("时间到!游戏结束");// 启用"开始"按钮document.getElementById("startButton").disabled = false;gameStarted = false; // 重置游戏状态}}// 页面加载完成后,显示初始提示信息window.onload = function() {document.getElementById("timeleft").innerHTML = "请设置游戏参数并点击设置按钮";document.getElementById("timebar-inner").style.backgroundColor = "#ddd";document.getElementById("startButton").disabled = true;}</script>
</body>
</html>