Główna zawartość
Programowanie
Kurs: Programowanie > Rozdział 5
Lekcja 8: Systemy cząstek- Wprowadzenie do systemów cząstek
- Pojedyncza cząsteczka
- Wyzwanie: opadające liście
- System cząstek
- Wyzwanie: Rybie bąbelki
- Systemy systemów cząstek
- Wyzwanie: Podpalacz
- Typy cząstek
- Wyzwanie: Magiczny kocioł
- System cząstek z siłami
- Wyzwanie: Rzeczne skały
- Projekt: Kolonie stworzeń
© 2023 Khan AcademyWarunki użytkowaniapolitykę prywatnościInformacja o plikach cookie
System cząstek
Do tej pory stworzyliśmy jedną cząsteczkę, która ponownie pojawia się po tym, jak umrze. Teraz chcemy stworzyć ciągły strumień cząsteczek, dodając nowe w każdym cyklu za pomocą
draw()
. Możemy po prostu stworzyć tablicę i dodawać do niej nowe cząsteczki za każdym razem: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();
}
};
Jeśli spróbujesz tej metody i uruchomisz ten kod na parę minut, zdasz sobie sprawę, że liczba klatek na sekundę będzie spadać i spadać, aż w końcu program się zawiesi. Jest to spowodowane tym, że dodajemy coraz więcej cząstek które musimy przetworzyć i wyświetlić, a żadnych nie usuwamy. Gdy cząsteczki są martwe, nie mamy z nich więcej pożytku, więc możemy zaoszczędzić naszemu programowi niepotrzebnej pracy i je po prostu usunąć.
By usunąć rzeczy z tablicy w JavaScript, możemy użyć metody
splice()
, podając identyfikator rzeczy, którą chcemy usunąć oraz liczbę elementów (w tym wypadku tylko jeden). Zrobimy to po sprawdzeniu, czy cząsteczka rzeczywiście jest martwa: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);
}
}
};
Chociaż powyższy kod będzie działał dobrze (a program nigdy się nie zawiesi), otworzyliśmy małą puszkę pandory. Gdy manipulujemy zawartością tablicy podczas iterowania po niej, możemy mieć do czynienia z pewnymi problemami. Weźmy dla przykładu następujący kod:
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.run();
particles.push(new Particle(new PVector(width/2, 50)));
}
To dosyć ekstremalny przykład (plus nie ma on większego sensu), ale przedstawia dobrze o co chodzi. W powyższym przykładzie dla każdej cząstki w tablicy dodajemy nową cząstkę do tablicy(przez to zmieniając
długość(length)
tablicy). Sprawi to, ze znajdziemy się w pętli nieskończonej, gdyż i
nigdy nie dogoni wartości particles.length
.Pomimo, że usuwanie obiektów z tablicy cząsteczek podczas pętli nie powoduje awarii programu (jak ma to miejsce w przypadku dodawania), problem jest jeszcze gorszy, gdyż nie zostawia żadnych śladów. By odkryć przyczynę, wpierw musimy zdać sobie sprawę z jednej rzeczy. Gdy coś jest usuwane z tablicy, wszystkie pozostałe obiekty przesuwają się o jedno miejsce w lewo. Zauważ na poniższym wykresie moment, gdy cząstka C (indeks 2) jest usuwana. Cząstki A i B zachowują ten sam indeks, a cząstki D i E zmieniają pozycję kolejno z 3 i 4 na 2 i 3.
Udajmy, że jesteśmy wartością
i
która iteruje w pętli po tablicy.- gdy i = 0 → Sprawdź cząstkę A → Nie usuwaj
- gdy i = 1 → Sprawdź cząstkę B → Nie usuwaj
- gdy i = 2 → Sprawdź cząstkę C → Usuń!
- (Przesuń cząsteczki D i E z pozycji 3 i 4 na 2 i 3)
- gdy i = 3 → Sprawdź cząstkę E → Nie usuwaj
Widzisz problem? Nigdy nie sprawdziliśmy cząsteczki D! Gdy C zostało usunięte z miejsca #2, D przeniosło się na miejsce #2, ale
i
już przeniosło się na pozycję #3. To nie jest żadna katastrofa, gdyż cząstka D zostanie sprawdzona następnym razem. Jednak mimo wszystko oczekujemy, że nasz kod będzie przeglądał wszystkie przedmioty w tablicy. Pomijanie elementu jest niedopuszczalne.Istnieje proste rozwiązanie: po prostu iterujmy tablicę od tyłu. Jeśli przesuwamy przedmioty z prawej do lewej usuwając przedmioty z tablicy, niemożliwe jest wtedy pominąć przypadkiem element. Musimy tylko zmienić trzy znaki w naszej pętli:
for (var i = particles.length-1; i >= 0; i--) {
var p = particles[i];
p.run();
if (p.isDead()) {
particles.splice(i, 1);
}
}
Składając to wszystko w jedną całość, otrzymujemy:
OK. Zrobiliśmy w sumie dwie rzeczy. Napisaliśmy obiekt opisujący pojedynczą
Cząsteczkę
. Odkryliśmy, jak używać tablic by zarządzać wieloma Cząsteczkami
(oraz jak je dodawać i usuwać według potrzeby).Moglibyśmy na tym skończyć. Jednak możemy, a nawet powinniśmy dołożyć jeszcze jeden dodatkowy krok i stworzyć obiekt opisujący kolekcję Cząsteczek — obiekt
ParticleSystem
. Pozwoli nam to usunąć niepotrzebna logikę przeglądania wszystkich cząsteczek z głównej tablicy, a także pozwoli na posiadanie więcej niż jednego systemu cząsteczek.Przypomnij sobie, że na początku rozdziału zażyczyliśmy sobie, aby nasz program wyglądał tak:
var ps = new ParticleSystem(new PVector(width/2, 50));
draw = function() {
background(0, 0, 0);
ps.run();
};
Weźmy program napisany powyżej i sprawdźmy, jak będzie pasował do obiektu
ParticleSystem
.Oto co mieliśmy wcześniej - zwróć uwagę na pogrubione linie:
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);
}
}
};
Oto jak przepisać to w obiekt - stworzymy tablicę
particles
jako pole obiektu, dodając metodę addParticle
do dodawania nowych cząsteczek, a logikę działania cząsteczek w run
: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);
}
}
};
Możemy również dodać parę nowych funkcji do samego systemu cząsteczek. Dla przykładu przydatne może być pozwolenie obiektowi
ParticleSystem
przechowywać informacje o punkcie, gdzie tworzone są cząsteczki. To pasuje do pomysłu, by system cząstek był “emiterem”, miejscem gdzie cząstki są tworzone i wysyłane w świat. Punkt początkowy powinien być zainicjowany w konstruktorze.var ParticleSystem = function(position) {
this.origin = position.get();
this.particles = [];
};
ParticleSystem.prototype.addParticle = function() {
this.particles.push(new Particle(this.origin));
};
Oto, jak wygląda nasz kod w całości:
Chcesz dołączyć do dyskusji?
Na razie brak głosów w dyskusji