优化p5.js项目/沙盒/像素模拟器



我已经开始了一个pixel-sim项目,但它运行不好。我可以做些什么来优化它?我很有信心,问题出在所有涉及粒子阵列的检查中。我正在寻找任何解决方案,做任何不同的事情,或任何一般的优化,不仅仅是关于粒子数组,而是整个代码。我这个项目的目标是创建一个像素模拟网络应用程序,如果它在几秒钟的产卵后不能很好地运行,它就无法实现网络应用程序的目的。感谢您的帮助。

主文件:

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());
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>

托管示例:https://pixsim.loganstottle202.repl.co/如果这不起作用,代码就在这里:https://replit.com/@LoganStottle202/pixsim?v=1

不幸的是,我没有时间提供一个很好的详细的可运行示例,比如你有趣的草图。

希望我能给你几个方向:

  • 正如p5get()参考文献提到的:;使用get(x,y)获取单个像素的颜色很容易,但不如直接从pixels[]获取数据快&";。您可以在帧开始时调用loadPixels()一次,然后在d = pixelDensity()处使用pixels[4 * (x * d + i) + ((y * d + j) * width * d + )]
  • CCD_ 6和比较字符串对于许多粒子来说可能会变得计算繁重。考虑将粒子的状态(碰撞、移动、静止/不活动等)标记为可以直接比较的整数值(例如,再次使用描述性名称的const整数状态)。(然后,如果愿意,可以重复使用此状态以不同的颜色渲染粒子)。如果你真的想使用颜色,你可以通过对不同状态(例如255、254、253等)使用阿尔法值来简化,在这些状态下,你很难注意到,你可以只与.levels[3]进行比较,避免JSON.stringify()和字符串比较。我建议使用全局或静态粒子状态const变量,该变量具有描述性名称,是粒子实例的基本state属性
  • 你可以把你的图形分成两个";层";使用CCD_ 12:;活动/活动";活动粒子(它甚至可以是全局p5图形缓冲区)。另一层可用于渲染已沉降的静态粒子。然后,您可以使用image(yourStaticSandLayer, 0, 0);重用/重新绘制该层,从而消除对静态粒子的rect()调用(因为它们已经绘制/缓存到层中)

(此外,您可以将sandColor()简化为简单的return random(a);,因为p5的random()也可以为您在数组中随机选择一个项目。这不会加快任何速度,只是简化代码:使其更容易读取/维护)

更新以下是基于Particle类并使用p5.Graphics:的基本演示

let particles = [];
let activeParticlesLayer;
let inactiveParticlesLayer;
function setup() {
createCanvas(300, 150);

activeParticlesLayer = createGraphics(width, height);
inactiveParticlesLayer = createGraphics(width, height);

for(let i = 0;  i < 10; i++){
particles.push(new Particle(color(0, 192, 0), random(width), random(90), 10, 10));
}
}
function draw() {
// clear only active layer: don't clear inactive layer
activeParticlesLayer.background(255);

// for debugging only: count active particles
let numActiveParticles = 0;
for(let i = 0;  i < 10; i++){
let p = particles[i];
// only update particles if they're active
if(p.isActive){
// if the current particle collided (with stage bottom for now)
// then make it inactive (change colour as a visual cue) and cache to inactive layer
// replace this with pixels[] collision logic
if(p.y > height - p.s){
p.isActive = false;
p.c = color(0, 128, 0);
// particle is inactive: cache into inactive layer (which isn't cleared)
p.draw(inactiveParticlesLayer); 
}
// otherwise our particle is active: update position and render to active layer
else{
p.y += 1;
p.draw(activeParticlesLayer);
numActiveParticles++;
}  
}
}

// display layers
image(activeParticlesLayer, 0, 0);
image(inactiveParticlesLayer, 0, 0);
text("active particles: " + numActiveParticles, 10, 15);
}
class Particle {
constructor(c, x, y, s) {
this.isActive = true;
this.c = c;
this.x = x;
this.y = y;
this.s = s;
}

update(){
if(this.y > height - this.s){
this.isActive = false;
this.c = color(0, 128, 0);
}else{
this.y += 1;  
}
}
draw(buffer) {
buffer.fill(this.c);
buffer.rect(this.x, this.y, this.s, this.s);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>

注意:以上删除了原始演示中的一些功能,例如使用颜色检查碰撞。为了简单起见,只有极少数粒子只与画布底部碰撞(并且不堆叠)。希望这足以说明isActive状态的变化,并且在状态从活动变为非活动时只缓存rect()调用一次。

此外,我建议查看开发人员工具,并对代码进行分析,以准确地发现代码中较慢的部分,并将重点放在这些部分(而不是优化那些影响很小或没有影响的代码,但可能会使代码的可读性降低/在未来更难维护)

我昨天看到你有GitHub链接,并创建了一个PR来实现微小的性能改进。

它们大多是小的改进。我认为您需要改用基于像素的系统来进行性能检修,但如果您坚持这种方法,这些改进应该会有所帮助。

  1. 删除重新声明

twth这样的常量就在draw()循环内部声明,不必要地在每个循环中重新声明。我把它们放在draw()函数之外。

  1. 更改循环逻辑

您有2个for (i in particles)循环。我没有把它循环两次,而是把逻辑放在一个循环中,所以循环的次数是循环的一半。此外,为了提高性能,我将for...in循环更改为for循环。

  1. 去除死粒子

我看到只有添加粒子的代码,但没有删除它们。我添加了一个检查,看看粒子是否已降至windowHeight以下,并将它们从particles阵列中移除,以防止阵列无限增长。

希望这能有所帮助。

总的来说,我认为George对如何进一步重新构建和改进代码提供了很好的见解。希望这能帮助你创建一个有趣的项目。我很想看更多!

最新更新