Arcade-pelien ohjelmointi
Pythonilla ja PygamellaChapter 5: Johdanto tietokonegrafiikkaan
Nyt, kun hallitset toistolauseiden (silmukoiden) ohjelmoimisen, on aika tutustua tietokonegrafiikkaan. Tässä kappaleessa käsitellään:
- Kuinka tietokoneessa käsitellään x, y koordinaatistoa. Se ei ole aivan samanlaista kuin olet matematiikassa oppinut.
- Miten värit määritetään? Miljoonista valittavista värivaihtoehdoista oikean valitsemassa on omat 'temppunsa'.
- Miten saan avattua tyhjän ikkunan grafiikan piirtoa varten? Tähän tarvitaan canvas.
- Miten piirretään viivoja, suorakulmioita, monikulmioita, kaaria, ellipsejä, yms?
5.1 Koordinaatisto tietokoneessa
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.
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.
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.
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.
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.
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
Pygame kirjaston avulla saadaan grafiikan luonti helpommaksi. Pygame kirjasto on koodikirjasto, jolla saadaan:
- Piirrettyä graafisia objekteja
- Näytettyä bitmap-kuvia (bittikartta)
- Animoitua
- Voidaan käyttää näppäimistö-, hiiri- ja peliohjainkontrolleja
- Toistettua ääniä ja tehosteita
- Tunnistettua objektien tapahtumia
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.
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/
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
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:
- Kunnes ei lopeteta:
- Käy läpi kaikki tapahtumat for-silmukassa (näppäin, hiiri, jne.):
- Ketjuta if lauseet ja käsittele kaikki tapahtumat.
- Suorita objektien liikkeiden, törmäystapahtumien yms. laskennat.
- Tyhjennä näyttö
- Piirrä näytölle päivitetty pelitilanne, piirrokset yms.
- Käy läpi kaikki tapahtumat for-silmukassa (näppäin, hiiri, jne.):
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)
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
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)
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)
5.13 Suorakulmion piirtäminen
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).
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
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.
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
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.
# Piirretään kolmio polygon komennolla pygame.draw.polygon(screen, BLACK, [[100,100], [0,200], [200,200]], 5)
5.17 Tekstin piirtäminen
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
""" 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.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin