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

Se você está atrás de um filtro da Web, certifique-se que os domínios *.kastatic.org e *.kasandbox.org estão desbloqueados.

Conteúdo principal

Tipos de partículas

Agore nós vamos usar técnicas de orientação a objetos mais avançadas, como herança, então você talvez queira revisar "Herança" no curso de Introdução ao JS e voltar mais tarde. Não se preocupe, esperaremos por você!
Se sentindo confiante sobre como herança funciona? Ótimo, porque nós vamos usar herança para construir diferentes tipos de sub-objetos Particle, os quais compatilham muitas das funcionalidades mas também se diferenciam de algumas formas.
Vamos revisar uma implementação simplificada de 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);
};
Agora, nós criamos um novo tipo de objeto baseado em Particle, o qual nós vamos chamar de Confetti. Nós vamos começar com o construtor que aceita o mesmo número de argumentos, e simplesmente chamar o construtor de Particle, passando os argumentos adiante:
var Confetti = function(position) {
  Particle.call(this, position);
};
Agora, para ter certeza que os nossos objetos do tipo Confetti compartilhem os mesmos métodos dos objetos do tipo Particle, nós precisamos especificar que os seus protótipos devem ser baseados no protótipo de Particle:
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
Nesse ponto, nós temos os objetos Confetti que agem exatamente igual aos objetos Particle. O ponto da herança não é fazer cópias, mas criar novos objetos que compartilham um monte de funcionalidades mas também se diferenciam de algumas formas. Então, como um objeto Confetti se diferencia? Bem, somente baseado no nome, eles devem parecer diferentes. Os nossos objetos Particle são elipses, mas confete, pelo menos os americanos, são normalmente pequenos pedaços quadrados de papel, então ao menos nós devemos mudar o método display para mostrá-los como retângulos.:
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);
};
Aqui temos um programa com uma instância de objeto Particle e uma instância de objeto Confetti. Note que eles se comportam de forma parecida, mas são diferentes na aparência:

Adicionando rotação

Vamos fazer sofisticar um pouco. Digamos que nós queremos que o Confetti gire enquanto ele voa pelo ar. Nós poderíamos, claro, modelar a velocidade angular e aceleração como fizemos na seção Oscilações. Ao invés disso, vamos tentar uma solução rápida e simplificada.
Se as partículas estivessem no vácuo,
Nós sabemos que a partícula tem uma posição x com valor entre 0 e a largura da tela. E se nós disséssemos: quando a posição x da partícula for 0, sua rotação deve ser 0; quando a posição x for igual à largura da tela, sua rotação deve ser TWO_PI? Isso te lembra de alguma coisa? Sempre que nós temos um valor que um intervalo que nós queremos mapear em um outro intervalo, nós podemos usar a função map() do ProcessingJS para facilitar o cálculo do novo valor.
var theta = map(this.position.x, 0, width, 0, TWO_PI);
E somente para dar um pouco mais de rotação, nós podemos na verdade mapear o intervalo do ângulo de 0 a TWO_PI*2. Vamos ver como o código fica no método 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();
};
Aqui é como o resultado se parece - reinicie algumas vezes para ver o efeito da rotação:
Nós podíamos também basear o theta na posição y, que tem um efeito um pouco diferente. Por que isso? Bem, a partícula tem uma aceleração constante diferente de 0 na direção y, o que significa que a velocidade y é uma função linear do tempo, e que a posição y é na verdade uma função parabólica do tempo. Você pode ver o que isso significa nos gráficos abaixo (que foram gerados baseados no programa acima):
Isso significa que se basearmos a rotação do confete na posição y, a rotação também será parabólica. Isso não será fisicamente muito correto, já que a verdadeira rotação do confete caindo no ar é muito mais complicada, mas você pode tentar você mesmo e ver o quão realista parece! Você pode pensar em quais outras funções pareceriam mais realistas?"

Um sistema ParticleSystem diferente

Agora, o que nós realmente queremos é sermos capazes de criar vários objetos Particle e vários objetos Confetti. É para isso que nós fizemos o objeto ParticleSystem, então talvez nós possamos extendê-lo para também cuidar de objetos Confetti? Aqui está uma forma de fazer isso, copiando o que fizemos para os objetosParticle:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = []; // CARA TRISTE!
};

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();
  }
};
Note que temos dois arrays separados, um para as partículas e outro para os confetes. Cada vez que fazemos algo com o array partículas, temos que fazer o mesmo com o array confetes! Isso é irritante, porque significa que temos que escrever o dobro de código, e se mudarmos algo, temos que mudar em dois lugares. Nós poderíamos na verdade evitar a duplicidade, porque o JavaScript nos permite armazenar tipos diferentes de objetos em arrays, e porque os nossos objetos têm a mesma interface - nós estamos chamando o método run(), e os dois tipos de objeto definem essa interface. Então, vamos voltar e armazenar em um único array, nós vamos aleatoriamente decidir que tipo de objeto partícula adicionar, e vamos voltar a iterar através de um único array. Isso é muito mais simples - tudo que temos que modificar é o método 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);
    }
  }
};
Agora, tudo junto!

Quer participar da conversa?

Nenhuma postagem por enquanto.
Você entende inglês? Clique aqui para ver mais debates na versão em inglês do site da Khan Academy.