ძირითადი მასალა
კომპიუტერული პროგრამირება
კურსი: კომპიუტერული პროგრამირება > თემა 5
გაკვეთილი 8: ნაწილაკთა სისტემები- შესავალი ნაწილაკთა სისტემებში
- მხოლოდ ერთი ნაწილაკი
- გამოწვევა: დაცვენილი ფოთლები
- ნაწილაკთა სისტემა
- გამოწვევა: თევზის ბუშტუკები
- ნაწილაკთა სისტემების სისტემები
- გამოწვევა: ცეცხლის წამკიდებელი
- ნაწილაკთა ტიპები
- გამოწვევა: ჯადოსნური ქვაბი
- ნაწილაკთა სისტემები და ძალები
- გამოწვევა: მდინარის ქვები
- პროექტი: არსებათა კოლონიები
© 2023 Khan Academyგამოყენების პირობებიკონფიდენციალურობის პოლიტიკაშენიშვნა ქუქი-ჩანაწერებზე
ნაწილაკთა სისტემები და ძალები
ამ სექციაში აქამდე ყურადღებას ვამახვილებდით ჩვენი კოდის სტრუქტურის ობიექტზე ორიენტირებული გზით შექმნაზე ნაწილაკების კოლექციის სამართავად. შეიძლება, შეამჩნიეთ, შეიძლება, ვერა, მაგრამ ამ პროცესის განმავლობაში ჩვენ უნებლიეთ წინა სექციებიდან უკან დავიხიეთ. მოდით, გამოვიკვლიოთ ჩვენი მარტივი Particle ობიექტის კონსტრუქტორი:
var Particle = function(position) {
this.acceleration = new PVector(0, 0{,}05);
this.velocity = new PVector(random(-1, 1), random(-1, 0));
this.position = new PVector(position.x, position.y);
this.timeToLive = 255{,}0;
};
და ახლა შევხედოთ
update()
მეთოდს:Particle.prototype.update = function(){
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.timeToLive -= 2;
};
აღვნიშნოთ, რომ ჩვენი აჩქარება მუდმივია, მას არასოდეს ენიჭება მნიშვნელობა კონსტრუქტორის გარეთ. გაცილებით უკეთესი იქნებოდა, მივყოლოდით ნიუტონის მეორე კანონს (F, with, vector, on top, equals, M, A, with, vector, on top) და შეგვეთავსებინა ძალის დაგროვების ალგორითმი, რომელზეც ბევრი ვიმუშავეთ ძალების სექციაში.
პირველი ნაბიჯია
applyForce()
მეთოდის დამატება (გახსოვდეთ, ჯერ PVector
-ის ასლი უნდა შევქმნათ, სანამ მას გავყოფთ მასაზე).Particle.prototype.applyForce = function(force) {
var f = force.get();
f.div(this.mass);
this.acceleration.add(f);
};
ამის შემდეგ შეგვიძლია, დავამატოთ კოდის კიდევ ერთი ხაზი, რათა გავასუფთავოთ აჩქარება
update()
-ის ბოლოს.Particle.prototype.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
this.timeToLive -= 2{,}0;
};
და, შესაბამისად, გვაქვს
Particle
ობიექტი, რომელზეც შესაძლებელია ძალის გამოყენება. ახლა ვიფიქროთ, სად უნდა გამოვიძახოთ applyForce()
ფუნქცია? სად არის კოდში ლოგიკური ძალის გამოყენება ნაწილაკზე? ამ კითხვებზე არ არსებობს ერთი სწორი და არასწორი პასუხი; გააჩნია კონკრეტულ ფუნქციონალსა და კონკრეტული პროგრამის მიზნებს. ამის მიუხედავად, შეგვიძლია, შევქმნათ სტანდარტული სიტუაცია, რომელიც, დიდი ალბათობით, მოერგება შემთხვევების უმრავლესობას, და შევქმნათ მოდელი სისტემაში ძალების ცალკეულ ნაწილაკებზე გამოსაყენებლად.ქარის დამატება
განვიხილოთ შემდეგი მიზანი: გლობალურად გამოვიყენოთ ძალა ყოველ ჯერზე draw()-ს გამოყენებით ყველა ნაწილაკზე. დავიწყებთ მარტივი ქარის მსგავსი ძალით, რომელიც ნაწილაკს მარჯვენა მხარეს აძლევს ბიძგს:
var wind = new PVector(0,4, 0);
ჩვენ ვთქვით, რომ ის ყოველთვის უნდა გამოვიყენოთ, მაგალითად,
draw()
-ში, ასე რომ, შევხედოთ ჩვენს draw()
ფუნქციას ახლანდელ მდგომარეობაში.draw = function() {
background(168, 255, 156);
particleSystem.addParticle();
particleSystem.run();
};
როგორც ჩანს, პატარა პრობლემა გვაქვს.
applyForce()
არის Particle
ობიექტის შიგნით ჩაწერილი მეთოდი, მაგრამ ჩვენ არ გვაქვს არანაირი წვდომა ცალკეულ ნაწილაკებზე, წვდომა მხოლოდ ParticleSystem
ობეიქტზე გვაქვს: ცვლადი particleSystem
.ვინაიდან გვინდა, რომ ყველა ნაწილაკმა მიიღოს ძალა, შეგვიძლია, ნაწილაკების სისტემაზე გამოვიყენოთ ის და ამ სისტემას მივანდოთ ყველა ნაწილაკზე მისი გამოყენება.
draw = function() {
background(168, 255, 156);
particleSystem.applyForce(wind);
particleSystem.addParticle();
particleSystem.run();
};
რა თქმა უნდა, თუ გამოვიძახებთ ახალ ფუნქციას
ParticleSystem
ობიექტზე draw()
-ში, ეს ფუნქცია უნდა ჩავწეროთ ParticleSystem
ობიექტში. აღვწეროთ სამუშაო, რაც ფუნქციამ უნდა შეასრულოს: მან უნდა მიიღოს ძალა PVector
-ის სახით და გამოიყენოს ის ყველა ნაწილაკზე.ახლა კოდში:
ParticleSystem.prototype.applyForce = function(f){
for(var i = 0; i < this.particles.length; i++){
this.particles[i].applyForce(f);
}
};
სულელურადაც კი ჟღერს ამ ფუნქციის დაწერა. ჩვენ ვამბობთ: „გამოვიყენოთ ძალა ნაწილაკების სისტემაზე, რათა ამ სისტემამ შეძლოს ამ ძალის გამოყენება თითოეულ ნაწილაკზე“. თუმცა, ეს საკმაოდ ლოგიკურია.
ParticleSystem
ობიექტი ხელმძღვანელობს ნაწილაკებს, ასე რომ, თუ ნაწილაკებთან ლაპარაკი გვინდა, მათ მისი ხელმძღვანელის მეშვეობით უნდა ველაპარაკოთ.აი, ყველაფერი ერთად. სცადეთ ქარის ძალა და ნახეთ, როგორი გავლენა აქვს ნაწილაკების მოძრაობაზე, დააკვირდით, რომ სხვადასხვა მასის მქონე ნაწილაკები სხვადასხვანაირად რეაგირებენ. დაფიქრდით, რატომ ხდება ასე.
გრავიტაციის დამატება
ახლა გამოვიყენოთ უფრო რთული ძალა, გრავიტაცია, რომელიც განსხვავდება ქარისგან, რადგან ის სხვადასხვანაირი შეიძლება იყოს იმ ობიექტის მასის მიხედვით, რომელზეც მას ვიყენებთ.
გავიხსენოთ გრავიტაციის ძალის გამოთვლის განტოლება ორ მასას შორის: F, start subscript, g, end subscript, with, vector, on top, equals, start fraction, G, m, start subscript, 1, end subscript, m, start subscript, 2, end subscript, divided by, vertical bar, vertical bar, r, vertical bar, vertical bar, squared, end fraction, r, with, hat, on top
გახსოვდეთ, რომ, როცა ძალის დედამიწაზე მოდელირებას ვაკეთებთ, ძალა, რომლითაც დედამიწა მოქმედებს, დიდად აჭარბებს ყველა სხვა გრავიტაციულ ძალას, ასე რომ, ერთადერთი განტოლება, რომელთანაც გვაქვს შეხება, არის დედამიწასა და ობიექტს შორის არსებული გრავიტაციის ძალის გამოთვლა. G და m, start subscript, 1, end subscript არის ერთი და იგივე ყველა ნაწილაკისთვის, ხოლო r(რადიუსი დედამიწიდან) პრაქტიკულად იგივეა (რადგან დედამიწის რადიუსი ბევრად უფრო დიდია იმ მანძილთან შედარებით, რითაც ნაწილაკები შორდებიან მას), ასე რომ, მათ, ძირითადად, ვამარტივებთ g-მდე, რაც დედამიწის გრავიტაციის მუდმივაა:
ახლა გრავიტაციის ძალა არის რაღაც მუდმივა g, გამრავლებული ნაწილაკების მასაზე, გამრავლებული ერთეულოვან ვექტორზე ძალის მიმართულებით (რომელიც ყოველთვის იქნება მიმართული ქვემოთ):
კოდის ენაზე ეს ნიშნავს, რომ მოგვიწევს თითოეულ ნაწილაკზე მისი მასის მიხედვით სხვადასხვა გრავიტაციის გამოყენება. როგორ გავაკეთოთ ეს? ჩვენ არ შეგვიძლია
applyForce
ფუნქციის ხელახლა გამოყენება, რადგან ის თითოეული ნაწილაკისათვის იმავე ძალას ელის. შეიძლება, განვიხილოთ მისთვის ისეთი პარამეტრის გადაცემა, რომელიც აუხსნის applyForce
-ს, რომ მასაზე გაამრავლოს, მაგრამ, მოდით, ამ ფუნქციას თავი დავანებოთ და შევქმნათ ახალი ფუნქცია, applyGravity
, რომელიც გამოითვლის ძალას გლობალური მუდმივი ვექტორის მიხედვით:// მუდმივად ქვემოთ მიმართული ვექტორი, რომელიც გამოცხადებულია დასაწყისში
var gravity = new PVector(0, 0{,}2);
ParticleSystem.prototype.applyGravity = function() {
for(var i = 0; i < this.particles.length; i++) {
var particleG = gravity.get();
particleG.mult(this.particles[i].mass);
this.particles[i].applyForce(particleG);
}
};
ახლა, თუ ეს სწორად გვაქვს გაკეთებული, ჩვენი ყველა ნაწილაკი ერთითა და იმავე ტემპით უნდა დაეშვას ქვემოთ მოცემულ სიმულაციაში. ეს იმიტომ, რომ გრავიტაციის ძალა დაფუძნებულია მასაზე გამრავლებაზე, მაგრამ აჩქარება ეფუძნება მასაზე გაყოფას, ასე რომ, საბოლოოდ მასას არანაირი ეფექტი არ აქვს. შეიძლება, სულელურად ჟღერს ამდენი რამის გაკეთება შედეგის ქონის გარეშე, მაგრამ ეს მნიშვნელოვანი გახდება მას შემდეგ, რაც რამდენიმე სხვადასხვა ძალის გაერთიანებას დავიწყებთ.
განმზიდავების დამატება
ერთი ნაბიჯით წინ ხომ არ წავიდეთ ამ მაგალითით და დავამატოთ განმზიდავი ობიექტი, რომელიც ნაწილაკებს განიზიდავს, როდესაც ისინი ახლოვდებიან? ეს ანალოგიური იქნებოდა მიმზიდველი ობიექტისა, რომელიც ადრე შევქმენით, უბრალოდ, საპირისპირო მიმართულებით მიაწვება. კიდევ ერთხელ, როგორც გრავიტაციის შემთხვევაში, თითოეული ნაწილაკისათვის უნდა გამოვთვალოთ განსხვავებული ძალა, მაგრამ განმზიდავის შემთხვევაში განსხვავება ისაა, რომ გამოთვლა არ ეფუძნება მასას - ის ეფუძნება მანძილს. გრავიტაციის შემთხვევაში ძალის ჩვენს ყველა ვექტორს ჰქონდა ერთი და იგივე მიმართულება, მაგრამ განმზიდავის შემთხვევაში ძალის ყველა ვექტორს ექნება განსხვავებული მიმართულება:
ვინაიდან განმზიდავის ძალის გამოთვლა ოდნავ უფრო რთულია, ვიდრე გრავიტაციის გამოთვლა (და საბოლოოდ შეიძლება, ბევრი განმზიდავი მოგვინდეს!), ამ ამოცანას ამოვხსნით ჩვენი მარტივი ნაწილაკების სისტემისა და გრავიტაციის მაგალითში ახალი Repeller (განმზიდავი) ობიექტის შემოღებით. დაგვჭირდება ორი დიდი დამატება ჩვენს კოდში:
Repeller
ობიექტი (გამოცხადებული, ინიციალიზებული და ეკრანზე ნაჩვენები).- ფუნქცია, რომელიც გადასცემს
Repeller
ობიექტსParticleSystem
-ში, რათა მან შეძლოს ამ ძალის გამოყენება თითოეულ ნაწილაკის ობიექტზე.
var particleSystem = new ParticleSystem(new PVector(width/2, 50));
var repeller = new Repeller(width/2-20, height/2);
var gravity = new PVector(0, 0{,}1);
draw = function() {
background(214, 255, 171);
// Apply gravity force to all Particles
particleSystem.applyForce(gravity);
particleSystem.applyRepeller(repeller);
repeller.display();
particleSystem.addParticle();
particleSystem.run();
};
ეკრანზე ჩვენებადი
Repeller
ობიექტის გაკეთება იოლია; ის არის Attractor
ობიექტის დუბლიკატი, რომელიც ადრე შევქმენით:var Repeller = function(x, y) {
this.position = new PVector(x, y);
};
Repeller.prototype.display = function() {
stroke(255);
strokeWeight(2);
fill(127);
ellipse(this.position.x, this.position.y, 32, 32);
};
უფრო რთული კითხვა შემდეგია: როგორ უნდა დავწეროთ
applyRepeller()
მეთოდი? PVector
-ის ფუნქციაში ისე გადაცემის ნაცვლად, როგორც applyForce()
-ის შემთხვევაში ვაკეთებთ, applyRepeller()
-ს გადავცემთ Repeller
ობიექტს და ამ ფუნქციას ვთხოვთ, შეასრულოს განმზიდავსა და ყველა ნაწილაკს შორის ძალის გამოთვლის სამუშაო:ParticleSystem.prototype.applyRepeller = function(r) {
for(var i = 0; i < this.particles.length; i++){
var p = this.particles[i];
var force = r.calculateRepelForce(p);
p.applyForce(force);
}
};
აქ დიდი განსხვავება ის არის, რომ ახალი ძალა გამოითვლება თითოეული ნაწილაკისათვის, რადგან, როგორც ადრე ვნახეთ, ძალა განსხვავდება თითოეული ნაწილაკის თვისებების მიხედვით განმზიდავის მიმართ. ამ ძალას გამოვთვლით
calculateRepelForce
ფუნქციის გამოყენებით, რომელიც არის calculateAttractionForce
ფუნქციის შებრუნებული ჩვენი Attractor
-ებიდან (მიმზიდველებიდან).Repeller.prototype.calculateRepelForce = function(p) {
// გამოთვალეთ ვექტორი ობიექტებს შორის ძალისთვის
var dir = PVector.sub(this.position, p.position);
// გამოვთვალოთ მანძილი ობიექტებს შორის
var dist = dir.mag();
// მანძილი უნდა იყოს ზომიერ დიაპაზონში
dist = constrain(dist, 1, 100);
// გამოვთვალოთ განზიდვის ძალა,
// უკუპროპორციული მანძილის კვადრატისა
var force = -1 * this.power/ (dist * dist);
// მიმართულების ვექტორს გავუკეთოთ ნორმალიზაცია
// (მანძილის შესახებ ინფორმაციას უგულებელვყოფთ)
dir.normalize();
// გამოვთვალოთ ძალის ვექტორი: მიმართულება გამრავლებული სიდიდეზე
dir.mult(force);
return dir;
};
აღვნიშნოთ, რომ გარემოში განმზიდავის დამატების მთელი პროცესის განმავლობაში თვითონ
Particle
ობიექტის რედაქტირება არც ერთხელ არ განგვიხილავს. არ არის აუცილებელი, ნაწილაკმა იცოდეს დეტალები მისი გარემოს შესახებ; მან, უბრალოდ, უნდა მართოს თავისი ადგილმდებარეობა, სიჩქარე და აჩქარება, აგრეთვე უნდა ჰქონდეს გარე ძალის მიღებისა და შესაბამისად მოქმედების უნარი.ასე რომ, ახლა შეგვიძლია, ამ მაგალითს შევხედოთ მთლიანობაში. სცადეთ ნაწილაკებზე მოქმედი ძალების - გრავიტაციისა და განმზიდავის - სიდიდის შეცვლა და ნახეთ, როგორ შეცვლის ეს მათ:
გსურთ, შეუერთდეთ დისკუსიას?
პოსტები ჯერ არ არის.