像素/沙盒模拟器性能优化求助:沙粒放置后程序卡顿
像素模拟器沙粒卡顿问题优化方案
问题描述
我开发了一款像素模拟器,目前仅包含沙元素,正在调试沙粒物理效果以作为其他元素的基础。但发现放置沙粒一段时间后程序运行明显卡顿,现寻求代码优化方案或改进思路。
核心优化思路
1. 网格索引替代全局遍历碰撞检测
原代码中check函数每次遍历所有粒子判断位置是否被占据,粒子数量增多时会导致O(n²)的时间复杂度,这是卡顿的核心原因。改用二维网格数组记录每个网格位置的粒子引用,检测时直接通过坐标索引访问,时间复杂度降为O(1)。
2. 调整粒子遍历顺序
沙粒是向下运动的,从画布底部向上遍历粒子,避免同一帧内粒子重复移动导致的逻辑冲突,同时减少不必要的重复检测。
3. 优化绘制性能
- 将粒子绘制从逐个调用
rect改为批量遍历绘制,减少API调用次数; - 避免在
draw函数内重复定义常量(如网格大小),提升运行效率。
4. 简化粒子添加逻辑
添加新沙粒时,直接通过网格索引判断目标位置是否被占据,替代原有的全局数组遍历,避免额外性能开销。
5. 修复未声明变量问题
原代码中大量未用let/const声明的变量会成为全局变量,引发性能损耗和潜在bug,全部补全变量声明。
修改后的完整代码
JavaScript 代码
let alter = true; let particles = []; // 网格数组,记录每个位置的粒子 let grid = []; const CELL_SIZE = 4; // 统一网格大小 function setup() { createCanvas(windowWidth, windowHeight); frameRate(120); // 初始化网格 initGrid(); } function initGrid() { const cols = Math.ceil(width / CELL_SIZE); const rows = Math.ceil(height / CELL_SIZE); grid = new Array(cols).fill(null).map(() => new Array(rows).fill(null)); } function sandColor() { const r = Math.floor(Math.random() * (255 - 230 + 1) + 230); const g = Math.floor(Math.random() * (230 - 200 + 1) + 200); const b = Math.floor(Math.random() * (150 - 130 + 1) + 130); return color(r, g, b); } class Particle { constructor(type, color, x, y) { this.type = type; this.color = color; this.x = x; this.y = y; // 更新网格索引 this.col = Math.floor(x / CELL_SIZE); this.row = Math.floor(y / CELL_SIZE); } // 更新粒子位置并同步网格 updatePosition(newX, newY) { // 清除旧位置的网格记录 grid[this.col][this.row] = null; this.x = newX; this.y = newY; this.col = Math.floor(newX / CELL_SIZE); this.row = Math.floor(newY / CELL_SIZE); // 设置新位置的网格记录 grid[this.col][this.row] = this; } } // 检查指定位置是否有粒子 function isPositionEmpty(x, y) { const col = Math.floor(x / CELL_SIZE); const row = Math.floor(y / CELL_SIZE); // 边界判断 if (col < 0 || col >= grid.length || row < 0 || row >= grid[0].length) { return false; } return grid[col][row] === null; } function draw() { // 绘制背景 background(37, 150, 190); // 批量绘制所有粒子 noStroke(); particles.forEach(particle => { fill(particle.color); rect(particle.x, particle.y, CELL_SIZE, CELL_SIZE); }); alter = !alter; if (!alter) { // 从下往上遍历粒子,避免沙粒重复移动 for (let i = particles.length - 1; i >= 0; i--) { const particle = particles[i]; if (particle.type === 's') { let movedDown = false; const downY = particle.y + CELL_SIZE; // 检查正下方是否为空 if (isPositionEmpty(particle.x, downY)) { particle.updatePosition(particle.x, downY); movedDown = true; } if (!movedDown) { // 随机选择左下方或右下方 const direction = Math.random() > 0.5 ? -1 : 1; const sideX = particle.x + direction * CELL_SIZE; const sideY = downY; if (isPositionEmpty(sideX, sideY)) { particle.updatePosition(sideX, sideY); } else { // 尝试反方向 const oppositeX = particle.x - direction * CELL_SIZE; if (isPositionEmpty(oppositeX, sideY)) { particle.updatePosition(oppositeX, sideY); } } } } } // 处理鼠标添加沙粒 if (mouseIsPressed) { const baseCol = Math.floor(mouseX / CELL_SIZE); const baseRow = Math.floor(mouseY / CELL_SIZE); // 生成3x3范围的沙粒 for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { const col = baseCol + i; const row = baseRow + j; if (col >= 0 && col < grid.length && row >=0 && row < grid[0].length && grid[col][row] === null) { const x = col * CELL_SIZE; const y = row * CELL_SIZE; const sand = new Particle('s', sandColor(), x, y); grid[col][row] = sand; particles.push(sand); } } } } } } function windowResized() { resizeCanvas(windowWidth, windowHeight); initGrid(); // 重新同步粒子到新网格 particles.forEach(particle => { particle.col = Math.floor(particle.x / CELL_SIZE); particle.row = Math.floor(particle.y / CELL_SIZE); if (particle.col < grid.length && particle.row < grid[0].length) { grid[particle.col][particle.row] = particle; } }); } // 禁用右键菜单 document.addEventListener('contextmenu', event => event.preventDefault());
HTML 代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
内容的提问来源于stack exchange,提问作者bread
相关产品推荐
相关产品推荐

