We gaan met 2D game framework Löve2D een spel met vallende blokken maken.
De regels
Er zijn 7 verschillende stukken. Elk stuk bestaat uit 4 kleinere blokken.
Stukken vallen vanaf boven het speelveld. De speler kan het stuk verplaatsen naar links en rechts en het stuk ronddraaien. Als een stuk is geland, valt er een volgend stuk.
Boven het speelveld wordt de vorm van het volgende stuk dat gaat vallen getoond.
Als er een volledig gesloten rij van blokken is gevormd van links naar rechts op het speelveld, verdwijnt het en schuiven alle blokken erboven 1 rij naar beneden.
Het spel eindigt als een nieuw stuk direct op een al liggend stuk terecht zou komen.
Controls
toets | actie |
---|---|
pijl links | beweeg links |
pijl rechts | beweeg rechts |
z | draai tegen de klok in |
x | draai met de klok mee |
c | val op liggende blokken |
Overzicht
Een rooster bevat de blokken die al zijn gevallen. Een vak in het rooster kan leeg zijn of gevuld met een blok van een bepaalde kleur.
Tekst ' '
(spatie) is een leeg vak zonder blok en 'i'
, 'j'
, 'l'
, 'o'
, 's'
, 't'
en 'z'
zijn vakken met
een blok met verschillende kleuren.
Alle verschillende stukken worden in het rooster opgeslagen met de hoe ze gedraaid waren toen ze vielen.
Een vallend stuk wordt opgeslagen met een getal dat aangeeft welk type stuk het is, hoe het gedraaid is en wat de positie is in het rooster. Voor de positie worden X en Y waarden gebruikt.
Een nieuw stuk wordt boven het rooster getoond, maar niet als er te weinig voor is door al gestapelde blokken. In dat geval is het spel afgelopen.
De speler kan de stukken verplaatsen naar links en rechts, maar niet als het een stuk dat al gestapeld is overlapt. Een stuk kan ook niet buiten het rooster verplaatst worden.
Als er wat tijd is verlopen, verplaatst het stuk één rij blokken naar beneden. Dit gebeurt alleen als de nieuwe positie van het stuk niet overlapt met al gestapelde blokken en niet buiten het rooster valt.
Als knop z
of x
wordt ingedrukt, draait het stuk tegen de klok in of met de klok mee. Maar niet als het stuk dan
overlapt met al gestapelde blokken of buiten het rooster valt.
Als de val knop c
wordt ingedrukt, gaat het stuk sneller naar beneden, totdat het landt op al geplaatste stukken.
Als het stuk is geland, wordt een nieuw stuk gemaakt boven het rooster.
De 7 verschillende stukken worden in een willekeurige volgorde aangeboden.
Programmeren
Voor het programmeren zelf heb je alleen een editor zoals bijvoorbeeld Visual Studio Code nodig. Maar om het programma uit te voeren, heb je het Löve2D programma nodig. Deze kun je downloaden vanaf de site https://love2d.org/. Kijk voor meer informatie over het installeren op /instructies/love2d-shooter/#installatie-löve.
Als je Löve2D hebt geïnstalleerd, open je een terminal of cmd op Windows en zou je love.exe
moeten kunnen starten.
Bij het starten van het script is het belangrijk om te onthouden dat je de directory meegeeft aan Love en niet main.lua
.
Dus:
|
|
Hierbij bevat blocks
het bestand main.lua
waarin we de code schrijven van de volgende hoofdstukken.
Het venster tekenen
We beginnen in een lege conf.lua
met:
|
|
en een lege main.lua
.
Als je nu love.exe blocks/
uitvoert, krijg je een leeg rechthoekig scherm.
Het rooster tekenen
Voor ieder blok in het speelveld wordt een vierkant getekend.
Zet de volgende code in main.lua
:
|
|
Het rooster inkleuren
De achtergrondkleur en de kleur van een leeg vak worden ingesteld.
Voeg functie love.load()
toe aan main.lua
, boven functie love.draw()
die er al in staat:
|
|
Pas nu functie love.draw()
aan:
|
|
De gevallen blokken bewaren
Het rooster voor de gevallen blokken is gemaakt en elk blok wordt als ' '
(spatie) gezet wat betekend
dat het vak leeg is.
We gaan de breedte en hoogte van het rooster gebruiken om de blokken te tekenen, dus we zetten de waarden 10 en 18 in variabelen.
Pas de functies aan met:
|
|
De kleur van de blokken
De kleur van de blokken die we gaan tekenen wordt bepaald door het type.
Om dit testen, zetten we verschillende typen blokken in het rooster.
De types geven we aan met een letter: i
, j
, l
, o
.. etc. Vervolgens koppelen we een kleur aan elk type.
Bijvoorbeeld:
|
|
Maak de volgende aanpassingen:
|
|
De stukken opslaan
Een puzzelstuk bestaat uit verschillende blokken. We slaan de variaties van puzzelstuk op als een nieuw raster. In dit raster kunnen we ook de gedraaide versies van het puzzelstuk bewaren.
|
|
Elk puzzelstuk kan 4 varianten hebben, want hij kan 4 kanten op gedraaid zijn. We slaan dus 4 varianten op voor elk puzzelstuk. In het geval van de rechte lijn zijn dit er eigenlijk maar 2. Deze vorm is namelijk symmetrisch over 1 as. De kubus heeft maar 1 variant. Deze is namelijk symmetrisch over 2 assen. De rechte lijn ziet er als volgt uit:
|
|
Dit zijn alle puzzelstukken; voeg deze toe aan functie love.load()
:
|
|
Het vallende stuk opslaan
Er kan altijd maar een puzzelstuk tegelijkertijd vallen. We kunnen dit puzzelstuk opslaan met de variabelen pieceType
en pieceRotation
. pieceType
bevat welk stuk het is (lijn, blokje, etc). pieceRotation
bevat hoe het stuk gedraaid is.
|
|
Het stuk tekenen
Elk puzzelstuk wordt getekend door langs elk vakje in het rooster te gaan en de blokken een voor een in te kleuren.
De kleur is afhankelijk van het type blok.
|
|
En verwijder dit stuk uit functie love.load()
:
|
|
Code vereenvoudigen
De code voor het tekenen van een stilstaand blok lijkt erg op de code voor het tekenen van een vallend blok.
We voegen daarom de functie drawBlock
toe en verplaatsen de code in de twee for-loops er naartoe.
Vervang alle code in love.draw()
met deze code:
|
|
Het stuk draaien
Wanneer we op de x-toets drukken willen wat dat het stuk draait.
pieceRotation
bevat een getal dat de draaiing aangeeft. 1 is geen draaiing, 2 is 90° met de klok mee en 3 is 180°.
Als het rotation getal hoger is dan de verschillende draai posities (4, 2 of 1). Dan wordt het getal terug gezet naar 1.
Als we op z-toets drukken willen we dat het het stuk de andere kant op draait. Dit doen we door de waarde in
pieceRotation
met 1 te verlagen.
Als het getal lager wordt dan 0. Zetten we het weer terug naar 1.
Voeg dit stuk code toe onderaan het bestand:
|
|
De stukken testen
We willen graag de code testen. Daarom voegen we toe dat we het puzzelstuk kunnen veranderen met de omhoog en omlaag pijltjestoetsen.
De tijdelijke code onderaan functie love.keypressed(key)
:
|
|
Als je hebt gezien dat de stukken veranderen, kun je de net toegevoegde code weer verwijderen.
De positie van het vallende stuk bijhouden
We bewaren de positie van het vallende puzzelstuk op in het rooster en we tekenen het puzzelstuk op die positie.
|
|
En het tekenen van het blok op de juiste positie:
|
|
Het stuk verplaatsen
We gaan nu de linker en rechter pijltjestoetsen gebruiken om het stuk te verplaatsen.
Voeg de volgende regels toe onderaan love.keypressed(key)
:
|
|
Timer
We willen dat elke 0.5 seconden het puzzelstuk valt.
We maken daarom een timer
variabele, die we steeds met dt
(delta time: de tijd die verstreken is) verhogen.
Als de waarde van timer
gelijk aan 0.5 of hoger is, zetten we het terug naar 0.
|
|
En een nieuwe functie onderaan het bestand:
|
|
main.lua
Na het testen mag je print('tick')
weer weghalen.
Stukken vallen
We gebruiken de timer om het stuk elke 0.5 seconden verder naar beneden te laten vallen.
De waarde van pieceY
wordt met 1 verhoogd om het stuk 1 regel lager te tekenen:
|
|
Linker, rechter en onderrand van het rooster
We willen voorkomen dat de puzzelstukken links of rechts van het scherm af kunnen bewegen. Daarom checken we altijd eerst of alle blokken nog wel in het speelveld zijn.
We willen deze check vaker uitvoeren, daarom stoppen we het in een functie. Dan kunnen we het makkelijk herbruiken.
De functie heeft de positie en rotatie nodig. De functie geeft true
of false
terug om aan te geven of we mogen bewegen of draaien.
We noemen de functie: canpieceMove(testX, testY, testRotation)
. Voor nu zal de functie alleen true
teruggeven,
dan kunnen we namelijk de functie in de rest van de code verwerken.
We passen de code aan zodat de check functie eerst wordt uitgevoerd, voordat we bewegen of draaien.
|
|
Links
We beginnen met links checken. Als het blok niet leeg is, en de x
positie lager is dan 1, geeft de functie false
terug.
|
|
Code vereenvoudigen
Het aantal blokken van elk puzzelstuk op de X en Y worden herbruikt uit het tekenen. We stoppen deze waardes in variabelen.
|
|
Rechts
We gaan nu rechts checken. Als het blok niet leeg is, en de x
positie groter is dan het grid, geeft de functie false
terug.
|
|
Onderkant
Het puzzelstuk moet stoppen als het de onderkant aanraak. Als de onderkant van het puzzelstuk lager is dan de onderkant
van het veld geeft de functie false
terug.
|
|
Controle op botsingen
Als er al een ander puzzelstuk is, kan het puzzelstuk daar niet naartoe bewegen. Daarom checken we of er al een blok is
op die plek. Als dat zo is, geeft de functie ook false
terug.
We testen dit door zelf een blokje op het rooster te zetten.
|
|
Als het stuk valt, wordt het geblokkeerd door het blok. Schuif het stuk eens naar links of rechts. Wat gebeurt er?
Verwijder na het testen de tijdelijke code.
Code vereenvoudigen
De posities van de blokken die we testen kunnen we herbruiken. We stoppen deze in variabelen.
|
|
Sneller vallen
Als de speler c indrukt laten we het puzzelstuk snel vallen. Zolang c is ingedrukt verhogen we de Y positie met 1 totdat het puzzelstuk iets raakt.
|
|
Een stuk terugzetten
Als de timer tikt, en het puzzelstuk niet verder kan bewegen, resetten we de positie en rotatie en type.
|
|
Code vereenvoudigen
We resetten het puzzelstuk vaker, dus we stoppen de code in een functie om her te gebruiken.
|
|
Een reeks met volgende stukken
We maken nu een sequence (volgorde) waarin de puzzelstukken gaan vallen. We slaan deze op als een tabel met getallen die aangeven welk puzzelstuk het is.
We stoppen alle verschillende nummers/puzzelstuk types in de tabel op een willekeurige positie.
We testen dit door een reeks te maken en te printen als je op s
drukt
|
|
Resultaat van drukken op knop s
in tijdelijke code:
3, 2, 4, 1, 7, 5, 6
Verwijder de tijdelijke code na het testen.
Een nieuw stuk uit de reeks
Wanneer er een nieuw puzzelstuk wordt gemaakt, halen we de vorige uit de reeks en herbruiken we het.
Als de reeks leeg is, is maken we een nieuwe.
Let op! We verplaatsen de newPiece
functie naar onder de newSequence
functie.
|
|
Gevallen stukken bewaren
Als een stuk is gevallen, worden ze apart gezet en bewaard.
Elk blok in het stuk wordt bekeken en als het niet leeg is, wordt het blok als bezet gezet.
|
|
Een nieuw stuk direct na landing vorige
Als een stuk is gevallen, wordt de timer direct opnieuw ingesteld, zodat een volgende stuk direct wordt gemaakt.
|
|
Volle rijen vinden
Elke rij wordt gecontroleerd. Als er geen legen blokken in de rij zijn, is de rij compleet.
Om te controleren of het werkt worden complete rijen geprint naar de console.
|
|
Volle rijen verwijderen
Als een rij compleet is,
If the row is complete, the rows from the complete row to the row second from the top are looped through.
Each block in the row is looped through and set to the value of the block above it. Because there is nothing above the top row it doesn’t need to be looped through.
The top row is then set to all empty blocks.
De volledige code tot op dit punt:
|
|
Game over
Als een nieuw stuk meteen niet meer verplaatst kan worden omdat er andere blokken in de weg staan, is het spel over.
love.load
wordt aangeroepen om het spel opnieuw te starten.
|
|
Het rooster verplaatsen in het window
Tot nu toe is het rooster aan de linker boven kant van het venster getekend. We gaan het nu in het midden een stukje van de bovenrand van het venster zetten.
|
|
Het volgende stuk
Het volgende stuk uit de reeks dat gaat vallen, wordt boven het rooster getekend.
|
|
|
|
Het spel herstarten
Als het spel is afgelopen, wordt het herstart. Daarvoor moeten wat variabelen opnieuw worden ingesteld.
De code hierovor zetten we in een functie.
|
|
Veel plezier met spelen!
Bron
Deze instructie is een vertaling van de Engelstalige tutorial Blocks: A tutorial for Lua and LÖVE 11 van simple.game.tutorials@gmail.com.
Licentie
Deze instructies worden, net als alle andere instructies van CoderDojo Nijmegen, aangeboden onder een Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Licentie.