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é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 Particlet (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.
Tudsz angolul? Kattints ide, ha meg szeretnéd nézni, milyen beszélgetések folynak a Khan Academy angol nyelvű oldalán.