Arvutiteaduse instituut
  1. Esileht
  2. IT koolidele
EN
Logi sisse

IT koolidele

  • PyGame'i tutvustus
  • Animatsioon
  • Mängu loomine
  • Autorid ja õpetajamaterjal
  • Näidis
  • PyGame'i tutvustus
  • Animatsioon
  • Mängu mängitavus
  • Arvutimängu olemus?

Mängu loomine

Arvutimängu ülesanne on vastu võtta kasutaja sisendit ja sellele reageerida. Ehk mäng on mängitav parajasti siis, kui programm oskab reageerida nii hiireklõpsudele, klahvivajutustele kui ka mõne mängu puhul objektide kokkupõrkele. Selles peatükis õpime, kuidas kasutada PyGame'i võimalusi erinevatele sündmustele, mille kasutaja on läbi viinud või sisestanud. Peatüki lõpus on selgitatud ka heliefektide kasutamist.

  1. Sündmustest üldiselt
  2. Reageerimine hiireklõpsule
  3. Reageerimine klahvivajutusele
  4. Pildi lisamine liigutatavale tegelasele
  5. Objektide kokkupõrge
  6. Heli lisamine

Sündmustest üldiselt

Sündmustest oli natuke juttu juba esimeses peatükis, täpsemalt kasutasime päris esimeses PyGame'i programmis tingimust, mis reageeris akna sulgemise nupule vajutamise sündmusele. Mõtleme veelkord selle sündmuse selles programmis läbi.

import pygame
pygame.init()
ekraan = pygame.display.set_mode([800, 600])

pygame.display.flip()

running = True
while running:
    for syndmus in pygame.event.get():
        if syndmus.type == pygame.QUIT:
            running = False

pygame.quit()

Siin on lõpmatu mängutsükkel, mis kehtib kuni muutuja running väärtus on True. Selle while-tsükli sees on for-tsükkel, milles hakatakse muutujale syndmus andma sündmuste väärtusi (ehk põhimõtteliselt on selle tsükli ülesanne leida sündmusi). Ning et programm oskaks reageerida vajalikele sündmustele, siis on tarvis just selle tsükli sisse lisada kõikvõimalikud tingimused, mis vastavaid sündmusi registreerivad.

Selles materjalis on käsitletud kolme tüüpi sündmusi: klahvivajutused, hiireklõpsud ja objektide kokkupõrge, aga tervikliku loetelu sündmustest leiad aadressilt https://www.pygame.org/docs/ref/event.html.

Reageerimine hiireklõpsule

Arvutihiire roll on arvuti kasutamist mugavamaks teha, hiir juhib kursorit ning kursoriga saab klõpsata, lohistada, kerida jpm. Hiirega seotud sündmuste tüüp on pygame.MOUSE (https://www.pygame.org/docs/ref/mouse.html).

Loome programmi, mis tuvastab hiireklõpsu ja väljastab (print-käsklusega) ekraanile hiire koordinaadid klõpsamise hetkel:

import pygame
pygame.init()

ekraan = pygame.display.set_mode([800, 600])
pygame.display.set_caption("Pealkiri")
ekraan.fill([255, 255, 255])
pygame.display.flip()

running = True
while running:
    for i in pygame.event.get():
        if i.type == pygame.QUIT:
            running = False
        elif i.type == pygame.MOUSEBUTTONDOWN:
            hiir_x,hiir_y = i.pos
            print("Hiir x: " + str(hiir_x) + ", hiir y: " + str(hiir_y))
pygame.quit()

Kui me tahame kontrollida, kas mingile objektile klõpsatakse, siis tulebki kontrollida hiire klõpsamise hetkel hiirekursori x- ja y-koordinaati. Näiteks, kui joonistame ristküliku ja tahame, et ristkülik sellele klõpsates ära kaoks, siis peame vaatama, kas klõpsamise hetkel hiirekursori koordinaadid asuvad ristküliku peal. Seda saab teha selliselt:

import pygame
pygame.init()
ekraan = pygame.display.set_mode([800, 600])
pygame.display.set_caption("Pealkiri")
ekraan.fill([255, 255, 255])
pygame.draw.rect(ekraan, [0, 225, 0], [50, 60, 150, 80], 0)
pygame.display.flip()

running = True
while running:
    for i in pygame.event.get():
        if i.type == pygame.QUIT:
            running = False
        elif i.type == pygame.MOUSEBUTTONDOWN:
            hiir_x,hiir_y = i.pos
            if hiir_x > 50 and hiir_x < 200 and hiir_y > 60 and hiir_y < 140:
                ekraan.fill([255, 255, 255])
                pygame.display.flip()
pygame.quit()

Enesekontroll 1

<< Näita enesetesti >>


Reageerimine klahvivajutusele

Kõik klahvivajutused on sündmused, mille tüüp on pygame.KEYDOWN. Kui sündmuse tüüp on kindlaks tehtud, siis peab programm teada saama ka vajutatud klahvi. Selleks lisame sündmuse tüübi tingimusse veel ühe tingimuslause, mis kasutab funktsiooni key, et leida, mis klahvi vajutati. Näiteks, kui kasutaja vajutas tühikuklahvi, siis PyGame registreerib selle kui K_SPACE ja sellele reageerimiseks peab olema koodis tingimus if syndmus.key == pygame.K_SPACE: ning selle all ka mingi tegevus (tühikuklahvi vajutamine võib tähendada näiteks seda, et tegelane hüppab). Samamoodi võib programm registreerida klahvi K_a, kui kasutaja on vajutanud tähe a klahvi, või K_UP kui kasutaja on vajutanud nooleklahvi, mis näitab ülespoole. Igal klaviatuuril oleval klahvil on mingi vaste ning mooduli veebilehel on ka see loetelu olemas (https://www.pygame.org/docs/ref/key.html).

Aga katsetame! Loome programmi, milles on üks tegelane, kelle asukohta saab muuta nooleklahvidele vajutades.

Kirjutame programmi, mille tegelane on ristkülik ning mida kasutaja saab vastavate nooleklahvidega liigutada üles-alla ja paremale-vasakule. Aga mida tähendab tegelase liikumine teljestiku peal? Tuleta meelde, kuidas paiknesid x- ja y-telg ning mis suunas väärtused nendel telgedel kasvasid.

Horisontaalselt liigub tegelane vasakule, kui tema x-koordinaat väheneb, ja paremale, kui tema x-koordinaat suureneb. Aga vertikaalselt liigub tegelane üles, kui tema y-koordinaat väheneb, ja alla, kui tema y-koordinaat suureneb. Määrame sammuks 10 punkti, mis tähendab, et kui kasutaja vajutab kolm korda nooleklahvi alla, siis liigub ristkülik 30 punkti allapoole (ning seega suureneb tema y-koordinaat 30 punkti võrra).

Ristkülikul on vaja ka mingeid algkoordinaate. Varasemalt andsime alguspunkti koordinaatideks kindlad arvud, kuid et sündmuste toimumine tegelase asukohta muudaks, tuleb kasutusele võtta kaks muutujat, x ja y. Algväärtused tuleb muutujatele anda enne mängutsüklit ning tsükli sees suurendame-vähendame koordinaatide väärtusi sammu võrra.

Praegu näeb tingimusteahel välja järgmine:

if syndmus.type == pygame.KEYDOWN:
    if syndmus.key == pygame.K_LEFT:
        x -= samm
    elif syndmus.key == pygame.K_RIGHT:
        x += samm
    elif syndmus.key == pygame.K_UP:
        y -= samm
    elif syndmus.key == pygame.K_DOWN:
        y += samm

Tegelase liigutamine käib põhimõttel, et iga nooleklahvi vajutusega joonistatakse uus pilt. See tähendab, et tegelase loomine, ekraani täitmine ja pildi näitamine kasutajale tuleb teha mängutsükli sees (sest pilti on vaja uuendada iga nupulevajutusega). Seega enne mängutsüklit defineerime ekraani, määrame väärtused ristküliku x- ja y-koordinaatidele ning sammu suuruse. Tsükli sees toimub sündmuste otsimine (ja klahvivajutusele reageerimine) ning värvitakse üle ekraan, joonistatakse uus ristkülik ja näidatakse pilti kasutajale. Seega täielik lahendus näeb välja selline:

import pygame
pygame.init()

valge = [255,255,255]
oranz = [235,152,9]

ekraan = pygame.display.set_mode([800, 600])
pygame.display.set_caption("Aken")

x = 400
y = 300
samm = 10

running = True
while running:
    ekraan.fill(valge)
    pygame.draw.rect(ekraan, oranz, [[x, y], [50, 50]], 0)
    pygame.time.delay(20)
    pygame.display.flip()

    for syndmus in pygame.event.get():
        if syndmus.type == pygame.QUIT:
            running = False
        if syndmus.type == pygame.KEYDOWN:
            if syndmus.key == pygame.K_UP:
                y -= 10
            if syndmus.key == pygame.K_DOWN:
                y += 10
            if syndmus.key == pygame.K_LEFT:
                x -= 10
            if syndmus.key == pygame.K_RIGHT:
                x += 10

pygame.quit()

Enesekontroll 2

<< Näita enesetesti >>


Pildi lisamine liigutatavale tegelasele

import pygame
pygame.init()
ekraan = pygame.display.set_mode([800, 600])
pygame.display.set_caption("Mäng")
pilt = pygame.image.load("spaceship.png")
pilt = pygame.transform.scale(pilt, (200, 100))# Vähendatakse pilti
x = 200
y = 200
samm = 10
running = True
pygame.key.set_repeat(1,10)
while running:
    ekraan.fill([255, 255, 255])
    ekraan.blit(pilt, (x,y))
    pygame.display.flip()
    for i in pygame.event.get():
        if i.type == pygame.QUIT:
            running = False
            # Kui sündmuseks on klahvi allavajutamine…
        elif i.type == pygame.KEYDOWN:
            # ... ja klahviks on nooleklahv üles liikumiseks,...
            if i.key == pygame.K_UP:
                # ... siis vähendame y-koordinaati
                y = y - samm
            elif i.key == pygame.K_DOWN:
                y = y + samm
            elif i.key == pygame.K_LEFT:
                x = x - samm
            elif i.key == pygame.K_RIGHT:
                x = x + samm

pygame.quit()

Objektide kokkupõrge

Praeguseks oskame panna objekte iseseisvalt liikuma (vt peatükki Animatsioon ja ka nooleklahvidele reageerima. Mängudes on olulisel kohal objektide omavaheline reageerimine- näiteks kui nooleklahvidega tegelane saab kätte mingi teise objekti, siis see objekt kaob ära.

Sellise objektide omavahelise kattuvuse leidmiseks on mitu võimalust. vaatame siinkohal kaht varianti: 1. Koordinaatidega objektide kattuvuse tuvastamine. 2. Ristkülikutega (rect) objektide kattuvuse tuvastamine.

1. Koordinaatidega objektide kattuvuse tuvastamine

import pygame
pygame.init()
ekraan = pygame.display.set_mode([800, 600])
pygame.display.set_caption("Mäng")

pilt = pygame.image.load("hunt.png")
pilt = pygame.transform.scale(pilt, (120, 100)) #pildi vähendamine
pilt_x = 30
pilt_y = 450
samm = 1
pilt2 = pygame.image.load("muna.png")
pilt2 = pygame.transform.scale(pilt2, (60, 100)) #pildi vähendamine
pilt2_x = 130
pilt2_y = 130
punktid = 0

pygame.key.set_repeat(1,10)

running = True
while running:

    pygame.time.delay(10)
    ekraan.fill([0, 255, 255])
    #punktid
    teksti_font = pygame.font.Font(None, 50)
    tekst_pildina = teksti_font.render(str(punktid), 1, [0, 0, 0])
    ekraan.blit(tekst_pildina, [50, 200])
    ekraan.blit(pilt, (pilt_x,pilt_y))
    ekraan.blit(pilt2, (pilt2_x,pilt2_y))
    pilt2_y += 1
    if pilt2_y > 600:
        pilt2_y = 0

    if pilt_x > pilt2_x - 120 and pilt_x < pilt2_x + 60 and pilt_y > pilt2_y - 100 and pilt_y < pilt2_y + 100:
        pilt2_y = 0
        punktid += 1
        print(punktid)

    pygame.display.flip()
    for i in pygame.event.get():
        if i.type == pygame.QUIT:
            running = False
        elif i.type == pygame.KEYDOWN:
#             if i.key == pygame.K_UP:
#                 pilt_y = pilt_y - samm
#             if i.key == pygame.K_DOWN:
#                 pilt_y = pilt_y + samm
            if i.key == pygame.K_LEFT:
                pilt_x = pilt_x - samm
            if i.key == pygame.K_RIGHT:
                pilt_x = pilt_x + samm
pygame.quit()

2. Ristkülikutega (rect) objektide kattuvuse tuvastamine.

import pygame
pygame.init()
ekraan = pygame.display.set_mode([800, 600])
pygame.display.set_caption("Mäng")
pilt = pygame.image.load("hunt.png")
pilt = pygame.transform.scale(pilt, (120, 100)) #pildi vähendamine
pilt_x = 30
pilt_y = 530
samm = 1
rect = pilt.get_rect()
rect.topleft = (pilt_x, pilt_y)

pilt2 = pygame.image.load("muna.png")
pilt2 = pygame.transform.scale(pilt2, (60, 100)) #pildi vähendamine
pilt2_x = 130
pilt2_y = 130
rect2 = pilt2.get_rect()
rect2.topleft = (pilt2_x, pilt2_y)

pygame.key.set_repeat(1,10)

running = True
while running:
    ekraan.fill([0, 255, 255])
    ekraan.blit(pilt, (pilt_x,pilt_y))
    ekraan.blit(pilt2, (pilt2_x,pilt2_y))
    rect.topleft = (pilt_x, pilt_y)
    rect2.topleft = (pilt2_x, pilt2_y)
    pilt2_y += 1
    if pilt2_y > 800:
        pilt2_y = 0

    if rect.colliderect(rect2):
        pilt2_y = 0

    pygame.display.flip()
    for i in pygame.event.get():
        if i.type == pygame.QUIT:
            running = False
        elif i.type == pygame.KEYDOWN:
            if i.key == pygame.K_UP:
                pilt_y = pilt_y - samm
            if i.key == pygame.K_DOWN:
                pilt_y = pilt_y + samm
            if i.key == pygame.K_LEFT:
                pilt_x = pilt_x - samm
            if i.key == pygame.K_RIGHT:
                pilt_x = pilt_x + samm
pygame.quit()

Heli lisamine

Heli kasutamiseks Pygame'is on vahend pygame.mixer. Pygame toetab erinevad helifailiformaate (wav, mp3, ogg jne). Sarnaselt pildifailidega on soovitav panna helifailid kas programmifailiga samasse kausta või eraldi alamkausta.

Heli kasutamiseks peab mikser olema eelnevalt initsialiseeritud:

pygame.mixer.init()

Lühemate helinäidete puhul (heliefektid jne) on mõistlik kasutada Sound-moodulit, nii võetakse helifail korraga mällu ja kasutatakse seda vajalikus kohas:

heli_kolks = pygame.mixer.Sound("kolks.wav")
heli_kolks.play()

Meetodi play() sulgude sees olevaks argumendiks võib olla mängimiskordade arv: play(3) kordab heli kolm korda, play(-1) puhul aga korratakse helifaili seni, kuni see katkestatakse näiteks programmi sulgemisega.

Pikemate helide puhul, kus helifaili pole otstarbekas korraga mällu laadida, tarvitatakse moodulit music.load(), nii loetakse heli otse failist sisse mängimise ajal:

pygame.mixer.music.load("taustamuusika.mp3")
pygame.mixer.music.play()

Helitugevust muudetakse meetodiga set_volume(), sulgudes antud argument peaks olema nullist üheni e. vaikusest maksimaalse helitugevuseni:

heli_kolks.set_volume(0.6)

Näide heli kasutamisest:

import pygame, sys
pygame.init()
pygame.mixer.init()
ekraan = pygame.display.set_mode([640, 480])
heli_kolks = pygame.mixer.Sound("kolks.wav")
heli_kolks.set_volume(0.6)
heli_kolks.play()

Veel heli kohta lisalugemist: http://pygame.org/docs/ref/mixer.html

  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Courses’i keskkonna kasutustingimused