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

Si estás detrás de un filtro de páginas web, por favor asegúrate de que los dominios *.kastatic.org y *.kasandbox.org estén desbloqueados.

Contenido principal

Tipos de partículas

Ahora vamos a utilizar técnicas de programación orientada a objetos más avanzadas, como herencia, así que puede ser que quieras revisar "Herencia"  en el curso de Introducción a JS  y regresar. No te precupes, ¡te esperamos!
¿Te sientes bien acerca de cómo funciona la herencia? Qué bien, porque vamos a utilizar la herencia para hacer diferentes tipos de subobjetos de Particle, los cuales comparten gran parte de la misma funcionalidad pero también difieren en aspectos clave.
Vamos a revisar una implementación 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);
};
A continuación, creamos un nuevo tipo de objeto basado en Particle, al que llamaremos Confetti. Vamos a empezar con una función constructora que acepte el mismo número de argumentos y simplemente llame al constructor de Particle, pasándole esos argumentos:
var Confetti = function(position) {
  Particle.call(this, position);
};
Ahora, para asegurarnos de que nuestros objetos Confetti compartan los mismos métodos que los objetos Particle, tenemos que especificar que su prototipo debe basarse en el prototipo de Particle:
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
En este punto, tenemos objetos Confetti que actúan exactamente del mismo modo que los objetos Particle. El punto de la herencia no es hacer duplicados, es hacer nuevos objetos que compartan mucha de la funcionalidad pero también difieran de algún modo. Entonces, ¿cómo es diferente un objeto Confetti? Bueno, solo con base en el nombre, parece que debería verse diferente. Nuestros objetos Particle son elipses, pero el confeti generalmente son pequeños pedacitos cuadrados de papel, así que por lo menos, deberíamos cambiar el método display para mostrarlos como rectá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);
};
Aquí está un programa con una instancia del objeto Particle y una del objeto Confetti. Observa que se comportan de un modo parecido pero difieren en apariencia:

Agregar rotación

Hagamos esto un poco más sofisticado. Digamos que queremos hacer que la partícula Confetti rote mientras vuela por el aire. Podríamos, por supuesto, modelar la velocidad angular y la aceleración como lo hicimos en la sección de Oscilaciones. En vez de eso, intentaremos una solución rápida y sucia.
Sabemos que una partícula tiene una posición x entre 0 y el ancho de la ventana. ¿Qué pasa si dijéramos: cuando la posición x de la partícula sea 0, su rotación debe ser 0; cuando su posición x sea igual al ancho, la rotación debe ser igual a TWO_PI? ¿Te suena conocido? Cada vez que tengamos un valor con un rango que queramos mapear a otro rango, podemos usar la función map() de ProcessingJS para calcular fácilmente el nuevo valor.
var theta = map(this.position.x, 0, width, 0, TWO_PI);
Y para darle un poco más de giro, en realidad podemos mapear el rango del ángulo de 0 a TWO_PI * 2. Veamos cómo encaja este código en el 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();
};
Aquí está cómo se ve eso. Reinícialo unas cuantas veces para ver el efecto de la rotación:
También podríamos basar a theta en la posición y, lo cual tiene un efecto un poco diferente. ¿Por qué sucede esto? Bueno, la partícula tiene una aceleración constante distinta de cero en la dirección y, lo que significa que la velocidad en y es una función lineal del tiempo, y que la posición en y es en realidad una función parabólica del tiempo. Puedes ver lo que eso significa en las siguientes gráficas (que fueron generadas con base en el programa anterior):
Eso significa que si basamos la rotación del confeti en la posición en y, la rotación también será parabólica. Esto no será físicamente muy exacto ya que la rotación real del confeti cayendo a través del aire es bastante complicada, pero ¡pruébalo y ve qué tan realista se ve! ¿Puedes pensar en otras funciones que puedan parecer incluso más realistas?

Un ParticleSystem diverso

Ahora, lo que realmente queremos es ser capaces de crear muchos objetos Particle y muchos objetos Confetti. Para eso es que hicimos el objeto ParticleSystem, así que ¿tal vez podamos solamente extenderlo para también llevar un registro de los objetos Confetti? Aquí hay una manera como podríamos hacer eso, copiando lo que hicimos para los objetos 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();
  }
};
Observa que tenemos dos arreglos separados, uno para las partículas y otro para el confeti. Cada vez que le hagamos algo al arreglo de las partículas, ¡tenemos que hacérselo al arreglo de confeti! Eso es molesto, porque significa que tenemos que escribir el doble de código y, si cambiamos algo, tenemos que cambiarlo en dos lugares. En realidad podríamos evitar esta duplicación, porque tenemos permitido almacenar objetos de diferentes tipos en arreglos en JavaScript, y como nuestros objetos tienen la misma interfaz: estamos llamando el método run() y ambos tipos de objetos definen esa interfaz. Así que, regresaremos a solo almacenar un arreglo, vamos a decidir aleatoriamente qué tipo de partícula agregar y regresaremos a iterar a través del único arreglo. Este es un cambio mucho más simple. Lo que terminaremos modificando es el 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);
    }
  }
};
¡Todo junto ahora!

¿Quieres unirte a la conversación?

  • Avatar blobby green style para el usuario Copa Mamani Lex Edson
    :)
    angleMode = "radians";

    var Particle = function(position) {
    this.acceleration = new PVector(0, -0.05);
    this.velocity = new PVector(random(-1, 1), random(0, -1));
    this.position = position.get();
    this.timeToLive = 255.0;
    };

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

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

    Particle.prototype.display = function() {
    stroke(0, 0, 0, this.timeToLive);
    strokeWeight(2);
    fill(255, 0, 0, this.timeToLive);
    ellipse(this.position.x, this.position.y, 12, 12);
    };

    Particle.prototype.isDead = function(){
    if (this.timeToLive < 0) {
    return true;
    } else {
    return false;
    }
    };

    var Smoke = function(position){
    Particle.call(this, position);
    };

    Smoke.prototype = Object.create(Particle.prototype);

    Smoke.prototype.constructor = Smoke;

    Smoke.prototype.display = function() {
    noStroke();
    fill(245, 235, 235, this.timeToLive);
    ellipse(this.position.x, this.position.y, 12, 12);
    };

    var Star = function(position){
    Particle.call(this, position);
    this.size = random(10, 10);
    };

    Star.prototype = Object.create(Particle.prototype);

    Star.prototype.constructor = Star;

    Star.prototype.display = function() {
    image(getImage("cute/Star"),this.position.x, this.position.y, this.size, this.size);
    };

    var ParticleSystem = function(position) {
    this.origin = position.get();
    this.particles = [];
    };

    ParticleSystem.prototype.addParticle = function() {
    if (random(1)<0.5) {
    this.particles.push(new Smoke(this.origin));
    } else {
    this.particles.push(new Star(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);
    }
    }
    };

    var particleSystem = new ParticleSystem(new PVector(width/2, 280));

    draw = function() {
    background(72, 7, 105);
    particleSystem.addParticle();
    particleSystem.run();

    // The magical cauldron
    fill(36, 36, 36);
    var cauldronX1 = 150;
    var cauldronX2 = 250;
    var cauldronY = 285;
    bezier(cauldronX1, cauldronY,
    cauldronX1-100, cauldronY+145,
    cauldronX2+100, cauldronY+145,
    cauldronX2, cauldronY);
    };
    (4 votos)
    Avatar Default Khan Academy avatar para el usuario
¿Sabes inglés? Haz clic aquí para ver más discusiones en el sitio en inglés de Khan Academy.