4

I've been working on this project for a few days now, it's a pixel simulator, and the only element I have right now is sand. I'm in the process of fine tuning the physics for sand as a base for other elements. I've noticed something though, the program starts to slow down significantly after placing sand for a while, and I'm looking for any improvements or things I could do differently in my code to make it run better. Any help is appreciated. Thanks!

Code for the main file:

let alter = true;

particles = []

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

function sandColor() {
  r = Math.floor(Math.random() * (255 - 230 + 1) + 230)
  g = Math.floor(Math.random() * (230 - 200 + 1) + 230)
  b = Math.floor(Math.random() * (150 - 130 + 1) + 130)
  return color(r, g, b)
}

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) {
  found = false;
  let p;
  for (i in particles) {
    if (particles[i].x == x && particles[i].y == y) {
      found = true;
      p = particles[i].p;
    }
  }
  if (found) {
    return [found, p]
  } else {
    return [found];
  }
}

function draw() {

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

  tw = 4;
  th = 4;

  for (var i in particles) {
    particles[i].draw()
  }

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

    for (i in particles) {
      if (particles[i].p == 's') {
        let down = false
        if (!check(particles[i].x, particles[i].y + 4)[0]) {
          particles[i].y += 4;
          down = true;
        }
        if (!down) {
          let r = Math.floor(Math.random() * 2) + 1;
          if (r == 1) {
            if (!check(particles[i].x - 4, particles[i].y + 4)) {
              particles[i].y += 4;
              particles[i].x -= 4;
            } else {
              if (!check(particles[i].x + 4, particles[i].y + 4)) {
                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 (var m in particles) {
            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());
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>

Hosted example and full source code here: https://replit.com/@LoganStottle202/pixsim?v=1

ggorlen
  • 44,755
  • 7
  • 76
  • 106
bread
  • 160
  • 1
  • 10
  • `check` allocates an array just to compute the return value--very expensive. I'd refactor that to a primitive for starters. Are you sure you want to let the sand fall infinitely? If so, destroy sand that's offscreen. `for (i in particles) {` creates global variables and should be a `for..of` or traditional loop. `check` shouldn't have to loop over all particles to find an `x`/`y`-- use a keyed object or 2d array to represent the screen or particles. – ggorlen Sep 01 '22 at 14:53
  • @ggorlen I've switched to for loops, but I don't fully understand what you meant in the first sentence. Could you give a code example? You could put it in a new post. Thank you for the help – bread Sep 01 '22 at 20:47
  • `[]` is an expensive object allocation that hits memory. But then it's thrown out immediately, and there are O(n) of these allocations in the loop that increase as the particles increase. Bad news. Adjust the logic so you only need to return one primitive value from the call. The loop inside `check` is probably most of the pain though, so there are a number of potential issues here. – ggorlen Sep 01 '22 at 21:06
  • if you edit the code, keep in mind that might invalidate existing answers, and please keep the runnable snippet. It's so much easier to be able to run it without having to deal with other websites. Thanks. – ggorlen Sep 01 '22 at 23:03
  • 1
    Seemingly reposted to [Optimizing p5.js project / sandbox/pixel simulator](https://stackoverflow.com/questions/73572753/optimizing-p5-js-project-sandbox-pixel-simulator) – ggorlen Sep 02 '22 at 05:48
  • I was at school when I posted the other one. I didn't have access to this email address – bread Sep 02 '22 at 11:11

1 Answers1

0

a normal for() loop is 6 to 11 times faster than a for( in ) loop

Here’s your current code:

function check(x, y) {
  found = false;
  let p;
  for (i in particles) {
    if (particles[i].x == x && particles[i].y == y) {
      found = true;
      p = particles[i].p;
    }
  }
  if (found) {
    return [found, p] 
  } else {
    return [found];
  }
}

and I would also remove some unnecessary variables (they can be replaced with a return)

One more thing I would change, in javascript if you remove brackets from a for() loop or if() statement, it will only do the next statement. And that’s something you can do here

I would do this:

function check(x, y) {
  for (let i = 0; i < particles.length; i++)
    if (particles[i].x == x && particles[i].y == y)
      return [true, particles[i].p];
  return [false];
}

benefits:

  • Faster
  • Easy to read
  • Less text, smaller file
ITDW
  • 148
  • 8
  • I noticed this was accepted--did it actually improve the speed significantly? There's still a nested particles loop here in `check` that is probably most of the slowness, and other issues weren't addressed, so I'm surprised if this gives significant gains. – ggorlen Sep 01 '22 at 21:07
  • @ggorlen I'm new to stackoverflow, it did give a decent speed increase but it definitely wasn't the only thing that could have helped. I also kinda changed the code for the whole project and redid some things. Updated code + hosted example is here: https://replit.com/@LoganStottle202/pixsim?v=1 If you have any more suggestions that would be great, if you want to message me on discord that would be even better so you could show me everything wrong you see. I'm bread#7194 on discord, thanks for the help – bread Sep 01 '22 at 21:20
  • I'll take a look if I have time later on, all I have time for at the moment is suggestions. It takes awhile to write a serious, complete answer where I've verified all my claims. It's a good question, so I'm sure there'll be additional ideas showing up in this thread eventually. – ggorlen Sep 01 '22 at 21:26
  • I know you personally bread… – ITDW Sep 02 '22 at 02:10
  • Also, I only did your check function to keep the answer based on what you thought was the problem. But yes ggorlen is right there are many other things that can be improved – ITDW Sep 02 '22 at 02:14