Asteroids, pas op voor de brokstukken!

In het spel Asteroids bestuur je een ruimteschip dat midden in een galactische storm is geraakt. De asteroïden vliegen je letterlijk om je oren! Om jezelf te redden gebruik je het laserkanon van je schip. Een botsing met de brokstukken kan fataal aflopen. Het doel van het spel is om zo veel mogelijk asteroïden kapot te schieten en zo lang mogelijk in leven te blijven.

In 1979 bracht Atari het spel op de markt. In het originele spel komen ook ufo's voor die het op jou gemunt hebben. Wij hebben echter onze handen al vol aan de lastige besturing en de bewegende brokstukken. Wil je meer weten over Asteroids? Lees dan verder op Wikipedia.

Leerdoelen: wat kun je na het maken van dit spel?

Je leert in dit spel hoe je objecten in de rondte kunt laten draaien (rotatie). Een ruimteschip bestuur je namelijk niet zomaar! Ook leer je gebruik te maken van overerving. Je weet aan het eind van dit hoofdstuk dan ook precies hoe het zit met ouder-objecten en kind-objecten. Verder gebruik je de functie min() en leer je een beetje modulo-rekenen.

Eerste versie testen

Download het zip-bestand (asteroids.zip) met daarin alle bestanden die je nodig hebt om Asteroids te maken. Hierin vind je de sprites, geluidjes, achtergronden en Game Maker bestanden.

Open nu het bestand asteroids1.gmk in Game Maker. Start het spel, zet je luidsprekers aan en bekijk wat je tot nu toe kunt doen. Dat is nog niet erg veel. Je ziet een titelscherm, rondvliegende asteroïden en af en toe een hartje. Op de achtergrond klinkt een zenuwslopend muziekje. Het belangrijkste onderdeel mist nog: het bestuurbare ruimteschip. Dit schip ga je straks zelf maken. We kijken eerst even naar de belangrijkste dingen tot nu toe.

Aan de ene kant eruit, aan de andere kant erin

Als je goed kijkt, zie je dat de asteroïdes niet verdwijnen wanneer ze het scherm uitgaan. Ze komen er namelijk aan de andere kant weer in. Het worden er dan ook steeds meer! Dit "aan de andere kant verder gaan" wordt wrappen genoemd. Deze actie wordt uitgevoerd op het Step-event van obj_astroid. Zie afbeelding 1.

Wrappen in beide richtingen. De asteroïden komen weer terug het scherm in.Afbeelding 1. Wrappen in beide richtingen. De asteroïden komen weer terug het scherm in.

Willekeurig rondvliegende objecten

Om ervoor te zorgen dat een spelletje elke keer een beetje anders is, zijn er vaak dingen afhankelijk van kans. In Asteroids komen de brokstukken (en soms een hartje!) op willekeurige tijdstippen en met willekeurige snelheden het spel binnen. Bovendien zien er ook verschillende soorten brokstukken. Deze willekeurigheid is geprogrammeerd in obj_controller in het Step-event. Zie afbeelding 2.

Willekeur in het spel als het gaat om de asteroïden.Afbeelding 2. Willekeur in het spel als het gaat om de asteroïden.

Er wordt elke stap een dobbelsteen gegooid met 90 zijden. Het Step-event wordt 30 keer per seconde uitgevoerd. Dit betekent dat er per seconde een kans is van 1 op 3 dat er een nieuwe asteroïde wordt aangemaakt. Nu zie je in afbeelding 2 dat er 4 keer gedobbeld wordt. Vanuit elke hoek van de kamer kan namelijk een brokstok verschijnen, elk met een kans van 1 op 3.

De richting, de vorm en de snelheid van de aangemaakte asteroïde wordt vervolgens bepaald in het Create-event van obj_asteroid. Zie afbeelding 3.

Het Create-event van de asteroïde.Afbeelding 3. Het Create-event van de asteroïde.

Allereerst wordt de sprite willekeurig gekozen. Hierbij wordt gebruik gemaakt van het feit dat de sprite meerdere subimages bevat. Vervolgens wordt de richting bepaald. Dit gaat als volgt: als er een ruimteschip is dan beweegt de asteroïde richting de positie van dat schip. Als er nog geen ruimteschip is, zoals in het titelscherm het geval is, wordt er naar een willekeurig punt ergens in de kamer bewogen. Tot slot bestaat de mogelijkheid het brokstok in een hartje veranderd. Deze kans is 1 op 20.

Levensbalkje

Je hebt nog geen levensbalkje gezien, maar deze is al wel gemaakt. Zodra er straks een ruimteschip in het spel is zal de levensbalk linksboven in het scherm worden getoond. De levensbalk wordt "getekend" door middel van een Draw-event in obj_controller. Zie afbeelding 4.

De voorbereidingen voor het levensbalkje.Afbeelding 4. De voorbereidingen voor het levensbalkje.

De reden dat je nu nog geen balkje ziet, is dat er gecontroleerd wordt of er wel een ruimteschip is. Door middel van de actie Test Instance Count wordt het aantal ruimteschip-objecten geteld. Als dit aantal gelijk is aan 1, dan wordt de health bar dus getoond.

Je weet nu welke belangrijke dingen al geprogrammeerd zijn. De echte uitdaging zit hem echter in de besturing van het ruimteschip. Daar ga je in het volgende hoofdstuk mee de slag.

Zelf bouwen

Het ruimteschip dat we in dit hoofdstuk gaan bouwen, heeft een andere besturing dan je wellicht van een auto gewend bent. In de ruimte kunnen we namelijk niet remmen. De besturing gebeurt door te draaien (links, rechts) en te gassen (pijltje naar boven). Bovendien beweegt het ruimteschip een stukje door, wanneer we het gas los laten.

Het ruimteschip toevoegen

Volg de onderstaande stappen om de sprites en objecten voor het ruimteschip te maken.

  1. Met het vinkje aan bij Separate collision masks wordt voor elke subimage van de sprite een mogelijke botsing apart berekend. Dit maakt de spelervaring beter.
    Maak een nieuwe sprite met de naam spr_ship_normal. Laad hierin de afbeelding ship_strip72.png. Zet het vinkje aan bij Separate collision masks en zet Origin op Center (10,10). Zo draait het schip straks om zijn middelpunt. Het venster ziet er nu zo uit.
  2. Maak een nieuwe sprite met de naam spr_ship_flames. Laad hierin de afbeelding ship_flames_strip72.png. Zet het vinkje aan bij Separate collision masks en zet Origin op Center (11,11). Zo draait het schip straks om zijn middelpunt.
  3. Een kind erft eigenschappen van zijn ouder. Zo is het ook bij programmeren. In Game Maker worden de Events, Actions en Variabelen van een ouder geërfd door de kinderen.
    Maak een nieuw object met de naam obj_ship. Selecteer no sprite en laat verder alles voor wat het is. Dit object dient straks als parent.
  4. Maak een nieuw object met de naam obj_ship_normal. Selecteer sprite spr_ship_normal en zet Parent op obj_ship. Nu is dit object een kind van obj_ship. Het venster ziet er nu zo uit.
  5. Maak een nieuw object met de naam obj_ship_flames. Selecteer sprite spr_ship_flames en zet Parent op obj_ship. Nu is ook dit object een kind van obj_ship.
  6. Open nu room playroom en plaats obj_ship_normal ongeveer in het midden van de kamer. Het scherm ziet er nu zo uit.
  7. action_ifnumberOpen nu obj_controller en ga naar het Draw-event. Dubbelklik op Test Instance Count en selecteer bij object obj_ship. Nu zien we zodadelijk de levensbalk tijdens het spelen. Het venster ziet er nu zo uit.
  8. Start nu het spel. We zien linksboven een levensbalk en in het midden van het venster een ruimteschip ronddraaien. Het spel ziet er nu zo uit.
Is het bovenstaande niet gelukt? Geen zorgen. Open dan asteroids2.gmk om verder te gaan.

Eindelijk, het ruimteschip laten bewegen

Een rondraaiend ruimteschip is natuurlijk niet genoeg. In de volgende stappen leer je hoe je het schip laat vliegen. Open nu obj_ship_normal.

  1. action_variableVoeg een Create-event toe. Voeg vervolgens een Set Variable-actie toe. Bij variable typ je new_direction en bij value typ je direction. We gebruiken de variabele new_direction om straks een bewegingsrichting te kiezen. Het venster ziet er nu zo uit.
  2. De functie floor() rond een getal naar beneden af. Als er uit de deling bijvoorbeeld 71,8 komt, wordt dit afgerond tot 71.
    action_spriteVoeg hieronder een Change Sprite-actie toe. Kies als sprite spr_ship_normal, typ by subimage floor(new_direction/5) en zet speed op 0. Nu stopt het ruimteschip met draaien. Het venster ziet er nu zo uit.
  3. action_variableVoeg een Keyboard Left-event toe. Voeg vervolgens een Set Variable-actie toe. Bij variable typ je new_direction en bij value typ je (new_direction+5) mod 360. Dit betekent dat bij het indrukken van de linkerpijltjestoets new_direction met 5 graden wordt verhoogd. Het venster ziet er nu zo uit.
  4. action_spriteVoeg hieronder een Change Sprite-actie toe. Kies als sprite spr_ship_normal, typ by subimage floor(new_direction/5) en zet speed op 0. De juiste subimage wordt op deze manier gekozen. Het venster ziet er nu zo uit.
  5. mod staat voor modulo. Dat is de rest van een deling. Op deze manier kan het aantal graden nooit meer worden dan 359. Want 360 mod 360 = 0. De klok waar je de tijd op afleest werkt net zo, maar dan tot 60.
    action_variableVoeg een Keyboard Right-event toe. Voeg vervolgens een Set Variable-actie toe. Bij variable typ je new_direction en bij value typ je (360+new_direction-5) mod 360. Dit betekent dat bij het indrukken van de rechterpijltjestoets new_direction met 5 graden wordt verlaagd. Het venster ziet er nu zo uit.
  6. action_spriteVoeg hieronder een Change Sprite-actie toe. Kies als sprite spr_ship_normal, typ by subimage floor(new_direction/5) en zet speed op 0. De juiste subimage wordt op deze manier gekozen. Het venster ziet er nu zo uit.
  7. De functie min() geeft de kleinste van twee waarden terug. Zodra speed+0.3 groter is dan 10, wordt de snelheid dus 10. Op deze manier is de snelheid begrensd.
    action_move2Voeg een Keyboard Up-event toe. Voeg vervolgens een Move Free-actie toe. Bij direction typ je new_direction en bij speed typ je min(10,speed+0.3). Dit betekent dat bij het indrukken van de pijl omhoog de snelheid met 0.3 toeneemt. Tegelijkertijd wordt de snelheid begrensd op 10. Het venster ziet er nu zo uit.
  8. action_changeVoeg hieronder een Change Instance-actie toe. Kies bij change into voor obj_ship_flames en kies bij perform events voor yes. Het Create-event van obj_ship_flames wordt zodoende uitgevoerd. Het venster ziet er nu zo uit.

We zijn nu klaar met obj_ship_normal. Op dit moment kunnen we een richting kiezen en het schip veranderen in obj_ship_flames. Je zult zien dat wanneer je het spel speelt, we inderdaad bewegen en heel langzaam vliegen. Bovendien kunnen we niet meer stoppen met bewegen of een andere richting kiezen. Dat komt doordat we nog niets hebben ingesteld bij obj_ship_flames. Dat is dus wat we nu gaan doen.

Open het object obj_ship_flames. De volgende stappen laten ons ruimteschip eindelijk echt bewegen!

  1. action_spriteVoeg een Create-event toe. Voeg vervolgens een Change Sprite-actie toe. Kies als sprite spr_ship_flames, typ by subimage floor(new_direction/5) en zet speed op 0. Zo kiest ook dit ruimteschip de juiste subimage. Het venster ziet er nu zo uit.
  2. action_move2Voeg een Keyboard Up-event toe. Voeg vervolgens een Move Free-actie toe. Bij direction typ je direction en bij speed typ je min(10,speed+0.3). Dit betekent dat bij het indrukken van de pijl omhoog de snelheid met 0.3 toeneemt. Tegelijkertijd wordt de snelheid begrensd op 10. Het venster ziet er nu zo uit.
  3. action_changeVoeg een Keyboard Release Up-event toe. Voeg vervolgens een Change Instance-actie toe. Kies bij change into voor obj_ship_normal en kies bij perform events voor yes. Het Create-event van obj_ship_normal wordt zodoende uitgevoerd. Het venster ziet er nu zo uit.

Test het spel door het op te starten. Maar pas op! Zodra je de kamer uitvliegt is het lastig om het schip terug te halen. Dat én meer gaan we oplossen in het volgende stuk.

Is het bovenstaande niet gelukt? Geen zorgen. Open dan asteroids3.gmk om verder te gaan.

Het spel afmaken en de besturing verfijnen

Hieronder volgen de laatste stappen om het spel af te maken. Je gaat alleen obj_ship nog aanpassen. Dit object is het ouder-object (Parent) van de andere twee schip-objecten. Dit betekent dat Events, Actions en Variabelen van dit object worden geërfd door de kinderen (obj_ship_normal, obj_ship_flames). Alles wat we nu doen heeft dus effect op deze beide kinderen.

Open nu obj_ship en volg onderstaande stappen.

  1. action_captionVoeg een Create-event toe. Voeg vervolgens een Score Caption-actie toe. Laat alle instellingen staan zoals ze staan. In de titelbalk van ons spel krijgen we nu ons aantal punten te zien. Het venster ziet er nu zo uit.
  2. action_wrapVoeg een Step, Step-event toe. Voeg vervolgens een Wrap Screen-actie toe. Selecteer bij directions in both directions. Ons ruimteschip doet nu hetzelfde als de asteroïden: aan de ene kant het scherm uit, aan de andere kant er weer in. Het venster ziet er nu zo uit.
  3. action_frictionVoeg hieronder een Set Friction-actie toe. Typ bij friction 0.1. Dit betekent dat de snelheid van het ruimteschip elke stap met 0.1 wordt verminderd. Het venster ziet er nu zo uit.
  4. action_destroyVoeg een Collision-event toe met obj_astroid. Voeg vervolgens een Destroy Instance-actie toe. Zet applies to op other. Bij een botsing verdwijnt de asteroïde nu. Het venster ziet er nu zo uit.
  5. action_soundVoeg hieronder een Play Sound-actie toe. Selecteer bij sound snd_collision. Nu wordt er een geluidje afgespeeld als we botsen. Het venster ziet er nu zo uit.
  6. Relative betekent dat we uitgaan van de huidige waarde. Bij deze waarde wordt iets opgeteld of afgetrokken. In het laatste geval gebruik je het min-tekentje.
    action_healthVoeg hieronder een Set Health-actie toe. Typ bij value -5 en zet het vinkje aan bij relative. Elke keer wanneer we botsen verliezen we 5 levenspunten. Het venster ziet er nu zo uit.
  7. action_destroyVoeg een Collision-event toe met obj_health. Voeg vervolgens een Destroy Instance-actie toe. Zet applies to op other. Bij een botsing verdwijnt het hartje nu. Het venster ziet er nu zo uit.
  8. action_scoreVoeg hieronder een Set Score-actie toe. Typ bij value 50 en zet het vinkje aan bij relative. Elke keer wanneer we botsen krijgen we 50 bonuspunten. Het venster ziet er nu zo uit.
  9. action_soundVoeg hieronder een Play Sound-actie toe. Selecteer bij sound snd_health. Nu wordt er een geluidje afgespeeld als we botsen. Het venster ziet er nu zo uit.
  10. action_ifhealthVoeg hieronder een Test Health-actie toe. Typ bij value 100 en selecteer bij operation smaller than. We controleren hier of we minder dan 100 levenspunten hebben. Het venster ziet er nu zo uit.
  11. action_healthVoeg hieronder een Set Health-actie toe. Typ bij value 10 en zet het vinkje aan bij relative. Elke keer wanneer we botsen krijgen we er 10 levenspunten bij. Het venster ziet er nu zo uit.
  12. action_createspeedVoeg een Key Press, Space-event toe. Voeg vervolgens een Create Moving-actie toe. Selecteer bij object obj_laser. Laat x en y op 0 staan. Vul bij speed 15 in. Bij direction typ je new_direction. Zet ten slotte het vinkje aan bij relative. Op deze manier schieten we een laserstraal. Het venster ziet er nu zo uit.
  13. action_soundVoeg hieronder een Play Sound-actie toe. Selecteer bij sound snd_laser. Nu wordt er een geluidje afgespeeld elke keer als we schieten. Het venster ziet er nu zo uit.

Start nu het spel op en controleer of alles werkt. Het schieten (met de spatiebalk) werkt nu, als alles goed is gegaan, ook.

Is er iets niet helemaal gelukt? Open dan asteroids_final.gmk voor de volledige versie.

Samenvatting

Gefeliciteerd! Je hebt zojuist in 32 stappen een variant gemaakt op Astroids. Je hebt het ruimteschip laten draaien door gebruik te maken van een sprite met 72 subimages. Daarnaast heb je een variabele gebruikt om de richting van het schip te veranderen. Door middel van modulo-rekenen zorg je er voor dat de richting altijd tussen de 0 en 360 graden blijft. Zie afbeelding 34.

360 graden draaien.Afbeelding 34. 360 graden draaien.

Ook heb je weer gebruik gemaakt van overerving. Het ruimteschip met en zonder vlammen zijn beide kinderen van obj_ship. Een botsing met bijvoorbeeld een asteroïde hoeft dus alleen bij het ouder-object (obj_ship) geprogrammeerd te worden.

De functie min() heb je gebruikt om de snelheid van het ruimteschip te begrenzen. Deze functie geeft namelijk de kleinste van twee waarden terug.

Extra's

Het spel dat we hebben gemaakt kan op talloze manieren worden uitgebreid. Zou het bijvoorbeeld niet handig zijn als we zo nu en dan een schild om ons heen hebben? Dan hebben we even geen last van die asteroïden! Of wat te denken van brokstukken die meerdere keren moeten worden geraakt voordat ze kapot gaan? Bovendien hebben we ook geen echte tegenstanders. Wellicht kun je een ufo maken die jou beschiet? Ook vind dit spelletje nu plaats in slechts 1 level. Je zou een spel kunnen maken met meerdere levels en verschillende achtergronden. Wanneer ga je dan naar het volgende level? Bij een bepaald aantal punten bijvoorbeeld? Dit zijn zo maar een paar ideetjes. Je hebt er zelf vast nog veel meer!