7. Graafiliste mängude loomine
Mängude loomine on väga suur valdkond, kus programmeerijad saavad oma võimeid kasutada. Mänge on muidugi võimalik ka Pythoniga luua. Selle jaoks on loodud erinevaid teeke: Pygame, pyglet, Cocos2d, Panda3D ja palju muid. Siin peatükis vaatame, kuidas luua üks lihtne mäng Pygame'iga.
Pygame on populaarne Pythoni mängude loomise teek, mis lubab lihtsasti tekitada graafikat ja heli ning saada kasutajalt sisendit mitmel erineval moel. See on hea viis, kuidas alustada oma mänguarendaja karjääri. See pole ka ainult alustamiseks: päris palju mänge on Pygame'iga tehtud.
Ettevalmistus
Enne jätkamist tuleb paigaldada moodul pygame. Seda saab teha käsuga pip install pygame
. Katsetamiseks kirjuta Pythonis import pygame
. Kui erindit ei visata, on moodul paigaldatud. Kui ei ole kindel, kuidas mooduleid installida, siis on õpikus moodulite paigaldamise juhised.
Et peatükist aru saada, peab olema läbitud õpiku esimene osa. Soovitatav on läbida peatükk "Objektorienteeritud programmeerimine" läbimine, aga see ei ole vajalik.
Mängu akna kuvamine
Kui moodul pygame
importida, siis saab Pygame'i käivitada käsuga pygame.init()
. Enne seda peab veel määrama akna suuruse käsuga pygame.display.set_mode()
, mille parameetriks on ennik ekraani laiusega ja kõrgusega pikslites. Pygame'i akent saab sulgeda käsuga pygame.quit()
. Teeme akna laiusega 640 pikslit ja kõrgusega 480 pikslit.
import pygame pygame.display.set_mode((640, 480)) pygame.init() pygame.quit()
Kui see programm käivitada, siis ilmub korraks must aken suurusega 640 korda 480 pikslit ja läheb kinni.
Pygame'i loodud aken töötab eraldiseisvalt Pythoni programmile. See tähendab, et pärast pygame.init()
käsku kuvatakse eraldi aken, kuhu saab hakata joonistama ning programmi kood jookseb edasi. Aken läheb kinni alles siis, kui programm jõuab sulgemiskäsuni pygame.quit()
. Kui enne sulgemiskäsku jookseks programm 3 sekundit, siis aken on püsti 3 sekundit.
Kui enne sulgemiskäsku jooksutada lõpmatu tsükkel, siis aken ei lähegi kinni ning ristist sulgemine ka ei aita. Programmi töö peab muudmoodi lõpetama. Saame selles lõputus while-tsüklis kontrollida, kas kasutaja on vajutanud sulgemisnupule ja sel juhul saame tsükli lõpetada.
import pygame pygame.display.set_mode((640, 480)) pygame.init() while True: sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break pygame.quit()
Kui see programm käivitada ja ristile vajutada, siis aken läheb kinni. Oleme kirjutanud väga algelise programmi, millega kasutaja saab suhelda! Vaatame hiljem, kuidas tsüklisse muud funktsionaalsust lisada ja teistmoodi kasutajalt sisendit saada. Kõigepealt proovime aknasse midagi joonistada.
Joonistamine
Siiamaani oleme loonud musta akna, mis on graafilisest mängust tsipa kaugel. Järgmisena proovime selle akna millegi värvilisega täita.
Värvid
Saame täita selle akna mingi värviga, mida saab esindada erinevatel viisidel, aga Pygame'is on tavaks kasutada RGB mudelit, mis koosneb punasest, rohelisest ja sinisest värvi kogusest, 0 kuni 255. Neid esindatakse kolmeliikmeliste ennikutena. Näiteks on must värv (0, 0, 0), valge värv (255, 255, 255), punane värv (255, 0, 0), akvamariinsinine (128, 255, 212) jne. Lihtne viis enda värvi valimiseks on guugeldada "colour picker" ja võtta valitud värvi RGB väärtused.
Täidame akna enda soovitud värviga. Selleks peame ekraanisuuruse muutmise funktsiooni tagastuse salvestama muutujasse ekraan
. See on muutuja, kuhu hakkame edaspidi kõiki joonistusi lisama. Ekraani saab täita meetodiga fill()
, mille parameetriks läheb värv. Tehtud muudatused tuleb salvestada käsuga pygame.display.flip()
.
import pygame ekraan = pygame.display.set_mode((640, 480)) pygame.init() ekraan.fill((128, 255, 212)) pygame.display.flip() while True: sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break pygame.quit()
Programm käivitab nüüd akna, mis pole enam must, vaid akvamariinsinine. Edusammud!
Kujundid
Proovime lõpuks aknasse midagi lisada. Pygame võimaldab joonistada erinevaid kujundeid. Ristkülikut saab ekraanile joonistada funktsiooniga pygame.draw.rect()
. See nõuab kolme parameetrit:
- pind, mille peale joonistada (meie puhul muutuja
ekraan
) - värv, millega ristkülik täita (värve juba oskame)
- asukoha nelik: alguspunkti x-koordinaat, alguspunkti y-koordinaat, ristküliku laius, ristküliku kõrgus (kõik pikslites)
Koordinaatide lugemist alustatakse Pygame'is ülevalt vasakult ehk (0, 0) on üleval vasakul, (640, 0) on üleval paremal, (0, 480) on all vasakul ja (640, 480) on all paremal. Kõik vahepealsed on kuskil ekraani sees.
Joonistame roosa ristküliku, mis algab meie akna keskpunktist ehk (320, 240) ning on 200 piksli laiune ja 100 piksli kõrgune.
import pygame ekraan = pygame.display.set_mode((640, 480)) pygame.init() ekraan.fill((128, 255, 212)) pygame.draw.rect(ekraan, (255, 128, 171), (320, 240, 200, 100)) pygame.display.flip() while True: sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break pygame.quit()
Pygame lubab ka teisi kujundeid joonistada. Loe dokumentatsioonist erinevaid võimalusi ja proovi ekraanile joonistada ring, kolmnurk ja joon.
Pildid
Sarnaselt kujunditele saab mängu lisada pilte. Kõigepealt peame leidma pildi, mida mängus kasutada. Neid saab muidugi ise luua, aga Internetis on suur hulk vabalt kasutatavaid materjale olemas: itch.io/game-assets, opengameart.org jpm. Kui neid kasutada mängudes, on vaja autorile viidata. Siin peatükis kasutame rotti, mis on võetud siit, ja juustu, mis on võetud siit.
Et joonistada pilt aknasse, tuleb see kõigepealt sisse laadida pildiobjektina funktsiooniga pygame.image.load()
. Sulgudesse läheb failitee. Praegu paigutame pildifailid programmifailiga samasse kausta, aga suurema mängu puhul võiks need olla eraldi kaustas.
rott = pygame.image.load("rott.png")
Muutuja rott on nüüd pind (Surface), mida saab joonistada ekraanile. Pindade võimaluste kohta saab lugeda Pygame'i dokumentatsioonist.
Saame roti paigutada ekraanile meetodiga blit()
. Sulgudesse läheb pildifail ning koordinaadipaariga asukoht. Paigutame loodud roti objekti ekraani koordinaatidele (100, 200).
ekraan.blit(rott, (100, 200))
Roti laiuse ja kõrguse eraldi saame kätte meetoditega get_width()
ja get_height()
. Proovi paigutada rott täpselt ekraani keskele, kasutades seda meetodit, et arvutada paigutuse koordinaadid. (320, 240) ei ole õige siin vastus.
>>> rott.get_width() 116 >>> rott.get_height() 72
Tekst
Mängudes on tavaliselt teksti, mis näitab vestluseid, õpetusi, skoori, menüüd jpm. Pygame võimaldab teksti kuvada samamoodi nagu piltegi.
Kõigepealt tuleb valida font, milles teksti kuvada ja teha sellest objekt funktsiooniga pygame.font.SysFont()
. Esimeseks parameetriks läheb fondi nimi sõnena ja teiseks läheb fondi suurus. Kui fondi nimeks valida None, siis valib Pygame vaikimisi fondi. Funktsiooniga Font() saab ka fondi võtta failist. See on kasulik, kui fonti ei pruugi olla teiste arvutis.
font = pygame.font.SysFont("Comic Sans MS", 24) font = pygame.font.SysFont(None, 32) font = pygame.font.Font("Futura-Bold-Italic.ttf", 40)
Kasutades fonti saame meetodiga render()
luua pinna objekti. Sulgudesse läheb tekst sõnena, tõeväärtus True
ning teksti värv. Tõeväärtuse kohta võib lugeda dokumentatsioonist. Loodud objekt on põhimõtteliselt pilt ehk seda saab meetodiga blit()
paigutada ekraanile. Loome teksti "Tere, maailm!" musta fondiga ning paigutame selle koordinaatidele (40, 40).
tekst = font.render("Tere, maailm!", True, (0, 0, 0)) ekraan.blit(tekst, (40, 40))
Kogu kood siiamaani:
import pygame ekraan = pygame.display.set_mode((640, 480)) pygame.init() ekraan.fill((128, 255, 212)) rott = pygame.image.load("rott.png") ekraan.blit(rott, (100, 200)) font = pygame.font.SysFont(None, 40) tekst = font.render("Tere, maailm!", True, (0, 0, 0)) ekraan.blit(tekst, (40, 40)) pygame.display.flip() while True: sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break pygame.quit()
Pinna objekte saab moondada: pööramine, peegeldamine, suurendamine jne. Uuri dokumentatsiooni: pygame.transform.
Harjuta ekraanile joonistamist. Leia vastused küsimustele:
- Kas ühte pinda on võimalik mitu korda ekraanile joonistada?
- Kas pinda saab paigutada ekraanist väljapoole?
- Mis juhtub, kui joonistada pilt enne ja täita ekraan mingi värviga hiljem?
- Mis käskudega saab kätte peegelpildi ja pööramised?
- Kuidas võiks toimuda animeerimine?
Animeerimine
Siiani oleme joonistanud ekraanile ühe kaadri. Et saada paigutatud objekte liikuma, ei saa me lihtsalt muuta nende asukohti: me peame iga kaadri algusest peale joonistama. See võib kõlada ebaintuitiivsena, aga Pygame töötabki just niimoodi.
Jätame alles roti ekraanile, aga liigutame tema joonistamise meie põhitsüklisse, mis programmi käimas hoiab.
import pygame ekraan = pygame.display.set_mode((640, 480)) pygame.init() rott = pygame.image.load("rott.png") while True: sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break ekraan.fill((128, 255, 212)) ekraan.blit(rott, (100, 200)) pygame.display.flip() pygame.quit()
Kui programm käivitada, siis midagi väga muutunud ei ole. Rott on ikka paigal, ta lihtsalt joonistatakse igal tsükli käigul uuesti. See on kasulik, sest nüüd me saame näiteks muuta igal käigul roti asukohta.
Salvestame programmi alguses roti koordinaadid mingitesse muutujatesse, näiteks rott_x
ja rott_y
, algul olgu need 100 ja 200. Nüüd igal tsükli korral suurendame rott_x muutujat ühe võrra ja käivitame programmi uuesti.
rott_x = 100 rott_y = 200 ... rott_x += 1
Kui see programm käivitada, siis rott tõepoolest liigub väga kiiresti paremale ja jääbki liikuma. Proovi täitmiskäsk ekraan.fill()
tsüklist ära võtta ja ainult programmi algusesse panna. Mis juhtub?
Saime roti liikuma, aga mitte väga hästi. Probleem on praegu selles, et tsükli käik ei ole kuidagi ajaliselt piiratud: see kestab täpselt nii kaua, kui Pythonil läheb selle jooksutamiseks. See on esiteks väga lühike aeg: peaksime roti koordinaati liigutama palju vähem (nt 0.1 pikslit) iga kord. Teiseks: see võib erineda erinevates arvutites ja isegi samas arvutis erinevatel aegadel. Tahame, et mäng jookseks kõikides arvutites samamoodi igal hetkel.
Selle probleemi parandamiseks kasutame Pygame'i kella. Loome programmi alguses uue kella objekti:
kell = pygame.time.Clock()
Iga tsükli alguses peaks kell tiksuma ehk kutsume välja meetodi tick()
. Sulgudesse läheb arv, mitu kaadrit peaks sekundis kuvama. Valime 60, sest enamik kuvaritest värskenduvad 60 korda sekundis. Selle meetodi käivitamisel ootab programm umbes 1/60 sekundit.
dt = kell.tick(60)
See meetod tagastab, mitu millisekundit on kulunud eelmisest kella tiksumisest. 60 kaadri sekundis puhul see on enamasti 16 või 17. Seda on väga kasulik teada, sest kui arvuti näiteks kiilub korraks kinni ja mõnda kaadrit joonistatakse natuke kauem, siis meile on see teada ja me saame seda oma mängus arvestada nii, et mängu tegevus ei kiiluks kinni, vaid liiguks vastavalt sellele, kui palju aega on kulunud. Kui kaader peaks kestma 16 millisekundit ja kestab hoopis 32, siis mängu tegevus peaks sellel kaadril liikuma 2 korda kiiremini. Selle abil tagame ka selle, et mäng jookseb kõikidel arvutitel sama kiiresti.
Seega peame igal liikumisel arvestama kulunud aega (muutujat dt
). Roti liikumisel korrutame sellega läbi.
rott_x += 0.25 * dt
0.25 on siin roti kiirus, mis väljendab mitu pikslit me tahame, et rott liiguks millisekundis. 0.25 pikslit millisekundis on teisendatult 250 pikslit sekundis. Mitme sekundiga jõuab rott ekraanist välja, kui ekraani laius on 640 ja roti algne x-koordinaat on 100?
Kogu kood:
import pygame ekraan = pygame.display.set_mode((640, 480)) pygame.init() kell = pygame.time.Clock() rott = pygame.image.load("rott.png") rott_x = 100 rott_y = 200 while True: dt = kell.tick(60) sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break ekraan.fill((128, 255, 212)) ekraan.blit(rott, (rott_x, rott_y)) pygame.display.flip() rott_x += 0.25 * dt pygame.quit()
Proovime veel animatsiooni huvitavamaks teha. Kui rott jõuab teisele poole, siis paneme ta ümber pöörama. Selleks salvestame roti kiiruse eraldi muutujasse.
roti_kiirus = 0.25 ... ... ... rott_x += roti_kiirus * dt
Kui roti parem ots jõuab paremalt poolt ekraanist välja, siis lihtsalt teeme tema kiiruse negatiivseks. Teeme sama, kui roti vasak ots jõuab vasakult poolt ekraanist välja.
if (rott_x + rott.get_width()) > 640 or rott_x < 0: roti_kiirus *= -1
Kui roti kiirus on negatiivne, siis tema x-koordinaat väheneb igas tsükli käigus ja ta liigub vasakule.
Pöörame ka roti ümber. Rohkem infot dokumentatsioonis.
rott = pygame.transform.flip(rott, True, False)
Kogu kood:
import pygame ekraan = pygame.display.set_mode((640, 480)) pygame.init() kell = pygame.time.Clock() rott = pygame.image.load("rott.png") rott_x = 100 rott_y = 200 roti_kiirus = 0.25 while True: dt = kell.tick(60) sisend = pygame.event.poll() if sisend.type == pygame.QUIT: break if rott_x > (640 - rott.get_width()) or rott_x < 0: roti_kiirus *= -1 rott = pygame.transform.flip(rott, True, False) ekraan.fill((128, 255, 212)) ekraan.blit(rott, (rott_x, rott_y)) pygame.display.flip() rott_x += roti_kiirus * dt pygame.quit()
Rott nüüd põrkab lõputult akna parema ja vasaku ääre vahel. Proovi panna rott ringi liikuma, nagu vana DVD-mängija ekraanisäästja: kui jõuab ääreni, siis põrkab. Vihje: liikuval kahemõõtmelisel esemel saab olla 2 kiirust: horisontaalkiirus ja vertikaalkiirus.
Kasutajasisend
Panime roti liikuma, aga tavaliselt saab mänge mõjutada kasutaja. Paneme roti liikuma vastavalt klahvivajutustele!
Siiani oleme tegelikult mingit kasutajasisendit küsinud: kui ristile vajutada, siis mäng läheb kinni. Senist sisendipüüdmist peame natuke muutma. Kahe tsükli käigu vahel võib toimuda mitu sisendit, aga me oleme iga kord ainult ühe küsinud - peaksime kõike kontrollima. Kõik sisendid saab kätte funktsiooniga pygame.event.get()
. Kuna need peab tsükliga läbi käima, siis break
viiks meid ainult sellest tsüklist välja. Asendame selle sys.exit()
väljakutsega, mis lõpetab programmi töö.
for sisend in pygame.event.get(): if sisend.type == pygame.QUIT: sys.exit()
Samamoodi, nagu kontrollime ristile vajutamist, saame kontrollida ka klaviatuurivajutusi. Selle jaoks tuleb kontrollida, kas sisendi tüüp on pygame.KEYDOWN
. Kui on, siis saame vaadata selle klahvi väärtust. Nooleklahvid on näiteks pygame.K_UP
, pygame.K_DOWN
, pygame.K_LEFT
, pygame.K_RIGHT
. Vastavalt nendele võiksime rotti liigutada. Erinevaid nuppe saab kätte dokumentatsioonist.
for sisend in pygame.event.get(): if sisend.type == pygame.QUIT: sys.exit() elif sisend.type == pygame.KEYDOWN: if sisend.key == pygame.K_UP: rott_y -= roti_kiirus * dt elif sisend.key == pygame.K_DOWN: rott_y += roti_kiirus * dt elif sisend.key == pygame.K_LEFT: rott_x -= roti_kiirus * dt elif sisend.key == pygame.K_RIGHT: rott_x += roti_kiirus * dt
Kui nupule vajutada, siis rott tõepoolest liigub… natuke. Kui tahame, et nuppu all hoides rott jääkski liikuma, siis võiksime koordinaatide muutmise asemel hoopis roti kiiruseid muuta ning igal tsükli käigul rotti liigutada vastavalt kiirustele.
roti_kiirus = 0.25 roti_x_kiirus = 0 roti_y_kiirus = 0 ... if sisend.key == pygame.K_UP: roti_x_kiirus = 0 roti_y_kiirus = -roti_kiirus elif sisend.key == pygame.K_DOWN: roti_x_kiirus = 0 roti_y_kiirus = roti_kiirus elif sisend.key == pygame.K_LEFT: roti_x_kiirus = -roti_kiirus roti_y_kiirus = 0 elif sisend.key == pygame.K_RIGHT: roti_x_kiirus = roti_kiirus roti_y_kiirus = 0 ... rott_x += roti_x_kiirus * dt rott_y += roti_y_kiirus * dt
Kui nüüd nupule vajutada, siis rott jääbki liikuma. See võib mõnes mängus olla soovitud, näiteks ussimängus, aga vaatame, kuidas nupust lahti laskmisel rott seisma jääks. Saame vaadata, et kui sisenditüüp on pygame.KEYUP
ehk klahvist lastakse lahti, siis nullime kiirused vastavalt klahvile, mis lahti lasti. Kogu kood siiani:
for sisend in pygame.event.get(): if sisend.type == pygame.QUIT: sys.exit() elif sisend.type == pygame.KEYDOWN: if sisend.key == pygame.K_UP: roti_y_kiirus = -roti_kiirus elif sisend.key == pygame.K_DOWN: roti_y_kiirus = roti_kiirus elif sisend.key == pygame.K_LEFT: roti_x_kiirus = -roti_kiirus elif sisend.key == pygame.K_RIGHT: roti_x_kiirus = roti_kiirus elif sisend.type == pygame.KEYUP: if sisend.key == pygame.K_UP or sisend.key == pygame.K_DOWN: roti_y_kiirus = 0 elif sisend.key == pygame.K_LEFT or sisend.key == pygame.K_RIGHT: roti_x_kiirus = 0
Selle programmiga on veel probleeme. Kui hoida all paremat noolt, seejärel hoida all vasakut noolt ja lasta parem nool lahti, siis rott jääb seisma, kuigi vasak nool on veel all. Saame igal hetkel kontrollida vajutatud klahve funktsiooniga pygame.key.get_pressed()
. See on sõnastik, mille võtmeteks on klahvid ja väärtusteks on tõeväärtused, kas nuppu hoitakse all või mitte.
import pygame import sys ekraan = pygame.display.set_mode((640, 480)) pygame.init() kell = pygame.time.Clock() rott = pygame.image.load("rott.png") rott_x = 100 rott_y = 200 roti_kiirus = 0.25 roti_x_kiirus = 0 roti_y_kiirus = 0 while True: dt = kell.tick(60) for sisend in pygame.event.get(): if sisend.type == pygame.QUIT: sys.exit() vajutatud = pygame.key.get_pressed() if vajutatud[pygame.K_UP]: roti_y_kiirus = -roti_kiirus elif vajutatud[pygame.K_DOWN]: roti_y_kiirus = roti_kiirus else: roti_y_kiirus = 0 if vajutatud[pygame.K_LEFT]: roti_x_kiirus = -roti_kiirus elif vajutatud[pygame.K_RIGHT]: roti_x_kiirus = roti_kiirus else: roti_x_kiirus = 0 rott_x += roti_x_kiirus * dt rott_y += roti_y_kiirus * dt ekraan.fill((128, 255, 212)) ekraan.blit(rott, (rott_x, rott_y)) pygame.display.flip()
Mõningaid probleeme veel leidub. Kui hoida all nii üles- kui allanoole klahv, siis liigub rott alati ülespoole, sest seda kontrollitakse esimesena. Roti kiirus peaks igas suunas olema võrdne, aga diagonaalis liikudes on ta kiirem. Praeguseks on aga liikumine piisavalt hea, liigume edasi.
Juust
Mis mäng on mäng ilma eesmärgita? Lisame mängu juustu, mille rott peab kätte saama.
Laadi alla juust.png
ülevalt ja tuleta meelde, kuidas laadida see mängu ning joonistada see. Juustul võiks olla ka koordinaadid muutujatena, sarnaselt rotile.
Kui proovida juustu peale liikuda, siis midagi ei juhtu. Paneme kokkupuutumisel juustu juhuslikku asukohta hüppama!
Kuidas me kontrollime kokkupuudet? Saaksime manuaalselt vaadata, kui roti koordinaadid on juustu koordinaatide lähedal, aga see on palju tööd ning Pygame lubab seda palju lihtsamalt teha.
Kui me paigutame pinna ekraanile meetodiga blit()
, siis see tegelikult tagastab meile ühe ristküliku objekti (Rect). See on kasulik objekt, millelt saab kätte pildi koordinaate: keskpunkt, nurgad, servade keskpunktid, laius, kõrgus jne. Muuseas saab sellega kontrollida kahe ristküliku kokkupuutumist üksteisega, kasutades colliderect()
meetodit. Liigutame kokkupuutumisel juustu uuele juhuslikule asukohale.
juust_rect = ekraan.blit(juust, (juust_x, juust_y)) rott_rect = ekraan.blit(rott, (rott_x, rott_y)) if rott_rect.colliderect(juust_rect): juust_x = random.randint(0, 640 - juust.get_width()) juust_y = random.randint(0, 480 - juust.get_height())
Lõplik kood kommentaaridega:
import pygame import sys import random # Loome akna ning käivitame Pygame'i ekraan = pygame.display.set_mode((640, 480)) pygame.init() # Alustame Pygame'i kella kell = pygame.time.Clock() # Laadime sisse roti pildi rott = pygame.image.load("rott.png") # Algväärtustame roti asukoha rott_x = 100 rott_y = 200 # Kui kiiresti rott liigub klahvivajutustel roti_kiirus = 0.25 # Kui kiiresti rott parajasti liigub roti_x_kiirus = 0 roti_y_kiirus = 0 # Laadime sisse juustu pildi juust = pygame.image.load("juust.png") # Algväärtustame roti asukoha juust_x = 400 juust_y = 200 # Põhitsükkel, mis hoiab akent lahti while True: # Tiksume kella (ootame umbes 1/60 sekundit) # Salvestame kulunud aja eelmisest tiksumisest millisekundites dt = kell.tick(60) # Käime läbi kõik sisendid eelmisest tiksumisest saati for sisend in pygame.event.get(): # Kui on soovitud aken sulgeda, sulgeme programmi if sisend.type == pygame.QUIT: sys.exit() # Võtame kõik parajasti allavajutatud klahvid vajutatud = pygame.key.get_pressed() # Kui üles- või allanool on all, määrame roti vertikaalkiiruse if vajutatud[pygame.K_UP]: roti_y_kiirus = -roti_kiirus elif vajutatud[pygame.K_DOWN]: roti_y_kiirus = roti_kiirus else: # Vastasel juhul peatame roti vertikaalselt roti_y_kiirus = 0 # Kui vasak- või paremnool on all, määrame roti horisontaalkiiruse if vajutatud[pygame.K_LEFT]: roti_x_kiirus = -roti_kiirus elif vajutatud[pygame.K_RIGHT]: roti_x_kiirus = roti_kiirus else: # Vastasel juhul peatame roti horisontaalselt roti_x_kiirus = 0 # Liigutame roti asukohta vastavalt kiirustele # Korrutame liikumist kulunud ajaga, et rott liiguks sama # palju sõltumatult vahele jäänud kaadritest rott_x += roti_x_kiirus * dt rott_y += roti_y_kiirus * dt # Täidame akna akvamariinsinise värviga # Sellega kustutame ära kõik, mis siiamaani ekraanil on ekraan.fill((128, 255, 212)) # Joonistame pildid nende koordinaatidele # Saame nende ristküliku objektid juust_rect = ekraan.blit(juust, (juust_x, juust_y)) rott_rect = ekraan.blit(rott, (rott_x, rott_y)) # Kui juustu ja hiire ristkülikud puutuvad kokku if rott_rect.colliderect(juust_rect): # Liigutame juustu juhuslikele koordinaatidele juust_x = random.randint(0, 640 - juust.get_width()) juust_y = random.randint(0, 480 - juust.get_height()) # Värskendame ekraani pygame.display.flip()
Kui Pythonis klasside kasutamine on selge, siis võiks igale objektile teha enda klassid, millel on isendiväljad suurustest ja kiirustest ning meetodid liigutamise ja muude tegevuste jaoks: https://stackoverflow.com/a/35642955/12123296
Kokkuvõte
Vaatasime, kuidas Pygame töötab, joonistasime kujundeid, pilte ja teksti, panime pildi liikuma, liigutasime ise pilti klahvivajutustega, kontrollisime kahe eseme kokkupuudet ja saime lõpuks valmis midagi, mida saab enam-vähem mänguks nimetada. Graafiliste mängude loomise alustalad on nüüd all, aga väga palju jäi käsitlemata.
Et edasi õppida, pakub Pygame erinevaid õpetusi oma veebilehel. Tasub ka uurida Pygame'i projektide kogumikku. Sinna on postitatud palju erinevaid Pygame'iga tehtud mänge koos lähtekoodidega (Releases all), mida tasub uurida.
Ülikoolis saab arvutigraafikaga ja mängudega jätkata ainetes "Arvutigraafika" (MTAT.03.015) ning "Arvutimängude loomine ja disain" (MTAT.03.263).
Enesekontrolliküsimused
Ülesanded
1. Täienda peatüki jooksul loodud mängu skooriga, mis loeb juustu kättesaamise arvu. Kuva see skoor kuskil mänguaknas tekstina.
2. Kirjuta Pygame'iga animatsioon, kus mingi pilt või kujund liigub sinusoidi trajektoori või ringjoone järgi.
3. Kirjuta ise Pygame'iga üks mäng, millel on mingi eesmärk. Graafika joonistada ise või võtta Internetist. Viimase puhul tuleb lähtekoodis kommentaarina allikale viidata. Mõned ideed:
- Labürindist pääsemise mäng. Labürindi võib näiteks võtta tekstifailist, sarnaselt pykkarile.
- Lihtne Flappy Birdi kloon. Gravitatsiooni asemel võib vabalt ise kontrollida üles-alla liikumist.
- Esemed lendavad mängija suunas, kes peab eest ära hüppama, et ellu jääda.