You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

像素/沙盒模拟器性能优化求助:沙粒放置后程序卡顿

像素模拟器沙粒卡顿问题优化方案

问题描述

我开发了一款像素模拟器,目前仅包含沙元素,正在调试沙粒物理效果以作为其他元素的基础。但发现放置沙粒一段时间后程序运行明显卡顿,现寻求代码优化方案或改进思路。

核心优化思路

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:09:32