尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【生命游戏】从零实现一个可交互的Web前端模拟器

【生命游戏】从零实现一个可交互的Web前端模拟器 1. 生命游戏简介与实现思路生命游戏Game of Life是英国数学家约翰·康威在1970年提出的一种细胞自动机。它由一个二维网格组成每个格子代表一个细胞细胞有两种状态存活或死亡。游戏的规则非常简单孤单死亡如果细胞的邻居小于等于1个则该细胞在下一次状态将死亡拥挤死亡如果细胞的邻居在4个及以上则该细胞在下一次状态将死亡稳定如果细胞的邻居为2个或3个则下一次状态为稳定存活复活如果某位置原无细胞存活而该位置的邻居为2个或3个则该位置将复活一个细胞这些简单的规则却能产生极其复杂和有趣的行为模式这也是生命游戏经久不衰的魅力所在。实现一个Web版的生命游戏模拟器我们需要考虑以下几个核心部分网格系统用二维数组表示细胞状态渲染系统将细胞状态可视化展示规则引擎计算每个细胞的下一个状态交互系统允许用户与模拟器互动控制面板提供开始/暂停、速度调节等功能2. 搭建基础HTML结构我们先从最基础的HTML结构开始。创建一个index.html文件包含以下内容!DOCTYPE html html head meta charsetutf-8 title生命游戏模拟器/title link relstylesheet hrefstyle.css /head body div classcontainer h1生命游戏模拟器/h1 div classgame-container div classgame-intro h2游戏规则/h2 p生命游戏为1970年由英国数学家J. H. Conway所提出.../p !-- 规则说明 -- /div div classgame-grid idgrid/div div classgame-controls div classsettings div classsetting label网格大小:/label input typenumber idgrid-size min10 max100 value30 /div div classsetting label演化速度(ms):/label input typenumber idspeed min50 max2000 value200 /div /div div classbuttons button idstart开始/button button idpause暂停/button button idreset重置/button button idrandom随机填充/button button idnext下一步/button /div /div /div /div script srcscript.js/script /body /html这个结构包含了三个主要部分游戏规则说明区游戏网格展示区控制面板区3. 使用CSS美化界面接下来我们添加一些基本的样式。创建style.css文件* { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; } body { background-color: #f0f0f0; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; } h1 { text-align: center; margin: 20px 0; color: #333; } .game-container { display: flex; gap: 20px; margin-top: 20px; } .game-intro { flex: 1; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .game-grid { flex: 2; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); display: grid; grid-template-columns: repeat(30, 1fr); grid-template-rows: repeat(30, 1fr); aspect-ratio: 1/1; } .cell { border: 1px solid #eee; cursor: pointer; } .cell.alive { background-color: #4CAF50; } .game-controls { flex: 1; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .settings { margin-bottom: 20px; } .setting { margin-bottom: 15px; } .setting label { display: block; margin-bottom: 5px; font-weight: bold; } .setting input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } .buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } .buttons button { padding: 10px; border: none; border-radius: 4px; background-color: #4CAF50; color: white; cursor: pointer; transition: background-color 0.3s; } .buttons button:hover { background-color: #45a049; } .buttons button:active { background-color: #3e8e41; }4. JavaScript核心逻辑实现现在我们来编写JavaScript代码。创建script.js文件// 游戏状态 const gameState { gridSize: 30, speed: 200, isRunning: false, grid: [], nextGrid: [], timer: null }; // 初始化游戏 function initGame() { const gridElement document.getElementById(grid); gridElement.innerHTML ; gridElement.style.gridTemplateColumns repeat(${gameState.gridSize}, 1fr); gridElement.style.gridTemplateRows repeat(${gameState.gridSize}, 1fr); // 初始化网格数组 gameState.grid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); gameState.nextGrid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); // 创建网格元素 for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { const cell document.createElement(div); cell.className cell; cell.dataset.row i; cell.dataset.col j; cell.addEventListener(click, toggleCell); gridElement.appendChild(cell); } } } // 切换细胞状态 function toggleCell(e) { if (gameState.isRunning) return; const row parseInt(e.target.dataset.row); const col parseInt(e.target.dataset.col); gameState.grid[row][col] gameState.grid[row][col] ? 0 : 1; e.target.classList.toggle(alive); } // 计算下一代 function nextGeneration() { // 计算每个细胞的下一个状态 for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { const neighbors countNeighbors(i, j); // 应用生命游戏规则 if (gameState.grid[i][j] 1) { gameState.nextGrid[i][j] (neighbors 2 || neighbors 3) ? 1 : 0; } else { gameState.nextGrid[i][j] (neighbors 3) ? 1 : 0; } } } // 更新网格和UI updateGrid(); } // 计算邻居数量 function countNeighbors(row, col) { let count 0; for (let i -1; i 1; i) { for (let j -1; j 1; j) { if (i 0 j 0) continue; const newRow row i; const newCol col j; if (newRow 0 newRow gameState.gridSize newCol 0 newCol gameState.gridSize) { count gameState.grid[newRow][newCol]; } } } return count; } // 更新网格显示 function updateGrid() { const cells document.querySelectorAll(.cell); for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { gameState.grid[i][j] gameState.nextGrid[i][j]; const index i * gameState.gridSize j; if (gameState.grid[i][j] 1) { cells[index].classList.add(alive); } else { cells[index].classList.remove(alive); } } } } // 随机填充网格 function randomFill() { const cells document.querySelectorAll(.cell); for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { gameState.grid[i][j] Math.random() 0.7 ? 1 : 0; const index i * gameState.gridSize j; if (gameState.grid[i][j] 1) { cells[index].classList.add(alive); } else { cells[index].classList.remove(alive); } } } } // 重置游戏 function resetGame() { stopGame(); initGame(); } // 开始游戏 function startGame() { if (gameState.isRunning) return; gameState.isRunning true; gameState.timer setInterval(nextGeneration, gameState.speed); document.getElementById(start).disabled true; document.getElementById(pause).disabled false; } // 暂停游戏 function stopGame() { if (!gameState.isRunning) return; gameState.isRunning false; clearInterval(gameState.timer); document.getElementById(start).disabled false; document.getElementById(pause).disabled true; } // 更新游戏速度 function updateSpeed() { gameState.speed parseInt(document.getElementById(speed).value); if (gameState.isRunning) { stopGame(); startGame(); } } // 更新网格大小 function updateGridSize() { const newSize parseInt(document.getElementById(grid-size).value); if (newSize 10 newSize 100) { gameState.gridSize newSize; resetGame(); } } // 初始化事件监听 function initEvents() { document.getElementById(start).addEventListener(click, startGame); document.getElementById(pause).addEventListener(click, stopGame); document.getElementById(reset).addEventListener(click, resetGame); document.getElementById(random).addEventListener(click, randomFill); document.getElementById(next).addEventListener(click, nextGeneration); document.getElementById(speed).addEventListener(change, updateSpeed); document.getElementById(grid-size).addEventListener(change, updateGridSize); } // 初始化应用 function initApp() { initEvents(); initGame(); } // 启动应用 window.onload initApp;5. 性能优化与Canvas实现虽然上面的实现已经可以工作但当网格变大时使用DOM元素渲染性能会下降。我们可以改用Canvas来提升性能// 替换原来的game-grid div为canvas div classgame-area canvas idgame-canvas/canvas /div // 更新CSS .game-area { flex: 2; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); position: relative; } #game-canvas { width: 100%; height: 100%; display: block; } // 修改JavaScript部分 const canvas document.getElementById(game-canvas); const ctx canvas.getContext(2d); let cellSize 0; function initGame() { // 设置canvas尺寸 const container document.querySelector(.game-area); canvas.width container.clientWidth; canvas.height container.clientHeight; cellSize canvas.width / gameState.gridSize; // 初始化网格数组 gameState.grid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); gameState.nextGrid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); // 绘制网格 drawGrid(); // 添加点击事件 canvas.addEventListener(click, handleCanvasClick); } function drawGrid() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制背景 ctx.fillStyle white; ctx.fillRect(0, 0, canvas.width, canvas.height); // 绘制网格线 ctx.strokeStyle #eee; ctx.lineWidth 0.5; for (let i 0; i gameState.gridSize; i) { // 垂直线 ctx.beginPath(); ctx.moveTo(i * cellSize, 0); ctx.lineTo(i * cellSize, canvas.height); ctx.stroke(); // 水平线 ctx.beginPath(); ctx.moveTo(0, i * cellSize); ctx.lineTo(canvas.width, i * cellSize); ctx.stroke(); } // 绘制存活细胞 ctx.fillStyle #4CAF50; for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { if (gameState.grid[i][j] 1) { ctx.fillRect( j * cellSize 1, i * cellSize 1, cellSize - 2, cellSize - 2 ); } } } } function handleCanvasClick(e) { if (gameState.isRunning) return; const rect canvas.getBoundingClientRect(); const x e.clientX - rect.left; const y e.clientY - rect.top; const col Math.floor(x / cellSize); const row Math.floor(y / cellSize); if (row 0 row gameState.gridSize col 0 col gameState.gridSize) { gameState.grid[row][col] gameState.grid[row][col] ? 0 : 1; drawGrid(); } } function updateGrid() { for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { gameState.grid[i][j] gameState.nextGrid[i][j]; } } drawGrid(); }6. 添加更多功能与优化现在我们的生命游戏模拟器已经基本完成但还可以添加一些有用的功能模式选择预定义一些有趣的初始模式颜色主题允许用户自定义细胞颜色网格显示切换可以隐藏网格线历史记录记录演化历史导出/导入保存和加载特定模式// 在控制面板添加新按钮 div classbuttons !-- 原有按钮... -- button idpatterns模式/button button idtheme切换主题/button button idtoggle-grid隐藏网格/button /div // 添加模式选择对话框 div idpattern-modal classmodal div classmodal-content span classclosetimes;/span h3选择模式/h3 div classpattern-list div classpattern>
返回列表