이제 상속성과 같은 좀 더 심화된 객체지향 프로그래밍 기술을 사용할 것입니다. JS란?에서 "상속성"에 대해 복습하고 다시 돌아오세요! 걱정하지 마세요. 다 보실 때까지 기다리겠습니다!
상속성이 동작하는 방법을 잘 아시겠나요? 잘 알아야 하는 이유는 상속성을 이용하여 서로 다른 유형의 Particle 하위 객체를 생성할 것이기 때문입니다. 이 하위 객체는 중요한 기능을 공유하지만 어떤 면에서는 다릅니다.
단순화된 Particle의 구현을 복습해 봅시다:
var Particle = function(position) {
  this.acceleration = new PVector(0, 0.05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = position.get();
};

Particle.prototype.run = function() {
  this.update();
  this.display();
};

Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
};

Particle.prototype.display = function() {
  fill(127, 127, 127);
  ellipse(this.position.x, this.position.y, 12, 12);
};
다음으로 Particle을 기반으로 새로운 object 유형을 생성합니다. 이것을 Confetti라 합니다. 인수로 동일한 수를 입력받은 후 그 수를 전달하는 것과 함께 단순히 Particle 생성자를 호출하는 생성자 함수부터 시작할 것입니다.
var Confetti = function(position) {
  Particle.call(this, position);
};
이제 확실하게 Confetti 객체들이 Particle 객체들과 동일한 메소드를 공유하도록 하기 위해 해당 객체의 프로토타입이 Particle 프로토타입을 기반으로 해야 한다는 것을 기술하여야 합니다.
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
이 시점에서 Particle object와 정확하게 동일한 방법으로 동작하는 Confetti object가 생깁니다. 상속성의 요점은 복제를 하는 것이 아니라 많은 기능을 공유할뿐만 아니라 어떤 점에서는 다른 새로운 object를 만드는 것입니다. 그러면 Confetti object는 어떻게 다른가요? 이름에서와 같이 이것은 다르게 보여야 할 것 같습니다. Particle 객체들은 타원입니다. 그러나 색종이 조각(confetti)은 일반적으로 직사각형 모양의 작은 조각입니다. 그러므로 display 메소드를 변경해서 직사각형으로 보이도록 해야 합니다.
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255, this.timeToLive);
  stroke(0, 0, 0, this.timeToLive);
  strokeWeight(2);
  rect(0, 0, 12, 12);
};
Here's a program with one Particle object instance and one Confetti object instance. Notice they behave similarly but differ in their appearance:

회전 추가

약간 더 정교하게 만들겠습니다. Confetti 입자가 공기 중에 날아다닐 때 회전하도록 하고 싶다고 가정해 보겠습니다. 물론 진동에 관해 배울 때 했던 것처럼 각속도와 각가속도를 모델링할 수 있습니다. 대신에 빠르고 간편한 방법을 시도해 보겠습니다.
입자가 진공 상태에 있으면,
입자가 0과 윈도우 너비 사이의 어딘가 x 위치를 갖는다는 것을 압니다. 입자의 x 위치가 0이면 회전은 0이어야 합니다. 입자의 x 위치가 너비와 같으면 회전은 TWO_PI와 같아야 한다고 말하면 어떻게 될까요? 익숙하지 않나요? 한 범위의 값을 또 다른 범위에 대응하고자 할 때마다 ProcessingJS의 map() 함수를 이용하여 새로운 값을 쉽게 계산할 수 있습니다.
var theta = map(this.position.x, 0, width, 0, TWO_PI);
그리고 좀 더 많이 회전하게 하려면 실제로 각의 범위를 0에서 TWO_PI*2까지로 대응할 수 있습니다. 이 코드가 display() 메소드에 얼마나 적합한 지를 살펴 보겠습니다.
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255);
  stroke(0, 0, 0);
  strokeWeight(2);
  pushMatrix();
  translate(this.position.x, this.position.y);
  var theta = map(this.position.x, 0, width, 0, TWO_PI * 2);
  rotate(theta);
  rect(0, 0, 12, 12);
  popMatrix();
};
Here's how that looks - restart it a few time to see the effect of the rotation:
또한 y 위치 위에 세타를 기초로 할 수 있습니다. 이것의 효과는 좀 다릅니다. 왜 그럴까요? 입자는 y 방향으로 0이 아닌 상수 가속도가 있습니다. 즉, y 속도는 시간의 선형 함수이고 y 위치는 실제로는 시간에 대한 포물선 함수입니다. 아래 그래프에서 어떤 의미인지를 알 수 있습니다. (아래 그래프는 위 프로그램을 기반으로 생성되었습니다.)
즉, y 위치 위의 색종이 회전을 기반으로 했다면 회전도 포물선의 모양이 될 것입니다. 이것은 너무 물리적으로 정확하지 않습니다. 왜냐하면 공기 중에서 떨어지는 색종이의 실제 회전은 꽤 복잡하기 때문입니다. 그렇지만 시도 해보고 실제로는 어떤지를 살펴 보기 바랍니다! 좀 더 사실적으로 보일 수 있는 다른 함수를 생각해 볼 수 있나요?

다양한 입자 시스템

사실 하고자 했던 것은 많은 Particle 객체들과 많은 Confetti 객체들을 생성하는 것입니다. 그것이 ParticleSystem 객체를 만든 목적입니다. 아마도 이를 확대하여 Confetti 객체들을 기록할 수 있을까요? 다음은 그렇게 할 수 있는 방법중 하나입니다. Particle 객체에서 했던 것을 복사합니다:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = []; // SAD FACE!
};

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

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
  }
for (var i = this.confettis.length-1; i >= 0; i--) {
    var p = this.confettis[i]; p.run();
  }
};
입자를 위한 배열과 색종이를 위한 배열, 두 개의 별개의 배열이 있다는 것에 주의하기 바랍니다. 입자 배열에 뭔가를 할 때마다 색종이 조각 배열에도 그것을 수행해야 합니다! 성가신 방법이죠? 왜냐하면 많은 코드만큼 두 번 작성해야 한다는 것을 의미하기 때문입니다. 그리고 뭔가를 수정했으면 두 곳에서 수정해야 합니다. 실제로는 이러한 중복을 피할 수 있습니다. 왜냐하면 JavaScript에서 서로 다른 유형의 객체를 배열에 저장할 수 있기 때문입니다. 그리고 객체의 인터페이스가 동일하기 때문에 - run() 메소드를 호출하고 두 개의 유형의 객체가 해당 인터페이스를 정의합니다. 그러므로 단지 단일 배열을 저장하도록 하겠습니다. 임의적으로 어떤 유형의 입자 객체를 추가할지를 결정하고 단일 배열에 대해 반복을 수행합니다. 이것은 훨씬 더 간단한 수정입니다. 결국 수정할 것은 addParticle 메소드입니다.
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  var r = random(1);
  if (r < 0.5) {
    this.particles.push(new Particle(this.origin));
  } else {
    this.particles.push(new Confetti(this.origin));
  }
};

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);
    }
  }
};
이제 모두 함께 결합해 보았습니다!