函数 .push 继续替换与数组中最后一个元素相同的所有元素

function .push keep replacing all elements same as last one in array

我正在尝试通过在 p5js 中使用矢量历史数组来制作移动物体的轨迹。
但在推送更新后的矢量后,this.history 中的所有元素都被替换为最后一个。

我在这里搜索了一些问题,但仍然无法理解。

let ppp = [];

function setup() {
  createCanvas(400, 400);
  
  for (let i = 0; i < 3; i++) {
    let p = new Particle();
    ppp.push(p);
  }
}

function draw() {
  background(220);
  for (let i = 0; i < ppp.length; i++) {
    ppp[i].display();
    ppp[i].update();
  }
}

function Particle() {
  this.pv = createVector(random(width), random(height));
  this.history = [];

  let rndV = p5.Vector.random2D(); 
  this.spdV = rndV.mult(random(1, 3));
  
  this.update = function() {
    this.pv.add(this.spdV);
    this.history.push(this.pv); // replace all vector element
    
    console.log(this.history);
  }
  
  this.display = function() {
    fill(30);
    ellipse(this.pv.x, this.pv.y, 30);
    
    for (let i = 0; i < this.history.length; i++) {
      let trail = this.history[i];
      ellipse(trail.x, trail.y, 10);
    }
  }
}

或者如果您认为我的方法不是最好的,我很乐意听取任何建议^^

谢谢,

这在 javascript 中可能有点误导:

this.history.push(this.pv);

您正在推送对同一 this.pv pre-allocated 向量的引用

您正在尝试做的是:

this.history.push(this.pv.copy());

您正在为一个全新的 p5.Vector 对象分配内存,其中 x,y 坐标从 this.pv 复制而来(使用 copy() 方法)

演示:

let ppp = [];

function setup() {
  createCanvas(400, 400);
  
  for (let i = 0; i < 3; i++) {
    let p = new Particle();
    ppp.push(p);
  }
}

function draw() {
  background(220);
  for (let i = 0; i < ppp.length; i++) {
    ppp[i].display();
    ppp[i].update();
  }
}

function Particle() {
  this.pv = createVector(random(width), random(height));
  this.history = [];

  let rndV = p5.Vector.random2D(); 
  this.spdV = rndV.mult(random(1, 3));
  
  this.update = function() {
    this.pv.add(this.spdV);
    this.history.push(this.pv.copy()); // replace all vector element
    
    //console.log(this.history);
  }
  
  this.display = function() {
    fill(30);
    ellipse(this.pv.x, this.pv.y, 30);
    
    for (let i = 0; i < this.history.length; i++) {
      let trail = this.history[i];
      ellipse(trail.x, trail.y, 10);
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

请记住,草图 运行s 这将使用越来越多的内存。

如果只需要渲染轨迹而不需要矢量数据用于其他任何事情,您可以立即渲染到单独的图形层(使用 createGraphics()),这将节省长 运行:

let ppp = [];
let trailsLayer;

function setup() {
  createCanvas(400, 400);
  // make a new graphics layer for trails
  trailsLayer = createGraphics(400, 400);
  trailsLayer.noStroke();
  trailsLayer.fill(0);
  
  for (let i = 0; i < 3; i++) {
    let p = new Particle();
    ppp.push(p);
  }
}

function draw() {
  background(220);
  // render the trails layer
  image(trailsLayer, 0, 0);
  
  for (let i = 0; i < ppp.length; i++) {
    ppp[i].display();
    ppp[i].update();
  }
}

function Particle() {
  this.pv = createVector(random(width), random(height));
  
  let rndV = p5.Vector.random2D(); 
  this.spdV = rndV.mult(random(1, 3));
  
  this.update = function() {
    this.pv.add(this.spdV);
    // render trails
    trailsLayer.ellipse(this.pv.x, this.pv.y, 10);
  }
  
  this.display = function() {
    fill(30);
    ellipse(this.pv.x, this.pv.y, 30);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

Update 以淡化轨迹,您可以尝试类似 Moving On Curves 的示例。注意 noStroke();setup()

中调用
fill(0, 2);
rect(0, 0, width, height);

渲染淡出 (alpha=2) 矩形?

你可以做类似的事情:

let ppp = [];
let trailsLayer;

function setup() {
  createCanvas(400, 400);
  background(255);
  // make a new graphics layer for trails
  trailsLayer = createGraphics(400, 400);
  trailsLayer.noStroke();
  // set translucent fill for fade effect
  trailsLayer.fill(255, 25);
  
  for (let i = 0; i < 3; i++) {
    let p = new Particle();
    ppp.push(p);
  }
}

function draw() {
  background(220);
  // fade out trail layer by rendering a faded rectangle each frame 
  trailsLayer.rect(0, 0, width, height);
  // render the trails layer
  image(trailsLayer, 0, 0);
  
  for (let i = 0; i < ppp.length; i++) {
    ppp[i].display();
    ppp[i].update();
  }
}

function Particle() {
  this.pv = createVector(random(width), random(height));
  
  let rndV = p5.Vector.random2D(); 
  this.spdV = rndV.mult(random(1, 3));
  
  this.update = function() {
    this.pv.add(this.spdV);
    // reset at bounds
    if(this.pv.x > width){
      this.pv.x = 0;
    }
    if(this.pv.y > height){
      this.pv.y = 0;
    }
    if(this.pv.x < 0){
      this.pv.x = width;
    }
    if(this.pv.y < 0){
      this.pv.y = height;
    }
    // render trails
    trailsLayer.push();
    trailsLayer.fill(0);
    trailsLayer.noStroke();
    trailsLayer.ellipse(this.pv.x, this.pv.y, 10);
    trailsLayer.pop();
  }
  
  this.display = function() {
    fill(30);
    ellipse(this.pv.x, this.pv.y, 30);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

为了完整起见,这里有一个使用 history 向量数组的版本,但将其限制为一组大小并重复使用分配一次的向量(而不是连续创建新向量):

let ppp = [];

function setup() {
  createCanvas(400, 400);
  noStroke();
  
  for (let i = 0; i < 3; i++) {
    let p = new Particle();
    ppp.push(p);
  }
}

function draw() {
  background(220);
  for (let i = 0; i < ppp.length; i++) {
    ppp[i].display();
    ppp[i].update();
  }
}

function Particle() {
  this.pv = createVector(random(width), random(height));
  // limit number of history vectors
  this.historySize = 24;
  this.history = new Array(this.historySize);
  // pre-allocate all vectors
  for(let i = 0 ; i < this.historySize; i++){
      this.history[i] = this.pv.copy();
  }

  let rndV = p5.Vector.random2D(); 
  this.spdV = rndV.mult(random(1, 6));
  
  this.update = function() {
    this.pv.add(this.spdV);
    this.resetBounds();
    this.updateHistory();
  };
  
  this.updateHistory = function(){
    // shift values back to front by 1 (loop from last to 2nd index)
    for(let i = this.historySize -1; i > 0; i--){
      // copy previous to current values (re-using existing vectors)
      this.history[i].set(this.history[i-1].x, this.history[i-1].y);
    }
    // finally, update the first element
    this.history[0].set(this.pv.x, this.pv.y);
  };
  
  this.resetBounds = function(){
    // reset at bounds
    if(this.pv.x > width){
      this.pv.x = 0;
    }
    if(this.pv.y > height){
      this.pv.y = 0;
    }
    if(this.pv.x < 0){
      this.pv.x = width;
    }
    if(this.pv.y < 0){
      this.pv.y = height;
    }
  };
  
  this.display = function() {
    fill(30);
    ellipse(this.pv.x, this.pv.y, 30);
    
    for (let i = 0; i < this.historySize; i++) {
      let trail = this.history[i];
      // fade trails
      let alpha = map(i, 0, this.historySize -1, 192, 0);
      fill(30, alpha);
      ellipse(trail.x, trail.y, 10);
    }
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>