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
Nytt
- Egen metode som legger til krefter til et objekt: mover.applyForce()
- Summere to vektorer (acceleration og force): this.acceleration.add(force)
- 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;
- }
- if (this.pos.x > width) {
- }
- display() {
- fill(175);
- // Diameteren symboliserer massen
- ellipse(this.pos.x, this.pos.y, this.mass);
- }
- constructor() {
- }
før setup()
- Vi lager en egen variabel for gravitasjon: gravity
function setup()
- Den får x-verdi lik 0 og y-verdi lik 0.01. Den peker altså nedover.
function draw()
- Vi kaller på objektet «mover» sin «applyForce»-metode med «gravity» som parameter.
- Altså: vi legger til en kraft («gravity») på objektet «mover».
- Gravity blir addert sammen med acceleration.
class Mover
constructor()
- Massen settes lik 35
- Posisjonen settes oppe til høyre i punkt (30, 30).
- Start-farten er lik null.
- Start-akselerasjonen er lik null.
applyForce(force)
- Vi lager en egen metode som tar med seg en kraft-vektor («force») inn i klassen.
- «force» er en lokal variabel i metoden. Skal derfor ikke bruke «this.» foran.
- «this.» brukes bare foran klassevariabler som deklareres i konstruktøren.
update()
- 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.
- Vi multipliserer med null etter at akselerasjonen er lagt til hastigheten for å nullstille akselerasjonen før ny runde i draw() og ny applyForce()
checkEdges()
- Når sirkelen treffer kanten gir vi sirkel- posisjon lik kanten-posisjonen før vi endrer retningen.
- 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.
Nytt
- Legger til vind
- 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;
- }
- if (this.pos.x > width) {
- }
-
- display() {
- fill(175);
- ellipse(this.pos.x, this.pos.y, this.mass);
- }
- constructor() {
- }
før setup()
- Vi lager en vind-variabel.
function setup()
- Vi lager en vindtvektor: 0.01 langs x-aksen, ingen langs y-aksen. Den har altså retning vannrett mot høyre.
function draw()
- Bruker applyForce-metoden for å legge til vindens akselerasjons-bidrag til den samlede akselerasjonen.
Nytt
- Vi legger til flere objekter med tabell og for-løkke
- Objektene ligger bak hverandre og synes derfor ikke.
- 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;
- }
- if (this.pos.x > width) {
- }
-
- display() {
- fill(175);
- ellipse(this.pos.x, this.pos.y, this.mass);
- }
- constructor() {
- }
før setup()
- Vi lager en variabel som skal inneholde en tabell (en liste) med objekter.
- En tabellvariabel deklareres nesten på samme måte som andre variabler: vi skriver likhetstegn og klammeparenteser etter variabelnavnet: let movers = []
- Klammeparenteser skrives med alt gr + 8 og alt gr + 9.
- Vi lager også en variabel som skal inneholde et tall for antall celler i tabellen: numMovers.
- 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()
- Vi lager en for-løkke.
- Løkka har en indeksvariabel med navn «i»
- i starter på null
- Så lenge i er mindre enn numMovers utføres instruksjonene mellom krøllparentesene
- Deretter øker i med 1 (i++ er en kortform av i = i + 1)
- Les mer om for-løkke : Processingkurs kap 4.5
- For hver runde i for-løkka opprettes et nytt objekt med en egen indeks:
- movers[0]
- movers[1]
- movers[2]
- o.s.v.
function draw()
- Vi bruker en for-løkke for å hente fram alle ti objektene
- For hver runde i for-løkka legges det til gravitasjon og vind, akselerasjonen legges til farten, kantene sjekkes og sirkelen tegnes.
- Først for movers[0]
- Deretter for movers[1]
- o.s.v.
Ekstra forklaring
- Det er en liten hake ved dette programeksemplet
- Når movers-objektene lages med utgangspunkt i klassen Mover blir alle objektene identiske.
- Alle får samme masse, startposisjon, hastighet m.m.
- Vi ser derfor fremdeles bare en sirkel. Alle ti sirklene ligger bak hverandre
- I det neste eksempelet skal vi lage ti ulike objekter.
- Vi ser også at kraften som virker på objektene er uavhengig av massen
- Vi vil gjerne at de lettere objektene lettere skal bli tatt av vinden enn de tyngre.
Nytt
- Argumenter i konstruktøren.
- Akselerasjon er tilpasset massen.
- Vi legger til argumenter i klassens konstruktør for å gi objektene ulik masse.
- Vi bruker Newtons 2. lov og justerer akselerasjonen ut i fra massen.
- 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;
- }
- if (this.pos.x > width) {
- }
- display() {
- fill(175);
- circle(this.pos.x, this.pos.y, this.mass*16);
- }
- constructor(temp_m, temp_x, temp_y) {
- }
function setup()
- Vi legger til tre argumenter når vi lager objektene
- Argumentene plasseres i parentesen etter «new Mover»
- Første parameter: tilfeldig masse mellom 0.1 og 5
- Andre parameter: x-posisjon = 0
- Tredje parameter: y-posisjon = 0
class Mover
constructor()
- Konstruktøren mottar tre parametre.
- Disse mottas gjennom midlertidige variable:
temp_m, temp_x og temp_y (kunne hatt andre navn) - temp_m er en tilfeldig «masse»mellom 5 og 10
- temp_x er x-koordinaten i startposisjonen
- temp_y er y-koordinaten i startposisjonen
- Det første som skjer er verdien i de midlertidige variablene overføres til globale variabler i klassen
- Klassens globale variabler har forstavelsen «this.»
- this.mass får verdien til temp_mass
- this.pos blir en vektorvariabel med temp_x og temp_y som verdier:
- Verdien av temp_m endrer seg hver gang et nytt objekt dannes
- I dette eksempelet er temp_x og temp_y alltid null.
applyForce(force)
- Vi bruker Newtons andre lov F = ma -> a = F/m
- Vi lagrer akselerasjonen i variabelen «a»
- Vi legger akselerasjonen til den samlede akselerasjonen.
display()
- Ettersom massen nå er mellom 0.1 og 5, multipliserer vi med 16 for å få store synlige sirkler.
Ekstra forklaring
- De små sirklene tas lettere av vinden og treffer høyre kant raskere enn de store.
- Men de små sirklene har også større akselerasjon nedover.
- I virkeligheten faller alle legemer med samme akselerasjon mot jordoverflaten (hvis vi ser bort fra luftmotstand)
- Vi må justere tyngdeakselerasjonen.
- Det gjøres i neste eksempel.
Nytt
- Konstant gravitasjon uavhengig av massen
- For gravitasjonen opphever vi den divisjonen som foretas i metoden applyForce()
(der kraften divideres med massen). - For tyngdekraften skal ikke kraften divideres med massen.
- 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;
- }
- if (this.pos.x > width) {
- }
- display() {
- fill(175, 100); // 100 betyr litt gjennomsiktig
- circle(this.pos.x, this.pos.y, this.mass * 7);
- }
- constructor(temp_m, temp_x, temp_y) {
- }
function draw()
- Vi lager en variabel som lager massen til det aktuelle objektet.
- Når vi oppretter gravitasjonkraften multipliserer vi y-komponenten med massen.
class Mover
display()
- Vi legger til en alfa-parameter for gjennomskinnelighet
checkEdges()
- I virkeligheten forsvinner noe av energien når ballene kolliderer med kantene.
- Vi legger derfor inn litt demping.
- I stedet for å multiplisere med -1, multipliserer vi med et tall mellom -1 og 0.
Ekstra forklaring
- Gravitasjonen er flyttet ned i draw()
- Da kan vi hente ut objektets masse og justere gravitasjonen for massen før objektet påføres gravitasjonskraft.
- 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()
- Vi nuller altså ut effekten av massen slik at tyngdeakselerasjonen er konstant slik som i virkeligheten.
- I metoden «applyForce» dividerer vi med akselerasjonen, i draw() multipliserer vi.
- Når sirklene faller mot bunnen, flytter de seg i steg.
- Det kan hende steget passerer bunnlinja før checkEdges() får testet: if (this.pos.y > height)
- Dermed kan de bli hengende igjen i nedre kant.
- Prøv å kommentere ut denne: this.pos.y = height;
- For å unngå dette flyttes sirklene fra en posisjon under lerret og opp til nedre kant: this.pos.y = height
- 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å.
- For å unngå dette kan vi multiplisere med et tall mellom -1 og null, f.eks. -0.9 .
- Da reduseres spretten for alle hopp, ikke bare for de første 8-10 hoppene.
- Sjekk innlegg nr 8 i denne diskusjonen:
discourse.processing.org/t/bouncing-ball-hang-up-the-nature-of-code/8733 - I neste eksempel inkuderes friksjon.
- Da sørger friksjonen for kontinuerlig demping
skolekoding.no
Stein Olav Kivle