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észecskerendszer
Eljutottunk odáig, hogy definiáltunk egy részecskét, ami újraszületett, miután meghalt. Most részecskék folyamatos áramát szeretnénk megalkotni úgy, hogy minden egyes ciklusban hozzáadunk egyet a
draw()
metóduson keresztül. Tehetnénk azt is, hogy létrehozunk egy tömböt, amihez minden ciklusban hozzáadunk (push) egy elemet: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();
}
};
Ha ezt kipróbálod és futtatod néhány percig, a képváltási sebesség folyamatosan lassulni fog, majd a program leáll. Ez amiatt történik, mert egyre több részecskét készítünk – amiket kezelni kell és meg kell jeleníteni – anélkül, hogy akár egyet is eltávolítanánk. Ha egy részecske halott, már nem hasznos többé, úgyhogy megkímélhetjük a programunkat a felesleges munkától azzal, hogy eltávolítjuk a halott részecskéket.
JavaScriptben egy tömb elemének eltávolítására használhatjuk a
splice()
metódust, amiben megadjuk az eltávolítani kívánt elem sorszámát és az eltávolítani kívánt elemek számát (ez esetben csak egyet). Ezt azután iktatnánk be, miután lekérdeztük, hogy a részecske halott-e: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);
}
}
};
Bár a fenti kód jól fog futni (és nem is fog leállni), beletenyereltünk egy kisebb problémahalmazba. Ha az alatt változtatjuk egy tömb tartalmát, mialatt éppen végigmegyünk rajta, bajba juthatunk. Vegyük mondjuk az alábbi kódot:
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.run();
particles.push(new Particle(new PVector(width/2, 50)));
}
Ez egy extrém példa (és a logikájával sincs minden rendben), de a probléma bemutatására alkalmas. A fenti példában a tömb minden részecskéjének feldolgozása után hozzáadunk a tömbhöz egy elemet (ezáltal megváltozik a tömb
length
tulajdonsága). Ennek eredményeképpen végtelen ciklust kapunk, mivel az i
sohasem tudja meghaladni a particles.length
értékét.Noha a program nem fog összeomlani attól, hogy elhagyunk elemeket a ciklusban (mint az elemek hozzáadásakor), a probléma még alattomosabb, hiszen nem hagy látható nyomot. Ahhoz, hogy megértsük, először le kell szögeznünk egy fontos dolgot. Ha egy elemet eltávolítunk egy tömbből, akkor minden utána következő elem eggyel balra mozdul el. Figyeld meg a lenti ábrát, ahol a C részecskét (aminek az indexe 2 volt) töröltük! Az A és B részecske indexe változatlan maradt, míg a D és az E 3-ról és 4-ről rendre 2-re és 3-ra változott.
Képzeld el, hogy te vagy
i
, ahogy végiglépkedsz a cikluson!- ha i = 0 → ellenőrizd az A részecskét → ne töröld
- ha i = 1 → ellenőrizd a B részecskét → ne töröld
- ha i = 2 → ellenőrizd a C részecskét → töröld!
- (Csúsztasd át a D és E részecskéket a 3-as és 4-es helyzetről a 2-es és 3-as helyzetre)
- ha i = 3 → ellenőrizd az E részecskét → ne töröld
Észrevetted, mi a baj? A D részecskét nem vizsgáltuk meg! Amikor C-t töröljük a 2-es indexről, a 2-es indexre átkerül a D, de
i
már a 3-as indexre mutat. Ez nem végzetes hiba, mert a következő ciklusban a D-re is rákerül a sor, de az az elvárásunk, hogy olyan kódot írjunk, ami a tömb minden elemén minden ciklusban végigmegy. Nem elfogadható, hogy kihagyunk elemet.Erre van egy egyszerű megoldás: a tömböt visszafelé kell olvasni. Ha jobbról balra csúsztatod az elemeket törlés esetén, akkor nem tudsz átugrani egyet sem véletlenül. Ehhez csak három módosítást kell végeznünk a ciklusban.
for (var i = particles.length-1; i >= 0; i--) {
var p = particles[i];
p.run();
if (p.isDead()) {
particles.splice(i, 1);
}
}
Ha mindent összerakunk, ezt kapjuk: (A programban található megjegyzések: 1. sor: Dan Shiffman, natureofcode.com példája alapján; 3. sor: Egyetlen Részecske objektum.)
Rendben van. Két dolgot tettünk. Irtunk egy objektumot, ami leír egy egyedi
Particle
t (részecskét). Rájöttünk, hogyan kell tömböket kezelni több Particle
objektum vezérléséhez (tetszés szerint tudunk a tömbhöz elemeket hozzáadni és törölni).Itt akár meg is állhatnánk. De még hiányzik egy lépés, amit meg tudunk, és meg is kell tennünk: el kell készítenünk egy
ParticleSystem
objektumot, ami leírja a Particle
objektumok összességét. Ez lehetővé teszi számunkra, hogy a részecskéken végigpásztázó ciklus terjedelmes logikáját eltávolítsuk a főprogramból, valamint megnyitja az utat afelé, hogy több részecskerendszerünk legyen.Ha felelevenítjük a fejezet elején kitűzött célunkat, akkor egy ilyen programot képzeltünk el:
var ps = new ParticleSystem(new PVector(width/2, 50));
draw = function() {
background(0, 0, 0);
ps.run();
};
Vegyük a fenti programot, és nézzük meg, hogyan tudjuk beleilleszteni a
ParticleSystem
objektumot!Eredetileg ez volt meg:
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);
}
}
};
Így lehet a fentit objektumba átírni – a
particles
tömb az objektum egy tulajdonsága lesz, készítünk egy addParticle
metódust új részecskék létrehozására, és a teljes részecske-vezérlési logikát a run
metódusba tesszük: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);
}
}
};
Magához a részecskerendszerhez is rendelhetünk új tulajdonságokat. Például hasznos lehet egy
ParticleSystem
objektumnak nyomon követnie egy origó pontot, ahol a részecskék készülnek. Ez beleillik a részecskerendszer „kibocsátó” szerepkörébe, ami egy olyan hely, ahol a részecske megszületik, és kikerül a világba. Az origót a konstruktorban kell inicializálni.var ParticleSystem = function(position) {
this.origin = position.get();
this.particles = [];
};
ParticleSystem.prototype.addParticle = function() {
this.particles.push(new Particle(this.origin));
};
Most már mindent összerakunk. (A programban található megjegyzések: 1. sor: Dan Shiffman, natureofcode.com példája alapján; 3. sor: Egyetlen Részecske objektum.)
Szeretnél részt venni a beszélgetésben?
Még nincs hozzászólás.