I Scratch kan du definiera ett eget block som har ett valfritt antal parametrar. Efter att du definierat blocket kan du använda det i den övriga koden och skicka in argument som anger vilka värden parametrarna ska ha.
I Processing motsvaras egna block av egna funktioner. En egenhändigt definierad funktion används för att strukturera koden. Om du upprepar samma kod på flera ställen i ett program blir programmet kortare och enklare om den kod som upprepas läggs in i en funktion. Även om kod inte upprepas kan det i vissa fall vara enklare att förstå koden om den strukturerats med hjälp av funktioner.
Tänk dig att du ska göra ett dataspel i Processing och att nästan all kod ligger i draw-funktionen. För att strukturera arbetet kan koden delas upp i de tre tillstånd spelet befinner sig i.
Ett dataspel
Visa startsida
Låt användaren spela
Visa resultat
Vad som ska hända i varje tillstånd kan beskrivas separat.
Visa startsida
Visa meny som låter spelaren välja svårighetsgrad.
Visa startknapp som sätter igång spelet
Den separata beskrivningen kan implementeras av en egenhändigt definierad funktion.
Innan vi kommer in på funktioner ska vi gå igenom hur man gör enkla objekt i Processing. Liksom funktioner används enkla objekt för att strukturera koden.
Ett objekt inom programmering har ett antal egenskaper och ett antal metoder. Ett objekt inom Javascript som vi stött på är en indexerad lista. Varje lista i Javascript har en egenskap length
som håller reda på hur många element listan innehåller. En lista har också en metod push
som lägger till ett element längst bak i listan. Man kommer åt ett objekts egenskaper och metoder med punknotation, dvs efter objektets namn skriver man en punkt följt av en egenskap eller en metod.
Följande kod:
let bilar = ["SAAB", "Volvo", "Toyota"];
print("Antal bilar = " + bilar.length); //punknotation för att komma åt antalet
bilar.push("BMW"); //punktnotation för att lägga till ett element
print("Antal bilar = " + bilar.length);
ger utskriften
Antalet bilar = 3 Antalet bilar = 4
Vi ska inte gå igenom hur man gör egna objekt som har metoder. Däremot ska vi gå igenom hur man gör egna objekt som har ett antal egenskaper.
En egenskap till ett objekt har ett namn och ett värde, precis som en variabel. Om du har ett antal variabler vars värden hänger ihop, kan du skapa ett objekt som har dessa värden som egenskaper. Några exempel på objekt med egenskaper är:
pos
med egenskaperna x
och y
.hastighet
med egenskaperna x
och y
.cirkel
med egenskaperna x
, y
och radie
.rektangel
med egenskaperna x
, y
, bredd
och höjd
.cirkel
med egenskaperna x
, y
, deltax
, deltay
, radie
och färg
, där deltax
och deltay
representerar steget som tas per tidsenhet, dvs hastigheten.När du ska tilldela ett objekt ett antal egenskaper samlar du alla egenskaper mellan måsvingar. Egenskaperna separeras av komma och för varje egenskap skriver du namnet, kolon, därefter värdet.
function setup() {
let person;
person = {namn: "Eva", ålder: 36};
print(person.namn + " är " + person.ålder + " år gammal.");
}
Objekt som ska kunna användas i både setup- och draw-funktionen deklareras globalt, dvs överst i programmet.
let pos, hastighet;
function setup() {
createCanvas(windowWidth, windowHeight);
background(100);
pos = {x: 200, y: 300};
hastighet = {x: 5, y: 7};
}
function draw() {
ellipse(pos.x, pos.y, 20, 20);
pos.x += hastighet.x;
pos.y += hastighet.y;
}
Om ett objekt har många egenskaper blir koden enklare att läsa om man använder radbrytning vid tilldelningen. Lägg märke till att det inte ska vara ett kommatecken efter sista egenskapen.
let pos, hastighet;
function setup() {
createCanvas(windowWidth, windowHeight);
background(100);
pos = { // en egenskap per rad
x: 200,
y: 300
};
hastighet = {
x: 5,
y: 7
};
}
Du kan i variabeldeklarationen ange att en variabel ska vara ett objekt genom att tilldela den "ett tomt objekt". Därefter kan du tilldela egenskaperna värden genom att använda punktnotation.
let pos = {}, hastighet = {}; //variablerna är objekt
function setup() {
createCanvas(windowWidth, windowHeight);
background(100);
pos.x = 200; //punktnotation vid tilldelning
pos.y = 100;
hastighet.x = 5;
hastighet.y = 7;
}
På detta vis kan vi göra en cirkel som har alla de egenskaper som behövs för en studsande boll. Bollens färg kan vi lagra med hjälp av funktionen color(<röd>, <grön>, <blå>)
vilken ger oss en rgb-färg.
Koden ritar upp en boll som rör sig med konstant hastighet. Hastigheten ges av hur mycket x- och y-koordinaten förändras per tidsenhet. Denna förändringen kallar vi deltax
respektive deltay
.
let boll;
function setup() {
createCanvas(windowWidth, windowHeight);
boll = {
x: width / 2,
y: height / 2,
deltax: 5,
deltay: -4,
radie: 15,
färg: color(255, 150, 0)
};
}
function draw() {
background(255);
fill(boll.färg);
ellipse(boll.x, boll.y, 2 * boll.radie);
boll.x += boll.deltax;
boll.y += boll.deltay;
}
Lägg till if-satser i draw-funktionen som gör att bollen studsar i kanten, dvs ändra riktning i x-led om bollen är vid den vänstra eller högra kanten och ändra riktning i y-led om bollen är vid den övre eller nedre kanten.
I Exempel 1 hade vi kunnat använda sex variabler istället för ett objekt med sex egenskaper. Om vi behöver hantera flera olika saker som ska ritas upp, exempelvis flera bollar, blir koden dock mer strukturerad om vi använder objekt istället för väldigt många variabler. Om vi exempelvis hade haft bollar i listor, hade vi behövt flera listor, en för varje boll-egenskap som ska kunna variera. Genom att göra varje boll till ett objekt, räcker det med en enda lista av bollar.
Vi kan göra en lista med positioner där varje position är ett objekt med egenskaperna x
och y
.
Följande kod ritar upp 50 cirklar.
let pos = [];
function setup() {
let i;
createCanvas(windowWidth, windowHeight);
for (i = 0; i < 50; i += 1) {
pos[i] = { // varje element i listan är ett objekt
x: 100 + 20 * i,
y: height / 2
};
}
}
function draw() {
let i;
background(100);
for (i = 0; i < pos.length; i += 1) {
ellipse(pos[i].x, pos[i].y, 20);
}
ellipse(mouseX, mouseY, 20);
}
Lägg märke till att punktnotation används på pos[i]
.
Ändra i koden för setup-funktionen så att positionen i både x-led och y-led blir ett slumptal.
Istället för att göra en boll kan vi göra 50 bollar som kastas ut från mitten av ritområdet.
Följande kod ritar upp 50 bollar som rör sig från mitten av ritområdet. När en boll kommer till kanten börjar den om i mitten av ritområdet.
let boll = [];
function setup() {
let i;
createCanvas(windowWidth, windowHeight);
for (i = 0; i < 50; i += 1) {
boll[i] = {};
boll[i].x = width / 2;
boll[i].y = height / 2;
boll[i].deltax = random(-10, 10);
boll[i].deltay = random(-10, 10);
boll[i].radie = random(2, 30);
boll[i].färg = color(random(256), 150, 0);
}
}
function draw() {
let i;
background(255);
for (i = 0; i < boll.length; i += 1) {
fill(boll[i].färg);
ellipse(boll[i].x, boll[i].y, 2 * boll[i].radie);
boll[i].x += boll[i].deltax;
boll[i].y += boll[i].deltay;
if (boll[i].x > width || boll[i].x < 0 ||
boll[i].y > height || boll[i].y < 0) {
boll[i].x = width / 2;
boll[i].y = height / 2;
}
}
}
Ändra koden så att det istället är 500 bollar.
Om du försöker göra en kopia av ett objekt kommer koden att bete sig på ett till synes märkligt sätt. Med följande kod får objekt1
två egenskaper med varsitt värde. Därefter kopieras dessa värden till objekt2
.
function setup() {
let objekt1, objekt2;
objekt1 = {x: 100, y:50};
objekt2 = objekt1;
objekt1.x = 500;
print(objekt2.x);
}
När objekt1.x
får ett nytt värde, kommer även värdet på objekt2.x
att förändras. Utskriften blir:
500
För att förstå detta beteende kan man tänka sig att ett objekt refererar till ett antal minnesceller där egenskaperna lagras.
När objekt2
tilldelas objekt1
, kommer objekt2
att referera till samma sjok minnesceller, det skapas inte en lagring i nya minnesceller.
När objekt1.x
ändras, kommer värdet i en minnescell att ändras vilket även påverkar värdet på objekt2.x
.
Om du vill göra en kopia så att kopians egenskaper lagras i nya minnesceller, kan du kopiera varje egenskap istället för att kopiera hela objektet, exempelvis med koden:
function setup() {
let objekt1, objekt2;
objekt1 = {x: 100, y:50};
objekt2 = {};
objekt2.x = objekt1.x;
objekt2.y = objekt2.y;
objekt1.x = 500;
print(objekt2.x);
}
Eller med koden:
function setup() {
let objekt1, objekt2;
objekt1 = {x: 100, y:50};
objekt2 = {x: objekt1.x, y: objekt1.y};
objekt1.x = 500;
print(objekt2.x);
}
Då blir utskriften
100
Det finns två sorters funktioner i programmering:
Några vanliga funktioner som returnerar ett värde är de fördefinierade matematiska funktionerna som finns i Processing. Dessa funktioner beskrivs under rubriken Math i referensbiblioteket till p5.js, dvs på sidan p5js.org/reference/. Exempelvis tar funktionen sqrt()
emot ett argument som är ett tal och returnerar kvadratroten av talet. Det går exempelvis att skriva koden:
print("Roten ur 25 är " + sqrt(25) + ".");
Vid anropet sqrt(25)
beräknas kvadratroten av 25.
När du ska definiera en egen funktion använder du följande struktur:
function funktionsNamn(<parameter1>, <parameter2>, ...) {
//kod som ska utföras när funktionen anropas
}
Om du vill att din funktion ska returnera ett värde måste du sist i koden använda ordet return
följt av det värde som ska returneras.
Det går också bra att definiera en funktion som använder någon parameter alls. Vid anropet av en sådan funktion måste man skriva parenteser efter funktionsnamnet trots att inga argument ska skickas in till funktionen. Ett anrop av en sådan funktion kan se ut så här:
enFunktion();
Vi börjar med att göra en funktion som returnerar ett värde. Funktionen beräknar hypotenusan i en rätvinklig triangel. Funktionen har två parametrar för kateterna. Vi tänker oss att c är hypotenusan i en rätvinklig triangel med kateteterna a och b.
function setup() {
let a, b , c;
a = 3;
b = 4;
c = hypotenusa(a, b);
print("Hypotenusan är " + c + " längdenheter.");
}
function hypotenusa(katet1, katet2) {
let resultat;
resultat= sqrt(katet1*katet1 + katet2*katet2);
return resultat;
}
Koden i setup-funktionen kan skrivas helt utan variabler. Koden i definitionen av funktionen hypotenusa
kan också skrivas utan variabler, efter ordet return
kan det stå ett uttryck.
function setup() {
print("Hypotenusan är " + hypotenusa(3, 4) + " längdenheter.");
}
function hypotenusa(katet1, katet2) {
return sqrt(katet1 * katet1 + katet2 * katet2);
}
En funktion som inte returnerar någonting utan bara utför ett stycke kod kan exempelvis rita upp någonting.
Funktionen smiley
har tre parametrar och ritar upp en glad gubbe.
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES);
strokeWeight(3);
}
function draw() {
background(100);
smiley(mouseX, mouseY, 100);
}
function smiley(x, y, storlek) {
fill(255, 255, 0);
ellipse(x, y, storlek);
fill(0);
ellipse(x - 0.2 * storlek, y - 0.1 * storlek, 0.1 * storlek);
ellipse(x + 0.2 * storlek, y - 0.1 * storlek, 0.1 * storlek);
noFill();
arc(x, y + 0.1 * storlek, 0.5 * storlek, 0.5 * storlek, 0, 180);
}
Testa att ändra det tredje argumentet i anropet till en annan storlek. Testa också att låta mouseX
vara det tredje argumentet så att gubben blir större ju längre åt höger muspekaren är.
De två sista argumenten till kommandot arc
anger startvinkel och slutvinkel för cirkelbågen. Ändra i koden så att det istället ritas ut en sur gubbe. Se till att det är lagom avstånd mellan munnen och ögonen.
Lägg märke till att det inte spelar någon roll let funktionen smiley()
definieras. Definitionen ligger efter draw-funktionen där den anropas.
När du ska rita upp vad som ska hända i ett spel kan det som ska ritas upp bero på vilket tillstånd spelet befinner sig i. För att exemplifiera olika tillstånd tänker vi oss ett spel där användaren ska sikta och skjuta. Spelet har bara två tillstånd, tillståndet för att sikta och tillståndet för att skjuta.
Vi tänker oss att en boll ska skjutas iväg från någon startpunkt. Bollen ska ha en position och en hastighet. Vi börjar med att deklarera fyra variabler:
tillstånd
kan ha två värden 0 eller 1.startpunkt
är ett objekt för startpunktens position.boll
är ett objekt för en bollens position.hastighet
är ett objekt för en bollens hastighet.Sedan använder vi följande grundstruktur för koden:
let tillstånd, startpunkt, hastighet, boll;
function setup() {
createCanvas(windowWidth, windowHeight);
tillstånd = 0;
startpunkt = {x: 0.2 * width, y: 0.9 * height};
}
function draw() {
background(100);
ritaStartpunkt();
if (tillstånd == 0) {
sikta();
} else {
skjut();
}
}
function ritaStartpunkt() {
// kod för att rita startpunkt
}
function sikta() {
// kod för att sikta
}
function skjut() {
//kod för att skjuta
}
Efter att vi bestämt oss för denna övergripande struktur kan vi hantera detaljerna. Det är tre funktioner som ska definieras: ritaStartpunkt()
, sikta()
, och skjut()
. Bollen ska skjutas iväg när användaren klickar med musen. Funktionen mouseClicked()
måste också hanteras.
Funktionen ritaStartpunkt()
kan göras mer eller mindre avancerad. Man kan lägga in en bild eller med kod rita något komplext. Man kan också bara rita en vit cirkel.
function ritaStartpunkt() {
fill(255);
ellipse(startpunkt.x, startpunkt.y, 50);
}
Funktionen sikta()
ska rita en linje från startpunkten till muspekaren. Den ska också ge objektet hastighet
en riktning längs linjen. Vi multiplicerar hastigheten med någon faktor som är liten.
function sikta() {
let faktor = 0.1;
stroke(255);
strokeWeight(3)
line(startpunkt.x, startpunkt.y, mouseX, mouseY);
hastighet = {
x: faktor * (mouseX - startpunkt.x),
y: faktor * (mouseY - startpunkt.y)
}
}
När användaren klickar med musen ska bollen skjutas iväg, dvs tillståndet ska sättas till ett. Bollen ska få samma position som startpunkten. Detta ska bara hända om tillståndet är noll.
function mouseClicked() {
if (tillstånd == 0) {
tillstånd = 1;
boll = {x: startpunkt.x, y: startpunkt.y};
}
}
Funktionen skjut()
ska ändra bollens position och rita upp bollen. Om bollen når kanten ska tillståndet sättas till noll.
function skjut() {
boll.x += hastighet.x;
boll.y += hastighet.y;
noStroke();
fill(255, 0, 0);
ellipse(boll.x, boll.y, 20);
if (boll.x > width || boll.y > height || boll.x < 0 || boll.< 0) {
tillstånd = 0;
}
}
Genom att på detta vis arbeta med övergripande struktur och fylla i detaljerna i efterhand kan man skapa komplexa program.
Vi har hittills bara hanterat variabler som har varit antingen tal eller textsträngar. En variabel kan också vara en så kallad boolesk variabel som bara antar värdena true
eller false
. En boolesk variabel kan användas för tillstånd som är antingen "av" eller "på".
Med denna kod kan användaren dra i en cirkel. Variabeln dra
används för att avgöra om cirkeln dras eller inte. När användaren sätter ner musknappen med muspekaren på cirkeln sätts dra
på. När användaren släpper musknappen stängs dra
av.
let dra, x, y, radie;
function setup() {
createCanvas(windowWidth, windowHeight);
dra = false;
x = width / 2;
y = height / 2;
radie = 30;
}
function draw() {
background(100);
if (dra) {
x = mouseX;
y = mouseY;
}
ellipse(x, y, 2 * radie);
}
function mousePressed() {
if (dist(mouseX, mouseY, x, y) < radie) {
dra = true;
}
}
function mouseReleased() {
dra = false;
}
Testkör koden och testa att dra cirkeln till en annan position.
En funktion kan returnera ett booleskt värde som kan användas som villkor i en villkorssats. Om vi exempelvis vill avgöra huruvida muspekaren befinner sig på en rektangel kan vi skriva följande if-sats.
if ( mouseX > x && mouseY > y && mouseX < x + bredd && mouseY < y + höjd }
// kod som ska utföras när muspekaren är på rektangeln
}
Detta villkor kan läggas in som kod till en funktion som returnerar ett booleskt värde. De egenskaper en rektangel har (x
, y
, bredd
, höjd
) kan vara egenskaper till ett rektangel-objekt. Funktionen som kollar om muspekaren är på rektangeln kan ha en parameter som är ett rektangel-objekt
En funktion har ersatt ett villkor i en if-sats.
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
let rekt;
background(100);
rekt = {
x: 100,
y: 100,
bredd: 200,
höjd: 50
}
if (påRektangel(rekt)) {
fill(100, 100, 255);
} else {
fill(255);
}
rect(rekt.x, rekt.y, rekt.bredd, rekt.höjd);
}
function påRektangel(r) {
return mouseX>r.x && mouseY>r.y && mouseX<r.x+r.bredd && mouseY<r.y+r.höjd;
}
Testkör koden och dra med musen över rektangeln.
Nu kan vi använda en snarlik kod för en lista av slumpvis placerade rektanglar. Vi deklarerar en lista rekt
globalt och skapar listan i en funktion. Funktionen returnerar en lista som tilldelas rekt
. För att ge varje rektangel blå färg när man håller musen på rektangeln använder vi en loop. Vi återanvänder koden för påRektangel(r)
.
Kod för flera 20 slumpvis placerade rektanglar.
let rekt = [];
function setup() {
createCanvas(windowWidth, windowHeight);
rekt = görRektanglar();
}
function draw() {
let i;
background(100);
for (i = 0; i < rekt.length; i += 1) {
if (påRektangel(rekt[i])) {
fill(100, 100, 255);
} else {
fill(255);
}
rect(rekt[i].x, rekt[i].y, rekt[i].bredd, rekt[i].höjd);
}
}
function görRektanglar() {
let resultat = [], i;
for (i = 0; i < 20; i += 1) {
resultat[i] = {
x: random(width),
y: random(height),
bredd: 70,
höjd: 30
}
}
return resultat;
}
function påRektangel(r) {
return mouseX > r.x && mouseY > r.y &&
mouseX < r.x + r.bredd && mouseY < r.y + r.höjd;
}
Se till att det skapas nya rektanglar när användaren klickar med musen, dvs använd funktionen görRektanglar()
i funktionen mouseClicked()
.
Avslutningsvis ska vi visa tre längre kodexempel där rektanglar används för att låta användaren göra val.
Menyer används för att låta användaren göra val genom att klicka. En meny kan exempelvis låta användaren välja pennans utseende i ett ritprogram. Vi ska göra ett sådant ritprogram där användaren kan välja mellan att rita med en penna som är en ellips (som inte är en cirkel), en cirkel eller en kvadrat. Varje alternativ ska visas i en rektangel med en text.
Vi lagrar de tre alternativen i en lista meny
. Varje element i listan ska ha en rektangels egenskaper samt ett namn. Menyn ska ligga i den vänstraste femtedelen av ritområdet. För att göra alla positioneringar enkla använder vi rectMode(CENTER)
vilken låter en rektangels position le mitten av rektangeln. För de texter som ska visas använder vi textAlign(CENTER, CENTER)
vilken centrerar texten både vertikalt och horisontellt.
Förutom variabeln meny
behöver vi också en variabel val
vilken håller reda på vilken penna användaren valt.
Grundstrukturen kan se ut så här:
let val, meny = [];
function setup() {
let i;
createCanvas(windowWidth, windowHeight);
background(100);
val = 0;
textAlign(CENTER, CENTER);
rectMode(CENTER);
for (i = 0; i < 3; i += 1) {
meny[i] = {
x: 0.1 * width,
y: (i + 1) * height / 4,
bredd: 0.15 * width,
höjd: height * 0.1
}
}
meny[0].namn = "Ellips";
meny[1].namn = "Cirkel";
meny[2].namn = "Kvadrat";
}
function draw() {
ritaMeny();
fill(0)
ritaMedPennan();
}
function ritaMedPennan() {
// kod för att låta användaren rita
}
function ritaMeny() {
// kod för att rita upp menyn
}
function påRektangel(r) {
// kod som kollar om muspekaren är på en rektangel
}
function mouseClicked() {
// kod för att hantera menyval
}
Användaren ska bara kunna rita om muspekaren inte är i den vänstraste femtedelen av ritområdet. Koden för att låta användaren rita kan se ut så här:
function ritaMedPennan() {
if (mouseX > 0.2 * width && mouseIsPressed) {
if (val == 0) {
ellipse(mouseX, mouseY, 10, 30);
} else if (val == 1) {
ellipse(mouseX, mouseY, 20, 20);
} else {
rect(mouseX, mouseY, 20, 20);
}
}
}
Menyn ska ritas ut så att det valda alternativet ritas i en annan färg. Vi börjar med att rita över hela området till vänster med en mörkgrå färg. Koden kan se ut så här:
function ritaMeny() {
let i;
fill(50);
rect(0.1 * width, 0.5 * height, 0.2 * width, height);
for (i = 0; i < meny.length; i += 1) {
if (i == val) {
fill(200, 200, 255);
} else {
fill(255);
}
rect(meny[i].x, meny[i].y, meny[i].bredd, meny[i].höjd, 10);
fill(0);
text(meny[i].namn, meny[i].x, meny[i].y);
}
}
Eftersom rektangelns position nu är i mitten av rektangeln måste den funktion påRektangel(r)
som vi tidigare använt modifieras.
function påRektangel(r) {
return mouseX > r.x - r.bredd / 2 && mouseY > r.y - r.höjd / 2 &&
mouseX < r.x + r.bredd / 2 && mouseY < r.y + r.höjd / 2;
}
Slutligen gör vi koden som hanterar användarens val.
function mouseClicked() {
let i;
for (i = 0; i < meny.length; i += 1) {
if (påRektangel(meny[i])) {
val = i;
}
}
}
Att låta användaren göra val genom att klicka på rektangelformade menyer kan användas till att ställa flervalsfrågor. Vi kan göra ett enkelt program för att öva på multiplikationstabellen. Användaren får välja mellan tre olika svarsalternativ. Efter att användaren svarat ska det visa någon återkoppling som visar om svaret le rätt eller fel, därefter ska användaren kunna fortsätta öva.
Programmet kommer hela tiden att befinna sig i ett av två tillstånd, antingen ska det visas en fråga med tre svarsalternativ eller så ska det visas någon återkoppling.
Svarsalternativen lagras i en lista rekt
av objekt som förutom en rektangels egenskaper också innehåller ett tal.
Vi behöver variabler för de två tal som ska multipliceras, en variabel för rätt index och en variabel för det index som användaren valt. En grundstruktur kan se ut så här:
let tillstånd, rekt = [], valtIndex, rättIndex, tal1, tal2;
function setup() {
let i;
createCanvas(windowWidth, windowHeight);
background(100);
tillstånd = 0;
textAlign(CENTER, CENTER);
rectMode(CENTER);
textSize(32);
for (i = 0; i < 3; i += 1) {
rekt[i] = {
x: (i + 1) * width / 4,
y: 0.6 * height,
bredd: 0.15 * width,
höjd: height * 0.1
}
}
initieraVärden();
}
function draw() {
if (tillstånd == 0) {
visaAlternativ()
} else {
visaÅterkoppling()
}
}
function initieraVärden() {
// kod som generar de värden som ska användas
}
function visaAlternativ() {
// kod som visar fråga och svarsalternativ
}
function visaÅterkoppling() {
// kod som visar rätt eller fel
}
function påRektangel(r) {
return mouseX > r.x - r.bredd / 2 && mouseY > r.y - r.höjd / 2 &&
mouseX < r.x + r.bredd / 2 && mouseY < r.y + r.höjd / 2;
}
function mouseClicked() {
// kod för att hantera musklickningar
}
Vi kan återanvända koden för funktionen påRektangel(r)
som används för rektanglar med centrerad position.
Vid initieringen av värden ska tal1
, tal2
och rättIndex
slumptalsgenereras. Eftersom vi bara vill ha heltal använder vi funktionen floor()
på det slumptalsgenererade flyttalet. För att slumptalsgenera ett heltal mellan 1 och 10 används kommandot:
tal1 = floor(random(1, 11));
Varje listelement i listan rekt
ska tilldelas ett heltal som antingen är ett slumptal mellan 1 och 100 eller som är produkten av tal1
och tal2
beroende på om listelementets index är lika med rättIndex
eller inte.
Vi kan använda följande kod för detta:
for (i = 0; i < rekt.length; i += 1) {
if (i == rättIndex) {
rekt[i].tal = tal1 * tal2;
} else {
rekt[i].tal = floor(random(1, 101));
}
}
Problemet med koden ovan är att det finns en viss sannolikhet att samma tal kommer med flera gånger. För att programmet alltid ska fungera måste de tre talen garanterat vara olika. Om något av de tre talen är lika med något annat av de tre talen vill vi göra om hela slumptalsgenereringen. Vi använder en do-while-sats för att slumptalsgenerera tal tills de garanterat är olika. Hela for-satsen kan läggas inuti en do-while-sats.
do {
for (i = 0; i < rekt.length; i += 1) {
if (i == rättIndex) {
rekt[i].tal = tal1 * tal2;
} else {
rekt[i].tal = floor(random(1, 101));
}
}
} while (rekt[0].tal == rekt[1].tal || rekt[0].tal == rekt[2].tal ||
rekt[1].tal == rekt[2].tal);
Koden för funktionen initieraVärden()
är sammanfattningsvis:
function initieraVärden() {
let i;
tal1 = floor(random(1, 11));
tal2 = floor(random(1, 11));
rättIndex = floor(random(3));
do {
for (i = 0; i < rekt.length; i += 1) {
if (i == rättIndex) {
rekt[i].tal = tal1 * tal2;
} else {
rekt[i].tal = floor(random(1, 101));
}
}
} while (rekt[0].tal == rekt[1].tal || rekt[0].tal == rekt[2].tal ||
rekt[1].tal == rekt[2].tal);
}
I koden för visaAlternativ()
ska frågan ställas sedan ska de tre svarsalternativen visas. Om muspekaren befinner sig på en rektangel ska denna visas i en annan färg.
function visaAlternativ() {
let i;
background(100);
text("Vad är " + tal1 + "*" + tal2 + "?", 0.5 * width, 0.4 * height);
for (i = 0; i < rekt.length; i += 1) {
if (påRektangel(rekt[i])) {
fill(200, 200, 255);
} else {
fill(255);
}
rect(rekt[i].x, rekt[i].y, rekt[i].bredd, rekt[i].höjd, 10);
fill(0);
text(rekt[i].tal, rekt[i].x, rekt[i].y);
}
}
Funktionen visaÅterkoppling()
anropas efter det att användaren valt ett svarsalternativ. Vi kan förutsätta att variabeln valtIndex
har rätt värde.
function visaÅterkoppling() {
background(100);
if (valtIndex == rättIndex) {
text("Rätt!", 0.5 * width, 0.4 * height);
} else {
text("Fel!", 0.5 * width, 0.4 * height);
}
text("Klicka för att fortsätta!", 0.5 * width, 0.6 * height);
}
Avslutningsvis ska vi hantera användarens musklickningar.
function mouseClicked() {
let i;
if (tillstånd == 0) {
for (i = 0; i < rekt.length; i += 1) {
if (påRektangel(rekt[i])) {
valtIndex = i;
tillstånd = 1;
}
}
} else {
tillstånd = 0;
initieraVärden();
}
}
Det går att göra flervalsövningar där ett objekt ska paras ihop med ett annat objekt. Det kan handla om glosor, vad heter djurens barn, namngeografi eller andra par av ord. Det kan också vara ett objekt som är en bild och ett objekt som är ett ord, exempelvis en bild på en vinkel som ska paras ihop med något av orden "spetsig", "rät" eller "trubbig".
Vi ska göra ett program där användaren ska para ihop huvudstad och land. För detta behöver vi en lista land
som innehåller objekt med varje lands namn och dess huvudstad. För enkelhetens skull visar vi lika många alternativ som det finns element i listan land
. Vi lagrar rektangeldata i listan rekt
.
Vi lägger in fem länder i listan och utgår ifrån följande grundstruktur:
let land = [], rekt = [], rättIndex, valtIndex, tillstånd;
function setup() {
let i
createCanvas(windowWidth, windowHeight);
textAlign(CENTER, CENTER);
rectMode(CENTER);
textSize(24);
land[0] = {namn: "Sverige", stad: "Stockholm"};
land[1] = {namn: "Danmark", stad: "Köpenhamn"};
land[2] = {namn: "Norge", stad: "Oslo"};
land[3] = {namn: "Finland", stad: "Helsingfors"};
land[4] = {namn: "Island", stad: "Reykavik"};
for (i = 0; i < land.length; i += 1) {
rekt[i] = {
x: (i+1) * width / (land.length + 1),
y: 0.6 * height,
bredd: width / (land.length + 1) - 50,
höjd: height * 0.1
};
}
rättIndex = floor(random(land.length));
tillstånd = 0;
}
function draw() {
background(100);
if (tillstånd == 0) {
visaFråga();
} else {
visaResultat();
}
}
function påRektangel(r) {
return mouseX > r.x - r.bredd / 2 && mouseY > r.y - r.höjd / 2 &&
mouseX < r.x + r.bredd / 2 && mouseY < r.y + r.höjd / 2;
}
function visaFråga() {
// kod som visar frågan och alternativen
}
function visaResultat() {
// kod som visar rätt eller fel
}
function mouseClicked() {
// kod för att hantera musklickningar
}
Det enda som ska slumptalsgenereras är värdet på rättIndex
. Detta görs i setup-funktionen och om användaren vill fortsätta öva efter att ha sett resultatet, dvs om användaren klickar när tillståndet är 1. Om användaren klickar när tillståndet är 0, ska eventuellt valtIndex
få ett värde och tillståndet ska sättas till 1. Koden för musklickningar blir:
function mouseClicked() {
let i;
if (tillstånd == 0) {
for (i = 0; i < rekt.length; i += 1) {
if (påRektangel(rekt[i])) {
valtIndex = i;
tillstånd = 1;
}
}
} else {
tillstånd = 0;
rättIndex = floor(random(land.length));
}
}
Koden för att visa frågan och svarsalternativen är snarlik koden för att fråga om multiplikation.
function visaFråga() {
let i;
text("Vad heter huvudstaden i " + land[rättIndex].namn + "?", 0.5 * width, 0.3 * height);
for (i = 0; i < rekt.length; i += 1) {
if (påRektangel(rekt[i])) {
fill(100, 100, 255);
} else {
fill(255);
}
rect(rekt[i].x, rekt[i].y, rekt[i].bredd, rekt[i].höjd, 10);
fill(0);
text(land[i].stad, rekt[i].x, rekt[i].y);
}
}
Koden för att visa resultat är snarlik koden för återkoppling om multiplikation.
function visaResultat() {
if (valtIndex == rättIndex) {
text("Rätt!", width * 0.5, height * 0.4);
} else {
text("Fel!", width * 0.5, height * 0.4);
}
text(land[rättIndex].stad + " är huvudstad i " + land[rättIndex].namn + ".", width * 0.5, height * 0.5);
text("Klicka för att fortsätta!", width * 0.5, height * 0.6);
}
Den här koden fungerar men är inte särskilt generell. Tänk om vi hade velat öva på 50 länder, eller 500 glosor, då hade det inte fått plats att visa ett svarsalternativ per element i listan. Hur koden för en sådan övning skulle se ut ger vi tips om i en av uppgifterna.
Skriv ut i konsol
Deklarera tre objekt i setup-funktionen med följande egenskaper.
Ge objektens egenskaper valfria värden. Skriv sedan ut kort information om varje objekt i konsolen.
Snöfall
Gör 50 vita cirklar som rör sig nedåt. Se till att det inte ritas ut någon kantfärg. När en snöflinga når den nedre kanten ska den få en ny slumpmässig x-position och en y-position som är vid den övre kanten.
Resa genom rymden
Gör en lista av 50 stjärnor vilka ritas som cirklar. Ge stjärnorna en slumpmässig position och radie i en for-sats. Efter att stjärnorna fått en position ska de få riktningar i en ny for-sats. Riktningen ska inte vara slumpmässig utan peka ut ifrån mitten av ritområdet.
Riktningar kan exempelvis skrivas med koden
for (i = 0; i < 50; i += 1) {
stjärna[i].deltax = (stjärna[i].x-width/2)*0.05;
stjärna[i].deltay = (stjärna[i].y-height/2)*0.05;
}
där 0.05 är en faktor som gör vektorn kortare.
Se till att alla stjärnor rör sig i draw-funktionen.
När en stjärna når en kant ska den få en ny position och en ny radie. Därefter ska den få en ny riktning.
Studsande smiley
Gör ett program för en studsande boll. När allting fungerar ska du rita upp bollen med en funktion som du definierar själv istället för att bara rita en ellips. Du kan använda koden för att rita en smiley eller hitta på någonting själv.
Enkel "Sikta och skjut"
Utgå ifrån beskrivningen under rubriken Strukturera ett spel. Gör ett enkelt program för att "Sikta och skjuta".
Sikta och skjut med gravitation
Utgå ifrån ditt program för att sikta och skjuta men se till att bollen faller neråt så att den rör sig längs en parabel.
Sikta och skjut med poängräkning
Utgå ifrån ett program för att sikta och skjuta men gör det till ett spel med poängräkning. Det kommer att behövas ett tredje tillstånd för att visa resultat.
function draw() {
background(100);
ritaStartpunkt();
if (tillstånd == 0) {
sikta();
} else if (tillstånd == 1) {
skjut();
} else {
visaResultat();
}
}
Lägg in ett målobjekt som spelaren ska träffa. Om du använder rörelse utan gravitation kan målobjektet röra sig. Om du använder rörelse med gravitation kan målobjektet vara stilla på en slumpmässig position. Det går också att använda flera målobjekt i en lista.
Varje gång bollen träffar målobjektet ska spelaren få poäng och tillståndet ska ändras till noll. Använd funktionen dist(<x1>, <y1>, <x2>, <y2>)
för att upptäcka kollisioner.
Spelet kan avslutas efter en viss tid. Om spelaren bara ska få spela en gång kan du använda frameCount
och noLoop()
för att avsluta draw-funktionen efter att du visat spelarens resultat. Om du istället vill att spelaren ska få testa att spela igen efter att du visat resultatet, kan du göra en egen variabel timer
som räknas upp i draw-funktionen. Variabeln timer
sätts till noll när spelet startar och om användaren klickar med musknappen när tillståndet är 2.
Ritprogram med meny
Utgå ifrån beskrivningen under rubriken Menyer. Gör ett ritprogram med meny.
Ändra uppritningen av menyn så att det inte visas någon text, istället ska respektive form ritas upp inuti respektive rektangel.
Lägg till ett val till, exempelvis formen "punkt". Ändra rektanglarnas positioner så att de är jämnt fördelade i vertikal led. Ändra uppritningen så att användaren också kan rita punkter.
Avancerat ritprogram
Gör ett ritprogram där användaren kan göra flera val både genom att använda en meny och med tangentklickningar.
Låt något menyval vara att det ritas någon effekt, exempelvis ett antal slumpmässiga sträckor som utgår ifrån muspekaren.
Se till att användaren kan ta bort allt som ritats upp genom att klicka på någon tangent.
Låt användaren välja mellan några färger, antingen med menyval eller tangenttryckningar.
Det går också att låta användaren ändra storlek på pennan med pil upp eller pil ned, eller att låta användaren välja hur genomskinlig pennan ska vara med pil höger eller vänster.
Om du använder tangenttryckningar kan du visa en instruktion i början av programmet som berättar vilken tangent som gör var. Låt sedan själva ritandet börja när användaren klickar med musen eller på någon tangent.
Om du har många olika menyval kan det vara enklare att använda absoluta positioner än att använda width
och height
. Se då till att ritområdet alltid har samma bredd och höjd genom att exempelvis använda:
createCanvas(800, 450);
Öva multiplikationstabell
Utgå ifrån beskrivningen under rubriken Slumpade flervalsfrågor. Gör ett övningsprogram för multiplikation. Ändra koden som visar återkoppling så att användaren får se vad rätt sle är om användaren svarat fel.
Valfri matematikövning
Gör ett program som låter användaren öva på multiplikation, division, ekvationslösning eller annan matematikuppgift som lätt kan slumptalsgenereras. För att undvika avrundningsfel är det enklast om bara heltal används.
För att öva på division kan du utgå ifrån programmet för att öva multiplikationstabell men ändra på hur frågan ställs och hur svarsalternativen visas.
Det är förhållandevis enkelt att slumptalsgenerera en övning om enkel ekvationslösning med ekvationer av typen \( ax+b = c\). Börja med att slumptalgenerera lösningen som ett heltal, dvs talet \(x\). Slumptalsgenerera sedan ett heltal \(a\) och se till att \(a\) inte är lika med noll genom att använda en do-while-sats. Slumptalsgenerera sedan ett heltal \(b\) och beräkna slutligen talet \(c\). När du ställer frågan bör du skriva olika textsträngar beroende på om \(b\) är positivt eller negativt. Det bör se ut som:
Lös ekvationen 2x+5 = 11
eller Lös ekvationen 2x-5 = 1
Räkna antalet besvarade frågor och antalet rätta svar. Avsluta programmet när ett visst antal frågor besvarats eller efter det att användaren svarat rätt ett visst antal gånger. Visa något slutmeddelande om hur övningen gick. Ge programmet det utseende du tycker blir bra, exempelvis kan olika färger användas för rätt eller fel svar. Man kan också använda bilder för att visualisera rätt eller fel på olika sätt.
Öva namngeografi
Utgå ifrån beskrivningen under rubriken Öva geografi. Gör ett övningsprogram för namngeografi. Lägg in ett eller två länder till i listan. Koden ska fortfarande fungera men om fönstret är litet får inte huvudstäderna plats i rektanglarna.
Många länder
Utgå ifrån koden för att öva namngeografi. Nu ska koden göras om så att många länder kan användas men bara tre svarsalternativ ska visas. Deklarera en lista till överst i programmet kallad lång
. Byt ut koden där länderna läggs in i listan land
så att länderna istället läggs in i listan lång
.
lång[0] = {namn: "Sverige", stad: "Stockholm"};
lång[1] = {namn: "Danmark", stad: "Köpenhamn"};
lång[2] = {namn: "Norge", stad: "Oslo"};
lång[3] = {namn: "Finland", stad: "Helsingfors"};
lång[4] = {namn: "Island", stad: "Reykavik"};
Lägg till några länder till i listan lång
så att den blir just lång.
Nu ska du välja ut tre slumpmässiga länder från listan lång
och lägga in i listan land
. Gör en funktion med följande kod:
function väljTre() {
let ett, två, tre;
do {
ett = floor(random(lång.length));
två = floor(random(lång.length));
tre = floor(random(lång.length));
} while (ett == två || ett == tre || två == tre);
land[0] = {namn: lång[ett].namn, stad: lång[ett].stad};
land[1] = {namn: lång[två].namn, stad: lång[två].stad};
land[2] = {namn: lång[tre].namn, stad: lång[tre].stad};
}
Funktionen väljTre()
måste anropas efter att listan lång
fyllts på i setup-funktionen. Den måste också anropas när användaren klickar med musen då tillståndet är 0, innan rättIndex
slumptalsgenereras.
Testkör din kod för att se om du fått det att fungera.
Valfri flervalsövning som utgår ifrån listor
Gör ett program som till strukturen liknar programmet för många länder men där du också håller reda på hur många gånger användaren besvarat en fråga och hur många sle som varit rätt.
Istället för att para ihop ord kan du para ihop bilder och ord. Använd bildfiler som du har på din dator, eller producera bildfiler på valfritt sätt.
Avsluta programmet när ett visst antal frågor besvarats eller efter det att användaren svarat rätt ett visst antal gånger. Visa något slutmeddelande om hur övningen gick. Ge programmet det utseende du tycker blir bra, exempelvis kan olika färger användas för rätt eller fel svar. Man kan också använda bilder för att visualisera rätt eller fel på olika sätt.