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

如何优化p5.js像素模拟器项目?性能优化方案求助

p5.js像素模拟器性能优化方案

我开发了一个p5.js像素模拟器,但运行性能很差,生成沙子几秒后就卡顿,违背了网页应用流畅的初衷。我怀疑问题出在粒子数组的各类检查操作上,现在需要全面的优化方案,不仅针对粒子数组,还要覆盖整个代码逻辑。

原项目代码

主文件代码

let alter = true;

let particles = []

function setup() {
  let canvas = createCanvas(windowWidth, windowHeight);
  frameRate(120);
}

var a = ['rgb(244,220,148)', 'rgb(236,211,140)', 'rgb(252,228,156)', 'rgb(252,220,149)', 'rgb(244,212,148)', 'rgb(228,204,132)', 'rgb(240,220,156)']

function sandColor() {
  return color(a[Math.floor(Math.random() * a.length)]);
}

function drect(c, x, y, l, w) {
  noStroke();
  fill(c);
  rect(x, y, l, w);
}

class Particle {
  constructor(p, c, x, y, s) {
    this.p = p;
    this.c = c;
    this.x = x;
    this.y = y;
    this.s = s;
  }

  draw() {
    drect(this.c, this.x, this.y, this.s, this.s);
  }
}

function check(x, y) {
  return color(get(x, y));
}

function draw() {

  drect(color(37, 150, 190), 0, 0, windowWidth, windowHeight)

  tw = 4;
  th = 4;

  for (let i = 0; i < particles.length; i++) {
    particles[i].draw()
  }

  alter = !(alter)
  if (!alter) {

    for (let i = 0; i < particles.length; i++) {
      if (particles[i].p == 's') {
        let down = false
        if (JSON.stringify(check(particles[i].x, particles[i].y + 4).levels) == '[37,150,190,255]') {
          particles[i].y += 4;
          down = true;
        }
        if (!down) {
          let r = Math.floor(Math.random() * 2);
          if (r == 0) {
            if (JSON.stringify(check(particles[i].x - 4, particles[i].y + 4).levels) == '[37,150,190,255]') {
              particles[i].y += 4;
              particles[i].x -= 4;
            } else {
              if (JSON.stringify(check(particles[i].x + 4, particles[i].y + 4).levels) == '[37,150,190,255]') {
                particles[i].y += 4;
                particles[i].x += 4;
              }
            }
          }
        }
      }
    }

    if (mouseIsPressed) {
      for (let i = 0; i < 6; i++) {
        for (let j = 0; j < 6; j++) {
          let p = 's'
          let c = sandColor()
          let x = (Math.floor(mouseX / tw)) * tw + (i * 4) - 9;
          let y = (Math.floor(mouseY / th)) * th + (j * 4) - 9;
          let s = 4;

          let sand = new Particle(p, c, x, y, s)
          let d = true;
          for (let m = 0; m < particles.length; m++) {
            if (particles[m].x == x && particles[m].y == y && particles[m].p == "s") {
              d = false;
            }
          }
          if (d) {
            drect(c, x, y, s, s)
            particles.push(sand)
          }
        }
      }
    }
  }
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

document.addEventListener('contextmenu', event => event.preventDefault());

HTML引入

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>

优化方案与实施

1. 替换画布颜色检测为网格哈希表

get(x,y)和JSON.stringify对比颜色是性能瓶颈核心,改用网格记录粒子位置:

  • 创建全局grid对象,键为x,y拼接字符串,值存储粒子类型
  • 检查位置是否为空直接查询grid,时间复杂度O(1)

2. 移除粒子添加时的全数组遍历

利用grid直接判断目标位置是否有粒子,避免O(n)的遍历检查:

  • 鼠标生成粒子时,先查grid[${x},${y}],不存在才添加新粒子

3. 优化粒子更新循环

  • 逆序遍历沙子粒子,避免下方粒子移动干扰上方粒子判断
  • 单独维护沙子数组sandParticles,减少类型判断分支开销

4. 减少重绘开销

  • 用background()替代自定义drect绘制背景,p5.js内部优化更高效
  • 批量绘制所有粒子,减少rect()调用次数

5. 常量与预计算优化

  • 定义CELL_SIZE = 4全局常量,统一粒子大小和网格单元尺寸
  • 预转换沙子颜色数组为p5.jscolor对象,避免每次调用color()转换字符串

6. 简化帧更新逻辑

去掉alter隔帧更新逻辑,每帧都处理粒子下落,保证动画流畅性


优化后完整代码

const CELL_SIZE = 4;
const BACKGROUND_COLOR = color(37, 150, 190);
// 预转换沙子颜色为p5.js color对象
const SAND_COLORS = [
  color(244,220,148),
  color(236,211,140),
  color(252,228,156),
  color(252,220,149),
  color(244,212,148),
  color(228,204,132),
  color(240,220,156)
];

let sandParticles = [];
let grid = {}; // 键格式:"x,y",值为粒子类型(如's')

function setup() {
  createCanvas(windowWidth, windowHeight);
  frameRate(120);
  noStroke();
}

function sandColor() {
  return SAND_COLORS[Math.floor(Math.random() * SAND_COLORS.length)];
}

class SandParticle {
  constructor(c, x, y) {
    this.c = c;
    this.x = x;
    this.y = y;
    // 加入网格
    grid[`${x},${y}`] = 's';
  }

  draw() {
    fill(this.c);
    rect(this.x, this.y, CELL_SIZE, CELL_SIZE);
  }

  update() {
    let downX = this.x;
    let downY = this.y + CELL_SIZE;
    let downLeftX = this.x - CELL_SIZE;
    let downRightX = this.x + CELL_SIZE;
    let moved = false;

    // 检查正下方
    if (!grid[`${downX},${downY}`]) {
      // 从网格移除旧位置
      delete grid[`${this.x},${this.y}`];
      this.y = downY;
      grid[`${this.x},${this.y}`] = 's';
      moved = true;
    } else if (!moved) {
      // 随机检查左下或右下
      const checkLeft = Math.random() < 0.5;
      if (checkLeft && !grid[`${downLeftX},${downY}`]) {
        delete grid[`${this.x},${this.y}`];
        this.x = downLeftX;
        this.y = downY;
        grid[`${this.x},${this.y}`] = 's';
      } else if (!grid[`${downRightX},${downY}`]) {
        delete grid[`${this.x},${this.y}`];
        this.x = downRightX;
        this.y = downY;
        grid[`${this.x},${this.y}`] = 's';
      }
    }
  }
}

function draw() {
  // 绘制背景
  background(BACKGROUND_COLOR);

  // 批量绘制所有沙子粒子
  sandParticles.forEach(particle => particle.draw());

  // 逆序更新沙子粒子,避免下方粒子移动干扰上方
  for (let i = sandParticles.length - 1; i >= 0; i--) {
    sandParticles[i].update();
  }

  // 鼠标生成沙子
  if (mouseIsPressed) {
    const baseX = Math.floor(mouseX / CELL_SIZE) * CELL_SIZE;
    const baseY = Math.floor(mouseY / CELL_SIZE) * CELL_SIZE;
    // 生成3x3区域的沙子(原6x6调整为3x3避免粒子过多,可按需修改)
    for (let i = -1; i <= 1; i++) {
      for (let j = -1; j <= 1; j++) {
        const x = baseX + i * CELL_SIZE;
        const y = baseY + j * CELL_SIZE;
        // 检查网格中是否已有粒子
        if (!grid[`${x},${y}`]) {
          const sand = new SandParticle(sandColor(), x, y);
          sandParticles.push(sand);
        }
      }
    }
  }
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // 窗口 resize 时清空网格和粒子(可选,也可保留但需处理超出画布的粒子)
  grid = {};
  sandParticles = [];
}

document.addEventListener('contextmenu', event => event.preventDefault());

内容的提问来源于stack exchange,提问作者user19897564

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:24:26