1. Met de for-loop bloempjes in de game plaatsen
In de Level1 class hebben we 10 bloempjes geplaatst met telkens een nieuwe regel.
addObject(new Flower(),x,y);
We hebben telkens alleen maar de x- en de y-waarde veranderd. Als je een dergelijke regel 10 keer tegenkomt moet je je afvragen of dat niet korter en overzichtelijker kan. Dat kan met deze code wel degelijk met behulp van een for-loop. Deze wordt als volgt geïmplementeerd:
Eerst maken we een nieuwe class variabele pinkFlowers.
Daarna implementeren we in de methode prepare() de for-loop waar de roze bloempjes hebben gestaan.
Zo'n forloop bestaat uit drie gedeelten:
- blauw omlijnd: de counter (teller) wordt op 0 gezet (deze waarde is meestal het handigst);
- groen omlijnd: er vindt een test plaats of de counter kleiner is dan de variabele pinkFlowers;
- zwart omlijnd: iedere keer als de loop wordt uitgevoerd, wordt de counter met één verhoogd.
Dus als de variabel pinkFlowers op 10 is gezet dan wordt de code 10 keer uitgevoerd. M.a.w. er wordt 10 keer een object Flower geplaatst.
Nu zie je bij de code addObject(new Flower(),counter*50,195)
dat er een bijzonder stukje in zit namelijk een berekening counter * 50. Hier gebeurt het volgende:
- de eerste loop is de waarde van counter 0 dus dan krijg je addObject(new Flower(),0,195)
- de tweede loop is de waarde van counter 1 opgehoogd, dus 1, dan krijg je (50 x 1= 50) addObject(new Flower(),50 ,195)
- de derde loop is de waarde van counter 1 opgehoogd, dus 2, dan krijg je (50 x 2= 100) addObject(new Flower(),100 ,195)
- etc......
- de laatste loop is counter 9 (het is kleiner dan 10 en niet kleiner of gelijk dan 10) en dan krijg je (50 x 9= 50) addObject(new Flower(),450 ,195)
Uiteindelijk ziet het er zo uit:
2. bloempjes random plaatsen
Je weet nu wat een for loop is. We kunnen deze eenvoudig gebruiken om de bloempjes te plaatsen. We hebben ook de methode getRandomNumber(int min, int max)
gemaakt. Deze twee technieken gaan we nu combineren.
Als het goed is kunnen de wolven nu ook Roodkapje opeten met dezelfde techniek als Roodkapje bloempjes plukt.
Eerst plaatsen we de methode getRandomNumber(int min, int max)
in de class Level1. Kopiëer deze uit de BaseClass. Dit moet, omdat we niet kunnen erven van de Baseclass. Vervolgens passen we de for-loop als volgt aan:
Voor de overzichtelijkheid hebben we de code regel addObject(new Flower()...;
op meerdere regels geplaatst. Dat is voor de verwerking geen probleem.
De code levert het volgende resultaat op:
Telkens staan de bloempjes nu op een andere plaats vanwege de methode getRandomNumber(int min, int max) waar een minimum en een maximum ingesteld kan worden. Het gaat echter nog niet helemaal goed. De bloempjes kunnen te dicht bij de rand van de game komen of zelfs op het huisje - zie rood omlijnd.
Om dit soort problemen te voorkomen zullen we een marge voor de rand gaan instellen. Als we dat nu zo doen dat de bloempjes op de noordzijde alleen tot het huisje kunnen komen en in de rest van game minimaal 50 pixels van de rand. Hiervoor maken we eerst een class variabele margin.
Vervolgens passen we de instellingen van de methode getRandomNumber op de volgende manier aan:
getRandomNumber(0+margin,600-margin)
Op deze manier wordt bij een marge van 50 getallen gemaakt tussen de 50 (inclusief) en de 550 (exclusief), dus 50 pixels van de rand.
3. Een scoreboard maken
In Greenfoot is het mogelijk teksten en plaatjes te construeren. In de API van Greenfoot vinden we ook GreenfootImage (de API staat ook online) en die heeft de volgende constructors.
GreenfootImage(GreenfootImage image)
Create a GreenfootImage from another GreenfootImage.
|
GreenfootImage(int width, int height)
Create an empty (transparent) image with the specified size.
|
GreenfootImage(java.lang.String filename)
Creates an image from an image file.
|
GreenfootImage(java.lang.String string, int size, java.awt.Color foreground, java.awt.Color background)
Create an image with the given string drawn as text using the given font size, with the given foreground color on the given background color.
|
GreenfootImage(java.lang.String string, int size, java.awt.Color foreground, java.awt.Color background, java.awt.Color outline)
Create an image with the given string drawn as text using the given font size, with the given foreground color on the given background color.
|
Maak een nieuwe class genaamd Scoreboard en plaats er de volgende code in:
Wat is een public static final variabele?
Een statische variabele is gerelateerd aan een class, niet aan een object in de game. Je kunt een statische variabele dus direct aanroepen zonder een object aan te maken. Bijvoorbeeld
Scoreboard.WIDTH
Op de plaats van Scoreboard.WIDTH
komt dan de inhoud van de variabele te staan. In dit geval is dat de integer 150.
We maken zo'n variabele final als we niet meer willen dat de waarde nog veranderd wordt. Java staat dat dan niet toe. Een final variabele schrijven we met hoofdletters. We hebben deze variabele ook public gemaakt omdat we misschien deze variabele ook ergens in een andere class willen opvragen.
We zullen nu het scoreboard verder afmaken.
Maak een nieuwe methode:
public void makeScoreBoard()
{
}
Maak in deze methode een nieuwe Greenfoot Image. Hierbij maken we gebruik van de constructor GreenfootImage(int width, int height)
die om een breedte en een hoogte vraagt. In deel 1 en deel 2 hebben we al een beetje uitgelegd wat een constructor is. De constructor is het startpunt om het object te maken. In dit geval dus het object GreenfootImage met 2 parameters. We moeten daarom de image als volgt maken:
public void makeScoreBoard()
{
GreenfootImage image = new GreenfootImage(WIDTH, HEIGHT); //creates a new image with Java
}
We maken nu dus gebruik van de static variabelen WIDTH en HEIGHT. We geven de image dus een breedte en een hoogte mee van 150 en 50.
De class GreenfootImage heeft een aantal methoden (zie API) waaronder setColorAt(int x, int y, java.awt.Color color). Je ziet dat er dus een java.awt.Color color
nodig is. Daarvan kunnen we ook de API vinden en dan zien we dat er een constructor is als volgt:
Constructor and Description |
Color(int r, int g, int b, int a)
Create an sRGB color with the specified red, green, blue, and alpha values in the range (0 - 255).
|
We kunnen dus een kleur maken met een mix van de kleuren rood, groen en blauw. De laatste is het alphakanaal en dat betekent de transparantie. Voor deze instellingen maken we ook weer static variabelen.
En vervolgens breiden we de methode uit door een nieuwe kleur met bovenstaande instellingen te maken.
En nu kunnen we de methode uitbreiden.
public void makeScoreBoard()
{
GreenfootImage image = new GreenfootImage(WIDTH, HEIGHT); //creates a new image with Java
Color color = new Color (RED,GREEN,BLUE,TRANSPARENCY); //creates a new color
}
En om de rechthoek te tekenen voeren we de volgende coderegels in die de image tekenen. Deze hebben we ook weer uit de API gehaald.
public void makeScoreBoard()
{
GreenfootImage image = new GreenfootImage(WIDTH, HEIGHT); //create a new image with Java
Color color = new Color (RED,GREEN,BLUE,TRANSPARENCY ); //create a new color
image.setColor(color); //set the color of the image with the object color
image.fillRect(0, 0, WIDTH, HEIGHT); //fill the rectangle with the setted color
setImage(image); //create the image
}
We moeten nu alleen nog het scoreboard initiëren. Dat doen we in de class Level1.
addObject(new Scoreboard(),75,25);
Als je deze regels als laatste plaatst gaan de objecten er mooi onderdoor.
We kunnen het scoreboard ook nog een mooie rand geven. Dat gaat zo:
image.setColor(new Color(RED,0,0,TRANSPARENCY));
image.fillRect(5, 5, WIDTH-10, HEIGHT-10);
Wat we in feite hebben gedaan is een nieuwe rechthoek tekenen over de transparante, witte heen. Het scoreboard komt er dan zo uit te zien.
4. Het scorebord werkend maken
We hebben nu de ondergrond voor het scorebord. We moeten nu nog zorgen dat het werkt door er een teller in te plaatsen. Iedere keer als Roodkapje een bloempje plukt krijgt ze een bepaald aantal punten. Als Roodkapje een blauw, giftig, bloempje plukt krijgt ze (een flink aantal) minpunten. Als het aantal punten onder de 0 uitkomt, is de game over.
public void makeScoreBoard()
{
Color color = new Color (RED,GREEN,BLUE,TRANSPARENCY); //create a new color
image.setColor(color); //set the color of the image with the object color
image.fillRect(0, 0, WIDTH, HEIGHT); //fill the rectangle with the setted color
image.setColor(new Color(RED,0,0, TRANSPARENCY));
image.fillRect(5, 5, WIDTH-10, HEIGHT-10);
image.setColor(new Color(0, GREEN, 0, TRANSPARENCY));
image.fillRect(10, 10, WIDTH-20, HEIGHT-20);
Font font = image.getFont();
font = font.deriveFont(FONTSIZE);
image.setFont(font);
image.setColor(new Color(0,0,0,TRANSPARENCY));
image.drawString("score: " + score, 30, 32);
setImage(image); //create the image
}
Schrijfwijzen van variabelen, classes en methoden
In het vorige lesonderdeel had je al een static variabele FONT_SIZE gemaakt. Om je bewust te maken dat dit geen goede schrijfwijze is veranderen we deze variabele in FONTSIZE. We maken wat afspraken over schrijfwijzen van variabelen, classes en methoden:
- De variabele begint altijd met een kleine letter. Het eerstvolgende zelfstandig naamwoord begint met een hoofdletter. Voorbeeld:
randomWalk
.
- Een static variabele wordt met hoofdletters geschreven.
- Een variabele is in principe een zelfstandig naamwoord. De naam van een methode is altijd een werkwoord. Voorbeeld:
walkRandom()
.
- In de naam van een variabele gebruiken we geen tekens zoals een underscore of minstreepje.
- De naam van een class begint altijd met een hoofdletter.
Nu verder met Roodkapje. We zullen eerst de class Flower aanpassen zodat we kunnen detecteren of we met een giftige (blauwe) of een gewone bloem te maken hebben. Eerst plaatsen we bovenin de class een classvariabele.
private boolean poisonous;
Vervolgens passen we de constructor aan door de waarde van de parameter met dezelfde naam in de class variabele te plaatsen.
this.poisonous=poisonous;
Het keyword this slaat op de classvariabele. Je kunt het lezen als de classvariabele van deze class.
Omdat de scope van de classvariabele poisonous
de gehele class betreft kunnen we deze nu gebruiken in een andere methode. Dit is een zogenaamde getter. Een getter is bedoeld om de inhoud van een classvariabele op te vragen. In dit geval dus true or false. Plaats de getter onderaan in de class.
public boolean getPoisonous()
{
return this.poisonous;
}
Het verschil tussen een opdracht een een expressie
Bovenstaande methode is een expressie. Een void, zoals je al eerder hebt gebruikt, is een opdracht. Als een methode geen void is, dan noemen we dat een expressie omdat er een waarde wordt geëvalueerd en terug gegeven met het keyword return.
Voor de zekerheid geven we ook nog een afbeelding van de gehele class.
5. Het scoreboard werkend maken - vervolg
De volgende stap die we nu moeten nemen is de methode pickFlowers()
aanpassen. Hierin halen we eerst het betreffende Flower object binnen. Dit is het bloempje wat Roodkapje gaat plukken en wat uit de game verdwijnt. Let goed op het verschil tussen een class en een object!
Plaats onderstaande code aan het begin van het blok if(canSee(Flower.class))
.
Flower flower = (Flower) getOneObjectAtOffset(0, 0, Flower.class);
Met deze code kopiëren we het Flower-object in de variabele flower. Op de achtergrond maakt Java overigens alleen een verwijzing. Het object wordt niet echt gekopiëerd. Hiermee bespaart Java geheugenruimte.
Tussen haakjes staat (Flower)
. Dit heet een type cast, Type casting dwingt de variabele van een bepaald type te zijn. In dit geval wordt de variabele een Flower. De methode getOneObjectAtOffset
neemt het dichtstbijzijnde object van het type Flower.class.
Vervolgens gaan we detecteren of we met een giftige bloem te maken hebben of niet. We hadden dat in de class Flower al voorbereid en gaan dat nu gebruiken.
if(flower.getPoisonous())
{
score=score-5;
}else {
score++;
}
Als we een giftige bloem tegenkomen wordt de score -5. Bij een gewone bloem wordt het scoreboard één opgehoogt.
Als laatste wordt het scorebord verwijderd en opnieuw geplaatst met de nieuwe score.
removeObject(Flower.class);
Level1 level1 = (Level1)getWorld();
World world = getWorld();
world.removeObjects(world.getObjects(ScoreBoard.class));
world.addObject (new ScoreBoard(score), 75,25);
Hieronder nog eens de gehele methode.
6. Game over door de wolf
We hopen dat je uit de laatste opdrachten bent gekomen en de game nu een game over geeft als Roodkapje de giftige bloempjes plukt. Wat er nu echter ook nog moet gebeuren is de game over als Roodkapje wordt opgegeten door de wolf. Dat is eigenlijk niet meer zo moeilijk.
Als het goed is heb je een methode gameOver()
gemaakt in LittleRedCap.
De GameOver class heb je omgezet naar de class Announcement en de constructor aangepast opdat deze class allerlei mededelingen kan doen.
Omdat we de methode gameOver() ook bij de de class Wolf willen gebruiken gaan we deze methode verplaatsen naar de BaseClass. Ook de private int score verplaatsen we naar de BaseClass. Deze maken we echter niet meer private maar protected.
protected int score=0;
Protected betekent dat je de variabele score kunt gebruiken in de onderliggende classes. We zeggen: de scope van de variabele strekt zich uit over de onderliggende classes.
We kunnen in de class LittleRedCap alles laten staan vanwege de overerving en scope. We kunnen nu ook de methode gameOver()
gebruiken in de class Wolf.
7. Hoe laat je de actor stoppen bij een muurtje
Stel dat je een doolhof wilt maken dan kan met de volgende code de actor binnen de muurtjes worden gehouden:
public void setLocation(int x, int y)
{
int oldX = getX();
int oldY = getY();
super.setLocation(x, y);
if(!getIntersectingObjects(Leaf.class).isEmpty())
{
super.setLocation(oldX, oldY);
}
}
8. Hoe maak je een nieuw Level?
Hoe maak je een nieuw Level?
if(/*hier plaats je een voorwaarde, bijvoorbeeld als Roodkapje het huisje binnen gaat*/)
{
Greenfoot.setWorld(new Level2());
}
9. Een methode voor het maken van muurtjes voor een doolhof
Hieronder een instructie hoe je met behulp van parameters geautomatiseerd muurtjes kunt maken. Begin met het maken van een methode makeWall() en roep deze aan in de constructor. Deze methode maakt telkens op dezelfde plek een horizontaal muurtje.
Het resultaat is hieronder te zien:
We maken nu een parameter (instelling) die maakt dat het aantal bomen ingesteld kan worden.
Kijk zelf welk resultaat dit oplevert en wat er gebeurt als je bijvoorbeeld makeWall(7) instelt.
De volgende stap is het instellen van de positie op de Y-as.
Het muurtje zal nu worden gemaakt op positie 150. Dat is dus 50 pixels lager. Probeer eerst het resultaat uit.
Hierna kunnen we ook de positie op de X-as instellen. Deze moeten we optellen bij de berekening met de counter.
Je ben nu in staat om overal in de game een muurtje te plaatsen. Het zijn echter alleen horizontale muurtjes. We gaan in het volgende filmpje een boolean isHorizontal instellen die bepaalt of een muurtje horizontaal of verticaal is.
Bij het maken van een doolhof is het ook van belang om een gat in het muurtje te kunnen instellen. Dat is in onderstaande afbeelding gerealiseerd. Tevens is de code efficiënter gemaakt. Dit houdt in dat de dubbele forloop eruit is gehaald en het if-statement isHorizontal naar binnen is gehaald. De code wordt nu sneller en er is geen redundante code meer.
10. feedback van leerlingen wat nog moet worden verwerkt.
Dit onderdeel is uitgeschakeld door de docent.