Animatsioon
Eelmises tunnis tegime tutvust PyGame’iga, õppisime, mis osadest programm koosneb, kuidas joonistada kujundeid ja teksti ning kuidas kasutada koordinaate. Selles tunnis uurime, kuidas luua animatsiooni.
Animatsioon on optiline illusioon pidevast liikumisest, kus objektide liikumise efekt saavutatakse joonistades objekt iga kaadriga uude kohta, seejuures muudetakse objekti asukohta vähehaaval, et liikumine paistaks sujuv. Animatsiooni kasutatakse nt filmides eriefektide loomiseks, sh multifilmides, arhitektuuris ruumiliste mudelite loomiseks, lisaks arvutimängudes ja ka virtuaalreaalsuses ning paljudes muudes valdkondades.
PyGame'is tuleb iga uue kaadriga ekraan uuesti üle värvida (vastasel juhul jäävad kõik eelnevalt joonistatud kujundid pildile alles) ja objekt ekraanile kuvada (et iga muudatus kasutajale nähtav oleks). Kuna kogu see tegevus (ekraani värvimine, objektide joonistamine ja pildi näitamine) toimub iga kaadri jaoks uuesti, siis tuleb kogu tegevus lisada mängutsükli sisse.
Seega programmi põhi võib välja näha umbes selline:
import pygame pygame.init() ekraan = pygame.display.set_mode([800, 600]) pygame.display.set_caption("Aken") running = True while running: # kõigepealt värvime ekraani üle ekraan.fill([255, 255, 255]) # seejärel joonistame vajalikud objektid ... # viimaseks näitame pilti arvutiekraanil pygame.display.flip() for syndmus in pygame.event.get(): if syndmus.type == pygame.QUIT: running = False pygame.quit()
Kaadrite vahetumine peaks toimuma võimalikult sujuvalt, mis tähendab objektide koordinaatide muutmist vähehaaval. See protsess on mõistlik kirja panna tsüklina, sest tegevused ja nende järjekord jäävad samaks, muutuvad ainult koordinaadid. Esiteks on sel moel kood kompaktsem ja teiseks võimaldab meil see animatsioon või tegevust korrata teadmata ette täpset kordade arvu. Võtame näiteks Tetrise mängu: akna ülemisest servast hakkab allapoole liikuma mingi klots, mis saab allapoole hüpata täpselt nii mitu astet kuni järgmine klots ette jääb. Mängu erinevates etappides on kõige kõrgem klots ükskõik millisel tasemel ja langev klots peab suutma sellega arvestada. Juba järgmises peatükis õpime, kuidas tuvastada objektide kokkupõrget ning kuidas sellele reageerida.
Loome animatsiooni, kus üks ristkülik lendab vasakult paremale ja kui jõuab ekraani paremasse serva, siis ilmub jälle vasakult. Selleks peame: 1. Määrama ristküliku algpositsiooni ekraani vasakus servas. 2. Igal sammul suurendama selle ristküliku x-koordinaati sammu võrra. 3. Kontrollima igal sammul, kas ristkülik on jõudnud paremasse serva ja kui on, siis taastama algasendi vasakus servas.
Kõigepealt defineerime enne tsüklit ristküliku x-koordinaadi, liikumise sammu ja tsüklis joonistame rohelise ristküliku, millel on kindel suurus ja koordinaadid:
pygame.draw.rect(ekraan, [0, 255, 0], [[x, 200], [100, 50]])
Käivitame programmi, avaneb aken, milles on kujutatud roheline ristkülik.
Alustame risküliku liigutamist. Selleks suurendame igal sammul ristküliku x-koordinaati x += samm ja kontrollime if-lausega, et kui ristkülik on jõudnud ekraani paremasse serva (x > 700), siis muudame x-koordinaadi taas nulliks.
Lendava ristküliku programm võiks välja näha selline:
import pygame pygame.init() ekraan = pygame.display.set_mode([800, 600]) pygame.display.set_caption("Aken") x = 0 samm = 5 running = True while running: ekraan.fill([255, 255, 255]) pygame.draw.rect(ekraan, [0, 255, 0], [[x, 100], [100, 50]]) if x > 700: x = 0 else: x += samm pygame.time.delay(20) pygame.display.flip() for syndmus in pygame.event.get(): if syndmus.type == pygame.QUIT: running = False pygame.quit()
Pane tähele, et programmis on rida pygame.time.delay(20). See määrab ühe kaadri ehk korduse pikkuse. Väärtus sõltub ka protsessorist ja tuleb ise valida argumendiks sobiv paus millisekundites, et animatsioon oleks sujuv.
Kui me tahame, et ristkülik ilmuks iga kord ekraani vasakust servast erinevalt kõrguselt, siis selleks tuleks kasutada ka y-koordinaadi määramiseks muutujat ja kasutada moodulist random funktsiooni randint
kujundi y-koordinaadi asendamiseks suvalise arvuga selliselt:
import pygame from random import randint pygame.init() ekraan = pygame.display.set_mode([800, 600]) pygame.display.set_caption("Aken") x = 0 y = randint(0, 550) samm = 5 running = True while running: ekraan.fill([255, 255, 255]) pygame.draw.rect(ekraan, [0, 255, 0], [[x, y], [100, 50]]) if x > 700: x = 0 y = randint(0, 550) else: x += samm pygame.time.delay(20) pygame.display.flip() for syndmus in pygame.event.get(): if syndmus.type == pygame.QUIT: running = False pygame.quit()
Enesekontroll 1
Enesekontroll 2
Arvutimängus võib kasutada geomeetriliste kujundite asemel tegelaste pilte. Siinkohal tuleb meeles pidada, et internetist võetud piltidele tuleb alati korrektselt viidata, et ei tekiks probleeme autoriõigusega. Lisaks kui jagad enda lahendusi teise inimesega, siis on tema jaoks kasulik, kui ta saab koodist kätte vastava lingi, et pilt alla laadida, et programm töötaks samamoodi.
Kõik piltide kasutamiseks vajalikud käsud kuuluvad moodulisse pygame.image
(uuri mooduli kohta lähemalt siit: https://www.pygame.org/docs/ref/image.html). Pildi joonistamiseks ekraanile tuleb see kõigepealt programmi sisse laadida. Selleks salvestame vastava nt .png või .jpg laiendiga faili samasse kausta, kus asub programm, ja käsuga pygame.image.load()
, mis võtab argumendiks pildi nime, laeme pildi programmi. Ekraanile joonistamine käib käsu blit()
abil (millele anname kaks argumenti: pilt ja soovitud koordinaadid). Olgu tarvis programmi üles laadida mingi logo. Kasutame praegu PyGame'i logo, mille salvestame nimega pygame_logo.png:
Kõigepealt laeme pildi programmi (tuleb teha üks kord programmi alguses):
logo = pygame.image.load("pygame_logo.png")
Ja siis asetame ekraanile:
ekraan.blit(logo, [50, 50])
(Määrame akna suuruseks nt 800x600 punkti ning taustavärviks valge.)
Programmi käivitades on tulemus järgmine:

Pilt on akna jaoks liiga suur, seega muudame seda pisemaks, et see aknasse ära mahuks. Pildi suuruse muutmiseks kasutame funktsiooni pygame.transform.scale()
, mis võtab kaks argumenti: pilt ja soovitud suurus kujul [laius, kõrgus].
Alati ei pruugi me teada pildi mõõtmeid, nagu ka selle logo puhul, kuid siin tulevad kasuks funktsioonid get.width()
ja get.height()
. Rakendame funktsioone pildi muutujale (praegu logo
) ning salvestame leitud suurused uutesse muutujatesse laius
ja pikkus
:
laius = logo.get_width() pikkus = logo.get_height()
Seejärel defineerime uue muutuja vaiksem_logo
. Kui pildi mõõtmed on programmil teada, siis paneme nende muutujate kaudu kirja väiksema pildi mõõtmed. Kui soovime, et pilt oleks 10% suurem, siis korrutame tegeliku laiuse ja kõrguse 1,1-ga, ning kui soovime, et pilt oleks poole väiksem, korrutame 0,5-ga.
vaiksem_logo = pygame.transform.scale(logo, [0.3 * laius, 0.3 * pikkus])
Pildi asetamiseks ekraanile kasutame ikka käsku blit()
:
ekraan.blit(vaiksem_logo, [50, 50])
Terve kood:
import pygame pygame.init() ekraan = pygame.display.set_mode([800, 600]) pygame.display.set_caption("Aken") ekraan.fill([255, 255, 255]) logo = pygame.image.load("pygame_logo.png") laius = logo.get_width() pikkus = logo.get_height() vaiksem_logo = pygame.transform.scale(logo, [0.3 * laius, 0.3 * pikkus]) ekraan.blit(vaiksem_logo, [50, 50]) running = True while running: for syndmus in pygame.event.get(): if syndmus.type == pygame.QUIT: running = False pygame.display.flip() pygame.quit()
Käivitame programmi:

PyGame'i moodul pygame.transform
on mõeldud pindade (kujundite ja piltide) teisendamiseks ja muutmiseks. Selles õppematerjalis neid võimalusi lähemalt ei uurita, kuid veebilehelt https://www.pygame.org/docs/ref/transform.html leiad kõik vajaliku (nt pinna pööramine, resolutsiooni muutmine), kui soovid enda programmis selliseid funktsioone kasutada.