3. Gravitasjon

Skolekoding.no > Tekst-koding > p5.js > NOC trinn for trinn


Eksempler fra The Nature of Code av Daniel Shiffman
2.1 Forces
2.2 Forces acting on manny objects
2.3 Gravity scaled by mass



2.1 a Gravitajon

Nytt

  1. Egen metode som legger til krefter til et objekt: mover.applyForce()
  2. Summere to vektorer (acceleration og force): this.acceleration.add(force)
  3. Multiplisere en vektor med et tall: vektor.mult(tall)
  • let mover;
  • ​let gravity;
  • function setup() {
    • createCanvas(400, 400);
    • mover = new Mover();
    • gravity = createVector(0, 0.1); // Gravitasjon loddrett ned 
  • }
  • function draw() {
    • background(220);
    • // Ny metode i klassen Mover
    • mover.applyForce(gravity);
    • mover.update();
    • mover.checkEdges();
    • mover.display();
  • }
  • class Mover {
    • constructor() {
      • // «Massen» av ballen
      • this.mass = 35;
      • // Startposisjon
      • this.pos = createVector(30, 30);
      • // Starthastighet
      • this.velocity = createVector(0, 0);
      • //Startakselerasjon
      • this.acceleration = createVector(0, 0); 
    • }
    • applyForce(force) {
      • this.acceleration.add(force); 
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • // Nullstiller akselerasjonen
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(175);
      • // Diameteren symboliserer massen
      • ellipse(this.pos.x, this.pos.y, this.mass);
    • }
  • }

før setup()

  1. Vi lager en egen variabel for gravitasjon: gravity

function setup()

  1. Den får x-verdi lik 0 og y-verdi lik 0.01. Den peker altså nedover.

function draw() 

  1. Vi kaller på objektet «mover» sin «applyForce»-metode med «gravity» som parameter.
  2. Altså: vi legger til en kraft («gravity») på objektet «mover».
  3. Gravity blir addert sammen med acceleration.

class Mover 
constructor()

  1. Massen settes lik 35
  2. Posisjonen settes oppe til høyre i punkt (30, 30).
  3. Start-farten er lik null.​
  4. Start-akselerasjonen er lik null.

applyForce(force)

  1. Vi lager en egen metode som tar med seg en kraft-vektor («force») inn i klassen.
  2. «force» er en lokal variabel i metoden. Skal derfor ikke bruke «this.» foran.
  3. «this.» brukes bare foran klassevariabler som deklareres i konstruktøren.

update()

  1. Metoden «applyForce» kalles i hver runde i draw(). Verdien av «gravity» legges dermed til i hver eneste runde. Akselerasjonen øker. Det skal den ikke. Akselerasjonen skal være konstant.
  2. Vi multipliserer med null etter at akselerasjonen er lagt til hastigheten for å nullstille akselerasjonen før ny runde i draw() og ny applyForce()


checkEdges()

  1. Når sirkelen treffer kanten gir vi sirkel- posisjon lik kanten-posisjonen før vi endrer retningen.
  2. Hvis vi ikke tar med this.pos.y = height, vil sirkelen henge seg opp i bunnen når spretten etterhvert blir mindre. Nærmere forklaring senere.

2.1 b Vind

Nytt

  1. Legger til vind
  2. Dette er en kraft som virker vannrett fra venstre
  • let mover;
  • ​let gravity;
  • let wind;
  • function setup() {
    • createCanvas(400, 400);
    • mover = new Mover();
    • gravity = createVector(0, 0.1);
    • // Lager vind
    • wind = createVector(0.01, 0); 
  • }
  • function draw() {
    • background(220);
    • mover.applyForce(gravity);
    • // Legger til vind
    • mover.applyForce(wind);
    • mover.update();
    • mover.checkEdges();
    • mover.display();
  • }
  • class Mover {
    • constructor() {
      • this.mass = 35;
      • this.pos = createVector(30, 30);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0); 
    • }
    • applyForce(force) {
      • this.acceleration.add(force); 
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      •  if (this.pos.x > width) {
        •  this.pos.x = width;
        •  this.velocity.x *= -1;
      •  } else if (this.pos.x < 0) {
        •  this.pos.x = 0;
        •  this.pos.x *= -1;
      •  }
      •  if (this.pos.y > height) {
        •  this.pos.y = height;
        •  this.velocity.y *= -1;
      •  } else if (this.pos.y < 0) {
        •  this.pos.y = 0;
        •  this.velocity.y *= -1;
      •  }
    •  }
    •  display() {
      •  fill(175);
      • ellipse(this.pos.x, this.pos.y, this.mass);
    • }
  • }

før setup()

  1. Vi lager en vind-variabel.

function setup()

  1. Vi lager en vindtvektor: 0.01 langs x-aksen, ingen langs y-aksen. Den har altså retning vannrett mot høyre.


function draw()

  1. Bruker applyForce-metoden for å legge til vindens akselerasjons-bidrag til den samlede akselerasjonen.

2.2 a Flere objekter

Nytt

  1. Vi legger til flere objekter med tabell og for-løkke
  2. Objektene ligger bak hverandre og synes derfor ikke.
  3. Tabellvariabel med  klammeparenteser [ ](AltGr+8 og AltGr+9)
  • let movers = []; // Tabellvariabel
  • let numMovers = 10; // 10 tabellceller
  • ​let gravity;
  • let wind;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover()
    • }
    • gravity = createVector(0, 0.1);
    • wind = createVector(0.01, 0); // Lager vind
  • }
  • function draw() {
    • background(220);
    • for (i = 0; i < numMovers; i++) {
    • movers[i].applyForce(gravity);
    • movers[i].applyForce(wind);
    • movers[i].update();
    • movers[i].checkEdges();
    • movers[i].display();
    • }
  • }
  • class Mover {
    • constructor() {
      • this.mass = 35;
      • this.pos = createVector(30, 30);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0); 
    • }
    • applyForce(force) {
      • this.acceleration.add(force); 
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(175);
      • ellipse(this.pos.x, this.pos.y, this.mass);
    • }
  • }

​før setup()

  1. Vi lager en variabel som skal inneholde en tabell (en liste) med objekter.
  2. En tabellvariabel deklareres nesten på samme måte som andre variabler: vi skriver likhetstegn og klammeparenteser etter variabelnavnet: let movers = []
  3. Klammeparenteser skrives med alt gr + 8 og alt gr + 9.
  4. Vi lager også en variabel som skal inneholde et tall for antall celler i tabellen: numMovers.
  5. Det er vanlig å skrive variabelnavn med liten forbokstav. Dersom variabelnavnet er sammensatt av to ord brukes stor bokstav på ord nr 2 : numMovers.


function setup() 

  1. Vi lager en for-løkke. 
  2. Løkka har en indeksvariabel med navn «i»
  3. i starter på null
  4. Så lenge i er mindre enn numMovers utføres instruksjonene mellom krøllparentesene
  5. Deretter øker i med 1 (i++ er en kortform av i = i + 1)
  6. Les mer om for-løkke : Processingkurs kap 4.5
  7. For hver runde i for-løkka opprettes et nytt objekt med en egen indeks:
    1. movers[0]
    2. movers[1]
    3. movers[2]
    4. o.s.v.

function draw()

  1. Vi bruker en for-løkke for å hente fram alle ti objektene
  2. For hver runde i for-løkka legges det til gravitasjon og vind, akselerasjonen legges til farten, kantene sjekkes og sirkelen tegnes.
    1. Først for movers[0]
    2. Deretter for movers[1]
    3. o.s.v.

Ekstra forklaring

  1. Det er en liten hake ved dette programeksemplet
  2. Når movers-objektene lages med utgangspunkt i klassen Mover blir alle objektene identiske.
  3. Alle får samme masse, startposisjon, hastighet m.m.
  4. Vi ser derfor fremdeles bare en sirkel. Alle ti sirklene ligger bak hverandre
  5. I det neste eksempelet skal vi lage ti ulike objekter.
  6. Vi ser også at kraften som virker på objektene er uavhengig av massen
  7. Vi vil gjerne at de lettere objektene lettere skal bli tatt av vinden enn de tyngre.

2.2 b Ulik masse

Nytt

  1. Argumenter i konstruktøren.
  2. Akselerasjon er tilpasset massen.
  3. Vi legger til argumenter i klassens konstruktør for å gi objektene ulik masse.
  4. Vi bruker Newtons 2. lov og justerer akselerasjonen ut i fra massen.
  5. Vi beholder konstant loddrett akselerasjon (gravitasjon).
  • let movers = []; // Tabellvariabel
  • let numMovers = 10; // 10 tabellceller
  • ​let gravity;
  • let wind;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover(random(0.5, 5), 0, 0)
    • }
    • gravity = createVector(0, 0.1);
    • wind = createVector(0.01, 0);
  • }
  • function draw() {
    • background(220);
    • for (i = 0; i < numMovers; i++) {
      • movers[i].applyForce(gravity);
      • movers[i].applyForce(wind);
      • movers[i].update();
      • movers[i].checkEdges();
      • movers[i].display();
    • }
  • }
  • class Mover {
    • constructor(temp_m, temp_x, temp_y) {
      • this.mass = temp_m;
      • this.pos = createVector(temp_x, temp_y);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0); 
    • }
    • applyForce(force) {
      • let a = p5.Vector.div(force, this.mass); //Finner akselereasjonen
      • this.acceleration.add(a); // Legger til akselerasjonen
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(175);
      • circle(this.pos.x, this.pos.y, this.mass*16);
    • }
  • }

function setup()

  1. Vi legger til tre argumenter når vi lager objektene
  2. Argumentene plasseres i parentesen etter «new Mover»
  3. Første parameter: tilfeldig masse mellom 0.1 og 5
  4. Andre parameter: x-posisjon = 0
  5. Tredje parameter: y-posisjon = 0

class Mover 
constructor()

  1. Konstruktøren mottar tre parametre.
  2. Disse mottas gjennom midlertidige variable:
    temp_m, temp_x og temp_y (kunne hatt andre navn)
  3. temp_m er en tilfeldig «masse»mellom 5 og 10
  4. temp_x er x-koordinaten i startposisjonen
  5. temp_y er y-koordinaten i startposisjonen
  6. Det første som skjer er verdien i de midlertidige variablene overføres til globale variabler i klassen
  7. Klassens globale variabler har forstavelsen «this.»
  8. this.mass får verdien til temp_mass
  9. this.pos blir en vektorvariabel med temp_x og temp_y som verdier:
  10. Verdien av temp_m endrer seg hver gang et nytt objekt dannes
  11. I dette eksempelet er temp_x og temp_y alltid null.

applyForce(force) 

  1. Vi bruker Newtons andre lov F = ma -> a = F/m
  2. Vi lagrer akselerasjonen i variabelen «a»
  3. Vi legger akselerasjonen til den samlede akselerasjonen.

display()

  1. Ettersom massen nå er mellom 0.1 og 5, multipliserer vi med 16 for å få store synlige sirkler.

Ekstra forklaring

  1. De små sirklene tas lettere av vinden og treffer høyre kant raskere enn de store.
  2. Men de små sirklene har også større akselerasjon nedover.
  3. I virkeligheten faller alle legemer med samme akselerasjon mot jordoverflaten (hvis vi ser bort fra luftmotstand)
  4. Vi må justere tyngdeakselerasjonen.
  5. Det gjøres i neste eksempel.

2.3 Lik tyngdeakselerasjon

Nytt

  1. Konstant gravitasjon uavhengig av massen
  2. For gravitasjonen opphever  vi den divisjonen som foretas i metoden applyForce()
    (der kraften divideres med massen).
  3. For tyngdekraften skal ikke kraften divideres med massen.
  4. Tyngdeakselerasjonen skal være uavhengig av massen.
  • let movers = []; 
  • let numMovers = 10; 
  • ​let gravity;
  • let wind;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover(random(0.5, 5), 0, 0); 
    • }
    • wind = createVector(0.01, 0); 
  • }
  • function draw() {
    • background(220);
    • for (let i = 0; i < numMovers; i++) {
      • let m = movers[i].mass;
      • gravity = createVector(0, 0.1 * m);
      • movers[i].applyForce(wind);
      • movers[i].applyForce(gravity);
      • movers[i].update();
      • movers[i].checkEdges();
      • movers[i].display();
    • }
  • }
  • class Mover {
    • constructor(temp_m, temp_x, temp_y) {
      • this.mass = temp_m;
      • this.pos = createVector(temp_x, temp_y);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0);
    • }
    • applyForce(force) { 
      • let a = p5.Vector.div(force, this.mass); 
      • this.acceleration.add(a);
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -0.5;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -0.9;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -0.9;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -0.9;
      • }
    • }
    • display() {
    • fill(175, 100); // 100 betyr litt gjennomsiktig
    • circle(this.pos.x, this.pos.y, this.mass * 7);
    • }
  • }

function draw()​

  1. Vi lager en variabel som lager massen til det aktuelle objektet.
  2. Når vi oppretter gravitasjonkraften multipliserer vi y-komponenten med massen.

class Mover
display()

  1. Vi legger til en alfa-parameter for gjennomskinnelighet

checkEdges()

  1. I virkeligheten forsvinner noe av energien når ballene kolliderer med kantene.
  2. Vi legger derfor inn litt demping.
  3. I stedet for å multiplisere med -1, multipliserer vi med et tall mellom -1 og 0.

Ekstra forklaring

  1. Gravitasjonen er flyttet ned i draw()
  2. Da kan vi hente ut objektets masse og justere gravitasjonen for massen før objektet påføres gravitasjonskraft.
  3. Hadde gravitasjonen blitt værende i setup() måtte vi tatt med objektets gravitasjon med en egen parameter da objektet ble opprettet. Ellers hadde vi ikke fått tak i gravitasjonen da objektet skulle tegnes i draw()
  4. Vi nuller altså ut effekten av massen slik at tyngdeakselerasjonen er konstant slik som i virkeligheten.
  5.  I metoden «applyForce» dividerer vi med akselerasjonen, i draw() multipliserer vi.
  6. Når sirklene faller mot bunnen, flytter de seg i steg. 
  7. Det kan hende steget passerer bunnlinja før checkEdges() får testet: if (this.pos.y > height)
  8. Dermed kan de bli hengende igjen i nedre kant.
  9. Prøv å kommentere ut denne: this.pos.y = height;
  10. For å unngå dette flyttes sirklene fra en posisjon under lerret og opp til nedre kant: this.pos.y = height
  11. Ulempen med dette er at sirklenes makshøyde også blir litt høyere enn den ellers ville vært. Maksimal spretthøyde  stabiliserer seg etterhvert på et fast nivå.
  12. For å unngå dette kan vi multiplisere med et tall mellom -1 og null, f.eks. -0.9 .
  13. Da reduseres spretten for alle hopp, ikke bare for de første 8-10 hoppene.
  14. Sjekk innlegg nr 8 i denne diskusjonen:
    discourse.processing.org/t/bouncing-ball-hang-up-the-nature-of-code/8733
  15. I neste eksempel inkuderes friksjon. 
  16. Da sørger friksjonen for kontinuerlig demping

skolekoding.no
Stein Olav Kivle