Fő tartalom
Programozás
Tantárgy/kurzus: Programozás > 5. témakör
8. lecke: Részecskerendszerek- Bevezetés a részecskerendszerekbe
- Egyetlen részecske
- Feladat: Levélhullás
- Részecskerendszer
- Feladat: Halbuborékok
- Részecskerendszerek rendszere
- Feladat: Tűzcsiholás
- Részecsketípusok
- Feladat: Varázsüst
- Részecskerendszerek erőkkel
- Feladat: Folyami sziklák
- Projekt: Lénykolóniák
© 2023 Khan AcademyFelhasználási feltételekAdatkezelési tájékoztatóSüti figyelmeztetés
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 ( ), é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 PVector
ró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 PVector
ban, é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:
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. és minden részecskére ugyanaz, és (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:
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):
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:
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:Repeller
objektum (deklarálva, inicializálva, és megjelenítve).- Függvény, ami a
Repeller
objektumot átadja aParticleSystem
nek, 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 PVector
t 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.