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.
- Sündmustest üldiselt
- Reageerimine hiireklõpsule
- Reageerimine klahvivajutusele
- Pildi lisamine liigutatavale tegelasele
- Objektide kokkupõrge
- Heli lisamine
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.
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
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
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()
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