If you're seeing this message, it means we're having trouble loading external resources on our website.

웹 필터가 올바르게 작동하지 않으면 도메인 *. kastatic.org*.kasandbox.org이 차단되어 있는지 확인하세요.

주요 내용

입자 유형

이제부터 상속성 같은 고급 객체지향 프로그래밍 기술을 사용할 겁니다. 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을 기반으로 새로운 객체형을 생성합니다. 이것을 Confetti라 합니다. 같은 개수의 인수를 입력받는 생성자 함수를 생성하고, 이를 전달하는 Particle 생성자를 호출하게 만드는 것부터 시작합시다:
var Confetti = function(position) {
  Particle.call(this, position);
};
이제 Confetti 객체들이 Particle 객체들과 같은 메소드를 공유하게 하려면 해당 객체의 프로토타입이 Particle 프로토타입과 같은 기반을 가져가도록 만들어야 합니다.
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
이제 Particle 객체와 완전히 똑같은 방법으로 작동하는 Confetti 객체가 생겼습니다. 상속성의 요점은 복제하는 것이 아니라 많은 기능을 공유지만 새로운 기능도 가진 객체를 만드는 것입니다. 그러면 Confetti 객체는 어떻게 다를까요? Confetti는 축제 때 뿌리는 형형색색의 색종이 조각입니다. 이름만 보면 Confetti 객체는 서로 다른 모양을 하고 있을 것 같은 생각이 듭니다. 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);
};
다음은 Particle 객체 인스턴스 한 개와 Confetti 객체 인스턴스 한 개가 있는 프로그램입니다. 두 객체 인스턴스는 모두 비슷하게 행동하나 서로 다른 모양을 하고 있습니다.

회전 추가하기

약간 더 정교하게 만들어 봅시다. Confetti 입자가 공기 중에 날아다닐 때 회전하게 만든다고 생각해 보세요. 물론 진동을 배울 때 했던 것처럼 각속도와 각가속도를 모델링할 수 있지만 대신 빠르고 간편한 방법을 써보겠습니다.
입자의 x 위치는 반드시 0과 화면 너비 사이 어느 지점에 있습니다. 만약 입자의 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();
};
코드를 실행하면 이렇게 보입니다. 몇 번 다시 시작해 회전이 주는 효과를 살펴보세요:
세타를 y 위치로 잡을 수도 있지만, 효과는 약간 달라집니다. 왜 그럴까요? 입자는 y 방향으로 상수만큼 가속을 받습니다. 즉, y 속도는 시간에 대한 1차 함수이고 y 위치는 시간에 대한 2차 함수입니다. 위 프로그램을 아래에 그래프로 나타냈습니다. 아래 그래프를 보고 이 말의 의미를 이해해보세요.
즉, confetti를 회전시킬 때 y 위치를 이용했다면 회전 정도가 포물선 곡선을 따라 움직일 겁니다. 이런 움직임은 물리적으로 정확하지 않습니다. 왜냐하면, 실제 공기 중에서 떨어지는 색종이는 꽤 복잡하게 회전하기 때문입니다. 그러나 실제로 해보면 프로그램이 상당히 현실감 있게 느껴집니다! 다른 함수를 써서 좀 더 현실감 있게 만들어 볼 수 있을 것 같나요?

다양한 입자 시스템

이제 원래 목표대로 Particle 객체와 Confetti 객체를 많이 생성해야 합니다. ParticleSystem 객체를 만든 목적이 바로 여기에 있습니다. 그럼 이 객체를 확장해서 Confetti 객체를 관리할 수 있도록 만들 수 있을까요? 아래에 Particle 객체 코드를 복사해서 이를 구현하는 코드로 나타냈습니다.
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = [];
};

ParticleSystem.prototype.addParticle = function() {
    this.particles.push(new Particle(this.origin));
    this.confettis.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();
  }
};
위 코드에는 입자를 위한 배열과 색종이를 위한 배열, 총 두 개의 별개 배열이 있습니다. 입자 배열에 무언가를 하면, 똑같이 색종이 조각 배열에도 해야 합니다! 그럼 코드를 두 번 써야 하고 한 번 수정해야 할 때마다 두 번 고쳐야 하므로 번거롭습니다. 사실 이런 번거로운 작업은 피할 수 있습니다. 왜냐하면, 자바스크립트에서는 서로 다른 유형의 객체를 배열에 저장할 수 있고 객체의 인터페이스가 동일하기 때문입니다. 위 코드는 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);
    }
  }
};
코드를 다 합치면 이렇게 됩니다!