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

如果你被网页过滤器挡住,请确保域名*.kastatic.org*.kasandbox.org 没有被阻止.

主要内容

粒子类型

现在,我们将使用更高级的面向对象编程技术(如继承),因此你可能想要复习 JS 入门课程里的“继承” 然后再回来。别担心,我们会等你的!
感觉对于继承(inheritance)是如何运行的理解得还不错?很好,因为我们将使用继承来制作不同类型的 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 对象拥有相同的方法,我们需要指定 Confetti 的原型(prototype)应该基于 Particle 原型:
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
到目前为止,我们有和Particle对象以完全相同的方式运作的 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 方向上有一个非零的恒定加速度(acceleration),这意味着 y 速度(velocity)是时间的线性函数,y 位置(position)实际上是时间的抛物线函数。你可以在下面的图表中看到这意味着什么(这是在上述程序的基础上生成的):
三张位置图 (一条向下的线)、速度 (一条从上到下的直线) 和加速度 (一条直线)。
这意味着如果我们以 y 位置为基准写confetti的旋转,那也是抛物线性的。这不会非常准确,因为真实的纸屑飘舞的运动非常复杂,你自己试试看它是有多真实吧!你可以想到其它能够运行得更加真实的方法吗?

多样化的粒子系统(ParticleSystem)

我们真正想要做到的是创建许多 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();
  }
};
请注意,我们有两个单独的数组,一个用于粒子,另一个用于彩色纸屑。每次对粒子数组做一些事情,都要对彩色纸屑数组做同样的事情!这很烦人,因为这意味着我们必须编写两倍多的代码,如果改变一些东西,就必须在两个地方改变它。实际上我们可以避免这种重复,因为 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);
    }
  }
};
现在所有都合在一起!

想加入讨论吗?

尚无帖子。
你会英语吗?单击此处查看更多可汗学院英文版的讨论.