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ä.

#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.

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

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.

#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()

pygame.draw.circle(screen, rgb , [0,0], 20) #pallon piirtäminen

Origo on ruudun vasemassa yläkulmassa.

pygame.draw.circle(screen,(255, 0, 0), [leveys/2,korkeus/2], 20) #pallon piirtäminen

pygame.draw.circle(screen,rgb, [leveys-20,korkeus-20], 20) #pallon piirtäminen

pygame.draw.circle(screen,(255, 0, 0), [leveys+10,korkeus-20],20) #pallon piirtäminen

On siis mahdollista.

#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()

#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()
  1. Lisää muuttujiin kuva = pygame.image.load("kaarme.png") # kuvan lataaminen
  2. Lisää pelisilmukkaan toiminto screen.blit(kuva, [x-koordinaatti, y-koordinaatti]). Lisää haluamasi koordinaatit.
  3. Kun kuva toimii, lisätään tekstit. Tuo fontit alussa pygame.font.init()-toiminnolla.
  4. Lisää muuttujiin fontti = pygame.font.SysFont('Arial', 40) # fontin luonti
  5. Kun fontti on ladattu, lisää teksti = fontti.render("Jokin teksti tähän", True, rgb) # tekstin muodostus
  6. 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 aika 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 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ä tehtävässä 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)

Ohjaaminen ja vuorovaikutus

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äintä painamalla 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()

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.

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()

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 koodia tulee olla vähintään 80 riviä ja siinä 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 koodia tulee olla vähintään 200 riviä ja siinä 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.

  1. Rakenna pelisilmukka.
  2. Mieti, miten liikkuvat objektit on toteutettu ja millaisessa muodossa ne ovat koodissa. Mallinna rakenne aluksi paikallaan.
  3. Lisää liike perustehtävissä opitulla tavalla. Onko liikkeessä ehtoja?
  4. Pohdi, mitä törmäys tarkoittaa koodissa ja miten se toteutuu kahden objektin välillä.
  5. Jaa toiminnalliset kokonaisuudet funktioihin.