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

თუ ვებფილტრს იყენებთ, დარწმუნდით, რომ *.kastatic.org და *.kasandbox.org დომენები არ არის დაბლოკილი.

ძირითადი მასალა

კურსი: კომპიუტერული პროგრამირება > თემა 5

გაკვეთილი 8: ნაწილაკთა სისტემები

ნაწილაკთა სისტემა

აქამდე მოვახერხეთ, შეგვექმნა ერთადერთი ნაწილაკი, რომელსაც ხელახლა ვაჩენთ მაშინვე, როგორც კი კვდება. ახლა გვსურს, შევქმნათ ნაწილაკების უწყვეტი დინება. 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() მეთოდი, უნდა მივუთოთ სასურველი წასაშლელი ინდექსი და წასაშლელი რიცხვი (მხოლოდ ერთი). ამას ვიზამთ მოთხოვნის შემდეგ, რომლითაც გავიგებთ, ნამდვილად მკვდარია თუ არა ნაწილაკი:
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-ზე დიდი.
ნაწილაკების მასივიდან ელემენტების ამოღება პროგრამაში შეცდომას (crash-ს) არ იწვევს (დამატების შემთხვევისგან განსხვავებით), მაგრამ პრობლემა უარესდება, როცა ის კვალს არ ტოვებს. პრობლემის ამოსაცნობად ჯერ ერთი მნიშვნელოვანი ფაქტი უნდა ჩამოვაყალიბოთ. როდესაც მასივიდან ელემენტს ვიღებთ, ყველა დანარჩენი ელემენტი ერთი ადგილით მარცხნივ გადაიწევს. იხილეთ ქვემოთ მოცემული დიაგრამა, რომელშიც ნაწილაკი C (მეორე ინდექსზე) არის ამოღებული. ნაწილაკები 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 გადავიდა მეორე პოზიციაზე, მაგრამ i უკვე გადასული იყო მესამე პოზიციაზე. ეს არ არის კატასტროფა, რადგან ნაწილაკი D შემდეგ ჯერზე შემოწმდება. მაინც, ჩვენი მიზანია კოდის დაწერა, რომელიც მასივში ყოველ ელემენტს გადაუყვება. ელემენტის გამოტოვება მიუღებელია.
ამ პრობლემის მარტივი გადაწყვეტა არსებობს: გადაუყევით მასივს უკუმიმართულებით. თუ ელემენტები მარჯვნიდან მარცხნივ გადაგაქვთ, შეუძლებელია ელემენტის შემთხვევით გამოტოვება. მხოლოდ 3 რამის შეცვლა გვიწევს for ციკლში:
  for (var i = particles.length-1; i >= 0; i--) {
    var p = particles[i];
    p.run();
    if (p.isDead()) {
      particles.splice(i, 1);
    }
  }
ამ ყოველივეს თავმოყრის შემდეგ, გვაქვს ეს:
კარგი. ახლა ჩვენ ორი რამ გავაკეთეთ. დავწერეთ ობიექტი თითოეული Particle-ის (ნაწილაკის) აღსაწერად. ჩვენ გავარკვიეთ, როგორ გამოვიყენოთ მასივები ბევრი 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));
};
აი, ყველაფერი ერთად:

გსურთ, შეუერთდეთ დისკუსიას?

პოსტები ჯერ არ არის.
გესმით ინგლისური? დააწკაპუნეთ აქ და გაეცანით განხილვას ხანის აკადემიის ინგლისურენოვან გვერდზე.