Ekstraoppgave med Pygame-Zero
Dette er en oppgave der man f?r lekt seg litt mer med Pygame Zero ved ? lage et enkelt spill. Gj?r gjerne denne oppgaven hvis du synes obligen g?r greit.
I denne oppgaven skal vi lage et enkelt Space Invaders-inspirert spill. Dette dokumentet inneholder noen instrukser og steg man kan f?lge, men v?r gjerne kreativ og implementer dine egne ideer eller gj?r ting p? din egen m?te.
F?r man starter
- S?rg for ? ha Pygame Zero installert (se uke 8)
- Last ned alle bildene fra denne mappen og legg i en mappe du kaller
images
som skal ligge inne i mappen du tenker ? skrive dette programmet.
Hva skal vi lage?
- Et spill der monstre kommer nedover skjermen og et romskip p? bunnen skal skyte monstrene. Man taper hvis et monster kommer helt ned
- Vi m? kunne representere romskipet, et ukjent antall monstre og kuler som romskipet skyter mot monstrene
- Vi m? kunne representere et spillbrett som holder p? all denne informasjonen, og vi m? blant annet kunne sjekke om kuler treffer monstre.
- Det er mange kule utvidelser man kan gj?re (men vi starter ganske enkelt)
- Man kan ha oppgraderinger til romskipet, slik at det kan bli bedre og f? bedre v?pen som skader monstrene mer
- Ulike monstre kan t?le ulik mengde skudd
- Man kan implementere ulike levler der ulike typer monstre kommer
- osv osv ...
Instruksene er med vilje litt vage og ?pne slik at man kan ta egne valg underveis.
Eksempel p? hvordan et spill kan se ut til slutt:
Steg 1: Lage et romskip
Lag en klasse Romskip
i en fil romskip.py
.
Konstrukt?ren til Romskip trenger ikke ? ta noen parametere. Et romskip starter alltid p? samme posisjon, s? konstrukt?ren kan initialisere disse instansvariablene:
_posisjon_venstre
: Et sted ca midt p? skjermen, f. eks 300_posisjon_topp
: Vi vil ha romskipet nesten p? bunnen av skjermen, og skjermen v?r er 700 px h?y, s? 620 b?r v?re en passe verdi_level
: 1 (denne vil vi ?ke senere)_bilde
: "romskip1" (planen er ? endre bildet n?r romskipet g?r opp i level)
Romskip trenger ogs? noen metoder:
beveg_hoyre
: Flytter romskipet f. eks 5 px mot h?yrebeveg_vensre
: Flytter tilsvarende mot venstre- I tillegg b?r det v?re metoder for ? hente ut de to posisjonene
S?rg for at romskipet ikke f?r lov til ? havne utenfor skjermen n?r det beveger seg.
Steg 2: F? romskipet opp p? skjermen og implementer at man kan bevege romskipet med piltastene
Lag en klasse Spill
i en fil spill.py
, hvor du kan ta utgangspunkt i prekoden under.
I konstrukt?ren blir det opprettet et romskip, og vi lager en liste som skal holde p? monstre (disse skal vi lage senere).
class Spill:
def __init__(self):
self._monstre = []
self._oppdatering = 0
self._romskip = Romskip()
def oppdater(self, keyboard):
# Sjekk for trykk p? keyboard
if keyboard.left:
self._romskip.beveg_venstre()
elif keyboard.right:
self._romskip.beveg_hoyre()
if keyboard.space and self._oppdatering - self._forrige_skudd > 5:
self.skyt()
self._forrige_skudd = self._oppdatering
self._oppdatering += 1
def skyt(self):
# denne metoden gj?r ingenting enda
return
def tegn(self, skjerm):
self._romskip.tegn(skjerm)
Oppdater-metoden v?r vil bli kalt mange ganger i sekundet. Her sjekker vi om noen av piltastene blir trukket inn. Hvis space-tasten er inne, skal vi skyte og kaller en skyt-metode som vi ikke trenger ? implemente helt enda. For ? unng? at man f?r skutt hver eneste gang oppdater kalles, lar vi det alltid v?re minst 5 oppdateringer mellom hver gang det er mulig ? skyte.
Lag ogs? f?lgende fil hovedprogram.py
med prekode for ? initialisere Pygame Zero og lage et Spill:
from monster import Monster
import pgzrun
from spill import Spill
# Dette er prekode som gjoer at pygame-zero fungerer. Ikke endre dette:
WIDTH = 900
HEIGHT = 700
spill = Spill()
# draw() er en metode Pygame Zero kaller hver gang den skal tegne noe p? skjermen (som den gj?r mange ganger i sekundet)
# Her sier vi at hver gang Pygame Zero skal tegne noe, s? vil vi at den skal kalle tegn-metoden til sauen v?r
def draw():
# Tegn f?rst et rektangel (bakgrunnen v?r)
screen.fill((0, 0, 0))
# Tegn deretter sauen
spill.tegn(screen)
# update() kalles ogs? mange ganger i sekundet. Her vil vi bevege sauen v?r
def update():
spill.oppdater(keyboard)
pgzrun.go()
Fordi vi importerer pgzrun og kaller pgzrun.go
nederst kan dette programmet kj?res som et vanlig python-program i terminalen (i obligen har vi brukt pgzrun-kommandoen, men det tilsvarer ? kalle pgzrun.go
).
Kj?r programmet og sjekk at du ser et romskip som du kan styre med piltastene. Sjekk at romskipet ikke g?r utenfor skjermen.
Steg 3: Monstre
Lag en klasse Monster
i en fil monster.py
.
- Konstrukt?ren skal ta bilde, antall_liv, posisjon_venstre og posisjon_topp som parametere
- Antall liv er et tall og sier hvor mange liv monsteret har (noen kan overleve flere skudd)
- Monster m? ha en beveg-metode (denne blir kalt mange ganger i sekundet). Velg selv hvordan du ?nsker at monsteret beveger seg. En mulighet er:
- Beveg mot h?yre helt til monsteret har kommet til enden av skjermen, g? deretter et hakk ned og beveg mot venstre tilbake igjen. G? et hakk ned og gjenta
- En tegn-metode som tar en "skjerm" som parameter. Skjermen er et objekt i Pygame Zero som vi tegner monsteret p?.
Et mulig skjelett til klassen Monster er:
class Monster:
def __init__(self, bilde, posisjon_venstre, posisjon_topp, antall_liv):
self._bilde = bilde
self._posisjon_venstre = posisjon_venstre
self._posisjon_topp = posisjon_topp
self._antall_liv = antall_liv
self._retning = 1
self._lever = True
def lever(self):
return self._lever
def beveg(self):
if self._retning == 1:
self._posisjon_venstre += 4
if self._posisjon_venstre >= 900 - 64:
self._posisjon_topp += 64
self._retning = -1
else:
self._posisjon_venstre -= 4
if self._posisjon_venstre <= 0:
self._retning = 1
self._posisjon_topp += 64
def tegn(self, skjerm):
skjerm.blit(self._bilde, (self._posisjon_venstre, self._posisjon_topp))
def hent_posisjon_venstre(self):
# ..
def hent_posisjon_topp(self):
# ..
Utvid oppdater-metoden i Spill-klassen slik at den kaller beveg-metoden til alle monstre i listen self._monstre
(men kun hvis monsteret lever). Utvid ogs? tegn-metoden i Spill-klassen slik at den tegner alle levende monstre.
Utvid ogs? oppdater-metoden
slik at det av og til lages nye monstre. Husk at oppdater kalles mange ganger i sekundet, s? du kan f. eks la det v?re 5% sannsynlighet for at et monster opprettes hver gang denne metoden kalles.
Steg 5: Skyte monstre
Vi trenger en klasse som representer hver kule. Lag en slik klasse Kule
. Kuler m? kunne bevege seg og tegnes p? skjermen:
- En kule beveger seg alltid rett oppover. I beveg-metoden holder det derfor ? bare endre posisjonen fra toppen.
Spill b?r ha en liste over kuler p? samme m?te som monstre. Skyt-metoden til spill kan implementeres slik:
- Lag et nytt Kule-objekt som starter med posisjonen til romskipet (gjerne midt p? toppen av romskipet).
- Legg kulen til i listen som holder p? alle kuler.
Utvid oppdater-metoden og tegn-metoden i Spill-klassen slik at kuler beveger seg og blir tegnet.
Sjekk at det blir skutt synlige kuler som beveger seg oppover n?r du spiller spillet ditt n? og trykker p? space.
Steg 6: Sjekk om kulene treffer monstrene
Lag en metode sjekk_kollisjoner
som g?r gjennom alle levende monstre og sjekker om noen av de har blitt truffet av en kule. Hvis det har skjedd, sett monsteret til d?d. Det kan ogs? v?re lurt ? implementere en m?te slik at kuler som har truffet monstre ogs? settes til ? v?re "d?de" slik at de ikke fortsetter ? bevege seg og slik at de ikke kan treffe flere monstre.
En mulig implementasjon man kan ta utgangspunkt i er (her er for enkelhets skyld kuler enten levende eller d?de):
def sjekk_kollisjoner(self):
for monster in self._monstre:
if not monster.lever():
continue
for kule in self._kuler:
if not kule.lever():
continue
if kule.hent_posisjon_venstre() >= monster.hent_posisjon_venstre() and kule.hent_posisjon_venstre() < monster.hent_posisjon_venstre() + 64 - 24:
if kule.hent_posisjon_topp() > monster.hent_posisjon_topp() and kule.hent_posisjon_topp() < monster.hent_posisjon_topp() + 64:
print("Monster blir truffet!")
monster.blir_truffet_av_kule(kule)
self._score += 1
Merk at monstre som blir truffet f. eks kan miste liv eller bare d? direkte. Hvis de mister liv, kan dette implementeres i en metode blir_truffet_av_kule
i Monster-klassen. Denne metoden kan ta trekke et liv fra monsteret og sette monsteret til ? v?re d?d hvis det har 0 liv igjen.
Kall metoden sjekk_kollisjoner
fra oppdater
-metoden.
Mulige utvidelser
- La romskipet f? 1 i score hver gang den enten treffer eller dreper et monster. La romskipet g? opp i level og f? bedre kuler som tar mer skade etter hvert som det f?r h?yere score. Du kan bruke de ulike romskip-bildene og kule-bildene til dette.
- Implementer at man d?r og at spillet er over hvis et monster kommer helt ned til bunnen av skjermen.
- Implementer ulike levler der ulike typer monstre kommer i de ulike levlene.
- Implementer lyd (f. eks n?r man skyter eller treffer monstre). Sjekk gjerne dokumentasjonen til Pygame Zero her.