Peliohjelmointi – Pygame




1. Perustehtävät
Asennukset ja Pygame tutuksi
Ensimmäinen Pygame-sovellus
Tehtävä .a. Aja annettu koodi ja seura, mitä tapahtuu. Kirjoita lyhyt vastaus siitä, mitä näit ja miksi niin kävi.
#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
# Peli-ikkunan määritys
screen = pygame.display.set_mode((400, 300))
pygame.display.set_caption("Terve Pygame")
Ikkuna välähtää ja sulkeutuu heti, koska ohjelmassasi ei ole pelisilmukkaa eikä tapahtumankäsittelyä. Kun Python suorittaa viimeisen rivin, ohjelma päättyy välittömästi, koska Pygame-ikkuna pysyy “hengissä” vain kun ohjelma on käynnissä.
b. Lisää annettuun koodiin toimiva pelisilmukka, joka pitää pelin toiminnassa. Muista huomioida myös peli-ikkunan sulkeminen.
#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
# Peli-ikkunan määritys
screen = pygame.display.set_mode((400, 300))
pygame.display.set_caption("Terve Pygame")
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Pygame lopetus/sulku
pygame.quit()
c. Muokkaa koodia niin, että peli-ikkunan koko muuttuu ja vaihda peli-ikkunan otsikoksi oma nimi ja luokka. Kokeile useita eri kokoja peli-ikunalle. Tee muutokset muuttujien avulla äläkä korvaa pohjakoodia.
HUOM! Tästä tehtävästä eteenpäin muista aina muuttaa peli-ikkunan nimeksi oma nimi ja luokka.
Esimerkkiarvot:
Peli-ikkunan otsikko: Maija Meikäläinen 26A
Peli-ikkunan koko: leveys = 800 ja korkeus = 600
#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
#muuttujat
leveys = 800
korkeus = 600
ruudun_kuvaus = "Testi-ikkuna"
# Peli-ikkunan määritys
screen = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption(ruudun_kuvaus)
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Pygame lopetus/sulku
pygame.quit()
d. Muuta peli-ikkunan taustan väri punaiseksi.
#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
#muuttujat
leveys = 800
korkeus = 600
ruudun_kuvaus = "Testi-ikkuna"
rgb = (255, 0, 0)#punainen
# Peli-ikkunan määritys
screen = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption(ruudun_kuvaus)
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(rgb)#taustan värin määritys
pygame.display.flip()
# Pygame lopetus/sulku
pygame.quit()
Piirtäminen ja koordinaatisto
Mistä tietää, mitä komentoja on ja miten niitä voi käyttää?
Pygamen dokumentaatiossa (pygame.font — pygame v2.6.0 documentation) kerrotaan, mitä funktioita on ja miten niitä käytetään. Löydätkö piirtämiseen liittyvä osion? Valitse yksi malli. Osaatko sanoa, mitä muuttujia funktio tarvitsee ja mitä se palauttaa?
Vastaus:
Pygamen dokumentaatiosta löytyy Most useful stuff -kohta, jonka vierstä löytyy draw-osio. Esimerkiksi funktio pygame.draw.line() toimii siten, että ensimmäinen parametri on surface, eli pinta, johon piirretään. Esimerkkikoodissa se on screen. Seuraava parametri on väri, joka annetaan RGB-muodossa. Tämän jälkeen määritetään viivan alku- ja loppupiste koordinaatteina, muodossa [alku x, alku y] ja [loppu x, loppu y]. Koordinaatit voidaan antaa joko hakasuluissa [] tai sulkeissa (). Viimeinen parametri on valinnainen ja määrittää viivan paksuuden. Jos sitä ei anneta, oletusarvona käytetään paksuutta 1.

Sivustolla alempana on esimerkki funktion käytöstä.

Kun hiiri viedään funktion linen päälle Visual Studio Codessa, ohjelma näyttää, mitä parametreja funktio tarvitsee.

Tehtävässä käytettävän värin, fontin ja kuvion saa valita vapaasti.
Tehtävä .
a. Piirrä haluamasi kuvio hyödyntämällä Pygamen dokumentaatiota (pygame.font — pygame v2.6.0 documentation).

#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
#muuttujat
leveys = 800
korkeus = 600
ruudun_kuvaus = "Testi-ikkuna"
rgb = (255, 0, 0)#punainen
# Peli-ikkunan määritys
screen = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption(ruudun_kuvaus)
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0,0,0)) #taustan värin määritys
pygame.draw.circle(screen, rgb, [600,300], 20) #pallon piirtäminen
pygame.display.flip()
# Pygame lopetus/sulku
pygame.quit()
b. Piirrä kuvio origoon. Mitä huomaat?

pygame.draw.circle(screen, rgb , [0,0], 20) #pallon piirtäminen
Origo on ruudun vasemassa yläkulmassa.
c. Piirrä kuvio keskelle.

pygame.draw.circle(screen,(255, 0, 0), [leveys/2,korkeus/2], 20) #pallon piirtäminen
d. Piirrä kuvio reunaan. Ota huomioon kuvion oma leveys ja pituus.

pygame.draw.circle(screen,rgb, [leveys-20,korkeus-20], 20) #pallon piirtäminen
e. Onko mahdollista piirtää kuvio ruudun ulkopuolelle? Kokeile piirtää kuvio peli-ikkunan ulkopuolelle.

pygame.draw.circle(screen,(255, 0, 0), [leveys+10,korkeus-20],20) #pallon piirtäminen
On siis mahdollista.
f. Piirrä jollekin korkeudelle yksi rivi kuviota. Katso apua rakenteelle linkistä Toisto – ALLU – Algoritmeja ja lukuteoriaa oppimassa. Tallenna muuttujiin tarvittavia tietoja, kuten säde, väli ja haluttu korkeus. Lisää pelisilmukan loppuun pygame.display.flip()

#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
#muuttujat
leveys = 800
korkeus = 600
ruudun_kuvaus = "Testi-ikkuna"
rgb = (255, 0, 0)#punainen
säde = 20 # pallon säde
väli = 2 * säde # pallojen väli
y = korkeus / 2 # haluttu korkeus
# Peli-ikkunan määritys
screen = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption(ruudun_kuvaus)
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0,0,0)) #taustan värin määritys
for x in range(säde, leveys, väli):
pygame.draw.circle(screen, rgb, [x, y], säde) #pallon piirtäminen
pygame.display.flip()
# Pygame lopetus/sulku
pygame.quit()
g. Täytä peli-ikkuna kokonaisilla kuviolla. Katso apua rakenteelle linkistä Toisto vaikeat – ALLU – Algoritmeja ja lukuteoriaa oppimassa

#kutsutaan tarvittavat kirjastot
import pygame
# Alustetaan Pygame
pygame.init()
#muuttujat
leveys = 800
korkeus = 600
ruudun_kuvaus = "Testi-ikkuna"
rgb = (255, 0, 0)#punainen
säde = 20 # pallon säde
väli = 2 * säde # pallojen väli
# Peli-ikkunan määritys
screen = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption(ruudun_kuvaus)
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0,0,0)) #taustan värin määritys
for x in range(säde, leveys, väli):
for y in range(säde, korkeus, väli):
pygame.draw.circle(screen, rgb, [x, y], säde) #pallon piirtäminen
pygame.display.flip()
# Pygame lopetus/sulku
pygame.quit()
h. Aloita tilanteesta ennen a-kohtaa eli tyhjästä mustasta ruudusta ja rakenna ohjelma seuraavien ohjeiden mukaisesti. Varmista, että kuva on samassa kansiossa kuin Python-tiedosto. Muussa tapauksessa lisää kuvan tiedostopolku. Apua saa alussa olevista ohjeista ja ongelmatilanteissa voi hyödyntää tekoälyä. Lataa kuva alla olevasta linkistä. Ladataessa kuvan nimeksi tulee kaarme-1.png. Vaihda nimeksi kaarme.png.
- Lisää muuttujiin
kuva = pygame.image.load("kaarme.png") # kuvan lataaminen - Lisää pelisilmukkaan toiminto
screen.blit(kuva, [x-koordinaatti, y-koordinaatti]). Lisää haluamasi koordinaatit. - Kun kuva toimii, lisätään tekstit. Tuo fontit alussa
pygame.font.init()-toiminnolla. - Lisää muuttujiin
fontti = pygame.font.SysFont('Arial', 40) # fontin luonti - Kun fontti on ladattu, lisää
teksti = fontti.render("Jokin teksti tähän", True, rgb) # tekstin muodostus - Kutsu tekstiä samalla tavalla pelisilmukassa kuin kuvaa.

import pygame
# Alustetaan Pygame
pygame.init()
pygame.font.init()
#muuttujat
leveys = 800
korkeus = 600
ruudun_kuvaus = "Testi-ikkuna"
rgb = (255, 0, 0)# punainen
kuva = pygame.image.load("kaarme.png")# kuvan lataaminen
fontti = pygame.font.SysFont('Arial', 40) # fontin luonti
teksti = fontti.render("Lisää tähän jokin teksti", True, rgb) # tekstin muodostus
# Peli-ikkunan määritys
screen = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption(ruudun_kuvaus)
# Pelisilmukan määritys
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0,0,0)) #taustan värin määritys
# Piirretään kuva ja teksti
screen.blit(kuva, [100, 100])
screen.blit(teksti, [300, 300])
pygame.display.flip()
# Pygame lopetus/sulku
pygame.quit()
Liike ja aika
Tehtävä . Tässä tehtävässä laitetaan neliö liikkumaan yhteen suuntaan.
a) Aloitetaan piirtämällä näytölle keltainen neliö kohtaan 20, 20. Neliön sivun pituus on 50.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
b) Laitetaan nyt neliö liukumaan kohti näytön oikeaa reunaa.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
neliön_nopeus = 0.5
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
neliö.x += neliön_nopeus
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
c) Edellisessä kohdassa neliö jätti ”jäljen”. Tämä pitäisi korjata. Mieti, mitä joka kierroksella tapahtuu ja mitä ei tapahdu.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
neliön_nopeus = 0.5
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
neliö.x += neliön_nopeus
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
d) Nyt neliöstä ei jää jälkeä, mutta se menee melko nopeasti pois näkyvistä. Yritä säätää neliön nopeutta. Mitä huomaat?
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
neliön_nopeus = 1
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
neliö.x += neliön_nopeus
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
Huomataan, ettei nopeus juuri muutu. Neliö häviää edelleen melko nopeasti.
e) Edellisessä kohdassa huomattiin, ettei nopeusmuuttujan säätäminen juuri vaikuttanut nopeuteen. Tämä johtuu siitä, että kuvataajuutta ei ole määritetty, eli ohjelma suoritetaan jotakuinkin niin nopeasti kuin kone pystyy sen suorittamaan. Jos lukitsemme kuvataajuuden (eli päivitystaajuuden) esim. 60:een, voimme oikeasti säätää nopeutta.
Tätä varten tarvitaan kelloa, eli määritä pygame.time.Clock() Katso dokumentaatiosta apua.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
# Määritetään kello
kello = pygame.time.Clock()
neliön_nopeus = 1
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
neliö.x += neliön_nopeus
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
kello.tick(60)
Tehtävä . Edellisessä tehtävässä saimme neliön liikkumaan kohtuullista vauhtia, mutta se karkaa näytön ulkopuolelle. Tässä tehtävässä pysäytetään neliö, kun se kohtaa näytön reunan. Rakenna ratkaisusi edellisen tehtävän ratkaisun päälle.
a) Pysäytä neliön liike sen kohdatessa näytön reunan.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
# Määritetään kello
kello = pygame.time.Clock()
neliön_nopeus = 1
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if neliö.right <= leveys:
neliö.x += neliön_nopeus
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
kello.tick(60)
b) Edellisessä kohdassa saimme neliön pysähtymään reunaan. Tässä kohdassa on tarkoitus saada se kääntymään takaisin. Kohdatessa näytön reuna, käännä neliön liike päinvastaiseen suuntaan.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
# Määritetään kello
kello = pygame.time.Clock()
neliön_nopeus = 1
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if neliö.right <= leveys:
neliö.x += neliön_nopeus
if neliö.right >= leveys:
neliön_nopeus *= -1
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
kello.tick(60)
c) Edellisessä kohdassa neliö tuli takaisin, mutta se jatkoi matkaansa jälleen näytön ulkopuolelle toiseen suuntaan. Tässä kohdassa neliö pitäisi saada pysymään näytön sisäpuolella.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
# Määritetään kello
kello = pygame.time.Clock()
neliön_nopeus = 1
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if neliö.right <= leveys:
neliö.x += neliön_nopeus
if neliö.right >= leveys or neliö.left <= 0:
neliön_nopeus *= -1
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
kello.tick(60)
d) Nyt neliö pysyy näytön sisäpuolella. Neliö voisi liikkua myös korkeussuunnassa. Lisää neliön liike myös ylös- ja alaspäin ja estä neliötä karkaamasta jälleen.
import pygame, sys
# Alustetaan Pygame
pygame.init()
leveys = 800
korkeus = 600
# Peli-ikkunan määritys
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Neliö liukuu")
# Määritetään kello
kello = pygame.time.Clock()
neliön_nopeus_vaaka = 1
neliön_nopeus_pysty = 1
neliö = pygame.Rect(20, 20, 50, 50)
# Pelisilmukan määritys
käynnissä = True
while käynnissä:
näyttö.fill((0, 0, 0,))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
neliö.x += neliön_nopeus_vaaka
neliö.y += neliön_nopeus_pysty
if neliö.right >= leveys or neliö.left <= 0:
neliön_nopeus_vaaka *= -1
if neliö.top <= 0 or neliö.bottom >= korkeus:
neliön_nopeus_pysty *= -1
pygame.draw.rect(näyttö, (255, 255, 0), neliö)
pygame.display.flip()
kello.tick(60)
Tehtävä . Pygamessa on useita tapoja näytön päivittämiseen. Näistä yleisin on pygame.display.flip(), joka päivittää koko näytön (ja sijoitetaan useimmiten pelisilmukan loppuun, ellei koko näyttöä tarvitse päivittää silmukan aikana). Toinen tapa on pygame.display.update(), jolla voi vastaavasti päivittää koko näytön, eli se toimii tässä tapauksessa täysin identtisesti kuin flip(). update() -funktiolla voi kuitenkin päivittää myös vain osia näytöllä olevista Pygame-elementeistä antamalla nämä parametriksi funktiolle.
Päivittävätkö seuraavat pelisilmukat koko näytön? Seuraava koodin alkuosa on sama kaikissa kohdissa.
import pygame, sys
pygame.init()
leveys = 800
korkeus = 600
näyttö = pygame.display.set_mode((leveys, korkeus))
pygame.display.set_caption("Näytön päivitys")
käynnissä = True
neliö = pygame.Rect(0, 0, 50, 50)
1.
while käynnissä:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.draw.rect(näyttö, (255, 0, 0), neliö)
pygame.display.flip()
2.
while käynnissä:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.draw.rect(näyttö, (255, 0, 0), neliö)
pygame.display.update()
3.
while käynnissä:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.draw.rect(näyttö, (255, 0, 0), neliö)
pygame.display.update(neliö)
4. Huomaa, että pelkästään pelisilmukka ei ole muuttunut.
neliö2 = pygame.Rect(10, 10, 20, 20)
luku = 0
while käynnissä:
luku += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if luku % 2 == 1:
neliö2.x = neliö2.x+60
else:
neliö2.x = neliö2.x-60
pygame.draw.rect(näyttö, (255, 0, 0), neliö)
pygame.draw.rect(näyttö, (255, 255, 0), neliö2)
pygame.display.update([neliö, neliö2])
kello.tick(10)
- päivittää koko näytön ilman muuta, sillä se on flip()-komennon tarkoitus.
- päivittää myös koko näytön, sillä update()-komennolla ei ole parametria, joten se toimii kuten flip()
- ei päivitä koko näyttöä, sillä update()-komennolle on annettu parametriksi neliö. Tällöin vain neliö päivittyy. Vaikka se onkin ainut asia, joka on piirretty näytölle, ei koko näyttö silti päivity.
- ei päivitä koko näyttöä, sillä update()-komennolle on annettu parametriksi lista. update() päivittää kaikki listan jäsenet, mutta koko näyttö ei taaskaan päivity.
Ohjaaminen ja vuorovaikutus
Tehtävä . Tutki annettua koodia ja selvitä, mitä näppäintä painamalla saat jotain tapahtumaan. Käytä apuna Pygame dokumentaatiota (pygame.key — pygame v2.6.0 documentation). Aja annettu koodi ja paina näppäintä. Kirjoita lyhyt vastaus siitä, mitä tapahtui.
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
rgb = (0, 0, 0)
running = True
kello = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
nappaimet = pygame.key.get_pressed()
if nappaimet[pygame.K_SPACE] == True:
rgb = (255, 0, 0)
if nappaimet[pygame.K_SPACE] == False:
rgb = (255, 255, 0)
screen.fill((0, 0, 0))
pygame.draw.rect(screen, rgb, (175, 125, 50, 50))
pygame.display.flip()
kello.tick(60)
pygame.quit()
Välilyöntinäppäimen painaminen saa muutoksen aikaiseksi. Painiketta painamalla ruudulla näkyvän neliön väri vaihtuu keltaisesta punaiseksi. Punainen väri näkyy niin kauan, kun välilyöntinäppäin on pohjassa.
Tehtävä .a. Kirjoita koodi, jolla voit liikuttaa kuviota (esimerkiksi neliö) ylös, alas ja sivuille. Liitä liike nuolinäppäimiin sekä wasd-näppäimiin.
Esimerkki ratkaisu:
#kirjasto(t)
import pygame
#alustetaan Pygame
pygame.init()
#Peli-ikkunan määritys
screen = pygame.display.set_mode((400, 300))
#muuttujat
x, y = 175, 125
nopeus = 10
running = True
kello = pygame.time.Clock()
nelio = pygame.Rect(x, y, 50, 50)
#Pelisilmukka
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
nappaimet = pygame.key.get_pressed()
if nappaimet[pygame.K_UP] or nappaimet[pygame.K_w]: #ylös
nelio.y = nelio.y - nopeus
if nappaimet[pygame.K_DOWN] or nappaimet[pygame.K_s]: #alas
nelio.y = nelio.y + nopeus
if nappaimet[pygame.K_RIGHT] or nappaimet[pygame.K_d]: #oikealle
nelio.x = nelio.x + nopeus
if nappaimet[pygame.K_LEFT] or nappaimet[pygame.K_a]: #vasemmalle
nelio.x = nelio.x - nopeus
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (255, 255, 255), nelio)
pygame.display.flip()
kello.tick(60)
#Pygame sulku/lopetus
pygame.quit()
b. Muokkaa a-kohdan koodia niin, että kuvio ei pääse poistumaan peli-ikkunasta. Löydät valmiin funktion dokumentaatiosta (pygame.Rect — pygame v2.6.0 documentation) tai voit käyttää muuta menetelmää.
Tapa 1. Valmiilla Pygame funktiolla
Esimerkki ratkaisu:
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
x, y = 175, 125
nopeus = 10
running = True
nelio = pygame.Rect(x, y, 50, 50)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
nappaimet = pygame.key.get_pressed()
if nappaimet[pygame.K_UP] or nappaimet[pygame.K_w]:
nelio.y = nelio.y - nopeus
if nappaimet[pygame.K_DOWN] or nappaimet[pygame.K_s]:
nelio.y = nelio.y + nopeus
if nappaimet[pygame.K_RIGHT] or nappaimet[pygame.K_d]:
nelio.x = nelio.x + nopeus
if nappaimet[pygame.K_LEFT] or nappaimet[pygame.K_a]:
nelio.x = nelio.x - nopeus
nelio.clamp_ip(screen.get_rect()) #varmistaa ettei hahmo poistu peli-ikkunasta
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (255, 255, 255), nelio)
pygame.display.flip()
pygame.quit()
Tapa 2. Ehtorakenteella
Esimerkki ratkaisu:
import pygame
pygame.init()
x, y = 175, 125
nopeus = 10
running = True
korkeus = 300
leveys = 400
screen = pygame.display.set_mode((leveys, korkeus))
nelio = pygame.Rect(x, y, 50, 50)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
nappaimet = pygame.key.get_pressed()
if nappaimet[pygame.K_UP] and nelio.y > 0:
nelio.y = nelio.y - nopeus
if nappaimet[pygame.K_DOWN] and nelio.y < korkeus-50: #50 = neliön korkeus
nelio.y = nelio.y + nopeus
if nappaimet[pygame.K_RIGHT] and nelio.x < leveys-50: #50 = neliön leveys
nelio.x = nelio.x + nopeus
if nappaimet[pygame.K_LEFT] and nelio.x > 0:
nelio.x = nelio.x - nopeus
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (255, 255, 255), nelio)
pygame.display.flip()
pygame.quit()
c. Muokkaa koodia vielä niin, että näppäintä painettaessa liike jatkuu niin kauan kuin näppäin on pohjassa.
Esimerkki ratkaisu:
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
x, y = 175, 125
nopeus = 10
running = True
kello = pygame.time.Clock()
nelio = pygame.Rect(x, y, 50, 50)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
nappaimet = pygame.key.get_pressed()
if nappaimet[pygame.K_UP] or nappaimet[pygame.K_w]:
nelio.y = nelio.y - nopeus
if nappaimet[pygame.K_DOWN] or nappaimet[pygame.K_s]:
nelio.y = nelio.y + nopeus
if nappaimet[pygame.K_RIGHT] or nappaimet[pygame.K_d]:
nelio.x = nelio.x + nopeus
if nappaimet[pygame.K_LEFT] or nappaimet[pygame.K_a]:
nelio.x = nelio.x - nopeus
nelio.clamp_ip(screen.get_rect())
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (255, 255, 255), nelio)
pygame.display.flip()
kello.tick(60)
pygame.quit()
d. Muuta ohjelman näppäinohjaus tapahtumapohjaiseksi (event-based). Korvaa siis aikaisempi tilakyselyyn (get_pressed) perustuva koodi tapahtumapohjaisella vaihtoehdolla. Ohjelman tulee edelleen toimia samalla tavalla kuin b-kohdassa.
Esimerkki ratkaisu:
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
x, y = 175, 125
nopeus = 10
running = True
kello = pygame.time.Clock()
nelio = pygame.Rect(x, y, 50, 50)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key in (pygame.K_UP, pygame.K_w):
nelio.y = nelio.y - nopeus
if event.key in (pygame.K_DOWN, pygame.K_s):
nelio.y = nelio.y + nopeus
if event.key in (pygame.K_RIGHT, pygame.K_d):
nelio.x = nelio.x + nopeus
if event.key in (pygame.K_LEFT, pygame.K_a):
nelio.x = nelio.x - nopeus
nelio.clamp_ip(screen.get_rect())
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (255, 255, 255), nelio)
pygame.display.flip()
kello.tick(60)
pygame.quit()
Tehtävä . Kirjoita ohjelma, jossa hahmoa pystyy liikuttamaan hiirellä, kun hiiren oikea painike on painettuna.
Esimerkki ratkaisu:
#kirjasto(t)
import pygame
# Alustus
pygame.init()
naytto = pygame.display.set_mode((800, 600))
rect = pygame.Rect(300, 200, 50, 50)
kello = pygame.time.Clock()
#muuttujat
raahata = False
kaynnissa = True
#Pelisilmukka
while kaynnissa:
for event in pygame.event.get():
if event.type == pygame.QUIT:
kaynnissa = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3:
raahata = True
elif event.type == pygame.MOUSEBUTTONUP:
raahata = False
elif event.type == pygame.MOUSEMOTION:
if raahata:
rect.move_ip(event.rel)
naytto.fill((30, 30, 30))
pygame.draw.rect(naytto, (0, 255, 0), rect)
pygame.display.flip()
kello.tick(60)
#peli lopetus/sulku
pygame.quit()
Pelilogiikka ja säännöt
Tehtävä 1.a. Toteuta peli-ikkunaan labyrintin pohja esimerkin mukaan tai voit myös suunnitella labyrintin itse. Pojan voit toteuttaa esimerkiksi listan avulla, jossa x=seinä, p=pelaaja ja m=maali.
| x | x | x | x | x | x | x | x | x | x | x | x | x |
| x | p | x | x | x | ||||||||
| x | x | x | x | x | x | x | x | x | ||||
| x | x | x | x | x | ||||||||
| x | x | x | x | x | x | |||||||
| x | x | x | x | x | x | x | x | x | ||||
| x | x | x | m | x | ||||||||
| x | x | x | x | x | x | x | x | x | x | x | x | x |
Esimerkkiratkaisu:
import pygame
pygame.init()
naytto = pygame.display.set_mode((650, 400))
kello = pygame.time.Clock()
running = True
kartta = [
"xxxxxxxxxxxxx",
"x px x x",
"x xxxxx x x x",
"x x x x x",
"x xxx x x",
"x xxxxxx xx",
"x x x mx",
"xxxxxxxxxxxxx"
]
# Luo seinät
seinat = []
maali = None
pelaaja = None
for y in range(len(kartta)):
for x in range(len(kartta[0])):
if kartta[y][x] == "x":
seinat.append(pygame.Rect(x*50, y*50, 50, 50))
if kartta[y][x] == "m":
maali = pygame.Rect(x*50, y*50, 50, 50)
if kartta[y][x] == "p":
pelaaja = pygame.Rect(x*50, y*50, 40, 40)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for s in seinat:
pygame.draw.rect(naytto, (100, 100, 100), s)
if maali:
pygame.draw.rect(naytto, (0, 255, 0), maali)
pygame.draw.rect(naytto, (255, 255, 255), pelaaja)
pygame.display.flip()
kello.tick(60)
pygame.quit()
b. Muokkaa pelin hahmoa (pelaaja) niin, että se pääsee liikkumaan sallitulla alueella (käytävät) eikä pysty läpäisemään seiniä. Apua törmäyksen tarkistamiseen saat dokumentaatiosta (pygame.Rect — pygame v2.6.0 documentation)
Esimerkkiratkaisu:
import pygame
pygame.init()
naytto = pygame.display.set_mode((650, 400))
kello = pygame.time.Clock()
running = True
nopeus = 1
seinat = []
maali = None
pelaaja = None
kartta = [
"xxxxxxxxxxxxx",
"x px x x",
"x xxxxx x x x",
"x x x x x",
"x xxx x x",
"x xxxxxx xx",
"x x x mx",
"xxxxxxxxxxxxx"
]
for y in range(len(kartta)):
for x in range(len(kartta[0])):
if kartta[y][x] == "x":
seinat.append(pygame.Rect(x*50, y*50, 50, 50))
if kartta[y][x] == "m":
maali = pygame.Rect(x*50, y*50, 50, 50)
if kartta[y][x] == "p":
pelaaja = pygame.Rect(x*50, y*50, 40, 40)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
nappaimet = pygame.key.get_pressed()
vanha = pelaaja.copy()
if nappaimet[pygame.K_UP]:
pelaaja.y = pelaaja.y - nopeus
if nappaimet[pygame.K_DOWN]:
pelaaja.y = pelaaja.y + nopeus
if nappaimet[pygame.K_RIGHT]:
pelaaja.x = pelaaja.x + nopeus
if nappaimet[pygame.K_LEFT]:
pelaaja.x = pelaaja.x - nopeus
for s in seinat:
if pelaaja.colliderect(s):
pelaaja = vanha
naytto.fill((0, 0, 0))
for s in seinat:
pygame.draw.rect(naytto, (100, 100, 100), s)
if maali:
pygame.draw.rect(naytto, (0, 255, 0), maali)
pygame.draw.rect(naytto, (255, 255, 255), pelaaja)
pygame.display.flip()
kello.tick(60)
pygame.quit()
c. Luo labyrinttiin lopetus (maali), johon hahmon päästyä ilmestyy peli-ikkunaan teksti “Pääsit maaliin”. Peli myös ns. jäätyy tässä kohtaa eli hahmoa ei pysty enää liikuttamaan. Peli siis päättyy.
Esimerkkiratkaisu:
import pygame
pygame.init()
naytto = pygame.display.set_mode((650, 400))
kello = pygame.time.Clock()
running = True
nopeus = 1
fontti = pygame.font.SysFont("Arial", 40)
nayta_teksti = False
kaynnissa = True
seinat = []
maali = None
pelaaja = None
kartta = [
"xxxxxxxxxxxxx",
"x px x x",
"x xxxxx x x x",
"x x x x x",
"x xxx x x",
"x xxxxxx xx",
"x x x mx",
"xxxxxxxxxxxxx"
]
for y in range(len(kartta)):
for x in range(len(kartta[0])):
if kartta[y][x] == "x":
seinat.append(pygame.Rect(x*50, y*50, 50, 50))
if kartta[y][x] == "m":
maali = pygame.Rect(x*50, y*50, 50, 50)
if kartta[y][x] == "p":
pelaaja = pygame.Rect(x*50, y*50, 40, 40)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if kaynnissa == True:
nappaimet = pygame.key.get_pressed()
vanha = pelaaja.copy()
if nappaimet[pygame.K_UP]:
pelaaja.y = pelaaja.y - nopeus
if nappaimet[pygame.K_DOWN]:
pelaaja.y = pelaaja.y + nopeus
if nappaimet[pygame.K_RIGHT]:
pelaaja.x = pelaaja.x + nopeus
if nappaimet[pygame.K_LEFT]:
pelaaja.x = pelaaja.x - nopeus
for s in seinat:
if pelaaja.colliderect(s):
pelaaja = vanha
if maali.contains(pelaaja):
nayta_teksti = True
kaynnissa = False
naytto.fill((0, 0, 0))
for s in seinat:
pygame.draw.rect(naytto, (100, 100, 100), s)
if maali:
pygame.draw.rect(naytto, (0, 255, 0), maali)
pygame.draw.rect(naytto, (255, 255, 255), pelaaja)
if nayta_teksti == True:
teksti_pinta = fontti.render("Pääsit maaliin!", True, (255, 255, 255))
# Lasketaan tekstin keskikohta
teksti_rect = teksti_pinta.get_rect(center=(650/2, 400/2))
naytto.blit(teksti_pinta, teksti_rect)
pygame.display.flip()
kello.tick(60)
pygame.quit()
d. Lisää labyrinttiin vihollinen, joka kulkee määritetyssä kohdassa säännöllisesti. Kohdan, johon asetat vihollisen voit päättää itse tai laittaa sen kulkemaan ylös-alas ensimmäiselle käytävälle. Jos viholliseen osuu peli päättyy.
Esimerkkiratkaisu:
import pygame
pygame.init()
naytto = pygame.display.set_mode((650, 400))
kello = pygame.time.Clock()
running = True
nopeus = 1
fontti = pygame.font.SysFont("Arial", 40)
nayta_teksti = False
kaynnissa = True
tappio = False
seinat = []
maali = None
pelaaja = None
vihollinen = pygame.Rect(52.5, 100, 40, 40)
vihollis_nopeus = 1
suunta = 1 # 1 = alas, -1 = ylös
kartta = [
"xxxxxxxxxxxxx",
"x px x x",
"x xxxxx x x x",
"x x x x x",
"x xxx x x",
"x xxxxxx xx",
"x x x mx",
"xxxxxxxxxxxxx"
]
for y in range(len(kartta)):
for x in range(len(kartta[0])):
if kartta[y][x] == "x":
seinat.append(pygame.Rect(x*50, y*50, 50, 50))
if kartta[y][x] == "m":
maali = pygame.Rect(x*50, y*50, 50, 50)
if kartta[y][x] == "p":
pelaaja = pygame.Rect(x*50, y*50, 40, 40)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if kaynnissa == True:
nappaimet = pygame.key.get_pressed()
vanha = pelaaja.copy()
if nappaimet[pygame.K_UP]:
pelaaja.y = pelaaja.y - nopeus
if nappaimet[pygame.K_DOWN]:
pelaaja.y = pelaaja.y + nopeus
if nappaimet[pygame.K_RIGHT]:
pelaaja.x = pelaaja.x + nopeus
if nappaimet[pygame.K_LEFT]:
pelaaja.x = pelaaja.x - nopeus
vihollinen.y += vihollis_nopeus * suunta
# vaihda suuntaa kun osuu reunoihin
if vihollinen.y < 50 or vihollinen.y > 300:
suunta *= -1
if pelaaja.colliderect(vihollinen):
kaynnissa = False
tappio = True
for s in seinat:
if pelaaja.colliderect(s):
pelaaja = vanha
if maali.contains(pelaaja):
nayta_teksti = True
kaynnissa = False
naytto.fill((0, 0, 0))
for s in seinat:
pygame.draw.rect(naytto, (100, 100, 100), s)
if maali:
pygame.draw.rect(naytto, (0, 255, 0), maali)
pygame.draw.rect(naytto, (255, 255, 255), pelaaja)
pygame.draw.rect(naytto, (255, 0, 0), vihollinen)
if tappio == True:
teksti_pinta = fontti.render("Hävisit!", True, (255, 255, 255))
# Lasketaan tekstin keskikohta
teksti_rect = teksti_pinta.get_rect(center=(650/2, 400/2))
naytto.blit(teksti_pinta, teksti_rect)
if nayta_teksti == True:
teksti_pinta = fontti.render("Pääsit maaliin!", True, (255, 255, 255))
# Lasketaan tekstin keskikohta
teksti_rect = teksti_pinta.get_rect(center=(650/2, 400/2))
naytto.blit(teksti_pinta, teksti_rect)
pygame.display.flip()
kello.tick(60)
pygame.quit()
e. Lisää peliin palkinto jokaiseen käytävä ruutuun (välilyönnit). Jokaisesta palkinnosta saa yhden pisteen. Palkinnon tulee kadota samalla, kun siihen osuu ja siitä saa pisteen.
Esimerkkiratkaisu:
import pygame
pygame.init()
naytto = pygame.display.set_mode((650, 400))
kello = pygame.time.Clock()
running = True
nopeus = 1
fontti = pygame.font.SysFont("Arial", 40)
nayta_teksti = False
kaynnissa = True
tappio = False
palkinnot = []
pisteet = 0
seinat = []
maali = None
pelaaja = None
palkinto = None
kartta = [
"xxxxxxxxxxxxx",
"x px x x",
"x xxxxx x x x",
"x x x x x",
"x xxx x x",
"x xxxxxx xx",
"x x x mx",
"xxxxxxxxxxxxx"
]
for y in range(len(kartta)):
for x in range(len(kartta[0])):
if kartta[y][x] == "x":
seinat.append(pygame.Rect(x*50, y*50, 50, 50))
if kartta[y][x] == "m":
maali = pygame.Rect(x*50, y*50, 50, 50)
if kartta[y][x] == "p":
pelaaja = pygame.Rect(x*50, y*50, 40, 40)
if kartta[y][x] == " ":
palkinnot.append(pygame.Rect(x*51.5, y*51.5, 30, 30)) #mahdollisimman keskellä "ruutua"
vihollinen = pygame.Rect(52.5, 52.5, 40, 40)
vihollis_nopeus = 1
suunta = 1 # 1 = alas, -1 = ylös
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if kaynnissa == True:
nappaimet = pygame.key.get_pressed()
vanha = pelaaja.copy()
if nappaimet[pygame.K_UP]:
pelaaja.y = pelaaja.y - nopeus
if nappaimet[pygame.K_DOWN]:
pelaaja.y = pelaaja.y + nopeus
if nappaimet[pygame.K_RIGHT]:
pelaaja.x = pelaaja.x + nopeus
if nappaimet[pygame.K_LEFT]:
pelaaja.x = pelaaja.x - nopeus
vihollinen.y += vihollis_nopeus * suunta
# vaihda suuntaa kun osuu reunoihin
if vihollinen.y < 50 or vihollinen.y > 300:
suunta *= -1
if pelaaja.colliderect(vihollinen):
kaynnissa = False
tappio = True
for s in seinat:
if pelaaja.colliderect(s):
pelaaja = vanha
if maali.contains(pelaaja):
nayta_teksti = True
kaynnissa = False
naytto.fill((0, 0, 0))
for s in seinat:
pygame.draw.rect(naytto, (100, 100, 100), s)
if maali:
pygame.draw.rect(naytto, (0, 255, 0), maali)
pygame.draw.rect(naytto, (255, 255, 255), pelaaja)
for palkinto in palkinnot[:]:
pygame.draw.rect(naytto, (255, 255, 0), palkinto)
if pelaaja.colliderect(palkinto):
pisteet += 1
palkinnot.remove(palkinto)
break
pygame.draw.rect(naytto, (255, 0, 0), vihollinen)
if tappio == True:
teksti_pinta = fontti.render("Hävisit!", True, (255, 255, 255))
# Lasketaan tekstin keskikohta
teksti_rect = teksti_pinta.get_rect(center=(650/2, 400/2))
naytto.blit(teksti_pinta, teksti_rect)
if nayta_teksti == True:
teksti_pinta = fontti.render("Pääsit maaliin!", True, (255, 255, 255))
# Lasketaan tekstin keskikohta
teksti_rect = teksti_pinta.get_rect(center=(650/2, 400/2))
naytto.blit(teksti_pinta, teksti_rect)
score_text = fontti.render(f"Pisteet: {pisteet}", True, (255, 255, 255)) # Valkoinen teksti
naytto.blit(score_text, (10, 10)) # Piirretään koordinaatteihin (10, 10)
pygame.display.flip()
kello.tick(60)
pygame.quit()
2. Lopputehtävä
Projektitehtävistä tulee toteuttaa joko kaksi projektitehtävistä 1–3 tai yksi laajempi projektitehtävä 4. Lisäksi tulee kirjoittaa vähintään 200 sanan raportti, jossa kuvailet, miten rakensit lopputyön. Raporttiin voi myös sisällyttää koodiesimerkkejä.
Projektin rakenteen tulee perustua perustehtävien kaltaisiin ratkaisuihin ja esimerkkeihin. Pelin tarkoituksena on tukea omaa oppimistasi, joten projekti tulee toteuttaa vaiheittain pienissä osissa. Vaikka tekoäly pystyy tuottamaan kokonaisen pelin kerralla, sitä ei saa tehdä lopputyössä. Tällöin et välttämättä ymmärrä, mitä koodissa tapahtuu, kokonaisuus on liian suuri hahmottaa kerralla, ja virheiden löytäminen sekä korjaaminen on vaikeampaa. Suuremmat ohjelmat, kuten esimerkiksi 10 000 rivin kokonaisuudet, rakennetaan ja ymmärretään jakamalla ne pienempiin osiin, koska koko koodia ei voi hahmottaa tai rakentaa kerralla.
Projektitehtävissä 1–3 tulee olla vähintään 50 koodiriviä. Tyhjiä rivivälejä ja kommentteja ei lasketa mukaan. Ohjelmassa tulee käyttää vähintään kolmea funktiota. Mallivideot näyttävät projektin vähimmäistason eli sen, mitä pelissä tulee vähintään olla. Halutessasi voit kuitenkin laajentaa peliä lisäämällä omia toiminnallisuuksia, kuten kuvia.
Projektitehtävässä 4 tulee olla vähintään 120 koodiriviä. Tyhjiä rivivälejä ja kommentteja ei lasketa mukaan. Ohjelmassa tulee käyttää vähintään kuutta funktiota. Pelissä tulee olla kuvia, piirrettyjä objekteja, ohjattavia osia, vuorovaikutusta eri osien välillä, pisteitä kasvattava elementti, pisteitä vähentävä elementti, pisteiden näyttäminen sekä pelin päättyminen jonkin ehdon perusteella.
- Rakenna pelisilmukka.
- Mieti, miten liikkuvat objektit on toteutettu ja millaisessa muodossa ne ovat koodissa. Mallinna rakenne aluksi paikallaan.
- Lisää liike perustehtävissä opitulla tavalla. Onko liikkeessä ehtoja?
- Pohdi, mitä törmäys tarkoittaa koodissa ja miten se toteutuu kahden objektin välillä.
- Jaa toiminnalliset kokonaisuudet funktioihin.
Projekti 1. Pong – virtuaalinen ilmakiekko
Luo kopio Pong-pelistä. Pelissä pelaajat liikuttavat mailoja ja yrittävät estää pallon pääsyn omaan päätyynsä viemällä mailan sen eteen. Vaatimuksena on, että pelaaja pystyy liikuttamaan mailaansa ja vastapelaajan maila liikkuu itsestään yrittäen estää pallon pääsyn päätyyn. Pallo liikkuu itsestään ja vaihtaa suuntaa osuessaan pelilaudan ylä- tai alareunaan tai pelaajien mailoihin.
