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

Um sistema de partículas

Até agora, conseguimos criar uma única partícula que ressurge sempre que morre. Dessa vez, nós queremos criar um fluxo contínuo de partículas, adicionando uma nova a cada ciclo através de draw(). Podemos criar um array e colocar uma nova partícula nele a cada vez:
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();
  }
};
Se você testar e executar esse código por alguns minutos, provavelmente verá a taxa de quadros diminuir mais e mais até que o programa trave. Isso ocorre pois estamos criando cada vez mais partículas que devem ser processadas e exibidas, sem remover as existentes. Quando as partículas morrem, elas são inúteis, então podemos poupar o nosso programa de trabalho desnecessário e remover tais partículas.
Para remover itens de um array em JavaScript, podemos utilizar o método splice(), especificando o índice desejado para exclusão e o número de itens a excluir (apenas um). Faríamos isso após consultar se a partícula se encontra de fato morta:
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);
    }
  }
};
Embora o código acima execute normalmente (o programa nunca irá travar), acabamos de encontrar um pequeno problema. Sempre que manipulamos o conteúdo de um array durante a iteração através desse mesmo array, podemos encontrar problemas. Vejamos, por exemplo, o código a seguir:
for (var i = 0; i < particles.length; i++) {
  var p = particles[i];
  p.run();
  particles.push(new Particle(new PVector(width/2, 50)));
}
Este é um exemplo um pouco extremo (com lógica imperfeita), mas ele serve como prova. No caso acima, para cada partícula no array, nós adicionamos uma nova partícula nele (alterando assim o length do array). Isso resultará em um loop infinito, já que o i nunca pode incrementar após particles.length.
Apesar de remover os itens do array de partículas durante um loop não faz o programa deixar de funcionar (como faz com a adição), o problema é quase mais traiçoeiro porque não deixa evidência alguma. Para descobrir o problema primeiro temos que estabelecer um fato importante. Quando um item é removido de um array, todos os itens são deslocados um ponto para a esquerda. Observe o esquema abaixo onde a partícula C (índice 2) é removida. As partículas A e B mantêm o mesmo índice, enquanto as partículas D e E mudam de 3 e 4 para 2 e 3, respectivamente.
Vamos fingir que somos o i dando voltas através do array.
  • quando i = 0 → Checar partícula A → Não deletar
  • quando i = 1 → Checar partícula B → Não deletar
  • quando i = 2 → Checar partícula C → Deletar!
  • (partículas movidas D e E de volta das posições 3 e 4 para 2 e 3)
  • quando i = 3 → Checar partícula E → Não deletar
Notou o problema? Nunca verificamos a partícula D! Quando C foi excluído do slot #2, D moveu-se para o slot #2, mas i já seguiu para o slot #3. Isto não é um desastre, uma vez que a partícula D vai ser verificada da próxima vez. Ainda assim, a expectativa é que estamos escrevendo código para iterar através de cada item do array. Saltar um item é inaceitável.
Há uma solução simples para este problema: iterar através do array de trás para frente. Se você está colocando itens da direita para a esquerda enquanto são removidos, é impossível ignorar um item acidentalmente. Tudo o que precisamos fazer é modificar os três bits no loop por:
  for (var i = particles.length-1; i >= 0; i--) {
    var p = particles[i];
    p.run();
    if (p.isDead()) {
      particles.splice(i, 1);
    }
  }
Juntando tudo, temos isso:
Ok. Agora já fizemos duas coisas. Nós escrevemos um objeto para descrever uma Particle individual. Já descobrimos como usar arrays para gerenciar muitos objetos Particle (com a capacidade de adicionar e excluir à vontade).
Podemos parar por aqui. No entanto, um passo adicional que podemos e devemos fazer é a criação de um objeto para descrever a coleção dos próprios objetos de partículas — o objeto ParticleSystem . Isto nos permitirá remover a lógica volumosa de loop através de todas as partículas da guia principal, bem como abrir a possibilidade de ter mais de um sistema de partículas.
Se você se lembra da meta que estabelecemos no início desse capítulo, vai saber que queremos que nosso programa se pareça com isso:
var ps = new ParticleSystem(new PVector(width/2, 50));

draw = function() {
  background(0, 0, 0);
  ps.run();
};
Vamos pegar o programa que escrevemos acima e ver como adaptá-lo ao objeto ParticleSystem .
Isso é o que tínhamos antes:
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);
    }
  }
};
É assim que podemos reescrever isso como um objeto - vamos fazer do array de particles uma propriedade do objeto, criar um método addParticle para adicionar novas partículas e colocar toda a lógica de execução da partícula em 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);
      }
    }
};
Também podemos adicionar algumas novas funcionalidades para o sistema de partículas em si. Por exemplo, pode ser útil para o objeto ParticleSystem manter o registro de um ponto de origem onde as partículas são feitas. Isso se encaixa com a ideia de um sistema de partículas ser um "emissor", um lugar onde as partículas nascem e mandadas para o mundo. O ponto de origem deve ser inicializado no construtor.
var ParticleSystem = function(position) {
  this.origin = position.get();
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  this.particles.push(new Particle(this.origin));
};
Juntando tudo, temos:

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.