Contenido principal
Programación de computadoras
Curso: Programación de computadoras > Unidad 5
Lección 8: Sistemas de partículas- Introducción a sistemas de partículas
- Una sola partícula
- Desafío: hojas que caen
- Un sistema de partículas
- Desafío: burbujas de pez
- Sistemas de sistemas de partículas
- Desafío: encender fuego
- Tipos de partículas
- Desafío: caldero mágico
- Sistemas de partículas con fuerzas
- Desafío: rocas de río
- Proyecto: colonias de criaturas
© 2023 Khan AcademyTérminos de usoPolítica de privacidadAviso de cookies
Sistemas de partículas con fuerzas
Hasta ahora en esta sección, nos hemos centrado en estructurar nuestro código de una manera orientada a objetos para gestionar una colección de partículas. Tal vez te hayas dado cuenta, o tal vez no, pero durante este proceso involuntariamente tomamos un par de pasos hacia atrás de donde estábamos en las secciones anteriores. Examinemos el constructor de nuestro objeto simple de partículas Particle:
var Particle = function(position) {
this.acceleration = new PVector(0, 0.05);
this.velocity = new PVector(random(-1, 1), random(-1, 0));
this.position = new PVector(position.x, position.y);
this.timeToLive = 255.0;
};
Y ahora veamos el método
update()
:Particle.prototype.update = function(){
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.timeToLive -= 2;
};
Observa que nuestra aceleración es constante, nunca se le da un valor fuera del constructor. Un marco de trabajo mucho mejor sería seguir la segunda ley de Newton (F, with, vector, on top, equals, M, A, with, vector, on top) e incorporar el algoritmo de acumulación de fuerzas en el cual trabajamos tan duro en la sección de Fuerzas.
El primer paso es agregar un método
applyForce()
. (Recuerda, tenemos que hacer una copia de PVector
antes dividirlo entre la masa).Particle.prototype.applyForce = function(force) {
var f = force.get();
f.div(this.mass);
this.acceleration.add(f);
};
Una vez que tengamos esto, podemos agregar una línea más de código para limpiar la aceleración al final de
update()
.Particle.prototype.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
this.timeToLive -= 2.0;
};
Y por lo tanto, tenemos un objeto
Particle
al que se le puede aplicar fuerza. Ahora, ¿dónde deberíamos llamar a la función applyForce()
? ¿En qué lugar del código es apropiado aplicarle una fuerza a una partícula? La verdad del asunto es que no hay una respuesta correcta o incorrecta; realmente depende de la funcionalidad exacta y de los objetivos de un programa en particular. Aún así, podemos crear una situación genérica que es probable que se aplique a la mayoría de los casos y hacer un modelo para aplicarle fuerzas a partículas individuales en un sistema.Agregar viento
Consideremos el siguiente objetivo: aplicar una fuerza globalmente cada vez a todas las partículas por medio de draw(). Comenzaremos por aplicar una fuerza sencilla como de viento que empuja a las partículas a la derecha:
var wind = new PVector(0.4, 0);
Dijimos que siempre debe aplicarse, es decir, en
draw()
, así que veamos como está nuestra función draw()
.draw = function() {
background(168, 255, 156);
particleSystem.addParticle();
particleSystem.run();
};
Bueno, parece que tenemos un pequeño problema.
applyForce()
es un método escrito dentro del objeto Particle
, pero no tenemos ninguna referencia a las partículas individuales, solo al objeto ParticleSystem
: la variable particleSystem
.Sin embargo, como queremos que todas las partículas reciban la fuerza, podemos decidir aplicarle la fuerza al sistema de partículas y dejarlo que gestione la aplicación de la fuerza sobre todas las partículas individuales.
draw = function() {
background(168, 255, 156);
particleSystem.applyForce(wind);
particleSystem.addParticle();
particleSystem.run();
};
Por supuesto, si llamamos una nueva función en el objeto
ParticleSystem
en draw()
, bueno, tendremos que escribir esa función en el objeto ParticleSystem
. Vamos a describir el trabajo que esa función debe realizar: recibir una fuerza como un PVector
y aplicarle esa fuerza a todas las partículas.Ahora en código:
ParticleSystem.prototype.applyForce = function(f){
for(var i = 0; i < this.particles.length; i++){
this.particles[i].applyForce(f);
}
};
Casi parece una tontería escribir esta función. Lo que estamos diciendo es “aplícale una fuerza a un sistema de partículas para que el sistema pueda aplicarle esa fuerza a todas las partículas individuales”. Sin embargo, es bastante razonable. Después de todo, el objeto
ParticleSystem
está encargado de gestionar a las partículas, así que si queremos hablarle a las partículas, tenemos que hablarles a través de su gestor.Aquí está, todo junto. Juega con la fuerza del viento y ve cómo afecta el movimiento de las partículas, y observa cómo las partículas de masa diferente responden de manera diferente. Piensa en por qué es eso.
Agregar la gravedad
Ahora apliquemos una fuerza más compleja, la gravedad, que es distinta del viento porque varía con base en la masa de los objetos a los que se les aplica.
Recordemos la ecuación para calcular la fuerza de gravedad entre dos masas: F, start subscript, g, end subscript, with, vector, on top, equals, start fraction, G, m, start subscript, 1, end subscript, m, start subscript, 2, end subscript, divided by, vertical bar, vertical bar, r, vertical bar, vertical bar, squared, end fraction, r, with, hat, on top
Recuerda que cuando modelamos la fuerza de gravedad en la Tierra, la fuerza ejercida por la Tierra sobrepasa a todas las otras fuerzas gravitacionales, así que la única ecuación con la que estamos tratando es la de calcular la fuerza de gravedad entre la Tierra y el objeto. G y m, start subscript, 1, end subscript son las mismas para cada partícula y r (el radio de la Tierra) es básicamente el mismo (ya que el radio de la Tierra es tan grande en comparación con lo poco que las partículas se alejan), así que típicamente las simplificamos solo como g, la constante de gravedad en la Tierra:
Ahora, la fuerza de gravedad es solo una constante g por la masa de las partículas, multiplicada por un vector unitario en la dirección de la fuerza (que siempre será hacia abajo):
En código, eso significa que tendremos que aplicarle una fuerza de gravedad diferente a cada partícula con base en su masa. ¿Cómo podemos hacer eso? No podemos reutilizar la función
applyForce
existente, porque espera la misma fuerza para cada partícula. Podríamos considerar pasarle un parámetro que le dé instrucciones a applyForce
de multiplicarla por la masa, pero vamos a dejar sin cambio a esa función y crear una nueva, applyGravity
, que calcule la fuerza con base en un vector constante global:// Un vector constante hacia abajo, declarado hasta arriba
var gravity = new PVector(0, 0.2);
ParticleSystem.prototype.applyGravity = function() {
for(var i = 0; i < this.particles.length; i++) {
var particleG = gravity.get();
particleG.mult(this.particles[i].mass);
this.particles[i].applyForce(particleG);
}
};
Ahora, si hicimos esto correctamente, todas nuestras partículas deberían caer a la misma tasa en la siguiente simulación. Eso es porque la fuerza de gravedad está basada en multiplicar la masa, pero la aceleración está basada en dividir entre la masa, así que al final, la masa no tiene un efecto. Puede parecer tonto pasar por todo ese esfuerzo para no tener efecto, pero es importante una vez que empecemos a combinar múltiples fuerzas diferentes.
Agregar repelentes
¿Qué pasa si quisiéramos llevar este ejemplo un paso más lejos y agregar un objeto repelente que empuje a las partículas a medida que se acercan? Sería similar al objeto atractor que creamos anteriormente, solo que empuja en la dirección opuesta. Una vez más, como la gravedad, debemos calcular una fuerza diferente para cada partícula, pero en el caso del repelente, la diferencia es que el cálculo no está basado en la masa, sino en la distancia. Para la gravedad, todas nuestros vectores de fuerza tenían la misma dirección, pero para el repelente, todos los vectores de fuerza tendrán diferentes direcciones:
Como el cálculo de una fuerza repelente es un poco más complejo que el cálculo de la gravedad (¡y en última instancia podríamos querer muchos repelentes!), resolveremos este problema incorporando un nuevo objeto Repeller (repelente en inglés) en nuestro ejemplo sencillo de un sistema de partículas más gravedad. Vamos a necesitar dos adiciones importantes a nuestro código:
- Un objeto
Repeller
(declarado, inicializado y desplegado). - Una función que pase el objeto
Repeller
alParticleSystem
para que pueda aplicarle una fuerza a cada partícula.
var particleSystem = new ParticleSystem(new PVector(width/2, 50));
var repeller = new Repeller(width/2-20, height/2);
var gravity = new PVector(0, 0.1);
draw = function() {
background(214, 255, 171);
// Apply gravity force to all Particles
particleSystem.applyForce(gravity);
particleSystem.applyRepeller(repeller);
repeller.display();
particleSystem.addParticle();
particleSystem.run();
};
Hacer que un objeto
Repeller
sea visible es fácil; es un duplicado del objeto Attractor
que creamos anteriormente:var Repeller = function(x, y) {
this.position = new PVector(x, y);
};
Repeller.prototype.display = function() {
stroke(255);
strokeWeight(2);
fill(127);
ellipse(this.position.x, this.position.y, 32, 32);
};
La pregunta más difícil es, ¿cómo escribimos el método
applyRepeller()
? En lugar de pasarle un PVector
a una función como lo hacemos con applyForce()
, vamos a pasarle un objeto Repeller
a applyRepeller()
y pedirle a esa función que haga el trabajo de calcular la fuerza entre el repelente y todas las partículas:ParticleSystem.prototype.applyRepeller = function(r) {
for(var i = 0; i < this.particles.length; i++){
var p = this.particles[i];
var force = r.calculateRepelForce(p);
p.applyForce(force);
}
};
La gran diferencia aquí es que se calcula una nueva fuerza para cada partícula, porque, como vimos anteriormente, la fuerza es diferente dependiendo de las propiedades de cada partícula en relación con el repelente. Calculamos esa fuerza utilizando la función
calculateRepelForce
, que es el inverso de la función calculateAttractionForce
de nuestros Attractor
es.Repeller.prototype.calculateRepelForce = function(p) {
// Calcular el vector para la fuerza entre los objetos
var dir = PVector.sub(this.position, p.position);
// Calcular la distancia entre los objetos
var dist = dir.mag();
// Mantener la distancia dentro de un rango razonable
dist = constrain(dist, 1, 100);
// Calcular la fuerza de repulsión,
// inversamente proporcional a la distancia al cuadrado
var force = -1 * this.power/ (dist * dist);
// Normalizar el vector de dirección
// (descartar la información de la distancia)
dir.normalize();
// Calcular el vector de fuerza: dirección * magnitud
dir.mult(force);
return dir;
};
Observa cómo a lo largo de todo este proceso de agregar un repelente al medio ambiente nunca consideramos editar el propio objeto
Particle
. Una partícula en realidad no tiene por qué saber nada acerca de los detalles de su entorno; simplemente tiene que gestionar su ubicación, velocidad y aceleración, así como tener la capacidad para recibir una fuerza externa y actuar en consecuencia.Así que ahora podemos ver este ejemplo en su totalidad. Intenta cambiar la intensidad de las fuerzas que actúan sobre las partículas (la gravedad y la repelente) y ver cómo eso las cambia:
¿Quieres unirte a la conversación?
Sin publicaciones aún.