Arcade-pelien ohjelmointi
Pythonilla ja Pygamella

Chapter 5: Johdanto tietokonegrafiikkaan

fig.ellie

Nyt, kun hallitset toistolauseiden (silmukoiden) ohjelmoimisen, on aika tutustua tietokonegrafiikkaan. Tässä kappaleessa käsitellään:

5.1 Koordinaatisto tietokoneessa

Video: The coordinate system for computer graphics

Karteesinen koordinaatisto on esitetty kuvassa 5.1 (Wikimedia Commons), joka vaikuttaa varsin tutulta. Sitä se onkin, koska tuo opetetaan koulussa. Tietokone käyttää lähes samanlaista, mutta eroavaisuus tähän tuttuun koordinaatistoon vaatii tutustumisen (mikro)tietokoneen historiaan.

fig.Cartesian_coordinates_2D
Figure 5.1: Karteesinen koordinaatisto

Mikrotietokoneen kehityksen alkuvaiheessa, 80-luvun alussa, tietokoneet olivat tekstipohjaisella käyttöliittymällä varustettuja eivätkä tukeneet grafiikkaa. Kuva 5.2 (Wikimedia Commons) esittää sen aikaista taulukkolaskentaohjelmaa suositussa Applen II tietokoneessa 80-luvun alussa. Kun tekstiä kohdistettiin näytölle, ohjelmoija aloitti riviltä 1, eli oikeasta yläkulmasta. Näytölle mahtui 24 riviä ja rivi oli 40 merkin mittainen.

fig.apple3screen
Figure 5.2: Early Apple text screen

Tekstimerkeillä saatiin tehtyä jopa alkeellista grafiikkaa muodostamalla ne näppäimistön merkeistä yhdistelemällä. Tästä saatiin aikaiseksi vaikutelma kuvasta. Oheisessa kuvassa 5.3 on esitetty kissa ja katsopa tarkkaan, miten se on tehty. Tällä tavalla tehtynä aloituskohta oli edelleen rivi 1 vasemmalla yläkulmassa.

fig.simpleASCII
Figure 5.3: Text screen

Myöhemmin merkkivalikoima laajeni ja alkeellisia graafisia kuvioita saatiin tehtyä jopa värillisinä. Kuten oheisessa kuvassa 5.4 nähdään, grafiikka harppasi aimo askeleen kehityksessä eteenpäin. Internetisä löytyy hakusanalla “ASCII art” lisää esimerkkejä pelien grafiikasta tuolta ajalta.

fig.spacewar
Figure 5.4: Spaceware text screen

Tekstipohjaisella näytöllä käytössä ollut koordinaatisto jäi edelleen käyttöön vaikka näyttöön saatiin aikaiseksi yksittäisiin pikseleihin perustuvaa grafiikkaa.

fig.Computer_coordinates_2D
Figure 5.5: Tietokoneen koordinaatisto

Tietokoneessa $x$ koordinaattiakseli on aivan sama kuin karteesisessa koordinaatistossa, mutta $y$ koordinaattiakseli on ylösalaisin. Tietokoneessa $y$ koordinaatit kasvavat ylhäältä alas, aivan kuin tekstirivit tekstipohjaisessa näytössä. Katso kuva 5.5.

Tietokoneen näyttöön piirretään siis koordinaatiston neljäs neljännes positiivisilla y-koordinaattiarvoilla. Piirto koordinaatison ulkopuolelle on mahdollista, tosin ne eivät silloin näy näytöllä. Esimerkiksi negatiivisilla y-koordinaattiarvoilla piirto tapahtuu näytön yläreunan yläpuolelle. Vastaavasti negatiiviset x-koordinaattipisteet sijaitsevat näytön vasemman reunan vasemmalla puolella. Joissakin pelisovelluksista tällaisesta off-screen alueesta saattaa olla hyötyä.

5.2 Pygame kirjasto

Video: Opening a Window

Pygame kirjaston avulla saadaan grafiikan luonti helpommaksi. Pygame kirjasto on koodikirjasto, jolla saadaan:

Pygame ohjelman alussa tulee huolehtia Pygame kirjaston käyttöönotosta ja pelimoottorin 'initialisoinnista'. Jokaisen Pygame kirjastoa käyttävän ohjelman tulee alkaa näin:

# Import lauseella ladataan funktiot yms. 'pygame'
import pygame
# Alustetaan  pelimoottori eli game engine
pygame.init()

Jos sinulla on Pygame vielä lataamatta, niin tässä kohtaa tuo viimeistään pitää tehdä. Pygame asennusohjeistus oli kappaleessa Ennen kuin aloitamme kappaleessa. Jos koneessa ei ole toimivaa Pygame asennusta tehtynä, niin import pygame lauseesta generoidaan virhe.

Älä nimeä mitään tiedostoa “pygame.py” nimiseksi

Tärkeää: import pygame hakee kirjastoa, joka on nimetty pygame. Jos ohjelmoija luo tiedoston nimellä pygame.py, niin tietokone lataakin tämän tiedoston pygame-kirjaston sijasta. Seurauksena on, että mikään kirjastofunktio ei toimi, kunnes pygame.py virheellisesti nimetty tiedosto on poistettu tai nimetty uudelleen.

5.3 Värit

Seuraavaksi katsomme muuttujia, joiden avulla saamme ohjelmissa värit käyttöön. Tietokoneessa värit määritettään kolmen värin avulla; punainen (red), vihreä (green) ja sininen (blue). Olet varmaan kuullutkin RGB monitoreista? Tästähän tuo lyhenne RGB tulee (Red-Green-Blue). Vanhoissa monitoreissa (kuvaputkinäytöissä) saattoikin läheltä katsoen erottaa nuo kolme väripistettä. Nykyisissä korkearesoluutioisissa näytöissä (ja LCD tekniikassa) tuo tuskin on mahdollista.

Jokaisella kolmella värielementillä (RGB) on lukuarvo väliltä 0 - 255. Tässä lukuarvo nolla tarkoittaa, että kyseistä väriä ei ole ollenkaan ja luku 255 vastaavasti asettaa värin 'täysille'. Värien yhdistäminen on ns. additiivinen, jolloin kaikkien kolmen värin täydellä arvolla (255,255,255) saadaan aikaiseksi valkoinen väri.(Tämä toimii siis eri tavalla kuin mustevärit ja maalit muodostavat värit.)

Pythonissa listat kirjoitetaan joko [hakasulkuihin] tai (kaarisulkeisiin). (Kappaleessa 7 listoja käsitellään tarkemmin sekä selvitämme näiden kahden listatyypin eron.) Listassa yksittäiset luvut erotetaan toisistaan pilkulla. Alla esimerkissä luodaan (lista)muuttujia, joiden arvoksi asetetaan lukulista. Näillä muuttujien (lukulista) arvoilla voidaan myöhemmin määrittää värejä (käytän selvyyden vuoksi käännöksessä englannin kielisiä nimiä) .

# Muutama värimääritys
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)
GREEN    = (   0, 255,   0)
RED      = ( 255,   0,   0)

Miksiköhän tässä muuttujien nimet on kirjoitettu isoilla kirjaimilla? Tähän oli selitys kappaleessa 1. Muuttuja, jonka arvoa ei muuteta, kirjoitetaan isoilla kirjaimilla ja kutsutaan vakioksi. Mustan värin arvoa ei tulla muuttamaan. Käsitellään sitä siis vakiona. Jos loisimmekin värimuuttujan, jonka arvo muuttuu, niin se tulee kirjoittaa pienillä kirjaimilla. Esimerkki tällaisesta muuttujasta voisi olla taivaan väriä ilmaiseva sky_color, jonka väriarvo muuttuu esim. auringon nousun ja laskun mukaan.

Kokeilepa koodata edellä esitetyt muuttujat Pythonin IDLE editorilla ja tulosta värit näkyville. Jos haluat määrittää omia värejä, voit hakea haluamasi RGB-koodin esim. jollakin värinpoiminta ohjelmalla. Netistä löytyy ns. on-line “color picker”ohjelmia, jolla saa määritettyä RGB-koodin. Kuvassa 5.6 yksi 'color picker', joka löytyy osoitteesta:
http://www.colorpicker.com/

fig.colorpicker
Figure 5.6: Color picker

Lisäys: Jotkut colorpickerit käyttävät värikoodeista heksadesimaalilukuesitystä. Voit syöttää parametrilistaan heksaluvun kunhan aloitat sen 0x. Esimerkiksi:

WHITE = (0xFF, 0xFF, 0xFF)

Piirtotoiminnoissa, esim. kaarien piirtämisessä, tulemme tarvitsemaan $\pi$ vakiota. Tässä yhteydessä voimmekin määrittää vakion $\pi$ ja asettaa sille yhdeksän desimaalin tarkkuuden. (Piin arvon saamiseksi voisimme ottaa importtaamalla käyttöön math.pi funktion.)

PI = 3.141592653

5.4 Uuden ikkunan luonti

Tähän asti kaikki ohjelmamme ovat vain tulostaneet tekstiä näytölle. Kaikki tulostus on tullut samaan ikkunaan toisin kuin nykyaikaisissa ohjelmissa, joissa ohjelma käynnistyy omaan ikkunaan. Uuden ikkunan avaaminen on varsin helppoa. Alla on esitettynä tähän tarvittava koodi. Siinä luodaan ikkuna kooltaan 700 pixeliä leveä ja 500 korkea:

size = (700, 500)
screen = pygame.display.set_mode(size)

Tässä käytimme set_mode funktiota? Miksi ei open_window funktiota? Syy tähän on se, että set_mode funktiolla voidaan tehdä paljon muitakin toimintoja kuin vain ikkunan luonti. Siihen voidaan avata koko näytön peli, se ei sisällä otsikkopalkkia eikä valikkoa ja siihen voidaan kohdistaa kaiken kontrollin peliin. Tämä funktio on monimutkaisempi ja useimmat suosivat tätä pelin ikkunoinnissa. Tässä ohitamme kuitenkin kokonäytön pelien yksityiskohtaisen tarkastelun, mutta jos asia kiinnostaa sinua tarkemmin, niin tutustu seuraavaan Pygamen dokumentaatiossa olevaan display komentoon.

Miksi kirjoitetaan size = (700, 500) eikä size = 700, 500? Samasta syystä kuin värin määrittämisessäkin, kirjoitetaan lukuarvot kaarisulkeisiin, listana. Pythonissa ei voida tallentaa kahta lukua yhteen muuttujaan, mutta lukulista voidaan. (Tarkentaen listaa: Kaarisulkeiden sisään pilkuin erotettuja listoja kutsutaan Pythonissa tuple nimiseksi (suom. monikko). tai mutatoitumaton lista. Listan alkiot kirjoitetaan hakasulkeiden sisään. Käytetään yksinkertaisuuden vuoksi kaarisulkeissakin olevasta listasta lista nimitystä. Listoista puhutaan tarkemmin kappaleessa 7.

Voit asettaa ikkunan otsikkoriville otsikkotekstin seuraavan koodin mukaisesti:

pygame.display.set_caption("Professor Craven's Cool Game")

5.5 Käyttäjän ja pelin vuorovaikutus

Tähän asti tehty koodi ei tee muuta kuin avaa ikkunan, lisää otsikon ja jättää ikkunan auki. Käyttäjä ei voi tehdä mitään, edes sulkea ikkunaa. Kaikki muu pitääkin sitten koodata. Ja koodaaminen pitää laittaa 'ikuiseen' silmukkaan niin, että käyttäjä voi sulkea ohjelman “exit” komennolla.

Tämä osa pelin ohjelmoimisesta onkin sitten se vaikein osa. Tämä selviää sinulle opiskelun edetessä pikku hiljaa, eikä sitä tarvitse hallita heti kokonaan, kunhan idea sen toiminnasta aukeaa sinulle.

# Loopataan, kunnes käyttäjä klikkaa sulje-painiketta.
done = False

# Näytön päivityksen asetus
clock = pygame.time.Clock()

# -------- Pääohjelman looppi, silmukka -----------
while not done:
    # --- Main event loop
    for event in pygame.event.get(): # käyttäjä teki jonkin tapahtuman
        if event.type == pygame.QUIT: # jos klikattiin close
            done = True # tällä asetuksella looppi lopetetaan

    # --- pelin logiikka ohjelmoidaan tähän

    # --- piirtokomennot tulevat tähän

    # Ensinnä, 'tyhjätään' näyttö valkoiseksi. Älä koodaa muita piirtokomentoja tämän yläpuolelle.
    # tai ne kumoutuvat tällä komennolla.
    screen.fill(WHITE)

    # --- Jatketaan päivittämällä näyttö sillä, mitä juuri piirrettiin. 
    pygame.display.flip()

    # --- Asetetaan näytön päivitys 60 framea sekunnissa.
    clock.tick(60)

Ohjelmaan pitäisi koodata näppis- ja hiiritoiminnot riville 9 kommentin alapuolelle ' main event looppiin'. Ammusten koodit, objektien liikkeet yms. koodataan pelin logiikka -osaan riviltä 14 alkaen. Piirto ja näytön päivitys koodataan riville 20. Näihin koodauksiin palaamme myöhemmin.

5.5.1 Tapahtumasilmukka, Event Loop

Pidä tapahtumasilmukka loogisena.

Huomio! Yksi turhauttavimmista ohjelmointivirheistä on se, että ohjelmoija on sekoittanut tapahtumasilmukkaan jotakin ohjelman osia, jotka eivät siihen kuulu. Tapahtumasilmukkaan saa koodata vain ohjelmassa käsiteltäviä tapahtumia; näppäinlyönnit, hiiren klikkaukset ja muita tapahtumia. Esimerkki tapahtumasilmkasta voisi olla seuraavanlainen:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("Käyttäjä haluaa lopettaa.")
        elif event.type == pygame.KEYDOWN:
            print("Käyttäjä painoi näppäintä.")
        elif event.type == pygame.KEYUP:
            print("Käyttäjä päästi näppäimen ylös.")
        elif event.type == pygame.MOUSEBUTTONDOWN:
            print("Käyttäjä klikkasi hiirtä.")

Tapahtumat (kuten näppäinpainallukset) ovat kaikki yhtenäisenä osana. Ohjelmassa on for silmukka, jossa kaikki tapahtumat ovat. Ketjutetuilla if lauseilla testataan, mikä tapahtuma aktivoitui ja if-lohkon koodissa käsitellään tämä tapahtuma.

Kaikkien if lauseiden tulee olla yhdessä ja samassa for silmukassa. Varsin yleinen virhe kopioitaessa ja liitettäessä koodia toisesta tiedostosta on se, että koodiin kopioituu huomaamatta useita tapahtumasilmukoita.

    # Here is one event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("Käyttäjä haluaa lopettaa.")
        elif event.type == pygame.KEYDOWN:
            print("Käyttäjä painoi näppäintä.")
        elif event.type == pygame.KEYUP:
            print("Käyttäjä päästi näppäimen ylös.")

    # Tähän kohtaan ohjelmoija on kopioinut koodia ja 
    # lisännyt toisen tapahtumaloopin. Tapahtumalooppi
    # prosessoidaan kuitenkin aiemmin ja nämä jäävät huomiotta
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("Käyttäjä haluaa lopettaa.")
        elif event.type == pygame.MOUSEBUTTONDOWN:
            print("Käyttäjä klikkasi hiirtä")

Koodissa rivillä 2 for silmukka sieppaa tapahtumat. Rivin 13 for silmukka ei käsittele mitään tapahtumaa, koska ne käsitellään aiemmassa tapahtumasilmukassa.

Toinen tyyppivirhe on aloittaa piirtotapahtumat ja sen jälkeen yrittää lopettaa tapahtumasilmukka:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            print("Käyttäjä haluaa lopettaa.")
        elif event.type == pygame.KEYDOWN:
            print("Käyttäjä painoi näppäintä.")


    pygame.draw.rect(screen, GREEN, [50,50,100,100])

    # Tässä on tapahtumasilmukan koodia, mutta se ei ole 
    # 'for' simukassa, jossa tapahtumat käsitellään. Ei siis toimi.
    if event.type == pygame.KEYUP:
        print("Käyttäjä päästi näppäimen ylös.")
    elif event.type == pygame.MOUSEBUTTONDOWN:
        print("Käyttäjä klikkasi hiirtä")

Tämän virheen seurauksena on, että jotkin näppäimistö ja hiiritapahtumat jätetään käsittelemättä. Miksi? for silmukka käsittelee kaikki tapahtumat listana. Jos siis esimerkiksi näppäillään kahta näppäintä, for silmukassa käsitellään molemmat näppäintapahtumat. Yllä olevassa esimerkissä if lauseet eivät ole for silmukan sisällä. Jos tapahtumia on useita, if lauseessa käsitellään vain viimeinen tapahtuma ja muut jäävät huomiotta.

5.5.2 Framen suoritus

Peruslogiikka ja suoritusjärjestys pelin 'framen' eli pelikehyksen suorituksessa on:

Tällä tavalla ohjelmasta tulee selkeä ja helppolukuinen. Älä siis tee koodia, jossa välillä lasketaan, sitten piirretään ja taas lasketaan vaan ryhmitä toimet järjestelmällisesti omiin 'käsittelyosiin'. Huomaat, kuinka ohjelma muistuttaa kappaleessa 1 esitettyä laskinohjelmaa. Lue käyttäjän syöte, suorita laskut ja tulosta saadut tulokset. Tässä siis käytetään samaa esitystapaa.

Kuvan piirtämisen suorittava koodi on while-silmukan sisällä. Päivitysajastimen arvoasetuksella 10, päivitetään näyttö 10 kertaa sekunnissa. Jos ajastimen arvo asetetaan liian suureksi, alkaa kone takkuamaan, koska kaikki prosessorin suoritusaika menee näytön päivittämiseen. Jos koodissa ei olisi silmukkkaa lainkaan, niin kuvaa ei päivitettäisi ollenkaan. Jos taasen piirto olisi silmukan ulkopuolella, ensimmäisessä framessa piirretty kuva jäisi näyttöön eikä ikkunan sulkemisen jälkeen kuvaa näkyisi enää ikkunassa. Samoin kävisi, jos jokin toinen ikkuna avattaisiin ja suljettaisiin peli-ikkunan päällä.

5.6 Ohjelman suorituksen lopettaminen

IDLE editorissa tämän Pygame ohjelman sulkeminen ikkunan sulje-painiketta klikkaamalla aiheuttaa ohjelman jumittumisen, vielä toistaiseksi. Tästä seuraa epäselvää häslinkiä, koska klikkailuja pitää tehdä paljon, jotta 'krässännyt' ohjelma saadaan suljettua

Ongelma aiheutuu siitä, että silmukasta poistuttaessa ohjelmassa ei ole koodattuna ikkunan sulkemistoimintoa. Koodaamalla alla olevan esimerkin mukaisesti quit-funktio(komento), saadaan avoin ikkuna suljettua hallitusti.

pygame.quit()

5.7 Näytön tyhjennys

Seuraavalla koodilla saadaan tyhjennettyä kaikki ikkunasta niin, että jäljelle jää valkoinen tausta. Muuttuja (vakio) WHITE olikin jo määritetty edellä asettamalla arvoksi kolmen luvun lista RGB arvoista.

# Tyhjennetään näyttö ja tausta valkoiseksi
screen.fill(WHITE)
Video: Drawing Lines

Näytön tyhjentäminen tulee tehdä ennen kuin näytölle kohdistetaan uusia piirtokomentoja. Jos tyhjennys tehdään piirtojen jälkeen saadaan piirtotuloksena tyhjä ikkuna.

Kun ikkuna luodaan ensimmäistä kertaa, siinä on musta tausta. Näytön tyhjennys on tärkeä toimenpide, koska muutoin ei ohjelman suorituksessa voida varmistaa, että ikkuna on tyhjä.

5.8 Näytön kuvaflippi

Erittäin tärkeä! Piirtokomentojen jälkeen täytyy tehdä 'kuvaflippi' eli eräänlainen 'kertapiirto' näytölle. Ilman tätä flippausta piirtokomennot aiheuttavat näytön välkkymistä. Tällä komennolla saadaan viivytettyä piirtoa siten, että kuva näytetään ikkunassa vasta, kunnes ohjelma on saanut piirtämisen valmiiksi. Alla oleva komento(funktio) tekee kuvaflipin näytölle.

Tämän komennon virheellinnen sijoittelu ohjelmaan saa aikaan vain tyhjän näytön. Tämän komennon jälkeen kirjoitetut piirtotoiminnot jäävät suorittamatta.

# Jatketaan flippaamalla näytölle kaikki piirrokset.
pygame.display.flip()

5.9 Avataan tyhjä ikkuna

Seuraavaksi yhdistämme kaiken edellä käsitellyn yhteen ja samaan ohjelmaan. Tätä koodia voit käyttää pohjana uusille Pygame ohjelmillesi. Ohjelma avaa uuden tyhjän ikkunan ja jää odottamaan käyttäjän sulkemiskomentoa.

Kurssin websivuilla kohdassa “Examples” löytyy kohta (linkki) “graphics examples” ja siitä löydät tänmän tiedoston... pygame_base_template.py.

"""
 Pygame base template for opening a window

 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

 Explanation video: http://youtu.be/vRB_983kUMc
"""

import pygame

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

pygame.init()

# Set the width and height of the screen [width, height]
size = (700, 500)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("My Game")

# Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# -------- Main Program Loop -----------
while not done:
    # --- Main event loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    # --- Game logic should go here

    # --- Screen-clearing code goes here

    # Here, we clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.

    # If you want a background image, replace this clear with blit'ing the
    # background image.
    screen.fill(WHITE)

    # --- Drawing code should go here

    # --- Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # --- Limit to 60 frames per second
    clock.tick(60)

# Close the window and quit.
pygame.quit()

5.10 Tutustutaan piirtotoimintoihin

fig.castle

Oheisesta linkista aukeaa Pygame-dokumentaatio, jossa on listattuna erilaisia piirtokomentoja:
http://www.pygame.org/docs/ref/draw.html
Ohjelmassa voidaan piirtää mm. suorakulmioita (rectangles), monikulmioita (polygons), ympyröitä (circles), ellipsejä (ellipses), kaaria (arcs) ja suoria (lines). Katsotaan myös, miten voidaan piirtää tekstiä. Bitmap-grafiikkaa (kuvat) käsitellään kappaleessa 11. Pygame referensseissä piirtofunktioiden kuvaukset on esitetty oheisella tavalla:

pygame.draw.rect(Surface, color, Rect, width=0): return Rect

Toistuvasti sekaannusta aiheuttaa parametrilistassa oleva width=0. Tässä parametrin merkitys on se, jos width-arvoa ei anneta, niin oletuksena se on nolla. Eli, funktion kutsu:

pygame.draw.rect(screen, RED, [55, 500, 10, 5])

...on samanlainen kuin kutsu:

pygame.draw.rect(screen, RED, [55, 500, 10, 5], 0)

Lauseen lopussa : return Rect tarkoittaa, että funktio palauttaa syötettyjen parametrien mukaisen suorakulmion. Voit jättää tämän huomiotta.

Komennon kopioinnissa tulee helposti tehtyä virhe, jossa parametrin arvo width=0 ei sellaisenaan kelpaa.

# Tässä generoituu varsin monimutkainen ja vaikeaselkoinen virhe
# width -arvoksi laitetaan pelkkä luku.
pygame.draw.rect(screen, RED, [55, 500, 10, 5], width=0)

5.11 Suorien piirtäminen

Alla on koodiesimerkki suoran piirtämiseksi. Siinä piirretään suora kahden pisteen väliin. Alkupiste (0, 0) ja loppupiste (100, 100) 5 pixelin levyisenä. GREEN (vakio)muuttujahan määriteltiinkin jo aiemmin kolmen luvun listan avulla RGB arvona.

# Piirretään suora pisteestä (0, 0) pisteeseen (100, 100)
# paksuus 5 pixeliä.
pygame.draw.line(screen, GREEN, [0, 0], [100, 100], 5)

Nyt voisit lisätä tämän viivanpiirtokoodin edellä esitettyyn kooditemplateen. Ole huolellinen, että lisäät piirtokoodin oikeaan kohtaan koodia. Kommentista näet kohdan, johon piirto lisätään. Kokeile piirtää suoria eripaksuisina, erivärisinä ja eri paikkoihin.

5.12 Silmukan hyödyntäminen suoriten piirtämisessä

Ohjelma saadaan suorittamaan komentoja loputtomasti. Seuraava koodiesimerkki piirtää suoria käyttäen silmukkaa. Tällä tekniikalla voidaan piirtää viivojen avulla vaikkapa kokonainen auto.

Asettamalla piirtokoodi silmukan sisälle, saadaan 'loopattua' suorien piirto. Tässä on kuitenkin huomioitava, että kunkin suoran alku- ja loppupisteet ovat samat, jolloin suoria näytöllä näkyy vain yksi. Suorat ovat siis päällekkäin samassa kohtaa.

Asia saadaan korjattua muuttamalla koordinaattipisteiden arvoja (ns. offset) jokaisella silmukkakierroksella. Ensimmäisellä toistokierroksella muuttujan y_offset arvo on nolla. Eli viivan alupiste on (0,10) ja loppupiste (100, 110). Silmukassa kasvatetaan muuttujan y_offsetarvoa 10:llä. Tästä seuraa, että seuraavan piirrettävän suoran koordinaatiti ovat (0, 20) ja (100, 120). Koordinaattipisteiden 'shiftausta' jatketaan koko silmukan suorituksen ajan ja näin suorat piirretään eri kohtiin.

# Piirretään useita suoria pisteestä (0, 10) pisteeseen (100, 110)
# 5 pixeliä leveänä, while silmukassa
y_offset = 0
while y_offset < 100:
    pygame.draw.line(screen,RED,[0,10+y_offset],[100,110+y_offset],5)
    y_offset = y_offset + 10

Samantapainen koodi voitaisiin koodata myös for silmukkaa käyttäen:

# Piirretään useita suoria pisteestä (0, 10) pisteeseen (100, 110)
# 5 pixeliä leveänä, for silmukassa
for y_offset in range(0, 100, 10):
    pygame.draw.line(screen,RED,[0,10+y_offset],[100,110+y_offset],5)

Koodaa ja aja tällainen viivanpiirto-ohjelma. Vaihda offset arvoa. Kokeile erilaisia arvoja ja varmista, että ymmärrät koodin toiminnan.

Tässä on monimutkaisempi esimerkki koodista, jolla saadaan aikaiseksi sini- ja kosinifunktioiden kuvaajaa. Katso kuva 5.7.

for i in range(200):

	radians_x = i / 20
	radians_y = i / 6

	x = int( 75 * math.sin(radians_x)) + 200
	y = int( 75 * math.cos(radians_y)) + 200

	pygame.draw.line(screen, BLACK, [x,y], [x+5,y], 5)
fig.complex_offsets
Figure 5.7: Complex Offsets

for silmukassa voidaan piirtää moninaisia kuvioita, kuten seuraavassa esimerkissä useita X:iä. Katso kuva 5.8.

    for x_offset in range(30, 300, 30):
        pygame.draw.line(screen,BLACK,[x_offset,100],[x_offset-10,90],2)
        pygame.draw.line(screen,BLACK,[x_offset,90],[x_offset-10,100],2)
fig.multiple_x
Figure 5.8: Multiple X's

5.13 Suorakulmion piirtäminen

Video: How to Draw Rectangles and Ellipses

Suorakulmion paikan määrittämiseksi tarvitaan vasemman yläkulman koordinaatit sekä leveys ja korkeus.

Kuvassa 5.9 esitetään suorakulmio (ja ellipsi, joka selvitetään myöhemmin), jonka vasen yläkulma on pisteessä (20, 20), a leveys 250 ja korkeus 100. Suorakulmion piirtämiseen tarvitaan siis neljän luvun parametrilista (x, y, width, height).

fig.ellipse
Figure 5.9: Drawing an Ellipse

Seuraavassa koodiesimerkissä piirretään tuollainen suorakulmio. Suorakulmion vasen ylänurkka on pisteessä (20, 20). Leveys on 250 pixeliä ja korkeus 100 pixeliä.

Luku 2 viimeisenä parametrina määrää viivan leveydeksi 2 pixeliä. Mitä suurempi tämä luku on, sitä paksumpi viiva piirretään. Arvolla 0 reunaviivaa ei piirretä, mutta suorakulmio täytetään asetetulla täyttövärillä.

# Piirretään suorakulmio
pygame.draw.rect(screen,BLACK,[20,20,250,100],2)

5.14 Ellipsin piirtäminen

Ellipsi piirretään samalla periaattella kuin suorakulmio. Parametreina annetaan ellipsin piirtorajat ja ellipsi piirretään näiden rajojen sisään.

Virheellisesti voisi ajatella, että alkupiste tarkoittaisi ellipsin keskipistettä. Näin ei kuitenkaan ole, vaan alkupiste on sellaisen suorakulmion vasemman yläkulman piste, johon ellipsi mahtuu sisään.

Katsopa vielä kuvaa 5.9 , jossa ellipsi oli piirrettynä 250 pixeliä leveänä ja 100 pixeliä korkeana. Kuvassahan on piirretty molemmat, sekä suorakulmio ja ellipsi, havainnollistamaan piirrosten ideaa.

#Piirretään ellipse suorakulmion rajoilla
pygame.draw.ellipse(screen, BLACK, [20,20,250,100], 2)

5.15 Kaaren piirtäminen

Video: How to Draw Arcs

Miten saisimme piirrettyä vain osan ellipsistä? Tuo onnistuu käyttämällä arc komentoa (funktiota). Tämä on samanlainen kuin ellipse komento, mutta parametriksi annetaan alkupiste ja kaaren piirtokulma. Kulma annetaan radiaaneina.

fig.arc
Figure 5.10: Kaaret

Koodiesimerkissä piirretään neljä eriväristä kaarta, joista muodostuu ellipsin neljännekset. Tuoksen näet kuvassa 5.10.

# Piirretään neljä kaarta osana ellipsiä. Kaaren kulmat annetaan radiaaneina
pygame.draw.arc(screen, GREEN, [100,100,250,200],  PI/2,     PI, 2)
pygame.draw.arc(screen, BLACK, [100,100,250,200],     0,   PI/2, 2)
pygame.draw.arc(screen, RED,   [100,100,250,200],3*PI/2,   2*PI, 2)
pygame.draw.arc(screen, BLUE,  [100,100,250,200],    PI, 3*PI/2, 2)

5.16 Monikulmion piirtäminen

Video: How to Draw Polygons

Seuraavassa koodissa piirretään monikulmio. Kolmikulmion piirtämiseen tarvitaan kolme pistettä: (100, 100) (0, 200) ja (200, 200). Kulmapisteitä voidaan listata niin monta kuin tarvitaan, kulmien määrän verran. Jokainen kulmapiste muodostuu kahdesta luvusta, x- ja y-koordinaatista. Pisteiden väiin piirretään jana ja näin muodostuu monikulmio. Katso kuva 5.11.

fig.triangle
Figure 5.11: Polygon
# Piirretään kolmio polygon komennolla
pygame.draw.polygon(screen, BLACK, [[100,100], [0,200], [200,200]], 5)

5.17 Tekstin piirtäminen

Video: How to Draw Text

Tekstin luominen on hieman monimutkaisempaa. Siinä on kolme asiaa, jotka pitää määrittää. Ensinnäkin tarvitaan muuttuja, johon tallennetaan tieto käytetystä kirjasimesta (fontista) ja fonttikoosta.

Toisekseen ohjelmassa tehdään tekstistä kuva. Tämän voisi ajatella eräänlaisena leimasimena, jossa on piirrokseen lisättävät kirjaimet.

Kolmantena asiana on määritettävä tekstin paikka näytöllä.

Esimerkiksi näin:

# Valitaan käytettävä fontti, koko,lihavointi, kursivointi
font = pygame.font.SysFont('Calibri', 25, True, False)

# Alla renderöidään teksti. "True" tarkoittaa anti-aliased tekstiä.
# Black asettaa mustan värin. Muuttuja BLACK asetettiin
# aaiemmin listana [0, 0, 0]
# Huomaa: Tämä rivi luo kuvan kirjaimista,
# mutta ei näytä niitä näytöllä vielä.
text = font.render("My text",True,BLACK)

# blit-funktiolla lisätään text-'leima' näytölle kohtaan 250x250
screen.blit(text, [250, 250])

Miten saisi pelin 'score'n näkyviin? Se onkin sitten astetta hankalampaa. Seuraava koodi EI toimi:

text = font.render("Score: ", score, True, BLACK)

Miksi ei toimi? font.render funktiolla ei ole mahdollista asettaa 'ylimääräisiä' tulostusparametreja, kuten print lauseessa oli mahdollista. Ainoastaan yksi merkkijonomuuttuja on mahdollista lisätä tähän komentoon. Siksi siis lisäämmekin “Score: ”sanan alkuperäiseen merkkijonoon. No, eipä tämäkään toimi:

text = font.render("Score: " + score, True, BLACK)

Jos score on kokonaislukumuuttuja, kone ei osaa lisätä tätä merkkijonoon. Sinun pitää siis konvertoida (muuntaa) kokonaisluku merkkijonoksi. Sen jälkeen voimme yhdistää merkkijonot yhteen näin:

text = font.render("Score: " + str(score), True, BLACK)

Nyt osaatkin lisätä score-muuttujan näytölle. Jos haluaisit tulostaa ajastimen eli timerin niin tähän palaamme myöhemmin. Voit tutustua asiaan Katsomalla esimerkkikoodin timer.py ohjelmasta:
ProgramArcadeGames.com/python_examples/f.php?file=timer.py

5.18 Toimivan ohjelman listaus

Tämän kappaleen lopuksi katsomme vielä kokonaista ohjelmalistausta, jossa kerrataan kappaleessa käsiteltyjä asioita. Voit ladata tämän ohjelman seuraavasta linkistä:
ProgramArcadeGames.com/index.php?chapter=example_code

fig.program_result
Figure 5.12: Result of example program
"""
 Simple graphics demo

 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

"""

# Import a library of functions called 'pygame'
import pygame

# Initialize the game engine
pygame.init()

# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

PI = 3.141592653

# Set the height and width of the screen
size = (400, 500)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Professor Craven's Cool Game")

# Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()

# Loop as long as done == False
while not done:

    for event in pygame.event.get():  # User did something
        if event.type == pygame.QUIT:  # If user clicked close
            done = True  # Flag that we are done so we exit this loop

    # All drawing code happens after the for loop and but
    # inside the main while not done loop.

    # Clear the screen and set the screen background
    screen.fill(WHITE)

    # Draw on the screen a line from (0,0) to (100,100)
    # 5 pixels wide.
    pygame.draw.line(screen, GREEN, [0, 0], [100, 100], 5)

    # Draw on the screen several lines from (0,10) to (100,110)
    # 5 pixels wide using a loop
    for y_offset in range(0, 100, 10):
        pygame.draw.line(screen, RED, [0, 10 + y_offset], [100, 110 + y_offset], 5)


    # Draw a rectangle
    pygame.draw.rect(screen, BLACK, [20, 20, 250, 100], 2)

    # Draw an ellipse, using a rectangle as the outside boundaries
    pygame.draw.ellipse(screen, BLACK, [20, 20, 250, 100], 2)

    # Draw an arc as part of an ellipse.
    # Use radians to determine what angle to draw.
    pygame.draw.arc(screen, BLACK, [20, 220, 250, 200], 0, PI / 2, 2)
    pygame.draw.arc(screen, GREEN, [20, 220, 250, 200], PI / 2, PI, 2)
    pygame.draw.arc(screen, BLUE, [20, 220, 250, 200], PI, 3 * PI / 2, 2)
    pygame.draw.arc(screen, RED, [20, 220, 250, 200], 3 * PI / 2, 2 * PI, 2)

    # This draws a triangle using the polygon command
    pygame.draw.polygon(screen, BLACK, [[100, 100], [0, 200], [200, 200]], 5)

    # Select the font to use, size, bold, italics
    font = pygame.font.SysFont('Calibri', 25, True, False)

    # Render the text. "True" means anti-aliased text.
    # Black is the color. This creates an image of the
    # letters, but does not put it on the screen
    text = font.render("My text", True, BLACK)

    # Put the image of the text on the screen at 250x250
    screen.blit(text, [250, 250])

    # Go ahead and update the screen with what we've drawn.
    # This MUST happen after all the other drawing commands.
    pygame.display.flip()

    # This limits the while loop to a max of 60 times per second.
    # Leave this out and we will use all CPU we can.
    clock.tick(60)

# Be IDLE friendly
pygame.quit()

5.19 Review

5.19.1 Multiple Choice Quiz

Klikkaa tästä monivalintatehtävään.

5.19.2 Short Answer Worksheet

Klikkaa tästä kappaleen kertaustehtäviin.

5.19.3 Lab

Klikkaa tästä ohjelmointitehtäviin.


You are not logged in. Log in here and track your progress.