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

Ако си зад уеб филтър, моля, увери се, че домейните *. kastatic.org и *. kasandbox.org са разрешени.

Основно съдържание

Система от частици

До сега успяхме да създадем една частица, която да се появява отново след като умре. Сега искаме да създадем постоянен поток от частици, като добавяме по една при всеки цикъл през функцията draw(). Можем да създадем масив и всеки път да добавяме по една частица в него:
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();
  }
};
Ако опиташ и пуснеш кода да се изпълнява няколко минути, вероятно ще започнеш да забелязваш как скоростта на кадрите намалява все повече и повече, докато програмата съвсем не спре. Това е така, защото създаваме все повече частици, които да обработим и да покажем, без да премахваме никоя от тях. Когато частиците умрат, стават безполезни, затова можем да спестим на нашата програма излишната работа и да премахнем тези частици.
За да премахваме елементи от масив в JavaScript можем да използваме метода splice(), в който да подадем индекса, който искаме да изтрием, и броя на елементите, които искаме да премахнем (само 1). Ще направим това след като направим проверка дали частицата действително е мъртва.
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);
    }
  }
};
Въпреки че горният код ще се изпълни съвсем правилно (и програмата ни няма да спре да работи), всъщност е все едно сме отворили средно голяма кутия с червеи. Когато манипулираме съдържанието на масива, докато обхождаме същия този масив, можем да си навлечем неприятности. Например разгледай този код:
for (var i = 0; i < particles.length; i++) {
  var p = particles[i];
  p.run();
  particles.push(new Particle(new PVector(width/2, 50)));
}
Това е донякъде краен пример (със сгрешена логика), но все пак доказва нашата теза. В горния случай за всяка частица от масива добавяме по една частица към масива (и така променяме свойството length на масива). По този начин ще получим безкраен цикъл, тъй като i никога няма да нарасне толкова, че да надвиши particles.length.
Въпреки че премахването на елементи от масива с частици по време на изпълнението на цикъла няма да счупи програмата ни (както става при добавянето), проблемът тук е доста по-коварен, защото не оставя след себе си никакви доказателства. За да намерим проблема, трябва първо да установим един важен факт. Когато от масива е премахнат един елемент, всички останали елементи се преместват с една позиция наляво. Разгледай диаграмата по-долу, в която частицата C (индекс 2) е премахната. Частиците A и B запазват своя индекс, докато частиците D и E се иместват съответно от 3 и 4 на 2 и 3.
Да си представим, че сме i, което обхожда масива.
  • когато i = 0 → Провери частица A → Не изтривай
  • когато i = 1 → Провери частица B → Не изтривай
  • когато i = 2 → Провери частица C → Изтрий я!
  • (Плъзни частиците D и E назад от позиции 3 и 4 на позиции 2 и 3)
  • когато i = 3 → Провери частица E → Не изтривай
Забеляза ли къде е проблемът? Никога не проверяваме частица D! Когато C беше изтрита от позиция #2, D се премести на позиция #2, но i вече се е преместило на позиция #3. Това не е фатално, тъй като частицата D ще бъде проверена при следващото обхождане. И все пак очакваме, че пишем код, който да обходи всеки един елемент от масива. Не е допустимо да пропускаме елементи.
За този проблем има едно просто решение: да обходим масива в обратна посока. Ако преместваме елементите от дясно на ляво, когато премахнем елемент, не е възможно да пропуснем някой елемент по случайност. Всичко, което трябва да направим, е да променим трите части на цикъла:
  for (var i = particles.length-1; i >= 0; i--) {
    var p = particles[i];
    p.run();
    if (p.isDead()) {
      particles.splice(i, 1);
    }
  }
Събираме всичко и получаваме това:
Добре. Дотук направихме две неща. Написахме обект, който да описва отделната частица Particle. Разбрахме как да използваме масиви, за да управляваме много обекти Particle (с възможността да добавяме и премахваме частици, когато пожелаем).
Можем да спрем дотук. Обаче има една допълнителна стъпка, която можем и трябва да направим – да създадем обект, който да описва самата колекция от частици – обекта ParticleSystem. Това ще ни позволи да премахнем тромавата логика по обхождането на всички частици от главния таб, и освен това ще ни даде възможността да имаме повече от една система от частици.
Ако си спомняш, целта, която си поставихме в началото на тази глава, беше да накараме програмата да изглежда така:
var ps = new ParticleSystem(new PVector(width/2, 50));

draw = function() {
  background(0, 0, 0);
  ps.run();
};
Да вземем програмата, която написахме по-горе, и да видим как можем да я вместим в обекта ParticleSystem.
Ето какво имахме преди – обърни внимание на удебелените редове:
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);
    }
  }
};
Ето как можем да пренапишем това в обекта – ще направим масива particles свойство на обекта, ще направим обвиващ метод за добавяне на нови частици addParticle и ще поствим цялата логика за обработване на частиците в 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);
      }
    }
};
Освен това можем да добавим нови функционалности към самата система от частици. Например за обекта ParticleSystem може да е полезно да следим изходната точка, от която се правят частиците. Това отговаря на идеята, че системата от частици е "емитираща", тя е място, на което частиците се раждат и се изпращат по света. Изходната точка трябва да се инициализира от конструктора.
var ParticleSystem = function(position) {
  this.origin = position.get();
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  this.particles.push(new Particle(this.origin));
};
Ето това е, всичко заедно сега:

Искаш ли да се присъединиш към разговора?

Все още няма публикации.
Разбираш ли английски? Натисни тук, за да видиш още дискусии в английския сайт на Кан Академия.