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

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

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

Видове частици

Сега ще използваме по-напреднали техники от обектно-ориентираното програмиране като наследяване, затова може би ще искаш да си припомниш "наследяването" от курса Въведение в JS и след това да се върнеш. Не се тревожи, ще почакаме!
Вече знаеш как работи наследяването? Добре, защото ще използваме наследяване, за да направим подобекти на различни типове Particle, които ще споделят еднаква функционалност, но и ще се различават по ключови начини.
Да разгледаме една опростена имплементация на Particle:
var Particle = function(position) {
  this.acceleration = new PVector(0, 0{,}05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = position.get();
};

Particle.prototype.run = function() {
  this.update();
  this.display();
};

Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
};

Particle.prototype.display = function() {
  fill(127, 127, 127);
  ellipse(this.position.x, this.position.y, 12, 12);
};
След това създаваме тип, базиран на Particle, който ще наречем Confetti. Ще започнем с функция конструктор, която приема същия брой аргументи и просто извиква конструктора на Particle, като му ги подава:
var Confetti = function(position) {
  Particle.call(this, position);
};
Сега, за да сме сигурни, че нашите обекти Confetti споделят същите методи като обектите Particle, трябва укажем, че техният прототип трябва да се базира на прототипа на Particle:
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
В този момент имаме обекти Confetti, които се държат по съвсем същия начин като обектите Particle. Смисълът на наследяването не е да правим двойници, а да правим нови обекти, които споделят много от функционалността си, но и се различават по някакъв начин. Каква е разликата в обекта Confetti? Ами, ако съдим само по името, изглежда че трябва да изглежда по различен начин. Нашите обекти Particle са елипси, но конфетите обикновено са малки квадратни парченца хартия, затова най-малкото трябва да променим display метода, за да ги покажем като правоъгълници:
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255, this.timeToLive);
  stroke(0, 0, 0, this.timeToLive);
  strokeWeight(2);
  rect(0, 0, 12, 12);
};
Ето програма с една инстанция на обекта Particle и една на обекта Confetti. Забележи, че те имат еднакво поведение, но се различават по външния си вид:

Добавяне на ротация

Да направим нещата малко по-сложни. Да кажем, че искаме нашата частица Confetti да се върти, докато се носи във въздуха. Разбира се, можем да моделираме ъглова скорост и ускорение, както направихме в секцията за Осцилация. Вместо това да опитаме едно бързо и мръсно решение.
Знаем, че частицата има позиция х между 0 и широчината на прозореца. Ами ако помислим върху следното: когато х позицията на частицата е 0, ротацията ѝ трябва да 0; когато x позицията на частицата е равна на широчината, ротацията ѝ трябва да е равна на TWO_PI? Звучи ли ти познато? Когато имаме стойност в някакъв обхват, която искаме да свържем с друг обхват, можем да използваме функцията на ProcessingJS map(), за да изчислим лесно новата стойност.
var theta = map(this.position.x, 0, width, 0, TWO_PI);
И за да дадем малко повече въртене, можем да свържем обхвата на ъгъла от 0 до TWO_PI*2. Да видим как този код се вписва в метода display().
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255);
  stroke(0, 0, 0);
  strokeWeight(2);
  pushMatrix();
  translate(this.position.x, this.position.y);
  var theta = map(this.position.x, 0, width, 0, TWO_PI * 2);
  rotate(theta);
  rect(0, 0, 12, 12);
  popMatrix();
};
Ето как изглежда – рестартирай няколко пъти, за да видиш ефекта на ротацията:
Освен това можем да базираме theta на позицията y, което ще даде малко по-различен ефект. Защо това е така? Частицата има ненулево постоянно ускорение по посока y, което означава, че скоростта y е линейна функция на времето, а позицията y е параболична функция на времето. Можеш да видиш какво означава това на графиките по-долу (които са генерирани въз основа на горната програма):
Това означава, че ако базираме ротацията на конфетите на позицията y, ротацията също ще бъде параболична. Това няма да е съвсем точно от физична гледна точка, тъй като действителната ротация на падащите във въздуха конфети е доста сложна, но опитай самостоятелно и виж колко реалистично изглежда това движение! Можеш ли да се сетиш за други функции, които биха изглеждали дори още по-реалистични?

Разнообразна система от частици ParticleSystem

Това, което наистина искаме, е да можем да създаваме много обекти Particle и много обекти Confetti. Затова направихме обекта ParticleSystem , така че вероятно бихме могли да го разширим, за да следи и обектите Confetti, нали? Ето един начин да го направим, като копираме това, което направихме за обектите Particle:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = [];
};

ParticleSystem.prototype.addParticle = function() {
    this.particles.push(new Particle(this.origin));
    this.confettis.push(new Confetti(this.origin));
};

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
  }
for (var i = this.confettis.length-1; i >= 0; i--) {
    var p = this.confettis[i]; p.run();
  }
};
Забележи, че имаме два отделни масив, един за частиците и един за конфетите. Всеки път, когато правим нещо с частиците, трябва да го направим и с масива на конфетите! Това е дразнещо, защото означава, че трябва да пишем два пъти повече код и ако променим нещо, трябва да го променяме на две места. Можем да избегнем това повторение, защото можем да запазваме обекти от различен тип в масивите в JavaScript, а тъй като нашите обекти имат един и същи интерфейс – извикваме метода run() и двата типа обекти дефинират този интерфейс. Затова ще се върнем на това да имаме само един масив и ще решим на случен принцип кой тип обекти частици да добавим, а след това ще се върнем на обхождането на единствения масив. Това е много по-проста промяна – всичко, което променихме, е методът addParticle:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  var r = random(1);
  if (r < 0{,}5) {
    this.particles.push(new Particle(this.origin));
  } else {
    this.particles.push(new Confetti(this.origin));
  }
};

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);
    }
  }
};
Ето го всичко заедно!

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

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