Greenfoot Tutorial 2 Version 1.0 (release 81), 27-3-2008 voor Greenfoot Version 1.3.0
Thijs Dorssers
In dit scenario laten we een mier (in het engels: Ant) in haar mierenwereld lopen, deze mier gaat een reukspoor achter zich aan trekken. Het reukspoor heeft een bepaalde levensduur, dat wil zeggen dat na een bepaalde tijd het spoor verdwenen is. De mier wordt met de cursortoetsen bestuurd. Open het ant scenario, je vind dit in dezelfde map als het wombat scenario. Mocht het om een of andere redenen daar niet staan dan kun je het hier downloaden. Pak vervolgens het scenario uit in dezelfde map waar ook wombat staat. Compileer en test het scenario, je ziet dan een met blauwe tegels bekleed mierenplein. Als je er een mier op plaatst en runt gebeurt er noch niks. Hieronder wordt uitgelegd wat je moet doen om het hierboven gestelde scenario tot leven te wekken. Als eerste bekijken we de constructor van onze mier, open de editor voor de klasse Ant door een dubbel-klik op de Ant klasse. De constructor is de methode met de naam public Ant() De constructor wordt aangeroepen zodra je een nieuwe mier in de wereld plaatst. Op dat moment worden de coordinaten van de mier op (0,0) gezet, dit is links boven. Ook maakt de constructor van Ant een geurSpoor aan van het type ArrayList, dit is eigenlijk gewoon een lijst waar we straks wat geurtjes aan gaan toevoegen, maar daar komen we later op terug. Daarna zal de wereld van het nieuwe aangemaakte object de methode addedToWorld() roepen, als wij er nu voor zorgen dat in deze methode geschikte code komt te staan, dan wordt deze uitgevoerd. In dit geval kiezen we ervoor de mier op (0,0) te zetten door aanroep van de methode setLocation(x,y). Opdracht 1.1: Pas de code zo aan dat de mier rechts boven begint. LET OP: Het is verstandig om steeds als je iets in de code gewijzigd hebt, de knop "Compile all" te gebruiken i.p.v. van de knop "Compile", doe je dat niet dan krijg je soms onverwachte dingen. Als we de mier tot actie willen brengen moeten we de methode act() aanpassen, op dit moment is de enige actie die de mier kan uitvoeren, een tegel naar links stappen. Klik maar eens op de Run-knop en druk daarna een paar keer op de pijltjestoets links. De mier verplaats zich nu naar links. In de code zie hoe we dit voor elkaar gekregen hebben, met een if () test wordt getest of de pijltjestoets links is ingedrukt, zo ja dan wordt de methode goLeft() aangeroepen. De methode goLeft() vraagt eerst nog de huidige x- en y-positie op, vermindert de x-positie met 1 en zet de de nieuwe locatie met setLocation(x,y) Opdracht 2.1: Pas de code zo aan dat de mier, met behulp van de andere pijltjestoetsen, alle kanten op kan lopen. Opdracht 2.2: Eventueel kun je nog een paar geheime toetsen nemen om de mier andere acties te laten doen, bedenk zelf wat, als het maar meerdere stappen in een keer zijn. In plaats van de mier met toetsen te bedienen, zou je dit ook over kunnen laten aan de computer. Je laat dan de computer kiezen welke kant de mier steeds op gaat, dit kan met een vast patroon of op een willekeurige manier. In de volgende vraag ga je de mier in een vast patroon laten lopen. LET OP: Enige toelichting bij de volgende opgave is nodig, zo zal in les uitgelegd worden hoe je if-then-else statements in elkaar zet, hoe je gebruik maakt van een logische expressie, en hoe je hiermee na een aantal stappen van richting verandert. Kijk ook bij de wombat, je kunt enkele methoden hiervan overnemen, zoals move() en turnLeft(). Maar als je deze methoden overneemt moet je ook nog wat andere zaken van wombat overnemen. Opdracht 2.3: (Dit is een extra opdracht en mag je eventueel overslaan.) Pas de code zo aan dat de mier eeuwig in een vierkant ter grootte 20 blijft lopen met start in x=10, y=10. Om de mier op een willekeurige manier te laten lopen heb je een soort doppelsteen nodig. De Greenfoot software heeft zo een doppelsteen ter beschikking, deze doppelsteen kun je als volgt oproepen: int aantalOgen; aantalOgen=Greenfoot.getRandomNumber(4); Hiermee gooi je in dit geval een doppelsteen op met 4 kanten en het aantal ogen dat je hiermee gooit is 0 of 1 of 2 of 3. Je kunt dit in je software gebruiken door op gelijkheid te testen, bijvoorbeeld alsvolgt: if (aantalOgen==0) .... Als 0 gegooid is zou je naar het westen kunnen lopen en als 1 gegooid is naar het oosten enzovoorts. Let hier goed op, bij een test op gelijkheid gebruiken we een dubbele =, dus ==. Bij toekennen van een berekende waarde aan een variabele (hierboven is aatalOgen een variabele) gebruiken we een enkele =. Opdracht 2.4: Pas je programma zodanig aan dat de mier automatisch willekeurig over het mierenplein gaat lopen, houdt er echter wel rekening mee dat zij niet van het plein afgeraakt. Laat de mier in het midden van het plein beginnen. De bedoeling is dat, als de mier zich beweegt een geurSpoor achterblijft op de plaatsen waar de mier al geweest is. Dit spoor gaan we nabootsen doormiddel van Geur gekleurde blokjes. Steeds als de mier een plaats verder gaat, plaatsen we op de vorige plek een Geur blokje. Hiertoe maken we als volgt een nieuwe actor aan met de naam Geur (denk aan de hoofdletter G van Geur): Rechtermuis klik op Actor en kies voor New subclass, vul in het veld "New class name:" Geur in, kies vervolgens het plaatje erbij, dit vind je in de "doos" onder de tekst "Scenario images", het bedoelde plaatje is yellow_8x8.png en staat hier al klaar voor gebruik. Opdracht 3.1: Voer bovenstaande instructie uit en probeer daarna een Geur blokje in de wereld te plaatsen. Telkens als nu de mier beweegt moet deze een geurSpoor achterlaten, d.w.z. de mier moet een Geur object in de wereld achterlaten op het veld dat zij verlaten heeft. De code hiervoor ziet er als volgt uit: Geur geurtje = new Geur(); getWorld().addObject(geurtje,x,y); geurSpoor.add(geurtje); Op de eerste regel maken we een object van het type Geur aan, het object krijgt de naam geurtje. Op de tweede regel zorgen we dat het geurtje op de goede plek in de wereld wordt gezet door met getWorld() de wereld op te vragen en met de . van de wereld de addObject() methode aan te roepen. Met de derde regel voegen we dit geurtje toe aan een geurSpoor, dit is een lijst. Hiermee kan elke mier zijn eigen geurSpoor bewaren. Opdracht 3.2: Plaats deze drie regels op de geschikte plaatsen in de code van Ant, zodat bij verplaatsing inderdaad een geurSpoor achterblijft. We zouden eigenlijk wel willen dat de geurtjes na een bepaalde tijd uitgewerkt zijn en vanzelf weer verdwijnen. Een manier om dit te doen is de geurtjes bij de start een levensduur te geven en elke keer als er een act wordt uitgevoerd de levensduur met 1 te verminderen. Zodra de levensduur 0 is kan die niet meer verder verminderd worden, maar dan moet de geur wel verwijderd worden. Hoe uitgewerkte geurtjes verwijderd worden staat in onderstaan stukje code, bekijk de code eens goed en vraag als er onduidelijkheden zijn. In de les zal deze code nog eens uitgelegd worden. public void removeOldSmell() { // omdat bij een lijst aan het einde wordt toegevoegd // is het voorste element het oudste, en omdat we per // keer maar een element willen verwijderen hoeven we // dus alleen maar van het eerste element te controleren // of het nog geurt, mits de lijst niet leeg is natuurlijk if (! geurSpoor.isEmpty()) { Geur oudsteGeurtje = (Geur) geurSpoor.get(0); if (oudsteGeurtje.getTimeLeft() == 0) { geurSpoor.remove(0); //uit de lijst verwijderen getWorld().removeObject(oudsteGeurtje); //uit de wereld verwijderen } } } Opdracht 3.3: Voeg bovenstaande code toe aan de code van Ant, en pas de klasse Geur zodanig aan dat die zijn levensduur gaat bijhouden te beginnen bij 10 bijvoorbeeld, en dat bij elke act de levensduur met 1 verminderd wordt. De methode removeOldSmell() moet natuurlijk nog op de juiste plaats aangeroepen worden. Idee voor een spel: laat de mier willekeurig in de wereld lopen en maak een geurvreter, die punten kan verdienen. En naarmate hij meer vreet een andere kleur gaat aannemen, de geurvreter kan met toetsen bediend worden. Het idee hierbij is dat de geurvreter steeds gaat kijken of op het veld waar hij op staat al een geurtje ligt, zo ja dan zet hij de levensduur ervan op 0. Het feitelijke verwijderen van het geurtje laten we de mier, die de geur heeft achtergelaten, zelf doen. Opdracht 3.4: Eerste stap: maak de geurvreter en test hem uit, hij hoeft voorlopig alleen nog maar te kunnen bewegen, geurvreten komt straks wel. Als plaatje kies je voorlopig even het greenfoot voetje, later in een van de volgende opdrachten ga je er een packman van maken. LET OP: De geurvreter gaat alsvolgt werken: Elke keer als hij een nieuw vak bereikt kijkt hij of er een geurtje aanwezig is, zo ja dan zet hij de levensduur op 0. Het vakje verdwijnt dan bij aanroep van de methode removeSmell(). Deze methode moet dan wel nog eerst geschreven worden en wel zodanig dat hij de hele lijst doorloopt en niet alleen naar het eerste element kijkt. Hiertoe wordt in de les uitgelegd hoe je een een for-loop (while-loop) kunt maken. Deze loop om de geurtjes te onderzoeken loopt ook nog eens van achter naar voren omdat de geurtjes ook verwijderd worden en de lijst daarmee korter wordt. Kan het ook anders om, dus van het begin naar het einde, of kan het dan mis gaan? We komen hier in hoofdstuk 6 op terug in opgave 6.2. De geurvreter moet zijn punten bij kunnen houden. Je zou levels kunnen inbouwen door gebruik te maken van de behaalde punten, ieder level zou je kunnen aanduiden met een andere kleur van de geurvreter. Bij het behalen van het maximaal aantal punten zou er vuurwerk moeten verschijnen met een overwinningstune. En natuurlijk kun je zelf ook nog aanpassingen aan het spel bedenken. Maar om dit te programmeren moeten je meer weten van de mogelijkheden. Voor elk object in Greenfoot bestaat een beschrijving, waar de mogelijkheden (lees methoden of ook wel functies genoemd) van dat object in opgesomd zijn. Van elke methode is steeds kort uitgelegd hoe je die moet aanroepen en wat je met die aanroep kunt bereiken. Je vind deze beschrijving door onder de menu-optie "Help" te kiezen voor "Greenfoot Class Documentation" of door hier op de volgende link te klikken: Greenfoot documentatie Opdracht 4.1: Ga in de documentatie opzoek naar de beschrijving van de setRotation() methode van Actor. Pas je programma vervolgens zo aan dat de mier meedraait als ze van richting veranderd, en dus haar kop steeds in de juiste richting staat. LET OP: Het is verstandig om steeds als je iets in de code gewijzigd hebt, de knop "Compile all" te gebruiken i.p.v. van de knop "Compile", doe je dat niet dan krijg je soms onverwachte dingen. Het geurvretertje heeft eigenlijk niet het juiste plaatje, leuker zou zijn als je een soort packman zou kunnen maken. Een manier om dat te doen is steeds het plaatje te wisselen tussen een packman met de mond dicht en een packman met de mond open. Deze plaatjes staan al klaar in de map images van je Ant programma, dat je gedownload hebt. Hieronder is de code gegeven om de geurvreter van plaatje te kunnen veranderen, door op een geschikte manier hier van gebruik te maken, kun je de geurvreter laten happen: setImage("pm-open.png"); Opdracht 4.2: Zorg ervoor dat je geurvreter eruit gaat zien als een packmannetje. Opdracht 4.3: We gaan de klasse Geur nog wat verbeteren, namelijk zodat we aan de kleur kunnen zien hoe oud de betreffende geur is: nieuw (tussen 20 en 14 tellen) geur krijgt de kleur rood, geur tussen 13 en 7 tellen krijgt de kleur oranje en geur tussen 6 en 0 tellen wordt geel. De benodigde plaatjes vind je de map images. Figure 4.1: Geurvreterspel
Als alles tot nu toe gelukt is, dan heb je een programma waar de mier willekeurig over haar mierenplein loopt, tijdens dat lopen een geurspoor achterlaat, het geurspoor van kleur verschiet van rood via oranje naar geel. Je hebt een packman-achtige geurvreter gemaakt die met de cursor-pijltjes over het mierenplein te sturen is. Nu willen we natuurlijk punten zien te verdienen, door met de packman de geurtjes op te eten. Bij elk geurtje dat de packman te pakken krijgt krijgt hij punten. Voor een rood geurtje 3 punten voor een oranje geurtje 2 punten en voor een geel geurtje 1 punt. Hier gaan we op eten van de geurtjes programmeren, en de score van de geurvreter bijhouden. Graag willen we ergens aan kunnen zien hoeveel punten de packman verdient. Ook moeten we ervoor zorgen dat opgegeten geurtjes ook verdwijnen van het speelveld op het moment dat ze opgegeten worden. We beginnen met het opeten en de score bijhouden. De vraag is hier, hoe kun je vast stellen dat de packman op een plaats staat waar ook een geurtje is achtergelaten? Dit laatste kunnen we doen door aan de greenfootwereld te vragen een lijst met geurtjes die op die bepaalde plaats staan. Er zouden immers meerdere mieren kunnen rondlopen, of een mier is twee keer op de zelfde plaats geweest. Het opvragen van deze lijst zie je in onderstaande code gedemonstreerd: ArrayList<Geur> geurtjes = (ArrayList<Geur>) getWorld(). getObjectsAt(getX(),getY(),Geur.class); Eigenlijk hoort dit allemaal op een regel te staan, maar dat pastte hier niet. Vergeet de . achter getWorld() in ieder geval niet. Opdracht 5.1: In welke klasse en waar gaan we deze regel nu toevoegen? Voeg de regel daar toe. Nu we de lijst met geurtjes op de plaats waar de packman staat hebben, hoeven eigenlijk alleen maar naar het voorste element te kijken. Dit voorste geurtje moeten we nu opeten. Opdracht 5.2: Hoe gaan we dat opeten implementeren (lees programmeren)? Ga hier eens met je klasgenoot over brianstormen. Als je hulp nodig hebt vraag je een van de begeleiders. Aanwijzing: In ieder geval moet je het zoeken in o.a. de klasse Geur. De opgave is dus de geurtjes eerst maar eens te laten weten dat ze opgegeten zijn, het echte verwijderen doen we in de volgende paragraaf. Opdracht 5.3: Hoe gaan we de score van de packman bijhouden? Ook hier eerst eens met je klasgenoot over brainstormen. Graag zouden we aan de packman willen zien dat hij punten aan het verdienen is. De opgave is dit te programmeren. De mier kent zijn eigen geurtjes, deze worden immers bijgehouden in de lijst geurspoor van het type ArrayList<Geur>, en de mier heeft deze geurtjes zelf aangemaakt. Dus de meest geschikte klasse om de geurtjes op te ruimen, die door de geurvreter opgegeten zijn, is de klasse Ant. We hebben hier al een methode removeOldSmell(), die steeds naar het eerste element kijkt in de lijst of dat nog geur verspreidt of niet if (oudsteGeurtje.getTimeLeft() == 0) In het laatste geval wordt dit geurtje uit de lijst verwijderd met; geurSpoor.remove(0); en de geurtje wordt van het mierenplein verwijderd met: getWorld().removeObject(oudsteGeurtje); Met getWorld() heb je toegang tot alles wat er zich het plein bevindt, we hebben dan o.a. de mogelijkheid om iets dat zich het plein bevindt te verwijderen, door via getWorld() de methode removeObject() aan te roepen. Tussen haakjes moet dan altijd het ding komen te staan dat je wilt verwijderen. Hierboven heb je gezien hoe je alleen het oudste geurtje kunt verwijderen van het plein. Voor het geval dat de geurvreter ook verse geurtjes kan opeten is dat niet voldoende. Immers er moet dan iedere keer in de hele lijst gekeken worden naar alle geurtjes of je nog wel stinken of niet. Hoe kun je nu alle geurtjes controleren en niet alleen de oudste aan het begin van het geurspoor? Hiervoor gaan we een zogenaamde for-loop gebruiken. Een voorbeeld van zo'n for-loop vind je hieronder, deze code is afkomstig van de AntSquare klasse. SQUARE_SIZE en CELL_SIZE zijn variabelen met respectievelijke waarden 32 en 15: for(int i = 0; i < SQUARE_SIZE; i++) { bg.drawLine(i * CELL_SIZE, 0, i * CELL_SIZE, SQUARE_SIZE * CELL_SIZE); bg.drawLine(0, i * CELL_SIZE, SQUARE_SIZE * CELL_SIZE, i * CELL_SIZE); } De code tussen accolades wordt in dit geval 32 keer uitgevoerd, beginnende met i-waarde 0 en zo oplopend. Vraag 6.1: Wat is de laatste i-waarde die voorkomt tussen de accolades? Alles wat je nodig hebt om de geurtjes op te ruimen, die door de geurvreter zijn opgegeten, is kort aan bod geweest. Ga nu aan de slag met de volgende opgave, misschien lukt het je al experimenterend de oplossing te vinden. Heb je hulp nodig, vraag dan een van de begeleiders. Opgave 6.2: Implementeer de methode removeSmell(). Hiertoe staat jullie een klasse CounterViewer ter beschikking die we al voor jullie voorbereid hebben, zie CounterViewer. Van deze klasse laat je de geurvreter een object aanmaken, en laat je de geurvreter dit object toevoegen aan de wereld. Elke keer als de geurvreter een geurtje opeet, roep je dan de methode setCounter(aantalVerwijderdeGeurtjes) aan, waarbij de variabele aantalVerwijderdeGeurtjes de verwijderde geurtjes bijhoudt. Misschien heb je wel ideeën voor uitbreidingen, om je fantasie een beetje te prikkelen hier wat ideeën van mij: De finale: Maak uitbreidingen aan de mierenspel of ontwikkel een compleet nieuw spel. Na afloop van de workshop zal een jury van docenten uit alle spellen de beste uitkiezen. De winnaar krijgt een verrassing. |