지금까지 만든 입자 체계에서 한 입자가 생성되고 소멸되면 다시 생성되는 기능을 구현했습니다. 이제는 draw()를 사용해 매 순환마다 새로운 입자를 추가하면서 연속적인 입자의 흐름을 만들어봅시다. 먼저 배열을 생성하여 매번 새로운 입자를 생성합니다:
var particles = [];
draw = function() {
  background(133, 173, 242);
  particles.push(new Particle(new PVector(width/2, 50)));

  for (var i = 0; i < particles.length; i++) {
    var p = particles[i];
    p.run();
  }
};
이 코드를 몇 분간 실행시켜보면 프레임 속도가 점점 느려지다가 결국엔 멈출 겁니다. 왜냐하면 처리하고 보여줘야 할 입자를 점점 더 많이 생성하는데 제거하지는 않기 때문입니다. 일단 입자가 죽으면 쓸모가 없으므로 그런 입자를 프로그램에서 제거해 불필요한 작업을 하지 않도록 만들어야 합니다.
자바스크립트에서 배열 안의 항목을 제거하려면 splice() 메소드를 사용하여 삭제하고자 하는 인덱스와 삭제하는 항목의 수(오직 하나)를 지정하면 됩니다. 그전에 먼저 입자가 실제로 죽었는지 확인한 후에 이 과정을 거쳐야 합니다.
var particles = [];
draw = function() {
  background(133, 173, 242);
  particles.push(new Particle(new PVector(width/2, 50)));

  for (var i = 0; i < particles.length; i++) {
    var p = particles[i];
    p.run();
    if (p.isDead()) {
      particles.splice(i, 1);
    }
  }
};
위 코드가 문제없이 잘 실행되더라도 (그리고 중간에 멈추지 않더라도) 꽤 큰 문제가 발생합니다. 배열에서 반복문을 실행하는 와중에 해당 배열의 내용을 조작하면 문제가 생길 수도 있습니다. 예시로 다음 코드를 살펴봅시다:
for (var i = 0; i < particles.length; i++) {
  var p = particles[i];
  p.run();
  particles.push(new Particle(new PVector(width/2, 50)));
}
다소 극단적이고 논리에 결함도 있는 예시지만 문제점을 정확하게 보여줍니다. 위 경우는 배열의 각 입자에 대해 새로운 입자를 추가합니다. 그 결과 배열의 길이, 즉 length가 바뀝니다. 이렇게 되면 i가 절대로 particles.length보다 커질 수 없으므로 무한 반복이 발생하게 됩니다.
반복문을 수행하며 입자 배열에서 어떤 항목을 제거하면 추가할 때와 마찬가지로 프로그램을 강제 종료하지는 않지만 아무런 증거가 남지 않는다는 점은 더 심각한 문제입니다. 문제점을 발견하기 위해선 먼저 가장 중요한 사실 하나를 짚고 넘어가야 합니다. 항목이 array에서 제거되면 모든 항목은 왼쪽으로 한 칸씩 이동합니다. 아래에 C 입자 (인덱스 2)가 제거된 그림을 보세요. A와 B 입자의 인덱스는 똑같지만 D와 E 입자의 인덱스는 각각 3과 4에서 2와 3으로 이동됩니다.
여러분이 배열 안의 i 이고 반복 실행 중이라고 상상해봅시다.
  • i = 0이면 → A 입자를 확인합니다. → 삭제하지 않습니다.
  • i = 1이면 → B 입자를 확인합니다. → 삭제하지 않습니다.
  • i = 2이면 → C 입자를 확인합니다. → 삭제합니다!
  • (D와 E입자를 각각 슬롯 3과 4에서 슬롯 2와 3으로 되돌립니다.)
  • i = 3이면 → E 입자를 확인합니다. → 삭제하지 않습니다.
문제를 알아채셨나요? D 입자는 한 번도 확인하지 않았습니다! C가 슬롯 #2에서 제거되면 D가 슬롯 #2로 가지만 i는 슬롯 #3으로 이동합니다. D 입자는 다음번에 확인하기 때문에 당장 큰일 나지는 않습니다. 그래도 원래 목표는 코드가 모든 항목을 확인하는 것이었습니다. 항목을 건너뛰는 것은 허용할 수 없습니다.
이 문제는 간단히 해결할 수 있습니다. 배열을 반대 방향으로 순환시키면 됩니다. 항목을 제거할 때 오른쪽에서 왼쪽으로 항목을 밀어 넣으면 실수로 항목을 건너뛰는 것이 불가능합니다. 지금 해야할 일은 for 반목문에서 세 부분을 수정하는 것입니다:
  for (var i = particles.length-1; i >= 0; i--) {
    var p = particles[i];
    p.run();
    if (p.isDead()) {
      particles.splice(i, 1);
    }
  }
모두 결합하면 다음과 같습니다:
좋습니다. 이제 두 가지를 했습니다. 개별 Particle을 묘사하는 객체를 만들었고 배열을 이용하여 여러 Particle 객체를 관리(마음대로 추가하고 삭제하는 기능)하는 방법을 알아냈습니다.
여기에서 끝낼 수도 있지만 한 단계 더 나아가야 합니다. 입자 객체의 집합체 그 자체를 다루는 객체인, ParticleSystem 객체를 만들어 봅시다. 이 객체를 이용하면 메인 탭에서 입자들을 다루는 복잡한 반복문을 없앨 뿐만 아니라 하나 이상의 입자 시스템을 만들 수 있습니다.
이 단원 처음에 세웠던 목표들을 생각해보면 프로그램이 다음과 같아야 합니다:
var ps = new ParticleSystem(new PVector(width/2, 50));

draw = function() {
  background(0, 0, 0);
  ps.run();
};
위에 작성한 프로그램을 어떻게 ParticleSystem 객체로 표현할 수 있을지 알아봅시다.
다음은 전에 작성한 코드입니다. 굵게 표시된 부분을 주의 깊게 보기 바랍니다:
var particles = [];

draw = function() {
  background(133, 173, 242);
  particles.push(new Particle(new PVector(width/2, 50)));

  for (var i = particles.length-1; i >= 0; i--) {
    var p = particles[i];
    p.run();
    if (p.isDead()) {
      particles.splice(i, 1);
    }
  }
};
여기서 이 코드를 객체로 다시 쓰는 방법을 볼 수 있습니다. Particle 배열을 객체의 프로퍼티로 만들고, 새로운 입자를 추가하기 위해 래퍼 메소드인 addParticle을 만들rh난 후 입자의 움직임에 관한 모든 코드를 run에 넣습니다:
var ParticleSystem = function() {
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  this.particles.push(new Particle());
};

ParticleSystem.prototype.run = function() {
  for (var i = this.particles.length-1; i >= 0; i--) {
      var p = this.particles[i];
      p.run();
      if (p.isDead()) {
        this.particles.splice(i, 1);
      }
    }
};
또한, 입자 시스템 자체에 새로운 특성을 추가할 수 있습니다. 예를 들어 ParticleSystem 객체가 입자가 만들어진 원점을 기억하게 만들 수도 있습니다. “이미터”가 입자를 생성하고 내보내는 개념과 잘 맞아 떨어집니다. 원점은 생성자에서 초기화되어야 합니다.
var ParticleSystem = function(position) {
  this.origin = position.get();
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  this.particles.push(new Particle(this.origin));
};
지금까지 만든 코드는 다음과 같습니다: