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

Ha webszűrőt használsz, győződj meg róla, hogy a *.kastatic.org és a *.kasandbox.org nincsenek blokkolva.

Fő tartalom

Részecskerendszerek erőkkel

Ebben a fejezetben arra koncentráltunk, hogyan tudjuk a programot objektumorientált szemléletben strukturálni részecskék vezérléséhez. Akár észrevetted, akár nem, eközben akaratlanul is kicsit visszaléptünk az előző fejezetekhez képest. Vizsgáljuk meg az egyszerű Particle objektum konstruktorát:
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;
};
És most nézzük meg az update() metódust:
Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.timeToLive -= 2;
};
Figyeld meg, hogy a gyorsulás konstans, a konstruktorban beállítottuk és azután többet nem változtattuk meg! Ennél sokkal jobb keretrendszer lenne, ha Newton második törvényét követnénk (F=MA), és beépítenénk az erők felhalmozódásának algoritmusát, amit annyi munkával dolgoztunk ki az Erők fejezetben.
Az első lépés egy applyForce() metódus hozzáadása. (Emlékszel? Mielőtt elosztjuk a tömeggel, másolatot kell készíteni a PVectorról!)
Particle.prototype.applyForce = function(force) {
  var f = force.get();
  f.div(this.mass);
  this.acceleration.add(f);
};
Ha ez megvan, akkor még hozzá kell adni egy sort a gyorsulás törléséhez az update() végén.
Particle.prototype.update = function() {
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  this.acceleration.mult(0);
  this.timeToLive -= 2.0;
};
Így most már van egy Particle objektumunk, amiben képesek vagyunk erőt aktiválni. Hol kellene meghívni az applyForce() függvényt? A kód melyik pontján kell az erőt aktiválni a részecskére? Megint az a helyzet, hogy erre nincs helyes vagy helytelen válasz, ez függ az elvárt működéstől és a program céljától. Ettől függetlenül vegyünk egy olyan általános helyzetet, amely a legtöbb esetre illeszthető, és alakítsunk ki egy modellt, amely egyedi részecskékre aktiválja az erőt egy rendszerben.

Szél hozzáadása

Tegyük fel, az a célunk, hogy globálisan szeretnénk aktiválni az erőt minden részecskére minden ciklusban a draw() függvényben. Egy egyszerű szelet szimuláló erő aktiválásával kezdjük, ami a részecskéket jobbra tolja:
var wind = new PVector(0.4, 0);
Azt mondtuk, hogy minden ciklusban aktiválni kell az erőt – például a draw()-ban, – úgyhogy vizsgáljuk meg, hogy néz ki jelenleg a draw() függvényünk.
draw = function() {
  background(168, 255, 156);
  particleSystem.addParticle();
  particleSystem.run();
};
Úgy tűnik, van egy kis gondunk. Az applyForce() a Particle objektumon belüli metódus, és nincs hozzáférésünk az egyedi részecskékhez, csak a ParticleSystem objektumhoz a particleSystem változón keresztül.
Mivel minden részecskén szeretnénk aktiválni az erőt, megtehetjük azt, hogy a részecskerendszerre aktiváljuk az erőt, és hagyjuk, hogy a részecskerendszer vezesse azt át az egyedi részecskékre.
draw = function() {
  background(168, 255, 156);
  particleSystem.applyForce(wind);
  particleSystem.addParticle();
  particleSystem.run();
};
Persze, ha meghívjuk a ParticleSystem egy új függvényét a draw()-ban, akkor azt a ParticleSystem objektumban definiálni is kell. Határozzuk meg, milyen feladatot kell annak a függvénynek elvégeznie: kap egy erőt egy PVectorban, és azt az összes részecskére aktiválja.
A kódban ez így néz ki:
ParticleSystem.prototype.applyForce = function(f){
  for(var i = 0; i < this.particles.length; i++){
    this.particles[i].applyForce(f);
  }
};
Majdhogynem butának tűnik ez a függvény. Azt mondjuk: „aktiváld az erőt egy részecskerendszerre, hogy azután a részecskerendszer aktiválja azt az erőt az egyedi részecskékre.” Pedig ez teljesen észszerű. Végül is a ParticleSystem objektum felelőssége a részecskék vezérlése, úgyhogy ha a részecskékkel akarunk kommunikálni, akkor csak az őket felügyelő objektumon keresztül tehetjük azt meg.
Íme az egész összerakva. Játssz egy kicsit a szélerővel, és nézd, hogyan változtatja meg a részecskék mozgását! Figyeld meg, hogy a különböző tömegű részecskék eltérő módon reagálnak. Gondold végig, miért van ez így!

Gravitáció hozzáadása

Most aktiváljunk egy összetettebb erőt, a gravitációt, ami abban különbözik a széltől, hogy a tárgyak tömegétől függően változik.
Idézzük fel a két különböző tömegű tárgy közötti gravitációs erő kiszámításának egyenletét:  Fg=Gm1m2||r||2r^
Emlékszel? Amikor a földi gravitációt modelleztük, a Föld által gyakorolt erő elnyomott minden más gravitációs erőt, úgyhogy egyetlen egyenletünk marad, ami a Föld és a tárgy közötti gravitációs erő kiszámításához kell. G és m1 minden részecskére ugyanaz, és r (a Földtől mért sugár) is alapjában véve ugyanaz (hiszen a Föld sugara hatalmas ahhoz képest, hogy a részecskék mennyire távolodnak el tőle). úgyhogy ezeket egyszerűsítjük a g földi gravitációra:
g=Gm1||r||2
Ebben a képletben a gravitációs erő egy konstans, g szorozva a részecske tömegével, szorozva az erő irányába mutató egységvektorral (ami mindig lefelé mutat):
Fg=gm2r^
A programban mindez azt jelenti, hogy minden részecskére a tömegtől függően más gravitációs erőt kell aktiválni. Hogyan tudjuk ezt megtenni? Nem tudjuk használni a már meglévő applyForce() függvényt, mert az minden részecske esetében ugyanazt az erőt tételezi fel. Gondolkodhatunk egy olyan megoldásban, hogy az applyForce() függvénynek átadunk egy paramétert, ami arra utasítja, hogy szorozzon a tömeggel, de inkább hagyjuk békén ezt a függvényt, és készítsünk egy újat, applyGravity(), ami egy globális konstans vektor alapján számítja ki az erőt:
// Konstans lefelé mutató vektor, melyet fent definiálunk
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);
    }
};
Ha mindent jól csináltunk, akkor az alábbi szimulációban minden részecske azonos sebességgel esik le. Ez azért van, mert a gravitációs erő azon alapul, hogy szorzunk a tömeggel, a gyorsulás esetében viszont osztunk a tömeggel, úgyhogy a végeredmény szempontjából a tömegnek nincs hatása. Butaságnak tűnik ennyit vesződni anélkül, hogy a végén bármilyen hatást látnánk, de fontossá válik, amikor különböző erőket kombinálunk. (A programban található megjegyzések: 1. sor: Dan Shiffman, natureofcode.com példája alapján; 32. sor: Metódus a megjelenítésre.)

Taszító hozzáadása

Mi lenne, ha ezt a példát egy lépéssel tovább vinnénk, és hozzáadnánk egy taszító objektumot, ami eltolja a részecskéket, ha azok a közelébe kerülnek? Hasonlítani fog a korábban készített Attractor objektumhoz, csak ez a részecskéket az ellenkező irányba fogja tolni. A gravitációhoz hasonlóan minden részecskéhez más erőt kell kiszámolni, de a taszító esetében a számítás nem a tömegen, hanem a távolságon fog alapulni. A gravitáció esetében az összes erővektor ugyanabba az irányba mutatott, a taszítónál az erővektoroknak más-más lesz az irányuk:
Gravitációs erő: az összes vektor iránya ugyanaz
Taszítóerő: az irányvektorok különbözőek
Mivel a taszítóerő számítása egy kicsit bonyolultabb, mint a gravitációs erőé (és később a taszítók számát akár meg is akarjuk sokszorozni!), ezt a problémát úgy fogjuk megoldani, hogy a gravitációval kibővített egyszerű részecskerendszerünkbe beépítünk egy új Repeller (taszító) objektumot. Két lényeges változtatást kell hozzáadni a programhoz:
  1. Repeller objektum (deklarálva, inicializálva, és megjelenítve).
  2. Függvény, ami a Repeller objektumot átadja a ParticleSystemnek, hogy azután minden benne lévő részecskére aktiválni tudja az erőt.
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);

  // Aktiváld a gravitációt minden részecskére
  particleSystem.applyForce(gravity);
  particleSystem.applyRepeller(repeller);
  repeller.display();
  particleSystem.addParticle();
  particleSystem.run();
};
A Repeller objektum megrajzolása könnyű; ez a korbban elkészített Attractor objektum másolata:
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);
};
A bonyolultabb kérdés az, hogy hogyan írjuk meg az applyRepeller() metódust? Ahelyett, hogy az applyForce()hoz hasonlóan egy PVectort adnánk át, egy Repeller objektumot fogunk átadni az applyRepeller()-nek, és a függvénnyel számíttatjuk ki a taszító és a részecske között fellépő erőt:
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);
  }
};
Itt az a nagy különbség, hogy minden részecskére új erőt számolunk ki, mert mint ahogy azt már korábban megállapítottuk, az erő a taszító és a részecske tulajdonságaitól függően eltérő lesz. Ezt az erőt a calculateRepelForce() függvénnyel fogjuk kiszámítani, ami a calculateAttractionForce() függvény inverze az Attractor osztályból.
Repeller.prototype.calculateRepelForce = function(p) {
  // Az objektumok közötti erővektor kiszámítása
  var dir = PVector.sub(this.position, p.position); 
  // Az objektumok közötti távolság kiszámítása
  var dist = dir.mag();
  // Távolság észszerű határok között tartása
  dist = constrain(dist, 1, 100);    
  // Taszítóerő számítása,
  // ami fordítottan arányos a távolságnégyzettel
  var force = -1 * this.power/ (dist * dist);     
  // Irányvektor normalizálása
  // (távolság eldobása)
  dir.normalize();
  // Erővektor kiszámítása: irány · hossz
  dir.mult(force);                                  
  return dir;
};
Figyeld meg, hogy a taszító környezetbe illesztésének folyamata során egyszer sem merült fel a Particle objektum módosítása! A részecskének semmit sem kell tudnia a környezet részleteiről; neki csak a helyzetét, sebességét és gyorsulását kell karbantartania, valamint képesnek kell lennie külső erőhatás fogadására és feldolgozására.
Most megnézhetjük a teljes példát. Próbáld meg változtatni a részecskékre ható erőket – a gravitációt és a taszítást – és figyeld meg a változásokat! (A programban található megjegyzések: 1. sor: Dan Shiffman, natureofcode.com példája alapján; 20. sor: Erő irányának számítása. 23. sor: Az objektumok közötti távolság; 24. sor: Irányvektor normalizálása 26. Távolság észszerű határok között tartása; 28. sor: A taszítóerő számítása fordítottan arányos a távolsággal; 30. sor: Erővektor kiszámítása ---> hossz · irány.)

Szeretnél részt venni a beszélgetésben?

Még nincs hozzászólás.
Tudsz angolul? Kattints ide, ha meg szeretnéd nézni, milyen beszélgetések folynak a Khan Academy angol nyelvű oldalán.