Om någon där ute har väntat på den sista delen i min tutorial om Pygame Zero så kan jag härmed meddela att väntan nu är över. Några kodexempel blir det dock inte den här gången. Eftersom jag inte ville att inlägget skulle bli ännu längre och rörigare än föregående mastodontinlägg så ger jag i texten nedan bara sammanfattande beskrivningar av de ändringar och tillägg som jag har gjort i vårt lilla spel, som ju är en version av klassiska Space Invaders. Och så får du som vill göra de här ändringarna i ditt eget program kika i källkoden som jag har lagt upp på Github. Är du med? Då kör vi.
(Är du inte med? I så fall hittar du samtliga tidigare inlägg om Pygame Zero här.)
Scener
I den tidigare versionen så startade själva spelet omedelbart när man körde programmet. Så brukar det ju inte vara i riktiga spel, utan först brukar man komma till någon form av startskärm eller meny där man kan välja att starta spelet när man själv är redo. Därför så har jag infört olika ”lägen”, eller scener som man brukar kalla det, i spelet. Den första scenen är menyscenen och den representeras av en klass som heter just MenuScene. Precis som spelkoden i den gamla versionen så har MenuScene två metoder som heter update och draw, och det är dessa metoder som nu anropas när programmet körs. Så här ser menyskärmen ut:
Så mycket till meny är det ju egentligen inte – det enda användaren kan göra är att starta spelet genom att trycka på tangenten ”s” (eller stänga fönstret igen genom att klicka på krysset). Men någonting ska scenen heta och jag valde MenuScene. Vill du hellre döpa din klass till t ex StartScene så gör det.
När användaren väl trycker på ”s” så växlar programmet till spelscenen, som är ett objekt av klassen PlayScene. Till denna klass har all den kod som tidigare låg sist i programmet, själva spelkoden, flyttats. Den tredje och sista scenen är GameOverScene och den kommer man till när man antingen har dödat alla rymdvarelser eller…
Ond bråd död
… när man själv har dött. Som du kanske minns så var kanonen, som är det objekt som spelaren styr, ”odödlig” i den tidigare versionen av spelet. Så är det inte längre, utan nu ”dör” man om man antingen krockar med en rymdvarelse eller om en rymdvarelse når botten på spelfönstret. Hur växlar man då mellan liv och död? Jo, om du tittar i klassen PlayScene så ser du att det finns en variabel som heter running
. Det är en så kallad boolesk variabel som bara kan ha två värden: True
(sant) eller False
(falskt). När spelet startas så sätts running
till True
. I metoden update i PlayScene så kontrolleras värdet och det är bara om running
har värdet True
som den kod som uppdaterar spelet körs. Om spelaren på det ena eller andra sättet dör så sätts running
till False
och spelscenen ”avvecklas” innan spelet växlar till slutskärmen.
Klassen Game
En annan ny klass är Game och den representerar just själva spelet, i sin helhet. Den innehåller inte särskilt mycket, det mesta av den kod som så att säga uträttar något i spelet ligger ju i scen-klasserna och i synnerhet då i PlayScene. Men den har en liten lista, scenes
, med de olika scenerna och en metod, change_scene, för att växla mellan scenerna. Uppmärksamma läsare kanske noterar att när scenes
skapas i Games metod __init__ så används inte hakparenteser utan vanliga parenteser. När man gör på detta vis så skapas en tuple, en speciell sorts lista som inte går att ändra på. Eftersom vi inte planerar att ta bort eller lägga till scener utan bara ska växla mellan samma tre scener så passar det bra att göra scenes
till en sådan lista. I Game finns även en metod som heter set_game_over_message och som används för att sätta det budskap som ska visas på slutskärmen.
Explosioner
Den sista nya klassen är Explosion och vad den representerar kan du nog lista ut. Varje gång en rymdvarelse dör och tas bort ur listan med rymdvarelse-objekt så skapas i stället en explosion som ritas på samma position och lagras i spelscenens lista explosions
. Explosionen visas bara under ett visst antal ”ticks”, det vill säga varv i spelloopen. Sedan får spelscenen via metoden is_finished besked att explosionen är klar och även den tas bort ur spelet. Samma sak händer när kanonen/spelaren dör, men då visas en annan bild.
”Konstanter”
Till sist så har jag, längst upp i källkoden, lagt till ett antal ”konstanter”. Med konstanter menas i kodsammanhang variabler vars värden man inte kan ändra på. Namn på sådana variabler brukar i Python och många andra språk skrivas med versaler. I Python kan man faktiskt ändra värdet på en variabel av detta slag, det är därför jag har satt citattecken kring konstanter ovan. Men versalerna är åtminstone en signal om att man inte ska ändra på dem. Hur som helst: I dessa variabler har jag lagrat värden som senare används i olika beräkningar, t ex spelfönstrets bredd och höjd, värden som bestämmer rymdvarelsernas positioner och med vilken hastighet olika objekt rör sig. Det finns flera fördelar med att göra på det här sättet i stället för att ”hårdkoda” värden, skriva dem uttryckligen i koden, som vi gjorde i den tidigare versionen. Dels så blir koden mer läsbar. Dels så behöver man, om man senare vill ändra på något som exempelvis spelfönstrets bredd och antalet rymdvarelser per rad, bara ändra på ett enda ställe.
Nu har jag redogjort för alla ändringar – tror jag, om du hittar något i koden som jag inte har tagit upp får du gärna ställa en fråga i kommentarsfältet – så det här inlägget och därmed hela denna tutorial börjar lida mot sitt slut.
Men om du själv är sugen på att bygga vidare på spelet så har jag ett par förslag. Till att börja med skulle du kunna lägga till andra typer av aliens. De bilder som jag har använt är hämtade från denna fina spritesheet där de ligger under rubriken ”Original Arcade”. Ett annat förslag, som nog kräver lite mer klurande, är att låta rymdvarelserna skjuta tillbaka mot kanonen. Ett sätt att implementera detta kan vara att ha en separat lista i PlayScene med kulor från rymdvarelserna, som t ex kan heta alien_bullets. Sedan kan man vid varje varv i spelloopen låta slumpen avgöra dels om rymdvarelserna ska skjuta och dels vilken rymdvarelse som i så fall ska avfyra kulan. Pythons modul random kan vara användbar i sammanhanget.
Nu har du förhoppningsvis lite att bita i. Lycka till!
/Mats