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.

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

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

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

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.

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.

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)

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)

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)

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

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

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

  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.
Projekti 1. Pong – virtuaalinen ilmakiekko
Projekti 2. Nappaajapeli
Projekti 3. Ampumispeli