Sisällysluettelo

Pe­le­jä

Har­joi­tel­laan peli­oh­jel­moin­tia pythonilla ja opi­taan sa­mal­la ma­te­ma­tii­kan ja New­to­nin me­ka­nii­kan so­vel­ta­mis­ta


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli (edit: 2019-2-3)

Muu­ta­ma python-kielinen video­peli

Muu­ta­ma python-kielinen video­peli, joi­den oh­jel­moin­ti ja pe­laa­mi­nen opet­taa ma­te­ma­tiik­kaa ja New­to­nin me­ka­niik­kaa.

Pa­ran­te­lin ja ko­ko­sin yh­teen pa­ket­tiin viime vuo­si­na oh­jel­moi­mia­ni pe­le­jä. Pe­li­ni eivät ole juo­nel­li­sia seik­kai­lu­pe­le­jä hie­noi­ne mai­se­mi­neen vaan inter­ak­tii­vi­sia yksin­ker­tais­ten jär­jes­tel­mien si­mu­laat­to­rei­ta. Pe­rim­mäi­nen tar­koi­tuk­se­ni oli kek­siä in­nos­ta­via oh­jel­moin­ti­har­joi­tuk­sia, jotka opet­ta­vat niin oh­jel­moin­tia kuin ma­te­ma­tiik­kaa ja fy­siik­kaa­kin.

Tuo­tok­se­ni eivät sel­lai­se­naan kel­van­ne ope­tus­ma­te­riaa­lik­si kuin innnokkaalle har­ras­ta­jal­le.

En­sim­mäi­sen pe­leis­tä ke­hi­tin tieto­tek­nii­kan ope­tus­har­joit­te­lus­sa. Idea­na oli tar­jo­ta video­pelin runko, jota vai­heit­tain täy­den­tä­mäl­lä syn­tyy toi­mi­va peli. Minun mie­les­tä­ni oh­jel­moin­nin al­keis­ope­tuk­ses­sa kan­nat­taa antaa opis­ke­li­joil­le val­mis pohja, jota läh­de­tään ke­hit­te­le­mään eteen­päin. En mi­nä­kään kos­kaan aloi­ta tyh­jäs­tä vaan jos­tain van­has­ta poh­jas­ta. Voi se­lit­tää, mitä kaik­kea poh­jas­sa on, mutta ei sitä aluk­si tar­vit­se ym­mär­tää.

Jos mal­lik­si va­lit­tu oh­jel­ma piir­tää näy­töl­le ym­py­rän, voi aluk­si sel­vit­tää, mistä se saa vä­rin­sä ja ko­keil­la muut­taa väriä. Sit­ten voi ko­keil­la siir­tää ym­py­rän eri paik­kaan. Sen jäl­keen pi­täi­si saada ym­py­rä vael­te­le­maan näy­töl­le ja ehkä vaih­te­le­maan väriä omia ai­ko­jaan. Miten saada ai­kaan lii­ket­tä? Sen kek­si­mi­seen tar­vin­nee val­miin esi­mer­kin, ka­ve­rin tai opet­ta­jan. Oleel­lis­ta on pääs­tä heti ko­kei­le­maan ja nähdä tulos näy­töl­lä.

On ole­mas­sa peli­moot­to­rei­ta — pe­lien oh­jel­moin­ti­ym­pä­ris­tö­jä, joita so­vel­taes­sa ei tar­vit­se ym­mär­tää, miten liike saa­daan ai­kai­sek­si. Minä käy­tän pygame-kirjastoa, joka ei pii­lo­ta peli­sil­muk­kaa, jolla liike saa­daan ai­kai­sek­si. Oh­jel­moin­nin op­pi­mi­sen kan­nal­ta se on vä­hin­tään­kin ok, peli­oh­jel­moin­nin ym­mär­tä­mi­sen kan­nal­ta jopa hy­väk­si. Aikaa toki kan­nat­taa va­ra­ta sen tut­ki­mi­seen, miten peli­sil­muk­ka toi­mii.

Kun pyö­ry­lä on saatu liik­ku­maan näy­töl­lä omia ai­ko­jaan, seu­raa­vak­si pitää sel­vit­tää, miten käyt­tä­jä voisi sitä oh­ja­ta. Kun sekin on­nis­tuu, al­ka­nee syn­tyä ideoi­ta siitä, mitä kaik­kea voi tehdä. Moni­mut­kai­sem­pien ideoi­den to­teut­ta­mi­nen käy han­ka­lak­si, ellei pe­reh­dy funk­tioi­hin, olioi­hin ja tieto­ra­ken­tei­siin. Niis­tä kai­kis­ta näis­sä oh­jel­mis­sa on esi­merk­ke­jä. Niitä on mu­ka­va opet­taa, jos opis­ke­li­jat voi­vat saman tien ko­keil­la niitä pelin kal­tai­ses­sa so­vel­luk­ses­sa. Tar­koi­tus käy sel­väk­si, kun näkee, miten pal­jon niis­tä on hyö­tyä. Hyvää oh­jel­moin­ti­tyy­liä­kin on hel­pom­pi opet­taa, kun voi kom­men­toi­da opis­ke­li­joi­den oh­jel­mia. (Vaik­ka pa­ran­te­lin­kin koo­dia­ni, eivät oh­jel­ma­ni kel­paa esi­mer­kik­si erin­omai­ses­ta koo­dis­ta.)

Pygame-kirjastosta käy­tän muu­ta­mia perus­toi­min­to­ja. Ken in­nos­tuu peli­oh­jel­moin­nis­ta, jak­sa­nee kai­vaa do­ku­men­taa­tios­ta tie­toa it­sel­leen hyö­dyl­li­sis­tä funk­tios­ta, vaik­ka vähän rank­kaa se on. Olen­nai­nen osa oh­jel­moin­tia on jat­ku­va tarve ope­tel­la uutta.

En­sim­mäi­nen pe­leis­tä — hyt­tys­jahti — on harjoitus luoda liikkuvia olioita — satunnaisesti liikkuvia ja pelaajan liikuttamia. Saattaisi toimia yläasteella.

Mörs­sä­ri on peli, jonka ohjelmointi edellyttää Newtonin mekaniikan soveltamista ja differentiaaliyhtälöiden numeerista ratkaisemista. Jälkimmäiseen toki annetaan valmis algoritmi, joten opittavaksi jää algoritmin soveltaminen ja ratkaisun ihasteleminen. Saattaisi toimia yläasteella ja lukiossa. Differentiaaliyhtälöt eivät kuulune opetussuunnitelmaan, mutta ovat olennainen osa monia videopelejä, sään ennustamista, ilmastonmuutoksen ennakointia, insinööritieteitä ja melkein kaikkea muuta olennaista. Johan mekaniikan peruslakikin — F = ma — on differentiaaliyhtälö.

Kuu­alus opettaa soveltamaan Newtonin mekaniikkaa ja gravitaatiolakia. Lisäksi pelin myötä saa harjoitusta avaruuslennon perusteista.

Silta­nos­tu­ri:n ohjailemista on hauska kokeilla. Siltanosturi on yksinkertaisen oloinen mekaaninen järjestelmä, jonka tilayhtälöiden johtaminen osoittautui niin vaikeaksi, että jouduin opiskelemaan lisää yliopistotasoista matematiikkaa.

pe­lien uu­sim­mat ver­siot zip-arkistona

Näi­den pe­lien ke­hit­tä­mis­tä esit­te­len yk­si­tyis­koh­tai­ses­ti seu­raa­vis­sa lu­vuis­sa. Esit­te­len, miten moni­mut­kai­sen­kin oh­jel­man voi tehdä vaihe vai­heel­ta. Kai­ken opet­te­le­mi­nen alus­ta saak­ka on hi­das­ta, joten ko­pioi­daan poh­jak­si oh­jel­ma, joka piir­tää näy­töl­le ym­py­rän ja läh­de­tään ke­hit­te­le­mään sitä.

(Oh­jel­man ke­hit­te­ly ei ole pel­käs­tään uuden koo­din li­sää­mis­tä vaan työn ede­tes­sä oppii uutta, mikä 'pa­kot­taa' pa­ran­te­le­maan aiem­paa koo­dia. Jat­kos­sa pää­see vä­hem­mäl­le, kun pe­rus­ta on tehty kun­nol­la ja tyy­lik­kääs­ti. Tämä teki vai­heit­tain ete­ne­mi­sen niin työ­lääk­si do­ku­men­toi­da, että au­to­ma­ti­soin do­ku­men­toin­nin. Mi­nul­la ei ole viit­tä eri oh­jel­maa vii­des­tä eri työ­vai­hees­ta, vaan yksi oh­jel­ma, johon on mer­kit­ty, missä ke­hi­tyk­sen vai­hees­sa mikin osa on sii­hen li­sät­ty. Kun oh­jel­ma on 'val­mis' jaan sen te­ke­mäl­lä­ni oh­jel­mal­la vii­dek­si eri oh­jel­mak­si työ­vai­hei­den mu­kaan. Toi­mii muu­ten hyvin, mutta tuot­taa liian pal­jon teks­tiä.)


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa (edit: 2015-04-25)

Python hyt­tys­jah­dis­sa

Seu­raa­vas­sa oh­jel­moi­daan vaihe vai­heel­ta Python-kielisiä peliohjelmia. Harjoitusten tekemistä helpottaa edelläesitetyn scratch-harjoituksen tekeminen.

Nämä har­joi­tuk­set te­ke­mäl­lä ei vält­tä­mät­tä opi kaik­kia oh­jel­moin­nin sa­lo­ja, mutta har­joi­tuk­sen oh­jel­mis­sa hyö­dyn­ne­tään useim­pia Python oh­jel­moin­nin perus­ra­ken­tei­ta niin, että harjoitusten tekemisen myötä voi tutustua syvemmin Python-ohjelmointiin.

Har­joi­tuk­set vaa­ti­vat jon­kin ver­ran ma­te­maat­tis­ta ajat­te­lua, mutta eivät edel­ly­tä ma­te­ma­tii­kan eikä fy­sii­kan esi­tie­to­ja, mutta niin ha­lut­taes­sa pe­le­jä voi­daan hyö­dyn­tää New­to­nin me­ka­nii­kan ja siihen liittyvien matematiikan osa-alueiden opetuksessa. Pelien avulla voi myös tutustua differentiaaliyhtälöiden numeeriseen ratkaisemiseen.

Se­li­tyk­set ja oh­jeet eivät vielä riit­tä­ne aloit­te­li­jal­le, joten vika ei ole si­nus­sa, jos har­joi­tuk­set tun­tu­vat liian vai­keil­ta.

Maa­lis­kuu 2015

Päi­vi­tys (huhti­kuu 2020): Jos alat tehdä omia pelejä, tutustu arcade moduliin. Se on Pygamea uudempi ja vaikuttaa sitä kätevämmältä.

Päi­vi­tys (helmi­kuu 2024): SGE on pelien ohjelmointiin tarkoitettu python-moduli. En ole kokeillut.

Har­joi­tus­ten te­ke­mi­seen tar­vit­set python-ohjelmointiympäristön kuten spyder/spyder3 tai IDLE sekä Pygame -pelimodulin. OpenSuse -linux järjestelmääni sain nämä installoitua ruksaamalla spyder3 ja pygame Yast- ohjelmistohallinnosta. Mikäli pygame-modulin installointi ei onnistu suoraan järjestelmäsi pakettien hallinnan avulla yritä antamalla komentoriviltä komento pip3 install pygame. pip3 on python ohjelmistoihin kuuluva ohjelma, jolla voi installoida python-ohjelmistomoduleja.

Pythonista on kaksi ei aivan täy­sin yh­teen­so­pi­vaa ver­sio­ta python2 (esim. ver­sio python 2.7) ja python3 (esim. ver­sio python 3.4). Toi­si­naan käy­te­tään nimiä spyder ja spyder3 sekä pip ja pip3 ker­to­maan, kum­man pythonin kans­sa ke­hi­tys­ym­pä­ris­tö ja lisämodulien ins­tal­loin­ti­oh­jel­ma onvat yh­teen­so­pi­via. (Vuon­na 2024+ python2 tus­kin tulee si­nul­le vas­taan.)

Win­dow­sis­sa tai Macissa en ole oh­jel­mia ke­hi­tel­lyt, joten kom­ment­te­ja­ni niis­tä ei kan­na­ta ottaa suu­re­na vii­sau­te­na.

Win­dow­siin on tar­jol­la usei­ta python-kehitysympäristöjä, jois­ta WinPythonia voi kokeilla jopa ilman admin-oikeuksia. WinPythoniin saa pygame modulin tääl­tä klikkaamalla sopivaa versiota, esimerkiksi pygame‑1.9.2a0‑cp34‑none‑win_amd64.whl

WinPython asennetaan käynnistämällä ladattu winpython exe-tiedosto. Sen jälkeen pygame moduli installoidaan käynnistämällä syntyneestä winpython hakemistosta WinPython Control Panel. Samasta hakemistosta löytyy myös Spyder Käynnistä se ja ala koodata tai avaa joku jatkossa esiteltävistä ohjelmista.

Päi­vi­tys (helmi­kuu 2024): Anaconda on pal­jon käy­tet­ty python-ohjelmointiympäristö.

Ins­tal­loin­tia Maciin — mi­nul­le vieraiseen jär­jes­tel­mään — en ole pääs­syt ko­kei­le­maan, mutta sen­kään ei pi­täi­si olla kovin vai­keaa.

Oh­jel­moin­nin opis­ke­lu al­keis­ta läh­tien

Ylei­sin ope­tuk­ses­sa pe­rin­tei­ses­ti käy­tet­ty en­sim­mäi­nen oh­jel­ma lie­nee Hello world- ohjelma, joka tulostaa näytölle "Heippa". Ohjelmointia voi opetella aloittamalla alkeista ja siirtymällä uuteen asiaan vasta kun on perusteellisesti sisäistänyt edellisen. Toimiva tapa oppia, mutta monen mielestä hidas ja tylsä.

"Tylsä" tapa opis­kel­la oh­jel­moin­tia ei vält­tä­mät­tä ole huono tai te­ho­ton. Tällä si­vus­tol­la eh­do­tan "tyvestä-puuhun" me­ne­tel­män si­jas­ta jo­tain val­lan muuta, silti suo­sit­te­len tu­tus­tu­maan esi­mer­kik­si Pythonin koti­si­vuil­la olevaan dokumentaatioon ja tutoriaaleihin ja tarjolla oleviin verkkokursseihin.

Voit ko­keil­la esi­mer­kik­si codecademyn python kurs­sia, BeginnersGuide for Non Programmers tai tätä tutoriaalia . Näistä kannattaa hakea apua myös seuraavien ohjelmien ymmärtäämiseen. Hakukoneilla löydät ehkä itsellesi vielä paremmin sopivaa oppimateriaalia.

Kirja making games selittää seikkaperäisesti, miten tehdä pelejä pygamesilla.

Val­miin oh­jel­man muok­kaa­mi­nen ja laa­jen­ta­mi­nen

Oh­jel­moin­tia voi ope­tel­la tu­tus­tu­mal­la jo­hon­kin ole­mas­sa ole­vaan mie­len­kiin­toi­seen oh­jel­maan ja yrit­tä­mäl­lä muo­ka­ta sitä. Yleen­sä muok­kaa­mi­seen riit­tää ym­mär­tää oh­jel­maa vain osit­tain, joten melko vä­häl­lä pe­reh­ty­mi­sel­lä voi saada pal­jon ai­kaan. Oh­jel­maa myös alkaa ym­mär­tää pa­rem­min, kun näkee sen toi­mi­van. Esi­mer­kik­si olio-oh­jel­moin­tiin voi yrit­tää tu­tus­tua lu­ke­mal­la kir­jois­ta ja sit­ten ko­kei­le­mal­la, mutta us­koak­se­ni op­pi­mis­ta aut­taa nähdä muu­ta­ma "olio" vi­pel­tä­mäs­sä ruu­dul­la ennen teo­riaan pe­reh­ty­mis­tä.

Seu­raa­vas­sa tu­tus­tu­taan yksin­ker­tai­sen peli­oh­jel­man runkoon. Se ei tee juuri mitään, mutta pienellä muokkauksella saamme ohjelman tynkämme tekemään monen laista hauskaa. Ilman valmista mallia joutuisimme opettelemaan paljon uutta Python-ohjelmoinnista ja keksimään uudestaan muiden jo keksimää. Erityisesti alkuun pääseminen on ohjelmoinnissa vaikeaa, koska ei tiedä, mitä pitäisi opetella ja selvittää.

Ale­taan tut­kia en­sim­mäi­sen peli­oh­jel­mam­me tyn­kää. Seu­raa­vas­sa esi­tel­lään vaihe vai­heel­ta peli­oh­jel­man ke­hit­te­ly. Kun­kin vai­heen lo­pus­sa on vai­het­ta vas­taa­va ver­sio oh­jel­mas­ta. Lataa vai­heen 0 lo­pus­ta koodi spyderiin tai muu­hun käyt­tä­mää­si ke­hi­tys­ym­pä­ris­töön.

Spyder-ympäristön ylä­pal­kis­sa on toi­min­to source → fix indendation, joka auttaa saamaan sisennykset kohdalleen. Komento source → run static code analysis (F8) auttaa löytämään sisennys- ja muita -virheitä ohjelmasta. Tab ja shift+tab auttavat sisennyksessä. Run (F5) käynnistää ohjelman. IDLE-ympäristössä on vastaavat toiminnot.

Oh­jel­moin­nis­sa on tyy­pil­lis­tä, että kaik­ki ei on­nis­tu en­sim­mäi­sel­lä yri­tyk­sel­lä. Ikävä kyllä on­gel­mien sel­vit­te­ly on ikä­vää puu­haa ja lip­sah­taa hel­pos­ti haku­am­mun­nak­si.

Seu­raa­vas­sa on pal­jon koo­dia ja sama koodi mo­neen ker­taan vähän muu­tet­tu­na ja täy­den­net­ty­nä. Pel­käs­tään koo­dia sil­mäi­le­mäl­lä et opi pal­joa.

Seu­raa­vas­sa oh­jel­maa täy­den­ne­tään ja muo­ka­taan vaihe vai­heel­ta. Kus­sa­kin vai­hees­sa näy­te­tään ai­kai­sem­pi koodi mus­tal­la ja ai­kai­sem­paan ver­sioon li­sät­ty koodi vih­reäl­lä ja mah­dol­li­nen pois­tet­ta­va koodi pu­nai­sel­la. Si­ni­sel­lä on joi­ta­kin ri­ve­jä, jois­sa on muu­tel­ta­via parametrejä. Ensin näy­te­tään kun­kin vai­heen oh­jel­ma­koodi ko­ko­naan ja sen jäl­keen erik­seen joi­ta­kin pät­kiä, joita se­li­te­tään tar­kem­min. Kun­kin vai­heen oh­jel­ma on la­dat­ta­vis­sa suo­raan tältä si­vul­ta. Näis­sä har­joi­tuk­sis­sa ei vält­tä­mät­tä tar­vit­se kir­joit­taa koo­dia vaan riit­tää ko­keil­la eri ver­sioi­ta val­miis­ta oh­jel­mas­ta. Yritä ym­mär­tää, miten oh­jel­ma toi­mii tai miten joku yk­sit­täi­nen toi­min­to tai efek­ti on to­teu­tet­tu. Kan­nat­taa myös yrit­tää muu­tel­la oh­jel­maa aina kun herää pie­ni­kin aja­tus jos­tain, mitä voisi ko­keil­la.


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > "Hyttysjahti" oppitunnin kulku (edit: )

"Hyt­tys­jahti" oppi­tun­nin kulku

Esit­te­ly­video pe­lis­tä

video varalle, jos yo. ei toimi

  • Kat­so­kaa esit­te­ly­video pe­lis­tä
  • Lataa pelin runko käy­tös­sä­si ole­vaan python ke­hi­tys­ym­pä­ris­töön (esim. Spyder tai IDLE). Tämän pi­täi­si on­nis­tua klik­kaa­mal­la tästä
  • Paina F5. Näy­töl­le pi­täi­si il­mes­tyä peli­oh­jel­man ik­ku­na. (Siinä ei vielä ta­pah­du mi­tään.)
  • Tu­tus­tu oh­jel­maan ke­hi­tys­ym­pä­ris­tös­sä. Alem­pa­na tällä si­vul­la on se­li­tyk­siä koo­diin. Tu­tus­tu nii­hin­kin. Lä­hes­kään kaik­kea tästä peli­oh­jel­man run­gos­ta ei kan­na­ta yrit­tää ym­mär­tää tässä vai­hees­sa.
  • Voit ko­keil­la muut­taa esi­mer­kik­si ik­ku­nan ot­sik­koa ja taus­ta­väriä. Sulje peli­oh­jel­man ik­ku­na pai­na­mal­la esc tai ruk­saa­mal­la se kiin­ni. Edi­toi koo­dia ja paina F5.
  • Siir­ry vai­hee­seen 1: "Luo­daan lätkä hyt­tys­ten pyy­dys­tä­mi­seen" Lu­kai­se koodi ja sen se­li­tyk­set ja lataa se­li­tys­ten ala­puo­lel­la ole­vas­ta lin­kis­tä vai­heen 1 koodi. Voit myös lei­ka­ta koo­din pät­kät se­lai­men ik­ku­nas­ta ja lii­ma­ta ne oi­keil­le pai­koil­leen oh­jel­maan. Se on vaikempaa, mutta myös opet­ta­vai­sem­paa
  • Ko­kei­le oh­jel­maa pai­na­mal­la F5. Peli-ik­ku­nan pi­täi­si ja sii­hen il­mes­tyä puna­vih­reä pyö­ry­lä, jota voi lii­kut­taa z ja x näp­päi­mil­lä ja ylä- ja ala­nuo­lel­la. Yritä ym­mär­tää, miten oh­jel­ma saa liik­keen ai­kai­sek­si. Se ei ole aivan help­poa, koska täl­lai­set peli-ohjelmat eivät ole aivan suora­vii­vai­sia ra­ken­teel­taan.
  • Jatka sa­mal­la lail­la jo­kai­nen vaihe lä­vit­se. Sel­vi­tä it­sel­le­si joka vai­hees­sa, miten oh­jel­ma toi­mii. Tee oh­jel­maan pie­niä muu­tok­sia.

Kyl­läs­tyt­tyä­si hyt­tys­jah­tiin siir­ry mörs­sä­ri­pe­liin. Siinä voit tu­tus­tua, miten ma­te­ma­tiik­kaa ja fy­sii­kan tie­to­ja voi käyt­tää pe­lis­sä hy­väk­si. Jos in­hoat ma­tik­kaa ja fyssaa, voit silti tehdä har­joi­tuk­sen. Ma­tik­ka ja fyssa on koo­dis­sa val­mii­na, joten sinun ei ole pakko yrit­tää ym­mär­tää, miten tri­go­no­met­riaa tai New­to­nin lakia on käy­tet­ty peliä koo­da­tes­sa. Näet joka ta­pauk­ses­sa, miten fy­sii­kan lait toi­mi­vat.

Mörs­sä­ri­pelin jäl­keen on tar­jol­la peli, jossa pitää oh­ja­ta ava­ruus­alus Maata kier­tä­väl­tä ra­dal­ta Kuuta kier­tä­väl­le ra­dal­le. Sen avul­la voi oppia vähän lisää fy­siik­kaa. Teh­tä­vä ei ole aivan help­po, mutta pe­laa­mi­nen tu­tus­tut­taa tai­vaan­me­ka­nii­kan pe­rus­tei­siin. Jos teh­tä­vä alkaa tun­tua liian hel­pol­ta, pie­nen­nä Kuun mas­saa. Nyt se on monta ker­taa to­del­lis­ta suu­rem­pi.


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py (edit: )

HyttysJahti.py

Ra­ken­ne­taan vaihe vai­heel­ta video­peli, jossa jah­da­taan hyt­ty­siä. Hyt­tys­lät­kää oh­ja­taan näp­päi­mil­lä z,x, nuoli-ylös ja nuoli-alas. Oh­jel­ma py­säy­te­tään joko esc-näppäimellä tai sul­ke­mal­la peli-ik­ku­na.

Wed Apr 22 08:13:34 2020


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py > Vaihe 0: Peliohjelman perusrakenne (edit: )

Vaihe 0: Peli­oh­jel­man perus­ra­ken­ne

# -*- coding: utf-8 -*-


# Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä


import pygame
import random
from math import sqrt


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


# Peli-ikkunan koko pikseleinä. Pikseli on piste näytöllä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


Nayttotaajuus = 24  # Voit kokeilla eri näyttötaajuuksia


Dt = 1.0/Nayttotaajuus


# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
loppu = False
while not loppu:


    clock.tick(Nayttotaajuus)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True


    screen.fill(Tausta)


    # näytetään kaikki yllä 'piirretty' näytöllä
    pygame.display.flip()
    #
    # while silmukka päättyy tähän
#
# Ohjelman loppu
pygame.quit()


Se­li­tyk­siä ylläolevaan

 #  -*- coding: utf-8 -*-


Oh­jel­man en­sim­mäi­sel­lä ri­vil­lä mää­rit­te­ly -*- coding: utf-8 -*- ker­too, missä muo­dos­sa kir­jai­met ja muut mer­kit tal­le­te­taan tieto­ko­neen muis­tiin. Siitä ei tar­vit­se tie­tää tämän enem­pää.

 #  Tässä pe­lis­sä läis­ki­tään ym­pä­riin­sä len­te­le­viä hyt­ty­siä


Ylläoleva on komentti eli se­li­tys oh­jel­man lu­ki­jal­le. Kom­men­tit eivät vai­ku­ta mil­lään lail­la oh­jel­man suo­ri­tuk­seen. Kom­ment­ti mer­ki­tään rivin alus­sa ole­val­la # -mer­kil­lä. Kom­men­tit voi­daan mer­ki­tä myös kol­mel­la lai­naus­mer­kil­lä, mutta suo­si­tel­ta­vam­paa on käyt­tää # -merk­kiä.

import pygame
import random
from math import sqrt



Import-käskyllä ote­taan käyt­töön mui­den oh­jel­moi­mia val­mii­ta toi­min­to­ja:

  • Import pygame tuo ohjelmamme käyttöön hyödyllisiä peliohjelmointiin liittyviä funktioita, kuten pygame.display.caption("Pelin nimi").
  • Import random tuo ohjelmamme käyttöön satunnaisluvut, joilla saamme hyttyset lennähtelemään minne sattuu.
  • from math import sqrt tuo käyttöömme funktion neliöjuuren laskemiseen. Sitä tarvitsemme mm. soveltaessamme Pythagoraan lausetta hyttysen ja lätkän välisen etäisyyden laskemiseen.
Musta = (0, 0, 0)  #  Mus­tas­sa ei ole mi­tään valoa
Sin = (0, 0, 255)  #  vain si­nis­tä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  #  val­koi­ses­sa on kai­ken­vä­ris­tä valoa
Tausta = (160, 200, 255)



Oh­jel­moin­nis­sa värit voi­daan esit­tää si­ni­sen, vih­reän ja pu­nai­sen valon yh­dis­tel­mi­nä – RGB-koodina – väri = (punaisen määrä, vihreän määrä, sinisen määrä). Kunkin perusvärin määrä valojen sekoituksessa ilmoitetaan kokonaisluvulla väliltä 0..255. Mustassa ei ole mitään valoa ja valkoisessa on kaiken väristä valoa. (128, 128, 128) lienee keskiharmaa. Värit voisi ohjelmassa esittää toisinkin, mutta RGB–koodia käyttämällä voi tehdä hauskoja väriefektejä.

Sulku­merk­kien avul­la voi­daan Pythonissa koota kä­te­väs­ti monta asiaa yh­teen – yh­dek­si muut­tu­jak­si. Jos in­nos­tut enem­män Python-ohjelmoinnista, googlaa jos­sain vai­hees­sa "python tuple". Musta, Sin, Pun, Vihr, Valk ovat muuttujien nimiä, jotka voi itse valita.

 #  Peli-ik­ku­nan koko pik­se­lei­nä. Pik­se­li on piste näy­töl­lä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  #  peli-ik­ku­nan kor­keus ja le­veys



Ylläoleva ker­too, että peli-ik­ku­nan le­veys on 800 pik­se­liä ja kor­keus 600 pik­se­liä. Näitä voit va­paas­ti muut­taa.

Pygame peli-ik­ku­nan koor­di­naa­tis­to on jos­tain syys­tä eri­lai­nen kuin ma­te­ma­tii­kas­sa nor­maa­lis­ti käy­tet­ty. Piste (0,0) on va­sem­mas­sa ylä­kul­mas­sa ja piste (Wx,Wy) oi­keas­sa ala­kul­mas­sa. x siis kas­vaa ihan jär­ke­väs­ti oi­keal­le men­täes­sä, mutta y kas­vaa alas­päin men­täes­sä.

Nayttotaajuus = 24  #  Voit ko­keil­la eri näyt­tö­taa­juuk­sia


Dt = 1.0/Nayttotaajuus



Peli-ik­ku­na piir­re­tään uu­del­leen – päi­vi­te­tään – näyt­tö­taa­juus ker­taa se­kun­nis­sa eli päi­vi­tys­ten väli on Dt se­kun­tia. Ta­val­li­sis­sa elo­ku­vis­sa kuva vaih­tuu 24 ker­taa se­kun­nis­sa. Kun­han saam­me oh­jel­man toi­mi­maan, voit ko­keil­la, mitä näyt­tö­taa­juu­den las­ke­mi­nen esi­mer­kik­si kym­me­neen vai­kut­taa. Millä taa­juu­del­la ih­mi­nen erot­taa kuvat eril­li­sik­si niin, että peli alkaa nykiä?

Pelin olioil­le pitää las­kea uusi paik­ka ajan Dt vä­lein, eli pitää las­kea, pal­jon­ko pelin oliot liik­ku­vat ajas­sa Dt.

Jot­kin Pythonin funk­tiot – kuten pygamen piirtofuktiot – vaa­ti­vat ar­gu­men­teik­seen koko­nais­lu­ku­ja, koska koko­nais­luvut ja reaa­li­luvut tal­len­ne­taan tieto­ko­neen muis­tiin eri ta­val­la. Reaa­li­lu­vus­ta saa koko­nais­luvun int-funtiolla seu­raa­vas­ti: Ko­men­to x = 4/3 antaa x:lle arvon 1.33333... ja i = int(x) tai komento i = int(4/3) antaa i:lle arvon 1

 #  # # # # # # # # # # # # # # # # # # # # # # # # # #
 #  Pää­oh­jel­ma alkaa tästä
 #  # # # # # # # # # # # # # # # # # # # # # # # # # #



Alus­tuk­sia, jotka ko­pioin saa­mas­ta­ni malli­oh­jel­mas­ta. Luo­daan peli-ik­ku­na screen ja annetaan sille nimi "Hyttysjahti".

Ilman malli­oh­jel­maa mi­nul­ta olisi men­nyt ties miten pit­kään ym­mär­tää, mitä täl­lai­sia ko­men­to­ja tar­vi­taan pelin alus­ta­mi­sek­si.

pygame.display.init()
 #  Sel­vi­te­tään peli-ik­ku­nan koko näy­töl­lä pik­se­lei­nä. Pik­se­li on piste näy­töl­lä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  #  peli-ik­ku­nan kor­keus ja le­veys



Ylläoleva ker­too, että peli-ik­ku­nan le­veys on Wx pik­se­liä ja kor­keus Wy pik­se­liä.

Pygame peli-ik­ku­nan koor­di­naa­tis­to on jos­tain syys­tä eri­lai­nen kuin ma­te­ma­tii­kas­sa nor­maa­lis­ti käy­tet­ty. Piste (0,0) on va­sem­mas­sa ylä­kul­mas­sa ja piste (Wx,Wy) oi­keas­sa ala­kul­mas­sa. x siis kas­vaa ihan jär­ke­väs­ti oi­keal­le men­täes­sä, mutta y kas­vaa alas­päin men­täes­sä.

screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()



 #
 #  Tois­te­taan allaolevaa while-silmukkaa, kun­nes käyt­tä­jä sul­kee peli-ik­ku­nan
 #  tai pai­naa ESC-iä
 #
loppu = False
while not loppu:



Alla ko­men­to clock.tick(Nayttotaajuus) oleva varmistaa, että tätä while-silmukkaa toistetaan Nayttotaajuus kertaa sekunnissa. Ohjelma jää odottamaan tähän, kunnes aikaa on kulunut tarpeeksi. Peliohjelman odotellessa tietokoneesi pääsee suorittamaan muita ohjelmia.

    clock.tick(Nayttotaajuus)



Jos käyt­tä­jä on sul­ke­nut peli-ik­ku­nan tai pai­na­nut ESC-näppäintä, ase­te­taan loppu to­dek­si niin, että tämä jää vii­mei­sek­si while-silmukan toistoksi

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True



Jo­kai­sel­la while-silmukan toistolla täytetään peli-ikkuna taustavärillä eli ikkuna pyyhkiytyy tyhjäksi

    screen.fill(Tausta)




    #  näy­te­tään kaik­ki yllä 'piir­ret­ty' näy­töl­lä
    pygame.display.flip()
    #
    #  while sil­muk­ka päät­tyy tähän
 #
 #  Oh­jel­man loppu
pygame.quit()



Tämä oh­jel­ma ei vielä tee mi­tään, mutta näy­töl­le olisi pi­tä­nyt il­mes­tyä Hyt­tys­jahti ni­mi­nen ik­ku­na.

Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Hyt­tys­Jahti_0.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py > Vaihe 1: Luodaan lätkä hyttysten pyydystämiseen (edit: )

Vaihe 1: Luo­daan lätkä hyt­tys­ten pyy­dys­tä­mi­seen

# -*- coding: utf-8 -*-


# Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä


import pygame
import random
from math import sqrt


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


# Peli-ikkunan koko pikseleinä. Pikseli on piste näytöllä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


Nayttotaajuus = 24  # Voit kokeilla eri näyttötaajuuksia


Dt = 1.0/Nayttotaajuus


# Peliin liittyviä muuttujia.
VLatka = 200.0  # Lätkän nopeus


# __init__(self, x, y, koko) on metodi, jolla lätkä luodaan


class Cl_Latka:
    def __init__(self, x, y, koko):
        self.paikka = (x, y)
        self.koko = koko
        self.color = Vihr
        self.liikkuu = False


    def piirra(self):
        (x, y) = self.paikka
        iPaikka = (int(x), int(y))
        pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        pygame.draw.circle(screen, Pun, iPaikka, int(self.koko/2), 0)


    def liiku(self, vxvy):
        (x, y) = self.paikka
        (vx, vy) = vxvy
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # Lisäämällä tähän väliin muutaman if lauseen, voit korjata ohjelmaa
        #  niin, että Lätkä ei karkaa peli-ikkunan reunan ulkopuolelle
        # jos x on suurempi kuin niin Wx x.stä kannattaa vähentää Wx jne...
        self.paikka = (x, y)
        self.liikkuu = True


# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()


# Luodaan 'olio' tyyppiä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
loppu = False
while not loppu:


    clock.tick(Nayttotaajuus)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True


    screen.fill(Tausta)


    Latka.liikkuu = False
#   Tarkistetaan, onko joku tai jotkin lätkää ohjaavista näppäimistä
# painettuna. Liikutetaan lätkää vastaavasti.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_DOWN]:
        Latka.liiku((0.0, VLatka))  # alas
    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0))  # vasemmalle
    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0))  # oikealle
    # Piirretään lätkä uudelle paikalleen
    Latka.piirra()


    # näytetään kaikki yllä 'piirretty' näytöllä
    pygame.display.flip()
    #
    # while silmukka päättyy tähän
#
# Ohjelman loppu
pygame.quit()


Se­li­tyk­siä ylläolevaan

import pygame
import random
from math import sqrt



Nayttotaajuus = 24  #  Voit ko­keil­la eri näyt­tö­taa­juuk­sia


 #  Pe­liin liit­ty­viä muut­tu­jia.
VLatka = 200.0  #  Lät­kän no­peus


 #  __init__(self, x, y, koko) on me­to­di, jolla lätkä luo­daan




class Cl_Latka:
    def __init__(self, x, y, koko):
        self.paikka = (x, y)
        self.koko = koko
        self.color = Vihr
        self.liikkuu = False



Mää­ri­tel­lään, minkä­lai­nen 'olio' on lätkä, jolla huis­kim­me hyt­ty­siä.

Lät­käl­lä on paik­ka (x, y) peli-ik­ku­nas­sa, koko ja väri. Vielä olioon lätkä kuu­luu tieto siitä, liik­kuu­ko se, koska oh­jel­mas­sa on ehto, että vain liik­ku­va lätkä tap­paa hyt­ty­sen. Ei riitä, että hyt­ty­nen osuu lät­kään.

Jot­kin Pythonin funk­tiot – kuten pygamen piirtofuktiot – vaa­ti­vat ar­gu­men­teik­seen koko­nais­lu­ku­ja, koska koko­nais­luvut ja reaa­li­luvut tal­len­ne­taan tieto­ko­neen muis­tiin eri ta­val­la. Reaa­li­lu­vus­ta saa koko­nais­luvun int-funtiolla seu­raa­vas­ti: Ko­men­to x = 4/3 antaa x:lle arvon 1.33333... ja i = int(x) tai komento i = int(4/3) antaa i:lle arvon 1

    def piirra(self):
        (x, y) = self.paikka
        iPaikka = (int(x), int(y))
        pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        pygame.draw.circle(screen, Pun, iPaikka, int(self.koko/2), 0)



Lätkä täy­tyy piir­tää uu­del­leen ajan Dt-välein. Tämä teh­dään olioon lätkä kuu­lu­val­la me­to­dil­la piirra(), joka piir­tää lät­kän vä­ril­lä ym­py­rän ja sen pääl­le pie­nem­män pu­nai­sen ym­py­rän. pygame.draw.circle on oh­jel­man alus­sa importoidun pygame modulin funk­tio, joka on do­ku­men­toi­tu pygame web-si­vul­la

    def liiku(self, vxvy):
        (x, y) = self.paikka
        (vx, vy) = vxvy
        (x, y) = (x + vx*Dt, y + vy*Dt)
        #  Li­sää­mäl­lä tähän vä­liin muu­ta­man if lau­seen, voit kor­ja­ta oh­jel­maa
        #   niin, että Lätkä ei kar­kaa peli-ik­ku­nan reu­nan ulko­puo­lel­le
        #  jos x on suu­rem­pi kuin niin Wx x.stä kan­nat­taa vä­hen­tää Wx jne...
        self.paikka = (x, y)
        self.liikkuu = True



Me­to­di liikux(vx) las­kee lät­käl­le uuden pai­kan, jos nuoli ylös tai alas tai z tai x on pai­net­tu­na.

Alem­pa­na – pelin pää­oh­jel­mas­sa – tar­kis­te­taan, mitä näp­päi­miä pe­laa­ja pitää poh­jas­sa tai on pai­na­nut.

vx on lät­kän no­peus, eli ajas­sa Dt lätkä kul­kee mat­kan vx*Dt

Huom: Oh­jel­moin­nis­sa a = a + 5 ei ole yh­tä­lö, vaan si­joi­tus­lause, joka tar­koit­taa, että a:lle an­ne­taan uusi arvo, joka on a:n vanha arvo + 5.

Lo­puk­si päi­vi­te­tään vielä tieto, että lätkä liik­kuu.

 #  # # # # # # # # # # # # # # # # # # # # # # # # # #
 #  Pää­oh­jel­ma alkaa tästä
 #  # # # # # # # # # # # # # # # # # # # # # # # # # #



. . .
 #  Luo­daan 'olio' tyyp­piä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)



 #
 #  Tois­te­taan allaolevaa while-silmukkaa, kun­nes käyt­tä­jä sul­kee peli-ik­ku­nan
 #  tai pai­naa ESC-iä
 #
loppu = False
while not loppu:



    Latka.liikkuu = False

 #    Tar­kis­te­taan, onko joku tai jot­kin lät­kää oh­jaa­vis­ta näp­päi­mis­tä
 #  pai­net­tu­na. Lii­ku­te­taan lät­kää vas­taa­vas­ti.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        Latka.liiku((0.0, -VLatka))  #  ylös
    if pressed[pygame.K_DOWN]:
        Latka.liiku((0.0, VLatka))  #  alas
    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0))  #  va­sem­mal­le
    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0))  #  oi­keal­le

    #  Piir­re­tään lätkä uu­del­le pai­kal­leen
    Latka.piirra()


    #  näy­te­tään kaik­ki yllä 'piir­ret­ty' näy­töl­lä
    pygame.display.flip()
    #
    #  while sil­muk­ka päät­tyy tähän
 #
 #  Oh­jel­man loppu
pygame.quit()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Hyt­tys­Jahti_1.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py > Vaihe 2: Luodaan hyttynen (edit: )

Vaihe 2: Luo­daan hyt­ty­nen

# -*- coding: utf-8 -*-


# Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä


import pygame
import random
from math import sqrt


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


# Peli-ikkunan koko pikseleinä. Pikseli on piste näytöllä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


Nayttotaajuus = 24  # Voit kokeilla eri näyttötaajuuksia


Dt = 1.0/Nayttotaajuus


# Peliin liittyviä muuttujia.
VLatka = 200.0  # Lätkän nopeus


Vmax = 250.0
MaxKiihtyvyys = 400.0


# __init__(self, x, y, koko) on metodi, jolla lätkä luodaan


class Cl_Latka:
    def __init__(self, x, y, koko):
        self.paikka = (x, y)
        self.koko = koko
        self.color = Vihr
        self.liikkuu = False


    def piirra(self):
        (x, y) = self.paikka
        iPaikka = (int(x), int(y))
        pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        pygame.draw.circle(screen, Pun, iPaikka, int(self.koko/2), 0)


    def liiku(self, vxvy):
        (x, y) = self.paikka
        (vx, vy) = vxvy
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # Lisäämällä tähän väliin muutaman if lauseen, voit korjata ohjelmaa
        #  niin, että Lätkä ei karkaa peli-ikkunan reunan ulkopuolelle
        # jos x on suurempi kuin niin Wx x.stä kannattaa vähentää Wx jne...
        self.paikka = (x, y)
        self.liikkuu = True


class Cl_Hyttynen:
    def __init__(self, x, y):
        self.paikka = (x, y)


        self.nopeus = (70.0, 50.0)  # kokeile tähän eri arvoja


        self.koko = 10
        self.color = (random.randint(0, 255),
                      random.randint(0, 255),
                      random.randint(0, 255))
        self.elossa = True


    def piirra(self):
        iPaikka = (int(self.paikka[0]), int(self.paikka[1]))
        iEllxy = (iPaikka[0], iPaikka[1], 4*self.koko, self.koko)
        if self.elossa:
            pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        else:
            pygame.draw.ellipse(screen, self.color, iEllxy, 0)


    def liiku_suoraan(self):
        (x, y) = self.paikka
        (vx, vy) = self.nopeus
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # poista seuraavista kommenttimerkki,
        # niin saat hyttysen pysymään ruudulla
#        if (x > Wx and vx > 0) or (x < 0 and vx < 0):
#            vx = -vx
#        elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
#            vy = -vy
        self.paikka = (x, y)
        self.nopeus = (vx, vy)


# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()


# Luodaan 'olio' tyyppiä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)


# Luodaan yksi hyttynen
hyttynen = Cl_Hyttynen(50, 50)


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
loppu = False
while not loppu:


    clock.tick(Nayttotaajuus)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True


    screen.fill(Tausta)


    hyttynen.liiku_suoraan()


    hyttynen.piirra()


    Latka.liikkuu = False
#   Tarkistetaan, onko joku tai jotkin lätkää ohjaavista näppäimistä
# painettuna. Liikutetaan lätkää vastaavasti.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_DOWN]:
        Latka.liiku((0.0, VLatka))  # alas
    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0))  # vasemmalle
    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0))  # oikealle
    # Piirretään lätkä uudelle paikalleen
    Latka.piirra()


    # näytetään kaikki yllä 'piirretty' näytöllä
    pygame.display.flip()
    #
    # while silmukka päättyy tähän
#
# Ohjelman loppu
pygame.quit()


Se­li­tyk­siä ylläolevaan

import pygame
import random
from math import sqrt



Nayttotaajuus = 24  #  Voit ko­keil­la eri näyt­tö­taa­juuk­sia


 #  Pe­liin liit­ty­viä muut­tu­jia.
VLatka = 200.0  #  Lät­kän no­peus


Vmax = 250.0
MaxKiihtyvyys = 400.0


class Cl_Hyttynen:
    def __init__(self, x, y):
        self.paikka = (x, y)


        self.nopeus = (70.0, 50.0)  #  ko­kei­le tähän eri ar­vo­ja


        self.koko = 10
        self.color = (random.randint(0, 255),
                      random.randint(0, 255),
                      random.randint(0, 255))
        self.elossa = True



Mää­ri­tel­lään olio­luok­ka Cl_Hyttynen, joka kertoo, millaisia olioita hyttyset ovat. Oliomäärittelyn sisällä määriteltyjä funktiota kutsutaan metodeiksi. Metodi __init__ luo olion, eli se kertoo, millaisia oliot ovat 'syntyessään': Jokainen luokkaan Otokka kuuluva olio luodaan – 'syntyy' – johonkin paikkaan koordinaatistossa, sillä on jokin nopeus ja se on jonkin kokoinen ja se on elossa syntyessään. Väriltään olio on mitä sattuu, koska läiskäisemme siihen satunnaisen määrän sinistä, punaista ja vihreää. random.randint(0, 255) on jokin kokonaisluku väliltä 0..255

Mää­rit­te­lys­sä käy­tet­ty self viittaa olioon itseensä. Sen tarkan merkityksen ja tarkoituksen ymmärtäminen on vaikeaa, mutta älä takerru siihen nyt. Olio-ohjelmoinnin kunnollinen ymmärtäminen on tärkeää melkein kaikessa ohjelmoinnissa, mutta tästä harjoituksesta selviämme seuraamalla valmista esimerkkiä, joten kaikkea ei tarvitse yrittää ymmärtää perin pohjin. Esimerkin myötä saanet aavistuksen olio-ohjelmoinnista niin, että teorian omaksuminen on tarvittaessa helpompaa.

Kuten lät­käl­le, hyt­ty­sel­le­kin pitää mää­ri­tel­lä me­to­di, joka piir­tää hyt­ty­sen peli-ik­ku­naan. Elävä hyt­ty­nen esi­te­tään ym­py­rä­nä, kuol­lut lit­teä­nä el­lip­si­nä.

pygame-modulista löytyy metodi draw.circle(screen, x, y, sade, reu­nan_le­veys), joka piirtää ympyrän näytölle, eikä meidän tarvitse vaivata päätämme sillä, miten se sen tekee.

Käy­täm­me las­kuis­sa reaa­li­lu­ku­ja, mutta draw.circle-funktio haluaa paikan koordinaatit kokonaislukuina. Siksi käytämme funktiota int(x), joka pyöristää reaaliluvun x lähinnä vastaavaksi kokonaisluvuksi. Tällainen käytännössä tarvittava pikkusälä tekee periaatteessa selkeästäkin algoritmista vaikeaselkoista.

    def piirra(self):
        iPaikka = (int(self.paikka[0]), int(self.paikka[1]))
        iEllxy = (iPaikka[0], iPaikka[1], 4*self.koko, self.koko)
        if self.elossa:
            pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        else:
            pygame.draw.ellipse(screen, self.color, iEllxy, 0)



Kuten lät­käl­le, hyt­ty­sel­le­kin pitää las­kea uusi paik­ka ajan Dt vä­lein. Seu­raa­va al­keel­li­nen me­to­di lii­kut­taa hyt­tys­tä suo­raa vii­vaa pit­kin

    def liiku_suoraan(self):
        (x, y) = self.paikka
        (vx, vy) = self.nopeus
        (x, y) = (x + vx*Dt, y + vy*Dt)
        #  pois­ta seu­raa­vis­ta kom­ment­ti­merk­ki,
        #  niin saat hyt­ty­sen py­sy­mään ruu­dul­la
 #         if (x > Wx and vx > 0) or (x < 0 and vx < 0):
 #             vx = -vx
 #         elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
 #             vy = -vy
        self.paikka = (x, y)
        self.nopeus = (vx, vy)



 #  # # # # # # # # # # # # # # # # # # # # # # # # # #
 #  Pää­oh­jel­ma alkaa tästä
 #  # # # # # # # # # # # # # # # # # # # # # # # # # #



. . .
 #  Luo­daan 'olio' tyyp­piä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)



 #  Luo­daan yksi hyt­ty­nen
hyttynen = Cl_Hyttynen(50, 50)


 #
 #  Tois­te­taan allaolevaa while-silmukkaa, kun­nes käyt­tä­jä sul­kee peli-ik­ku­nan
 #  tai pai­naa ESC-iä
 #
loppu = False
while not loppu:



    hyttynen.liiku_suoraan()


    hyttynen.piirra()


    #  näy­te­tään kaik­ki yllä 'piir­ret­ty' näy­töl­lä
    pygame.display.flip()
    #
    #  while sil­muk­ka päät­tyy tähän
 #
 #  Oh­jel­man loppu
pygame.quit()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Hyt­tys­Jahti_2.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py > Vaihe 3: Laitetaan hyttynen lentelemään ympäriinsä (edit: )

Vaihe 3: Lai­te­taan hyt­ty­nen len­te­le­mään ym­pä­riin­sä

# -*- coding: utf-8 -*-


# Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä


import pygame
import random
from math import sqrt


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


# Peli-ikkunan koko pikseleinä. Pikseli on piste näytöllä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


Nayttotaajuus = 24  # Voit kokeilla eri näyttötaajuuksia


Dt = 1.0/Nayttotaajuus


# Peliin liittyviä muuttujia.
VLatka = 200.0  # Lätkän nopeus


Vmax = 250.0
MaxKiihtyvyys = 400.0


# __init__(self, x, y, koko) on metodi, jolla lätkä luodaan


class Cl_Latka:
    def __init__(self, x, y, koko):
        self.paikka = (x, y)
        self.koko = koko
        self.color = Vihr
        self.liikkuu = False


    def piirra(self):
        (x, y) = self.paikka
        iPaikka = (int(x), int(y))
        pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        pygame.draw.circle(screen, Pun, iPaikka, int(self.koko/2), 0)


    def liiku(self, vxvy):
        (x, y) = self.paikka
        (vx, vy) = vxvy
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # Lisäämällä tähän väliin muutaman if lauseen, voit korjata ohjelmaa
        #  niin, että Lätkä ei karkaa peli-ikkunan reunan ulkopuolelle
        # jos x on suurempi kuin niin Wx x.stä kannattaa vähentää Wx jne...
        self.paikka = (x, y)
        self.liikkuu = True


class Cl_Hyttynen:
    def __init__(self, x, y):
        self.paikka = (x, y)


        self.nopeus = (70.0, 50.0)  # kokeile tähän eri arvoja


        self.koko = 10
        self.color = (random.randint(0, 255),
                      random.randint(0, 255),
                      random.randint(0, 255))
        self.elossa = True


    def piirra(self):
        iPaikka = (int(self.paikka[0]), int(self.paikka[1]))
        iEllxy = (iPaikka[0], iPaikka[1], 4*self.koko, self.koko)
        if self.elossa:
            pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        else:
            pygame.draw.ellipse(screen, self.color, iEllxy, 0)


    def liiku_suoraan(self):
        (x, y) = self.paikka
        (vx, vy) = self.nopeus
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # poista seuraavista kommenttimerkki,
        # niin saat hyttysen pysymään ruudulla
#        if (x > Wx and vx > 0) or (x < 0 and vx < 0):
#            vx = -vx
#        elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
#            vy = -vy
        self.paikka = (x, y)
        self.nopeus = (vx, vy)


    def liiku(self):


        self.nopeus = lenna(self.paikka, self.nopeus)


# Siirrytään eteenpäin nopeus kertaa aikaväli
        self.paikka = dfdt(self.paikka, self.nopeus)


def lenna(paikka, nopeus):
    kiihtyvyys = (random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys),
                  random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys))
    nopeus = reunat(paikka, dfdt(nopeus, kiihtyvyys))
    return nopeus
# matka = nopeus*aika
def dfdt(xy, dxdy):
    (x, y) = xy
    (dx, dy) = dxdy
    return (x + dx*Dt, y + dy*Dt)


def reunat(xy, vxvy):
    (vx, vy) = vxvy
    (x, y) = xy
    # ei päästetä hyttystä reunojen ulkopuolelle
    if (x > Wx and vx > 0) or (x < 0 and vx < 0):
        vx = -vx
    elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
        vy = -vy
    # pysäytetään hyttynen, mikäli se lentää tolkuttoman lujaa
    elif sqrt(vx**2 + vy**2) > Vmax:
        vx, vy = 0.0, 0.0
    return (vx, vy)


# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()


# Luodaan 'olio' tyyppiä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)


# Luodaan yksi hyttynen
hyttynen = Cl_Hyttynen(50, 50)


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
loppu = False
while not loppu:


    clock.tick(Nayttotaajuus)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True


    screen.fill(Tausta)


    hyttynen.liiku_suoraan()


    hyttynen.liiku()


    hyttynen.piirra()


    Latka.liikkuu = False
#   Tarkistetaan, onko joku tai jotkin lätkää ohjaavista näppäimistä
# painettuna. Liikutetaan lätkää vastaavasti.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_DOWN]:
        Latka.liiku((0.0, VLatka))  # alas
    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0))  # vasemmalle
    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0))  # oikealle
    # Piirretään lätkä uudelle paikalleen
    Latka.piirra()


    # näytetään kaikki yllä 'piirretty' näytöllä
    pygame.display.flip()
    #
    # while silmukka päättyy tähän
#
# Ohjelman loppu
pygame.quit()


Se­li­tyk­siä ylläolevaan

import pygame
import random
from math import sqrt



Nayttotaajuus = 24  #  Voit ko­keil­la eri näyt­tö­taa­juuk­sia


    def liiku_suoraan(self):
        (x, y) = self.paikka
        (vx, vy) = self.nopeus
        (x, y) = (x + vx*Dt, y + vy*Dt)
        #  pois­ta seu­raa­vis­ta kom­ment­ti­merk­ki,
        #  niin saat hyt­ty­sen py­sy­mään ruu­dul­la
 #         if (x > Wx and vx > 0) or (x < 0 and vx < 0):
 #             vx = -vx
 #         elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
 #             vy = -vy
        self.paikka = (x, y)
        self.nopeus = (vx, vy)



Lai­te­taan hyt­ty­nen len­te­le­mään ym­pä­riin­sä

    def liiku(self):


        self.nopeus = lenna(self.paikka, self.nopeus)


 #  Siir­ry­tään eteen­päin no­peus ker­taa aika­väli
        self.paikka = dfdt(self.paikka, self.nopeus)




lennä(paik­ka, no­peus) on funk­tio, joka

  • muut­taa hyt­ty­sen no­peut­ta sa­tun­nai­sel­la mää­räl­lä
  • kään­tää hyt­ty­sen, jos se on peli-ik­ku­nan reu­nal­la ja me­nos­sa ulos­päin
  • py­säyt­tää hyt­ty­sen, jos se len­tää liian lujaa ;-)

No­peu­den muu­tos on keski­mää­rin nolla, mutta sa­tun­nais­luvut ovat oi­kuk­kai­ta ja no­peus saat­taa kas­vaa pit­käk­si aikaa niin suu­rek­si, ettei hyt­ty­seen voi mi­ten­kään osua. Siksi py­säy­tys.



def lenna(paikka, nopeus):
    kiihtyvyys = (random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys),
                  random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys))
    nopeus = reunat(paikka, dfdt(nopeus, kiihtyvyys))
    return nopeus

 #  matka = no­peus*aika


def dfdt(xy, dxdy):
    (x, y) = xy
    (dx, dy) = dxdy
    return (x + dx*Dt, y + dy*Dt)





def reunat(xy, vxvy):
    (vx, vy) = vxvy
    (x, y) = xy
    #  ei pääs­te­tä hyt­tys­tä reu­no­jen ulko­puo­lel­le
    if (x > Wx and vx > 0) or (x < 0 and vx < 0):
        vx = -vx
    elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
        vy = -vy
    #  py­säy­te­tään hyt­ty­nen, mi­kä­li se len­tää tol­kut­to­man lujaa
    elif sqrt(vx**2 + vy**2) > Vmax:
        vx, vy = 0.0, 0.0
    return (vx, vy)



 #  # # # # # # # # # # # # # # # # # # # # # # # # # #
 #  Pää­oh­jel­ma alkaa tästä
 #  # # # # # # # # # # # # # # # # # # # # # # # # # #



. . .
 #  Luo­daan 'olio' tyyp­piä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)



 #  Luo­daan yksi hyt­ty­nen
hyttynen = Cl_Hyttynen(50, 50)


 #
 #  Tois­te­taan allaolevaa while-silmukkaa, kun­nes käyt­tä­jä sul­kee peli-ik­ku­nan
 #  tai pai­naa ESC-iä
 #
loppu = False
while not loppu:



    hyttynen.liiku_suoraan()


    hyttynen.liiku()


    hyttynen.piirra()


    #  näy­te­tään kaik­ki yllä 'piir­ret­ty' näy­töl­lä
    pygame.display.flip()
    #
    #  while sil­muk­ka päät­tyy tähän
 #
 #  Oh­jel­man loppu
pygame.quit()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Hyt­tys­Jahti_3.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py > Vaihe 4: Ohjelmoidaan hyttyneen reagoimaan lätkän osumaan (edit: )

Vaihe 4: Oh­jel­moi­daan hyttyneen rea­goi­maan lät­kän osu­maan

# -*- coding: utf-8 -*-


# Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä


import pygame
import random
from math import sqrt


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


# Peli-ikkunan koko pikseleinä. Pikseli on piste näytöllä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


Nayttotaajuus = 24  # Voit kokeilla eri näyttötaajuuksia


Dt = 1.0/Nayttotaajuus


# Peliin liittyviä muuttujia.
VLatka = 200.0  # Lätkän nopeus


Vmax = 250.0
MaxKiihtyvyys = 400.0


# __init__(self, x, y, koko) on metodi, jolla lätkä luodaan


class Cl_Latka:
    def __init__(self, x, y, koko):
        self.paikka = (x, y)
        self.koko = koko
        self.color = Vihr
        self.liikkuu = False


    def piirra(self):
        (x, y) = self.paikka
        iPaikka = (int(x), int(y))
        pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        pygame.draw.circle(screen, Pun, iPaikka, int(self.koko/2), 0)


    def liiku(self, vxvy):
        (x, y) = self.paikka
        (vx, vy) = vxvy
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # Lisäämällä tähän väliin muutaman if lauseen, voit korjata ohjelmaa
        #  niin, että Lätkä ei karkaa peli-ikkunan reunan ulkopuolelle
        # jos x on suurempi kuin niin Wx x.stä kannattaa vähentää Wx jne...
        self.paikka = (x, y)
        self.liikkuu = True


class Cl_Hyttynen:
    def __init__(self, x, y):
        self.paikka = (x, y)


        self.nopeus = (70.0, 50.0)  # kokeile tähän eri arvoja


        self.koko = 10
        self.color = (random.randint(0, 255),
                      random.randint(0, 255),
                      random.randint(0, 255))
        self.elossa = True


    def piirra(self):
        iPaikka = (int(self.paikka[0]), int(self.paikka[1]))
        iEllxy = (iPaikka[0], iPaikka[1], 4*self.koko, self.koko)
        if self.elossa:
            pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        else:
            pygame.draw.ellipse(screen, self.color, iEllxy, 0)


    def liiku(self):


        self.isku()  # Onko lätkä osunut?
        if self.elossa:
            self.nopeus = lenna(self.paikka, self.nopeus)


        else:
            self.nopeus = self.putoa()


# Siirrytään eteenpäin nopeus kertaa aikaväli
        self.paikka = dfdt(self.paikka, self.nopeus)


#   Jos Latka on lähellä tätä hyttystä sekä x- että y-suunnassa
#   lätkä osuu, hyttynen kuolee – litistyy ja muuttuu punaiseksi.
    def isku(self):
        if (etaisyys(self.paikka, Latka.paikka) < Latka.koko and
            Latka.liikkuu):
            self.elossa = False
            self.color = Pun


    def putoa(self):
        vx = random.uniform(-10, 10)
        if self.paikka[1] < Wy-self.koko:
            vy = random.uniform(20, 70)
        else:
            vx = 0
            vy = 0
        return (vx, vy)


def lenna(paikka, nopeus):
    kiihtyvyys = (random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys),
                  random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys))
    nopeus = reunat(paikka, dfdt(nopeus, kiihtyvyys))
    return nopeus
# matka = nopeus*aika
def dfdt(xy, dxdy):
    (x, y) = xy
    (dx, dy) = dxdy
    return (x + dx*Dt, y + dy*Dt)


def reunat(xy, vxvy):
    (vx, vy) = vxvy
    (x, y) = xy
    # ei päästetä hyttystä reunojen ulkopuolelle
    if (x > Wx and vx > 0) or (x < 0 and vx < 0):
        vx = -vx
    elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
        vy = -vy
    # pysäytetään hyttynen, mikäli se lentää tolkuttoman lujaa
    elif sqrt(vx**2 + vy**2) > Vmax:
        vx, vy = 0.0, 0.0
    return (vx, vy)


# kahden pisteen p0 ja p1 välinen etäisyys xy-tasossa
def etaisyys(p0, p1):
    (x0, y0) = p0
    (x1, y1) = p1
    return sqrt((x1-x0)**2 + (y1-y0)**2)
# def ei_hyttysia(parvi):
#   for a in parvi:
#       if a.elossa:
#           return False
#   return True


# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()


# Luodaan 'olio' tyyppiä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)


# Luodaan yksi hyttynen
hyttynen = Cl_Hyttynen(50, 50)


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
loppu = False
while not loppu:


    clock.tick(Nayttotaajuus)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True


    screen.fill(Tausta)


    hyttynen.liiku()


    hyttynen.piirra()


    Latka.liikkuu = False
#   Tarkistetaan, onko joku tai jotkin lätkää ohjaavista näppäimistä
# painettuna. Liikutetaan lätkää vastaavasti.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_DOWN]:
        Latka.liiku((0.0, VLatka))  # alas
    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0))  # vasemmalle
    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0))  # oikealle
    # Piirretään lätkä uudelle paikalleen
    Latka.piirra()


    # näytetään kaikki yllä 'piirretty' näytöllä
    pygame.display.flip()
    #
    # while silmukka päättyy tähän
#
# Ohjelman loppu
pygame.quit()


Se­li­tyk­siä ylläolevaan

import pygame
import random
from math import sqrt



Nayttotaajuus = 24  #  Voit ko­keil­la eri näyt­tö­taa­juuk­sia


        self.isku()  #  Onko lätkä osu­nut?
        if self.elossa:
            self.nopeus = lenna(self.paikka, self.nopeus)


        else:
            self.nopeus = self.putoa()


 #    Jos Latka on lä­hel­lä tätä hyt­tys­tä sekä x- että y-suun­nas­sa
 #    lätkä osuu, hyt­ty­nen kuo­lee – li­tis­tyy ja muut­tuu pu­nai­sek­si.
    def isku(self):
        if (etaisyys(self.paikka, Latka.paikka) < Latka.koko and
            Latka.liikkuu):
            self.elossa = False
            self.color = Pun


Kuol­lee­na pu­toa­van hyt­ty­sen liike: No­peus x-suun­taan on pieni sa­tun­nais­luku, eli hyt­ty­nen lei­jai­lee alas lähes pysty­suo­raan. Ellei hyt­ty­nen jo ole maas­sa, sillä on pieni sa­tun­nai­nen no­peus alas­päin (muis­ta, että y kas­vaa epä­loo­gi­ses­ti alas­päin). Jos hyt­ty­nen on jo maas­sa, y-suun­tai­nen no­peus ase­te­taan nol­lak­si.

    def putoa(self):
        vx = random.uniform(-10, 10)
        if self.paikka[1] < Wy-self.koko:
            vy = random.uniform(20, 70)
        else:
            vx = 0
            vy = 0
        return (vx, vy)


 #  kah­den pis­teen p0 ja p1 vä­li­nen etäi­syys xy-tasossa


def etaisyys(p0, p1):
    (x0, y0) = p0
    (x1, y1) = p1
    return sqrt((x1-x0)**2 + (y1-y0)**2)

 #  def ei_hyttysia(parvi):
 #    for a in parvi:
 #        if a.elossa:
 #            return False
 #    return True



 #  # # # # # # # # # # # # # # # # # # # # # # # # # #
 #  Pää­oh­jel­ma alkaa tästä
 #  # # # # # # # # # # # # # # # # # # # # # # # # # #



. . .
 #  Luo­daan 'olio' tyyp­piä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)



 #  Luo­daan yksi hyt­ty­nen
hyttynen = Cl_Hyttynen(50, 50)


 #
 #  Tois­te­taan allaolevaa while-silmukkaa, kun­nes käyt­tä­jä sul­kee peli-ik­ku­nan
 #  tai pai­naa ESC-iä
 #
loppu = False
while not loppu:



    hyttynen.liiku()


    hyttynen.piirra()


    #  näy­te­tään kaik­ki yllä 'piir­ret­ty' näy­töl­lä
    pygame.display.flip()
    #
    #  while sil­muk­ka päät­tyy tähän
 #
 #  Oh­jel­man loppu
pygame.quit()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Hyt­tys­Jahti_4.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Python hyttysjahdissa > HyttysJahti.py > Vaihe 5: Luodaan parvi hyttysiä (edit: )

Vaihe 5: Luo­daan parvi hyt­ty­siä

# -*- coding: utf-8 -*-


# Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä


import pygame
import random
from math import sqrt


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


# Peli-ikkunan koko pikseleinä. Pikseli on piste näytöllä.
Wx = 800
Wy = 600
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


Nayttotaajuus = 24  # Voit kokeilla eri näyttötaajuuksia


Dt = 1.0/Nayttotaajuus


# Peliin liittyviä muuttujia.
VLatka = 200.0  # Lätkän nopeus


Vmax = 250.0
MaxKiihtyvyys = 400.0


N_hyttyset = 100


# __init__(self, x, y, koko) on metodi, jolla lätkä luodaan


class Cl_Latka:
    def __init__(self, x, y, koko):
        self.paikka = (x, y)
        self.koko = koko
        self.color = Vihr
        self.liikkuu = False


    def piirra(self):
        (x, y) = self.paikka
        iPaikka = (int(x), int(y))
        pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        pygame.draw.circle(screen, Pun, iPaikka, int(self.koko/2), 0)


    def liiku(self, vxvy):
        (x, y) = self.paikka
        (vx, vy) = vxvy
        (x, y) = (x + vx*Dt, y + vy*Dt)
        # Lisäämällä tähän väliin muutaman if lauseen, voit korjata ohjelmaa
        #  niin, että Lätkä ei karkaa peli-ikkunan reunan ulkopuolelle
        # jos x on suurempi kuin niin Wx x.stä kannattaa vähentää Wx jne...
        self.paikka = (x, y)
        self.liikkuu = True


class Cl_Hyttynen:
    def __init__(self, x, y):
        self.paikka = (x, y)


        self.nopeus = (70.0, 50.0)  # kokeile tähän eri arvoja


        self.koko = 10
        self.color = (random.randint(0, 255),
                      random.randint(0, 255),
                      random.randint(0, 255))
        self.elossa = True


    def piirra(self):
        iPaikka = (int(self.paikka[0]), int(self.paikka[1]))
        iEllxy = (iPaikka[0], iPaikka[1], 4*self.koko, self.koko)
        if self.elossa:
            pygame.draw.circle(screen, self.color, iPaikka, self.koko, 0)
        else:
            pygame.draw.ellipse(screen, self.color, iEllxy, 0)


    def liiku(self):


        self.isku()  # Onko lätkä osunut?
        if self.elossa:
            self.nopeus = lenna(self.paikka, self.nopeus)


        else:
            self.nopeus = self.putoa()


# Siirrytään eteenpäin nopeus kertaa aikaväli
        self.paikka = dfdt(self.paikka, self.nopeus)


#   Jos Latka on lähellä tätä hyttystä sekä x- että y-suunnassa
#   lätkä osuu, hyttynen kuolee – litistyy ja muuttuu punaiseksi.
    def isku(self):
        if (etaisyys(self.paikka, Latka.paikka) < Latka.koko and
            Latka.liikkuu):
            self.elossa = False
            self.color = Pun


    def putoa(self):
        vx = random.uniform(-10, 10)
        if self.paikka[1] < Wy-self.koko:
            vy = random.uniform(20, 70)
        else:
            vx = 0
            vy = 0
        return (vx, vy)


def lenna(paikka, nopeus):
    kiihtyvyys = (random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys),
                  random.uniform(-MaxKiihtyvyys, MaxKiihtyvyys))
    nopeus = reunat(paikka, dfdt(nopeus, kiihtyvyys))
    return nopeus
# matka = nopeus*aika
def dfdt(xy, dxdy):
    (x, y) = xy
    (dx, dy) = dxdy
    return (x + dx*Dt, y + dy*Dt)


def reunat(xy, vxvy):
    (vx, vy) = vxvy
    (x, y) = xy
    # ei päästetä hyttystä reunojen ulkopuolelle
    if (x > Wx and vx > 0) or (x < 0 and vx < 0):
        vx = -vx
    elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
        vy = -vy
    # pysäytetään hyttynen, mikäli se lentää tolkuttoman lujaa
    elif sqrt(vx**2 + vy**2) > Vmax:
        vx, vy = 0.0, 0.0
    return (vx, vy)


# kahden pisteen p0 ja p1 välinen etäisyys xy-tasossa
def etaisyys(p0, p1):
    (x0, y0) = p0
    (x1, y1) = p1
    return sqrt((x1-x0)**2 + (y1-y0)**2)
# def ei_hyttysia(parvi):
#   for a in parvi:
#       if a.elossa:
#           return False
#   return True


# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy)  # peli-ikkunan korkeus ja leveys


screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Hyttysjahti")
pygame.init()
clock = pygame.time.Clock()
random.seed()


# Luodaan 'olio' tyyppiä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)


# Luodaan yksi hyttynen
hyttynen = Cl_Hyttynen(50, 50)


hyttyset = list(range(N_hyttyset))
for i in range(N_hyttyset):
    hyttyset[i] = Cl_Hyttynen(50, 50)


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
loppu = False
while not loppu:


    clock.tick(Nayttotaajuus)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            loppu = True


    screen.fill(Tausta)


    hyttynen.liiku()


    hyttynen.piirra()


#  Lasketaan hyttysille uudet paikat ja piiretään ne
    for i in range(N_hyttyset):
        hyttyset[i].liiku()
        hyttyset[i].piirra()


    Latka.liikkuu = False
#   Tarkistetaan, onko joku tai jotkin lätkää ohjaavista näppäimistä
# painettuna. Liikutetaan lätkää vastaavasti.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_DOWN]:
        Latka.liiku((0.0, VLatka))  # alas
    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0))  # vasemmalle
    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0))  # oikealle
    # Piirretään lätkä uudelle paikalleen
    Latka.piirra()


    # näytetään kaikki yllä 'piirretty' näytöllä
    pygame.display.flip()
    #
    # while silmukka päättyy tähän
#
# Ohjelman loppu
pygame.quit()


Se­li­tyk­siä ylläolevaan

import pygame
import random
from math import sqrt



Nayttotaajuus = 24  #  Voit ko­keil­la eri näyt­tö­taa­juuk­sia


 #  Pe­liin liit­ty­viä muut­tu­jia.
VLatka = 200.0  #  Lät­kän no­peus


Vmax = 250.0
MaxKiihtyvyys = 400.0


N_hyttyset = 100


 #  # # # # # # # # # # # # # # # # # # # # # # # # # #
 #  Pää­oh­jel­ma alkaa tästä
 #  # # # # # # # # # # # # # # # # # # # # # # # # # #



. . .
 #  Luo­daan 'olio' tyyp­piä Cl_latka
Latka = Cl_Latka(Wx-50, Wy-50, 15)



 #  Luo­daan yksi hyt­ty­nen
hyttynen = Cl_Hyttynen(50, 50)


Luo­daan parvi hyt­ty­siä. Hyt­tys­parvi esi­te­tään lis­ta­na:
hyttyset = [hyttynen0, hyttynen1, hyttynen2, ...]
hyttyset[0] = hyttynen0
hyttyset[1] = hyttynen1

Esi­täm­me siis hyt­tys­par­ven lis­ta­na.

../xml/Programming/PythonOpetus/spyder_figs/lista_esim.png

Voit tut­kia lis­to­ja iconsole ko­men­to­tul­kis­sa. Lista on luettelo hakasulkeiden sisällä. Jos parvi on lista niin parvi[0] viittaa listan ensimmäiseen alkioon. (Joissain ohjelmointikielissä indeksointi alkaa nollasta, toisissa yhdestä. )

 
hyttyset = list(range(N_hyttyset))
for i in range(N_hyttyset):
    hyttyset[i] = Cl_Hyttynen(50, 50)



 #
 #  Tois­te­taan allaolevaa while-silmukkaa, kun­nes käyt­tä­jä sul­kee peli-ik­ku­nan
 #  tai pai­naa ESC-iä
 #
loppu = False
while not loppu:



    hyttynen.liiku()


    hyttynen.piirra()


 #   Las­ke­taan hyt­ty­sil­le uudet pai­kat ja piiretään ne
    for i in range(N_hyttyset):
        hyttyset[i].liiku()
        hyttyset[i].piirra()



    #  näy­te­tään kaik­ki yllä 'piir­ret­ty' näy­töl­lä
    pygame.display.flip()
    #
    #  while sil­muk­ka päät­tyy tähän
 #
 #  Oh­jel­man loppu
pygame.quit()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Hyt­tys­Jahti_5.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä (edit: 2015-06-02)

Voi­mia ja lii­ket­tä

Seu­raa­vas­sa pe­lis­sä am­mu­taan mörs­sä­ril­lä. Sen kuu­laan vai­kut­ta­vat Maan veto­voima ja ilman­vas­tus. Mörssäpeliä seuraavan pelin avaruusalusta liikuttelevat Maan ja Kuun vetovoiman sekä rakettimoottorit. Siksi on hyvä kerrata, miten kappaleiden liikkeitä lasketaan.

Kesä­kuu 2015

Voi­man F kap­pa­leel­le an­ta­ma kiih­ty­vyys a on a = F/m, missä m on kappaleen massa. Kiihtyvyys on yhtä kuin nopeuden v muutos aikayksikössä eli, jos voima F on vakio, voimme laskea v(t+dt) = v(t) + F/m*dt. Nopeus v taas on aikayksikössä kuljettu matka s, eli jos nopeus on vakio, voimme laskea s(t+dt) = s(t) + v*dt. Voimat ja nopeudet eivät ole mörssäripelissä vakioita, mutta kun laskemme uudet voimat ja nopeudet aina lyhyen ajan dt välein, laskenta on niin tarkkaa, että virhettä ei huomaa.

Mörs­sä­ri­pe­lis­sä Maan veto­voima vai­kut­taa vain pysty­suun­taan — y-ak­se­lin suun­taan, mutta ilman­vas­tus lii­ket­tä vas­taan, eli kuu­lan nous­tes­sa vi­nos­ti ylös ilman­vas­tus vai­kut­taa vi­nos­ti alas ja len­non hui­pun jäl­keen päin­vas­toin. Voima kan­nat­taa jakaa x- ja y- suun­tai­seen osaan ja las­kea erik­seen no­peu­det ja siir­ty­mät x- ja y-suun­taan. Näin syn­tyy kuu­lan rata. Se­li­tän tämän uudellen mörs­sä­ri­pelin koo­din esit­te­lyn yh­tey­des­sä.

Pari seu­raa­va kap­pa­let­ta voit har­pa­ta, ellet ole kiin­nos­tu­nut nu­mee­ri­ses­ta ma­te­ma­tii­kas­ta.

Kiih­ty­vyys on no­peu­den de­ri­vaat­ta ja pai­kan toi­nen de­ri­vaat­ta ajan suh­teen, joten yh­tä­lö a = F/m on differentiaaliyhtälö. Ylläesitettyä tapaa laskea kuulan rata kutsutaan dif­fe­ren­tiaa­li­yh­tä­lön nu­mee­ri­sek­si rat­kai­se­mi­sek­si Eulerin menetelmällä. Differentiaaliyhtälöiden ratkaisemin ei kuulu peruskoulun eikä edes lukion oppimäärään, mutta jos vähänkään kiinnostaa, siihen kannattaa perehtyä, koska oikean fysiikan soveltaminen tekee tämän tyyppisistä peleistä paljon kiehtovampia.

Seu­raa­vas­sa kuuraketti-pelissä las­kin aluk­si ava­ruus­aluk­sen radan sa­mal­la me­ne­tel­mäl­lä kuin mörs­sä­ri­pe­lis­sä kuu­lan radan. Ellei alus­ta oh­ja­ta moot­to­reil­la, sen pi­täi­si pysyä vakio­ra­dal­la, mutta aluk­se­ni kier­si hi­taas­ti kas­va­vaa spi­raa­lia. Ar­ve­lin sen joh­tu­van Eu­le­rin me­ne­tel­män epätarkkuudesta. Virhe tulee siitä, että oletetaan voima ja kiihtyvyys vakioiksi aikavälin dt ajaksi, mistä tulee sitä enemmän virhettä, mitä suurempi on dt. Jos taas dt:n valitsee liian lyhyeksi, derivaatat saattavat pyöristyä nolliksi, koska tietokoneella luvut esitetään äärellisellä tarkkuudella. (Ehkä nollaksi pyöristymisen takia alus "juuttui" muutaman kerran ulkoavaruuteen, vaikka sen olisi pitänyt pikku hiljaa lähteä Maata kohti.)

Siir­ryin käyt­tä­mään aluk­sen liik­keen las­ke­mi­ses­sa Runge-Kutta me­ne­tel­mää ja alus alkoi pysyä kiltisti radallaan. Algoritmin ohjelmointi vaati minulta pikkuisen miettimistä, joten koodia ei liioin liene ihan helppo ymmärtää. En ole koskaan yrittänyt ymmärtää, mihin Runge-Kutta algoritmi perustuu — miten se toimii. Tietysti minun pitäisi perehtyä, mutta moni matematiikan tuloksia voi soveltaa, vaikkei niitä osaakaan johtaa. Ainakin peleissä. Lentokoneita suunnitellessa on syytä ymmärtää käytettyjen matemaattisten menetelmien perusteet ja rajoitukset.

Esi­merk­ke­jä dif­fe­ren­tiaa­li­yh­tä­löi­den nu­mee­ri­ses­ta rat­kai­se­mi­ses­ta pythonilla.

Esit­te­ly­video seu­raa­vas­ta pe­lis­tä — mörs­sä­ris­tä.

video varalle, jos yo. ei toimi


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py (edit: )

morssari.py

Tässä pe­lis­sä teh­tä­vä­nä on osua van­han ajan mörs­sä­ril­lä es­teen ta­ka­na ole­vaan maa­liin.

— Mörs­sä­riä suun­na­taan ylös ja alas nuoli­näp­päi­mil­lä.

— Nu­me­ro­näp­päi­mis­tön + ja - -merkkeillä sää­de­tään ajo­pa­nok­sen kokoa

— Nu­me­ro­näp­päi­mis­tön näp­päi­mil­lä 1, 2, ja 3 voit va­li­ta tykin­kuu­lan ti­hey­den.

— z- ja x- näp­päi­mil­lä voit siir­tää mörs­sä­riä.

Mörs­sä­ri on ladattu, kun sen piippu muuttuu punaiseksi. Se laukaistaan painamalla välilyöntiä.

F1-näppaimen vai­ku­tuk­sen voit sel­vit­tää itse.

Wed Apr 22 08:14:27 2020


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > v0: Ohjelman runko + mörssäri + "vuori" (edit: )

v0: Oh­jel­man runko + mörs­sä­ri + "vuori"

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

Jot­kin Pythonin funk­tiot — kuten pygamen piirtofuktiot — vaa­ti­vat ar­gu­men­teik­seen koko­nais­lu­ku­ja, koska koko­nais­luvut ja reaa­li­luvut tal­len­ne­taan tieto­ko­neen muis­tiin eri ta­val­la. Reaa­li­lu­vus­ta saa koko­nais­luvun int-funtiolla seu­raa­vas­ti: Ko­men­to x = 4/3 antaa x:lle arvon 1.33333... ja i = int(x) tai komento i = int(4/3) antaa i:lle arvon 1

 #  Peli-ik­ku­na päi­vi­te­tään Nayttotaajuus ker­taa se­kun­nis­sa
Nayttotaajuus = 48



 #  Tällä tykin säätö saa­daan jou­he­vak­si kai­kil­la näyt­tö­taa­juuk­sil­la
saatoaskel = 48.0/Nayttotaajuus



 #  No­peu­tus = 1.0 ->  oikea "fil­min" no­peus
Nopeutus = 5


Pa­ra­met­ril­la no­peu­tus kuula saadaan lentämään todellista nopeutta nopeammin, mikäli peli alkaa tuntua liian hitaalta.

g_maa = 9.81  #  pu­toa­mis­kiih­ty­vyys
RKuula = 0.1  #  kuu­lan säde met­rei­nä





 #  Las­ke­taan, mihin tiet­ty peli­maailman piste kuu­luu peli-ik­ku­nas­sa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)



pygame-peli-ikkunassa origo on va­sem­mas­sa ylä­kul­mas­sa ja y kas­vaa alas­päin. Ylläoleva funk­tio skaa­laa maas­ton peli-ik­ku­naan ja 'kään­tää' koor­di­naa­tis­ton niin, että peli­maail­mas­sa origo on va­sem­mas­sa ala­kul­mas­sa ja y kas­vaa ylös­päin siir­ryt­täes­sä.

Jos p_maas­to = (x_maas­to, y_maas­to) on piste pelimaailman maastossa niin p_ik­ku­na = xy_ruu­dul­la(p_maas­to) on sitä vastaava piste peli-ikkunassa.

Voit ko­keil­la funkiota kir­joit­ta­mal­la kon­so­lil­le esi­mer­kik­si xy_ruu­dul­la(0,0)

Sen miet­ti­mi­nen, mitä tuo tar­koit­taa ja miten muun­nos las­ke­taan, on hyvä geo­met­rian teh­tä­vä.

funk­tio int(z) muuttaa z:n kokonaisluvuksi, koska pygamen piirto-funktioille 'kelpaavat' vain kokonaislukuargumentit.

def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)




def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


Funk­tio r_xy(kulma, r) laskee pisteen x- ja y- koordinaatit, kun tiedetään, missä suunnassa (kulma) ja kuinka kaukana origosta (etäisyys = r) piste on. Kuvan piirtäminen auttanee ymmärtämään.

def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula



Haus­kan pelin olisi voi­nut to­teut­taa miet­ti­mät­tä kuu­lan mit­to­ja ja mas­saa, mutta va­rau­dum­me sii­hen, että voim­me ko­keil­la pe­lis­sä kuu­lia, joil­la on eri ti­heys ja siksi eri massa. Sama ajo­pa­nok­sen ruuti­määrä antaa (tässä pe­lis­sä) ras­kaam­mal­le kuu­lal­le pie­nem­män lähtö­no­peu­den, mutta ilman­vas­tus ku­lut­taa ras­kaam­man kuu­lan liike-ener­giaa suh­tees­sa vä­hem­män, joten ras­kas kuula len­tää pidemälle. Tämän tark­ka ym­mär­tä­mi­nen vaa­tii pe­reh­ty­mis­tä fy­siik­kaan.

def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)



Kiih­ty­vyys = kuu­laan vai­kut­ta­vien voi­mien ai­heut­ta­ma muu­tos kuu­lan no­peu­del­le. Otam­me aluk­si huo­mioon vain maan veto­voi­man ai­heut­ta­man pu­toa­mis­kiih­ty­vyy­den, mutta li­sääm­me myö­hem­min ilman­vas­tuk­sen vai­ku­tuk­sen.

 #  Vuori ja "tuuli­viiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)

    def piirra(self):
        #  Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


Piir­re­tään laa­tik­ko ku­vaa­maan vuor­ta tms. es­tet­tä. Piir­re­tään vuo­ren hui­pul­le tuuli­viiri.

Peli-ohjelmassa ei riitä saada piir­ret­tyä jo­tain, vaan kap­pa­lei­ta pitää voida siir­rel­lä ja pitää voida las­kea, tör­mää­vät­kö ne toi­siin­sa. Mitä moni­mut­kai­sem­pia muo­dot ovat, sitä han­ka­lam­paa las­ke­mi­nen on. Siksi vuori on pelk­kä laa­tik­ko tässä pe­lis­sä



 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
 #  Morssari
 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  #  pii­pun asen­to
        self.p1x = 0.0  #  pii­pun pään x-koor­di­naat­ti
        self.p1y = 0.0  #  pii­pun pään y-koor­di­naat­ti
        self.vaunu = pygame.Rect(0, 0, w, h)


Osan yllämääritellyistä mörs­sä­rin omi­nai­suuk­sis­ta otam­me käyt­töön vasta myö­hem­mis­sä vai­heis­sa

    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        #  piir­re­tään mörs­sä­rin piip­pu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        #  Ajo­pa­nos­ten ruuti­mää­rän osoi­tin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        #  vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)



muut­tu­jien x,y, w ja h arvot on mää­ri­tet­ty ko­kei­le­mal­la niin, että mörs­sä­ri näyt­tää so­pi­van ko­koi­sel­ta mai­se­mas­sa


    #  Vau­nun lii­kut­te­lua z ja x näp­päi­mil­lä.
    #  Vau­nul­la ei pääse vuo­ren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0




 #  num+ ja num- näp­päi­mil­lä kut­su­taan tätä me­to­dia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


funk­tioi­den min ja max avul­la ra­ja­taan "ruuti­määrä" vä­lil­le 1..10. Ky­sees­sä ei ole mi­kään oikea ruuti­määrä. Ruuti­pus­sien luku­määrä voisi olla pa­rem­pi nimi.

 #  num-1, num-2 ja num-3 näp­päi­mil­lä va­li­taan kuu­lan tyyp­pi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


Fy­sii­kas­ta kiin­nos­tu­neil­le:

Kuu­lan paino vai­kut­taa sii­hen, kuin­ka suu­ren lähtö­no­peu­den tiet­ty ruuti­määrä antaa kuu­lal­le. Kuu­lan ti­heys vai­kut­taa sii­hen, kuin­ka suu­ren osuu­den ilman­vas­tus syö liike-ener­gias­ta. (Ilman­vas­tuk­sen vai­ku­tus li­sä­tään myö­hem­min.)

Ti­hey­det eivät tark­kaan ot­taen ole puun, rau­dan eikä lyi­jyn ti­heyk­siä,mutta näil­lä ar­voil­la tuli so­pi­vas­ti ti­hey­den vaih­te­lua.

 #  Kään­ne­tään piip­pua nuoli­näp­päi­mil­lä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma




 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
 #  Pää­oh­jel­ma alkaa tästä
 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

print("alku")
pygame.init()



    #  Piir­re­tään kaik­ki oliot peli-ik­ku­naan ja las­ke­taan kuu­lil­le ja
    #  sir­pa­leil­le uudet pai­kat peli-ik­ku­nas­sa
    morssari.piirra()
    vuori.piirra()



    pygame.display.flip()
    #  while sil­muk­ka päät­tyy tähän

pygame.quit()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_0.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > v1: Kuula, mörssärin laukaiseminen ja kuulan lento (edit: )

v1: Kuula, mörs­sä­rin lau­kai­se­mi­nen ja kuu­lan lento

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)


# Siirtymä ja nopeuden muutos ajassa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys0()


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# laukaistaan mörssäri välilyöntinäppäimellä
    def ammu(self):


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Ammus
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  # kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


    def piirra0(self):
        pygame.draw.circle(screen, self.vari,
                           xy_ruudulla(self.x, self.y), 5, 0)


    def liiku0(self):
        (self.x, self.y, self.vx, self.vy) = integ_euler(
            self.x, self.y, self.vx, self.vy, self.A, self.M)


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Laukaistaan mörssäri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


#    if not kuulat =  []:
#        for kuula in kuulat:


#            kuula.liiku0()
#            kuula.piirra0()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku0()
            kuula.piirra0()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

Jot­kin Pythonin funk­tiot — kuten pygamen piirtofuktiot — vaa­ti­vat ar­gu­men­teik­seen koko­nais­lu­ku­ja, koska koko­nais­luvut ja reaa­li­luvut tal­len­ne­taan tieto­ko­neen muis­tiin eri ta­val­la. Reaa­li­lu­vus­ta saa koko­nais­luvun int-funtiolla seu­raa­vas­ti: Ko­men­to x = 4/3 antaa x:lle arvon 1.33333... ja i = int(x) tai komento i = int(4/3) antaa i:lle arvon 1

 #  Siir­ty­mä ja no­peu­den muu­tos ajas­sa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys0()


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)



../xml/Programming/PythonOpetus/integrointi.png

Kuvan uu­del­leen piir­tä­mis­ten väli on dt = 1.0/Nayttotaajuus se­kun­tia. Pelin olioil­le pitää siis las­kea uusi paik­ka ajan dt vä­lein, eli pitää las­kea, pal­jon­ko pelin oliot liik­ku­vat ajas­sa dt. (Ku­vas­sa dt on ase­tet­tu keino­te­koi­ses­ti hyvin pit­käk­si.) Koska matka = no­peus*aika, uusi paik­ka voi­daan las­kea seu­raa­vas­ti:

x(t+dt) =x(t)+ vxdt
y(t+dt) =y(t)+ vydt

Huo­maa, että python-ohjelmassa x = x + dx ei ole yhtälö vaan tarkoittaa, että x:lle annetaan uusi arvo joka on x:n vanha arvo lisättynä dx:llä.

Yllä siir­ty­mä las­ke­taan No­peu­tus kertaa, minkä takia kaikki liikkuu No­peu­tus kertaa oikeaa nopeutta nopeammin. Nopeutus on tarpeen, koska pelimme kuula noudattaa fysiikan lakeja ja niiden mukaan mörssärin kuula lentää kohteeseensa niin hitaasti, että hätäisempi pelaaja kyllästyy.

 

Lai­toin funk­tion ni­mek­si integ_euler, koska oi­keas­taan ky­sees­sä on dif­fe­ren­tiaa­li­yh­tä­lön rat­kai­se­mi­ses­ta Eu­le­rin me­ne­tel­mäl­lä.

 #  lau­kais­taan mörs­sä­ri väli­lyön­ti­näp­päi­mel­lä
    def ammu(self):


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)



Ase­te­taan la­taus­ajak­si näyt­tö­taa­juu­des­ta riip­pu­va sa­tun­nais­luku. La­taus­aikaa vä­hen­ne­tään yh­del­lä piirrä- methodissa jo­kai­sel­la näyttöruuden päi­vi­tyk­sel­lä, joten la­taus­ajan las­ku­ri nollaantuu ajas­sa Nayttotaajuus/2 — 2*Nayttotaajuus.

La­taus­aika ja sen sa­tun­nai­suus tekee mie­les­tä­ni am­pu­mi­ses­ta vähän haas­ta­vam­paa.


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))



print-komento tu­los­taa kon­so­lil­le tie­to­ja lau­kauk­ses­ta. print(self.ruuti, self.kulma*180.0/pi, M, v0) toimisi ihan hyvin. (voit kokeilla) Monimutkaisella format-komennolla voidaan määrätty, monenko desimaalin tarkuudella luvut tulostetaan niin, että niitä on helpompi lukea.

Tämä se­li­tys kuu­lan alku­no­peu­den las­ke­mi­ses­ta ei ehkä kerro hir­veän tar­kas­ti to­del­li­suu­des­ta eikä kiin­nos­ta­ne kuin tek­nii­kas­ta to­sis­saan kiin­nos­tu­nei­ta.

No­peus, millä kuula läh­tee put­kes­ta, riip­puu ruuti­mää­räs­tä. No­peu­den x- ja y- kom­po­nen­tit riip­pu­vat put­ken asen­nos­ta

Ole­tin, että ajo­pa­nok­sen pa­la­mi­nen ai­heut­taa pii­pus­sa ruu­din mää­rään ver­ran­nol­li­sen keski­mää­räi­sen pai­neen ja siis keski­mää­räi­sen voi­man mörs­sä­rin s met­riä pit­käs­sä pii­pus­sa ete­ne­vään kuu­laan. To­del­li­suu­des­sa paine yleen­sä nou­see nol­las­ta huip­pu­ar­voon­sa ennen kuin kuula ehtii pii­pun puoli­vä­liin ja alkaa sit­ten las­kea.

Joka ta­pauk­ses­sa ruu­din te­ke­mä työ on yhtä kuin kuu­lan saama liike-ener­gia:

Fruuti s = 1/2 M v2


 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
 #  Ammus
 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  #  kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


    def piirra0(self):
        pygame.draw.circle(screen, self.vari,
                           xy_ruudulla(self.x, self.y), 5, 0)



    def liiku0(self):
        (self.x, self.y, self.vx, self.vy) = integ_euler(
            self.x, self.y, self.vx, self.vy, self.A, self.M)



    #  Lau­kais­taan mörs­sä­ri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())



 #     if not kuu­lat =  []:
 #         for kuula in kuu­lat:


 #             kuula.liiku0()
 #             kuula.piirra0()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku0()
            kuula.piirra0()


Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_1.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > v2: Lisätään maali, tuuli ja tuuliviiri. (edit: )

v2: Li­sä­tään maali, tuuli ja tuuli­viiri.

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


Tuuli = random.uniform(-50.0, 50.0)
# Tuuli = -50.0


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)


# Siirtymä ja nopeuden muutos ajassa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys0()


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


        # Tuuliviirin masto
        tuulix = Tuuli/50.0*0.05*Wx
        mastonKorkeus = 0.05*Wy
        pygame.draw.line(screen, Musta, (a+w/2, b),
                         (a+w/2, b-mastonKorkeus), 4)
        # Tuuliviiri
        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


    # Tarkistetaan, onko kuula, jonka koordinaatit ovat x ja y osunut vuoreen
    def osuma(self, x, y):
        return y < self.h and self.x1 < x and x < self.x1 + self.leveys


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Maali
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_maali:
    def __init__(self, xmin):
        self.x = random.uniform(xmin, 0.95*Wxmaasto)
        self.y = 0.0
        self.leveys = 0.05*Wxmaasto
        self.korkeus = 0.03*Wymaasto
        (x, y) = xy_ruudulla(self.x, self.korkeus)
        (w, h) = xy_skaalaus(self.leveys, self.korkeus)
        self.vaunu = pygame.Rect(x, y, w, h)
        self.vari = (225, 255, 0)
        self.ehja = True


    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)
    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            return True
        else:
            return False


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# laukaistaan mörssäri välilyöntinäppäimellä
    def ammu(self):


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Ammus
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  # kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


    def piirra0(self):
        pygame.draw.circle(screen, self.vari,
                           xy_ruudulla(self.x, self.y), 5, 0)


    def liiku0(self):
        (self.x, self.y, self.vx, self.vy) = integ_euler(
            self.x, self.y, self.vx, self.vy, self.A, self.M)


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


maali = Cl_maali(vuori.x1+2.0*vuori.leveys)


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Laukaistaan mörssäri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


    if maali.ehja:
        maali.piirra()


#    if not kuulat =  []:
#        for kuula in kuulat:


#            kuula.liiku0()
#            kuula.piirra0()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku0()
            kuula.piirra0()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

Tuuli = random.uniform(-50.0, 50.0)
 #  Tuuli = -50.0



Tuuli vai­kut­taa tuuli­vii­riin. Myö­hem­mäs­sä vai­hees­sa se vai­kut­taa myös kuu­lan len­toon.
        #  Tuuli­vii­rin masto
        tuulix = Tuuli/50.0*0.05*Wx
        mastonKorkeus = 0.05*Wy
        pygame.draw.line(screen, Musta, (a+w/2, b),
                         (a+w/2, b-mastonKorkeus), 4)
        #  Tuuli­viiri
        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


    #  Tar­kis­te­taan, onko kuula, jonka koor­di­naa­tit ovat x ja y osu­nut vuo­reen

    def osuma(self, x, y):
        return y < self.h and self.x1 < x and x < self.x1 + self.leveys



Hyvä geo­met­rian teh­tä­vä on tehdä tyy­lik­kääm­pi vuori piir­tä­mäl­lä moni­kul­mio ja mää­rit­tää ylläolevaan funtioon ehto, joka ker­too, onko kuula osu­nut vuo­reen.

This draws a triangle using the polygon command

pygame.draw.polygon(screen, BLACK, [[100, 100], [0, 200], [200, 200]], 5)

 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
 #  Maali
 #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_maali:
    def __init__(self, xmin):
        self.x = random.uniform(xmin, 0.95*Wxmaasto)
        self.y = 0.0
        self.leveys = 0.05*Wxmaasto
        self.korkeus = 0.03*Wymaasto
        (x, y) = xy_ruudulla(self.x, self.korkeus)
        (w, h) = xy_skaalaus(self.leveys, self.korkeus)
        self.vaunu = pygame.Rect(x, y, w, h)
        self.vari = (225, 255, 0)
        self.ehja = True



Maali si­joi­te­taan mörs­sä­ris­tä kat­soen vuo­ren taak­se sa­tun­nai­seen paik­kaan.

    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)

    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            return True
        else:
            return False



Tar­kis­te­taan, onko kuula, jonka koor­di­naa­tit ovat x ja y osu­nut maa­liin. Jo­kai­nen kuula kut­suu tätä me­to­dia jo­kai­sel­la pää­oh­jel­man while-silmukan kier­rok­sel­la. Jos kuula on osu­nut, maali ei enää ole ehjä.

Osu­man voisi tar­kis­taa myös pygamen funk­tiol­la self.vaunu.collidepoint(x,y), kunhan x ja y ovat näyttöruudun koordinaatteja, eivät kuulan koordinaatteja pelimaailmassa.

    def piirra0(self):
        pygame.draw.circle(screen, self.vari,
                           xy_ruudulla(self.x, self.y), 5, 0)



    def liiku0(self):
        (self.x, self.y, self.vx, self.vy) = integ_euler(
            self.x, self.y, self.vx, self.vy, self.A, self.M)



maali = Cl_maali(vuori.x1+2.0*vuori.leveys)



    if maali.ehja:
        maali.piirra()



 #             kuula.liiku0()
 #             kuula.piirra0()


            kuula.liiku0()
            kuula.piirra0()


Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_2.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > v3: Pysäytetään kuula sen osuessa johonkin. (edit: )

v3: Py­säy­te­tään kuula sen osues­sa jo­hon­kin.

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


Tuuli = random.uniform(-50.0, 50.0)
# Tuuli = -50.0


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)


# Siirtymä ja nopeuden muutos ajassa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys0()


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


        # Tuuliviirin masto
        tuulix = Tuuli/50.0*0.05*Wx
        mastonKorkeus = 0.05*Wy
        pygame.draw.line(screen, Musta, (a+w/2, b),
                         (a+w/2, b-mastonKorkeus), 4)
        # Tuuliviiri
        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


    # Tarkistetaan, onko kuula, jonka koordinaatit ovat x ja y osunut vuoreen
    def osuma(self, x, y):
        return y < self.h and self.x1 < x and x < self.x1 + self.leveys


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Maali
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_maali:
    def __init__(self, xmin):
        self.x = random.uniform(xmin, 0.95*Wxmaasto)
        self.y = 0.0
        self.leveys = 0.05*Wxmaasto
        self.korkeus = 0.03*Wymaasto
        (x, y) = xy_ruudulla(self.x, self.korkeus)
        (w, h) = xy_skaalaus(self.leveys, self.korkeus)
        self.vaunu = pygame.Rect(x, y, w, h)
        self.vari = (225, 255, 0)
        self.ehja = True


    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)
    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            return True
        else:
            return False


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# laukaistaan mörssäri välilyöntinäppäimellä
    def ammu(self):


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Ammus
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  # kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


        self.rajahdys = False
        self.rajahdysvaihe = 0
        self.rajahtanyt = 0


    def piirra0(self):
        pygame.draw.circle(screen, self.vari,
                           xy_ruudulla(self.x, self.y), 5, 0)


    def piirra(self):
        if self.rajahdys:
            vari = Pun
        else:
            vari = self.vari
        if not self.rajahtanyt:
            pygame.draw.circle(screen, vari,
                               xy_ruudulla(self.x, self.y), 5, 0)
            (a, b) = xy_ruudulla(self.x, self.y)
            (c, d) = xy_ruudulla(self.x-self.vx, self.y-self.vy)
            pygame.draw.line(screen, Kelt, (a, b), (c, d), 2)


    def liiku0(self):
        (self.x, self.y, self.vx, self.vy) = integ_euler(
            self.x, self.y, self.vx, self.vy, self.A, self.M)


    def liiku(self):
        self.rajahdys = (vuori.osuma(self.x, self.y) or (self.y < 20) or
                         maali.osuma(self.x, self.y))
        if not self.rajahdys:
            (self.x, self.y, self.vx, self.vy) = integ_euler(
                    self.x, self.y, self.vx, self.vy, self.A, self.M)


        else:
            self.vx = 0.0
            self.vy = 0.0


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


maali = Cl_maali(vuori.x1+2.0*vuori.leveys)


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Laukaistaan mörssäri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


    if maali.ehja:
        maali.piirra()


#    if not kuulat =  []:
#        for kuula in kuulat:


#            kuula.liiku0()
#            kuula.piirra0()


#            kuula.piirra()
#            kuula.liiku()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku0()
            kuula.piirra0()


            kuula.liiku()
            kuula.piirra()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

        self.rajahdys = False
        self.rajahdysvaihe = 0
        self.rajahtanyt = 0



    def piirra0(self):
        pygame.draw.circle(screen, self.vari,
                           xy_ruudulla(self.x, self.y), 5, 0)



    def piirra(self):
        if self.rajahdys:
            vari = Pun
        else:
            vari = self.vari
        if not self.rajahtanyt:
            pygame.draw.circle(screen, vari,
                               xy_ruudulla(self.x, self.y), 5, 0)
            (a, b) = xy_ruudulla(self.x, self.y)
            (c, d) = xy_ruudulla(self.x-self.vx, self.y-self.vy)
            pygame.draw.line(screen, Kelt, (a, b), (c, d), 2)



Jos me­neil­lään on kuu­lan rä­jäh­dys, piir­re­tään kuula pu­nai­sel­la, muu­ten kuu­lan ti­hey­den ker­to­val­la vä­ril­lä.

Rä­jäh­dyk­sen lo­put­tua kuula on rä­jäh­tä­nyt, eikä sitä enää piir­re­tä.

Kuu­lal­le piir­re­tään — huvin vuok­si — sen no­peu­teen ver­ran­nol­li­nen pyrs­tö.

    def liiku0(self):
        (self.x, self.y, self.vx, self.vy) = integ_euler(
            self.x, self.y, self.vx, self.vy, self.A, self.M)



    def liiku(self):
        self.rajahdys = (vuori.osuma(self.x, self.y) or (self.y < 20) or
                         maali.osuma(self.x, self.y))
        if not self.rajahdys:
            (self.x, self.y, self.vx, self.vy) = integ_euler(
                    self.x, self.y, self.vx, self.vy, self.A, self.M)


        else:
            self.vx = 0.0
            self.vy = 0.0


Ammus rä­jäh­tää, jos se osuu vuo­reen, maa­liin tai maa­han ( y < 2) jos ammus ei rä­jäh­dä, se len­tää fy­sii­kan la­kien mu­kaan.

 #             kuula.liiku0()
 #             kuula.piirra0()


 #             kuula.piirra()
 #             kuula.liiku()



            kuula.liiku0()
            kuula.piirra0()


            kuula.liiku()
            kuula.piirra()


Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_3.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > v4: Lisätään kuulan räjähdys. (edit: )

v4: Li­sä­tään kuu­lan rä­jäh­dys.

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


Tuuli = random.uniform(-50.0, 50.0)
# Tuuli = -50.0


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)


# Siirtymä ja nopeuden muutos ajassa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys0()


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


        # Tuuliviirin masto
        tuulix = Tuuli/50.0*0.05*Wx
        mastonKorkeus = 0.05*Wy
        pygame.draw.line(screen, Musta, (a+w/2, b),
                         (a+w/2, b-mastonKorkeus), 4)
        # Tuuliviiri
        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


    # Tarkistetaan, onko kuula, jonka koordinaatit ovat x ja y osunut vuoreen
    def osuma(self, x, y):
        return y < self.h and self.x1 < x and x < self.x1 + self.leveys


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Maali
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_maali:
    def __init__(self, xmin):
        self.x = random.uniform(xmin, 0.95*Wxmaasto)
        self.y = 0.0
        self.leveys = 0.05*Wxmaasto
        self.korkeus = 0.03*Wymaasto
        (x, y) = xy_ruudulla(self.x, self.korkeus)
        (w, h) = xy_skaalaus(self.leveys, self.korkeus)
        self.vaunu = pygame.Rect(x, y, w, h)
        self.vari = (225, 255, 0)
        self.ehja = True


    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)
    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            return True
        else:
            return False


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# laukaistaan mörssäri välilyöntinäppäimellä
    def ammu(self):


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Ammus
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  # kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


        self.rajahdys = False
        self.rajahdysvaihe = 0
        self.rajahtanyt = 0


    def piirra(self):
        if self.rajahdys:
            vari = Pun
        else:
            vari = self.vari
        if not self.rajahtanyt:
            pygame.draw.circle(screen, vari,
                               xy_ruudulla(self.x, self.y), 5, 0)
            (a, b) = xy_ruudulla(self.x, self.y)
            (c, d) = xy_ruudulla(self.x-self.vx, self.y-self.vy)
            pygame.draw.line(screen, Kelt, (a, b), (c, d), 2)


    def liiku(self):
        self.rajahdys = (vuori.osuma(self.x, self.y) or (self.y < 20) or
                         maali.osuma(self.x, self.y))
        if not self.rajahdys:
            (self.x, self.y, self.vx, self.vy) = integ_euler(
                    self.x, self.y, self.vx, self.vy, self.A, self.M)


        else:
            self.vx = 0.0
            self.vy = 0.0


            self.rajahda()


    def rajahda(self):
        if self.rajahdysvaihe < Nayttotaajuus/2:


            self.rajahdysvaihe = self.rajahdysvaihe + 1
            for i in range(200):
                (p0x, p0y) = xy_ruudulla(self.x, self.y)
                (dx, dy) = r_xy(random.uniform(0, 2*pi),
                           self.rajahdysvaihe*random.uniform(0.25, 2.5))
                p1x = p0x + int(dx)
                p1y = p0y - int(dy)
                vari = (random.randint(160, 255),
                        random.randint(80, 255), 0)
                pygame.draw.line(screen, vari, (p0x, p0y), (p1x, p1y), 1)
        else:
            self.rajahtanyt = True


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


maali = Cl_maali(vuori.x1+2.0*vuori.leveys)


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Laukaistaan mörssäri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


    if maali.ehja:
        maali.piirra()


#    if not kuulat =  []:
#        for kuula in kuulat:


#            kuula.piirra()
#            kuula.liiku()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku()
            kuula.piirra()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

            self.rajahda()



    def rajahda(self):
        if self.rajahdysvaihe < Nayttotaajuus/2:


            self.rajahdysvaihe = self.rajahdysvaihe + 1
            for i in range(200):
                (p0x, p0y) = xy_ruudulla(self.x, self.y)
                (dx, dy) = r_xy(random.uniform(0, 2*pi),
                           self.rajahdysvaihe*random.uniform(0.25, 2.5))
                p1x = p0x + int(dx)
                p1y = p0y - int(dy)
                vari = (random.randint(160, 255),
                        random.randint(80, 255), 0)
                pygame.draw.line(screen, vari, (p0x, p0y), (p1x, p1y), 1)
        else:
            self.rajahtanyt = True



Rä­jäh­dys on moni­vai­hei­nen ja kes­tää Nayttotaajuus/3 peli-ik­ku­nan päi­vi­tyk­sen ajan. Joka vai­hees­sa piir­re­tään jouk­ko rä­jäh­dys­pis­tees­tä al­ka­via puna­kel­tai­sia ja­no­ja. Tähän ei ole fy­si­kaa­lis­ta pe­rus­tet­ta, mutta se näyt­tää mie­les­tä­ni haus­kal­ta.

Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_4.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > v5: Otetaan huomioon ilmanvastus, räjäytetään maali, lisätään ääniä. (edit: )

v5: Ote­taan huo­mioon ilman­vas­tus, rä­jäy­te­tään maali, li­sä­tään ääniä.

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


Tuuli = random.uniform(-50.0, 50.0)
# Tuuli = -50.0


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)


def kiihtyvyys(vx, vy, A, m):
    c = 0.15
    vx = vx - Tuuli  # Kuulan vaakanopeus ilman suhteen
    v2 = vx**2 + vy**2
    v = sqrt(v2)
    Fd = -c*A*v2/m
    ax = vx/v*Fd
    ay = vy/v*Fd-g_maa
    return (ax, ay)


# Siirtymä ja nopeuden muutos ajassa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys0()


        (ax, ay) = kiihtyvyys(vx, vy, A, M)


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)


def hehku(color):
    (r, g, b) = color
    r = max(0, min(255, r + random.randint(-10, 10)))
    g = max(0, min(255, g + random.randint(-10, 10)))
    b = max(0, min(255, b + random.randint(-10, 10)))
    return (r, g, b)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


        # Tuuliviirin masto
        tuulix = Tuuli/50.0*0.05*Wx
        mastonKorkeus = 0.05*Wy
        pygame.draw.line(screen, Musta, (a+w/2, b),
                         (a+w/2, b-mastonKorkeus), 4)
        # Tuuliviiri
        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


    # Tarkistetaan, onko kuula, jonka koordinaatit ovat x ja y osunut vuoreen
    def osuma(self, x, y):
        return y < self.h and self.x1 < x and x < self.x1 + self.leveys


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Maali
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_maali:
    def __init__(self, xmin):
        self.x = random.uniform(xmin, 0.95*Wxmaasto)
        self.y = 0.0
        self.leveys = 0.05*Wxmaasto
        self.korkeus = 0.03*Wymaasto
        (x, y) = xy_ruudulla(self.x, self.korkeus)
        (w, h) = xy_skaalaus(self.leveys, self.korkeus)
        self.vaunu = pygame.Rect(x, y, w, h)
        self.vari = (225, 255, 0)
        self.ehja = True


    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)
    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            for i in range(500):
                sirpaleet.append(Cl_sirpale(self.x, self.korkeus, self.vari))
            osuma_snd.play()


            return True
        else:
            return False


# Sirpale
class Cl_sirpale:
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.sx = self.x*random.uniform(2, 12)/100.0
        self.sy = self.y*random.uniform(2, 12)/100.0
        self.paksuus = self.y*random.uniform(2, 12)/100.0
        self.A = self.sx*self.sy
        self.M = self.A*self.paksuus*random.uniform(500.0, 2000.0)
        self.color = color
        (self.vx, self.vy) = r_xy(random.uniform(0.5, pi-0.5),
                             random.uniform(10.0, 150.0))


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        pygame.draw.rect(screen, self.color,
                         (x+int(self.sx/2), y+int(self.sy/2),
                          int(self.sx), int(self.sy)), 0)


    # Sirpaleet liikkuvat samalla dynamiikalla kuin kuulatkin
    def liiku(self):
        if self.y > 2.0:


            self.sx = min(12, max(2, self.sx + random.randint(-2, 2)))
            self.sy = min(12, max(2, self.sy + random.randint(-2, 2)))
            self.color = hehku(self.color)


            (self.x, self.y, self.vx, self.vy) = integ_euler(
                        self.x, self.y, self.vx, self.vy, self.A, self.M)


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


        self.ruuti = min(11.0, max(1.0, self.ruuti + 2.0*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# laukaistaan mörssäri välilyöntinäppäimellä
    def ammu(self):


        morssari_snd.play()


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Ammus
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  # kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


        self.rajahdys = False
        self.rajahdysvaihe = 0
        self.rajahtanyt = 0


    def piirra(self):
        if self.rajahdys:
            vari = Pun
        else:
            vari = self.vari
        if not self.rajahtanyt:
            pygame.draw.circle(screen, vari,
                               xy_ruudulla(self.x, self.y), 5, 0)
            (a, b) = xy_ruudulla(self.x, self.y)
            (c, d) = xy_ruudulla(self.x-self.vx, self.y-self.vy)
            pygame.draw.line(screen, Kelt, (a, b), (c, d), 2)


    def liiku(self):
        self.rajahdys = (vuori.osuma(self.x, self.y) or (self.y < 20) or
                         maali.osuma(self.x, self.y))
        if not self.rajahdys:
            (self.x, self.y, self.vx, self.vy) = integ_euler(
                    self.x, self.y, self.vx, self.vy, self.A, self.M)


        else:
            self.vx = 0.0
            self.vy = 0.0


            self.rajahda()


    def rajahda(self):
        if self.rajahdysvaihe < Nayttotaajuus/2:


            if self.rajahdysvaihe ==  0:
                kuula_snd.play()
                kuula_snd.fadeout(600)


            self.rajahdysvaihe = self.rajahdysvaihe + 1
            for i in range(200):
                (p0x, p0y) = xy_ruudulla(self.x, self.y)
                (dx, dy) = r_xy(random.uniform(0, 2*pi),
                           self.rajahdysvaihe*random.uniform(0.25, 2.5))
                p1x = p0x + int(dx)
                p1y = p0y - int(dy)
                vari = (random.randint(160, 255),
                        random.randint(80, 255), 0)
                pygame.draw.line(screen, vari, (p0x, p0y), (p1x, p1y), 1)
        else:
            self.rajahtanyt = True


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
osuma_snd = pygame.mixer.Sound(
    'Explosion_Ultra_Bass-Mark_DiAngelo-1810420658.wav')
morssari_snd = pygame.mixer.Sound('morssari.wav')
kuula_snd = pygame.mixer.Sound('kuula.wav')


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


maali = Cl_maali(vuori.x1+2.0*vuori.leveys)


sirpaleet = []


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Laukaistaan mörssäri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


    if maali.ehja:
        maali.piirra()


#            kuula.piirra()
#            kuula.liiku()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku()
            kuula.piirra()


            if kuula.rajahtanyt:
                del kuulat[k]


    if not sirpaleet ==  []:
        for sirpale in sirpaleet:
            sirpale.liiku()
            sirpale.piirra()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

Ilman­vas­tuk­sen takia tuuli vai­kut­taa kuu­lan lento­ra­taan ja mitä kevyempi kuula, sitä enemmän ilmanastus ja tuuli vaikuttavat.

def kiihtyvyys0():
    ax = 0.0
    ay = -g_maa
    return (ax, ay)



def kiihtyvyys(vx, vy, A, m):
    c = 0.15
    vx = vx - Tuuli  #  Kuu­lan vaaka­no­peus ilman suh­teen
    v2 = vx**2 + vy**2
    v = sqrt(v2)
    Fd = -c*A*v2/m
    ax = vx/v*Fd
    ay = vy/v*Fd-g_maa
    return (ax, ay)



Funk­tio kiih­ty­vyys() laskee kuulan x ja y -suuntaisen kiihtyvyyden, kun ilmanvastus otetaan huomioon. Ilmanvastuksen kerroin c on haettu kokeilemalla peliin sopiva arvo, jolla kuula lentää mukavasti. Se ei siis ole todellinen pyöreälle kuulalle oikeasti laskettu kerroin. Seuraava kuva yrittää selittää, miten fysiikan lakien, yhdenmuotoisten kolmioiden ja Pythagoraan lauseen perusteella ilmanvastuksen vaikutus lasketaan ylläolevassa funktiossa. Ilman aiempia tietoja selitystä voi olla vaikea ymmärtää, mutta sitä ei olekaan pakko ymmärtää.

../xml/Programming/PythonOpetus/voimat.png

Kuu­lan koh­dis­tu­vat voi­mat

Ilman ai­heut­ta­ma vas­tus on ver­ran­nol­li­nen no­peu­den ne­liöön ja kap­pa­leen poik­ki­pin­taan:

F d = c A v 2

Voi­man ai­heut­ta­ma kiih­ty­vyys saa­daan New­ton liike­lais­ta:

a = F d / m
 
        (ax, ay) = kiihtyvyys0()


        (ax, ay) = kiihtyvyys(vx, vy, A, M)


def hehku(color):
    (r, g, b) = color
    r = max(0, min(255, r + random.randint(-10, 10)))
    g = max(0, min(255, g + random.randint(-10, 10)))
    b = max(0, min(255, b + random.randint(-10, 10)))
    return (r, g, b)



Muu­te­taan värin ku­ta­kin kom­po­nent­tia sa­tun­nai­ses­ti. Tätä funk­tio­ta käy­te­tään maa­lin sir­pa­lei­den värin as­teit­tai­seen muut­ta­mi­sen. Tämä efek­ti on ko­kei­lu, jolle ei tässä ta­pauk­ses­sa ole fy­si­kaa­lis­ta 'esi­kuvaa'. Hiil­lok­sen heh­kun voisi ehkä to­teut­taa tähän tyy­liin.

    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)

    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            for i in range(500):
                sirpaleet.append(Cl_sirpale(self.x, self.korkeus, self.vari))
            osuma_snd.play()


            return True
        else:
            return False



Maali rä­jäh­tää sir­pa­leik­si. Jo­kai­nen sir­pa­le li­sä­tään lis­taan sir­pa­leet. Käyn­nis­te­tään rä­jäh­dyk­sen ääni

 #  Sir­pa­le
class Cl_sirpale:
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.sx = self.x*random.uniform(2, 12)/100.0
        self.sy = self.y*random.uniform(2, 12)/100.0
        self.paksuus = self.y*random.uniform(2, 12)/100.0
        self.A = self.sx*self.sy
        self.M = self.A*self.paksuus*random.uniform(500.0, 2000.0)
        self.color = color
        (self.vx, self.vy) = r_xy(random.uniform(0.5, pi-0.5),
                             random.uniform(10.0, 150.0))



Maa­lin sir­pa­leet ole­te­taan suora­kul­mai­sik­si sär­miöik­si, joil­le an­ne­taan sa­tun­nai­nen ti­heys ja mitat. Ilman­vas­tuk­seen vai­kut­ta­vaa pinta-ala A ei tie­ten­kään ole to­del­li­nen. Tar­koi­tus on vain luoda kap­pa­lei­ta, jotka eivät kaik­ki lennä aivan sa­mal­la ta­val­la.

Sir­pa­le läh­tee liik­keel­le sa­tun­nai­sel­la no­peu­del­la sa­tun­nai­seen suun­taan 0.5 - pi-0.5 ra­diaa­nia (pi ra­diaa­nia = 180 as­tet­ta)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        pygame.draw.rect(screen, self.color,
                         (x+int(self.sx/2), y+int(self.sy/2),
                          int(self.sx), int(self.sy)), 0)



    #  Sir­pa­leet liik­ku­vat sa­mal­la dy­na­mii­kal­la kuin kuu­lat­kin
    def liiku(self):
        if self.y > 2.0:


            self.sx = min(12, max(2, self.sx + random.randint(-2, 2)))
            self.sy = min(12, max(2, self.sy + random.randint(-2, 2)))
            self.color = hehku(self.color)


            (self.x, self.y, self.vx, self.vy) = integ_euler(
                        self.x, self.y, self.vx, self.vy, self.A, self.M)



Vaih­del­laan sir­pa­lei­den kokoa as­teit­tain, niin ne näyt­tä­vät elä­väm­mil­tä. Hehku on ko­kei­lu, joka antaa sir­pa­lei­den värin muut­tua as­teit­tain.

        self.ruuti = min(11.0, max(1.0, self.ruuti + 2.0*m))


        morssari_snd.play()


            if self.rajahdysvaihe ==  0:
                kuula_snd.play()
                kuula_snd.fadeout(600)


Rä­jäh­dyk­sen en­sim­mäi­ses­sä vai­hees­sa käyn­nis­te­tään rä­jäh­dyk­sen ääni.
Ääni­efek­te­jä. Maa­lin rä­jäh­dyk­sen san­gen so­tai­san äänen löy­sin we­bis­tä. Muut — ei ehkä kaik­kien mie­les­tä kovin tyy­lik­käät ääni­efek­tit — tein rumpu­syn­te­ti­saat­to­ril­la.
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
osuma_snd = pygame.mixer.Sound(
    'Explosion_Ultra_Bass-Mark_DiAngelo-1810420658.wav')
morssari_snd = pygame.mixer.Sound('morssari.wav')
kuula_snd = pygame.mixer.Sound('kuula.wav')



sirpaleet = []



            if kuula.rajahtanyt:
                del kuulat[k]



kuula.piirra ei piirra rä­jäh­tä­nei­tä kuu­lia, joten ne hä­viä­vät nä­ky­vis­tä. Peri­aat­tees­sa rä­jäh­tä­neet kuu­lat sa­moin kuin maa­lin sir­pa­leet olisi kui­ten­kin pa­rem­pi hä­vit­tää ko­ko­naan del ko­men­nol­la ylläesitetyllä ta­val­la, koska jo­kai­nen oh­jel­mas­sa luotu olio varaa hie­man ko­neen muis­tia. Tämän pelin ta­pauk­ses­sa sillä ei kui­ten­kaan ole käy­tän­nön mer­ki­tys­tä.

    if not sirpaleet ==  []:
        for sirpale in sirpaleet:
            sirpale.liiku()
            sirpale.piirra()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_5.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > morssari.py > ??? (edit: )

???

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)
MaastoVari = (95, 95, 20)


# Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
Nayttotaajuus = 48


# Tällä tykin säätö saadaan jouhevaksi kaikilla näyttötaajuuksilla
saatoaskel = 48.0/Nayttotaajuus


# Nopeutus = 1.0 ->  oikea "filmin" nopeus
Nopeutus = 5


Tuuli = random.uniform(-50.0, 50.0)
# Tuuli = -50.0


g_maa = 9.81  # putoamiskiihtyvyys
RKuula = 0.1  # kuulan säde metreinä


# Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
def xy_ruudulla(xmaasto, ymaasto):
    xpikseli = int(xmaasto*scale)
    ypikseli = int(Wy-ymaasto*scale)
    return (xpikseli, ypikseli)


def xy_skaalaus(w, h):
    xpikseli = int(w*scale)
    ypikseli = int(h*scale)
    return (xpikseli, ypikseli)


def r_xy(kulma, r):
    return (r*cos(kulma), r*sin(kulma))


def kuulanMassa(tiheys):
    VKuula = 4/3*pi*RKuula**3
    return tiheys*VKuula


def kiihtyvyys(vx, vy, A, m):
    c = 0.15
    vx = vx - Tuuli  # Kuulan vaakanopeus ilman suhteen
    v2 = vx**2 + vy**2
    v = sqrt(v2)
    Fd = -c*A*v2/m
    ax = vx/v*Fd
    ay = vy/v*Fd-g_maa
    return (ax, ay)


# Siirtymä ja nopeuden muutos ajassa dt
def integ_euler(x, y, vx, vy, A, M):
    dt = 1.0/Nayttotaajuus
    for i in range(Nopeutus):


        (ax, ay) = kiihtyvyys(vx, vy, A, M)


        (x, y, vx, vy) = (x + vx*dt, y + vy*dt, vx + ax*dt, vy + ay*dt)
    return (x, y, vx, vy)


def hehku(color):
    (r, g, b) = color
    r = max(0, min(255, r + random.randint(-10, 10)))
    g = max(0, min(255, g + random.randint(-10, 10)))
    b = max(0, min(255, b + random.randint(-10, 10)))
    return (r, g, b)


# Vuori ja "tuuliviiri"
class Cl_vuori:
    def __init__(self):
        self.x1 = random.uniform(0.3*Wxmaasto, 0.6*Wxmaasto)
        self.leveys = 0.1*Wxmaasto
        self.h = random.uniform(0.0, 0.6*Wymaasto)
    def piirra(self):
        # Vuori
        (a, b) = xy_ruudulla(self.x1, self.h)
        (w, h) = xy_skaalaus(self.leveys, self.h)
        pygame.draw.rect(screen, MaastoVari, (a, b, w, h), 0)


        # Tuuliviirin masto
        tuulix = Tuuli/50.0*0.05*Wx
        mastonKorkeus = 0.05*Wy
        pygame.draw.line(screen, Musta, (a+w/2, b),
                         (a+w/2, b-mastonKorkeus), 4)
        # Tuuliviiri
        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


    # Tarkistetaan, onko kuula, jonka koordinaatit ovat x ja y osunut vuoreen
    def osuma(self, x, y):
        return y < self.h and self.x1 < x and x < self.x1 + self.leveys


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Maali
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_maali:
    def __init__(self, xmin):
        self.x = random.uniform(xmin, 0.95*Wxmaasto)
        self.y = 0.0
        self.leveys = 0.05*Wxmaasto
        self.korkeus = 0.03*Wymaasto
        (x, y) = xy_ruudulla(self.x, self.korkeus)
        (w, h) = xy_skaalaus(self.leveys, self.korkeus)
        self.vaunu = pygame.Rect(x, y, w, h)
        self.vari = (225, 255, 0)
        self.ehja = True


    def piirra(self):
        if self.ehja:
            pygame.draw.ellipse(screen, self.vari, self.vaunu, 0)
    def osuma(self, x, y):
        if (self.ehja and y < self.korkeus and
          self.x < x < self.x + self.leveys):
            self.ehja = False


            for i in range(500):
                sirpaleet.append(Cl_sirpale(self.x, self.korkeus, self.vari))
            osuma_snd.play()


            return True
        else:
            return False


# Sirpale
class Cl_sirpale:
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.sx = self.x*random.uniform(2, 12)/100.0
        self.sy = self.y*random.uniform(2, 12)/100.0
        self.paksuus = self.y*random.uniform(2, 12)/100.0
        self.A = self.sx*self.sy
        self.M = self.A*self.paksuus*random.uniform(500.0, 2000.0)
        self.color = color
        (self.vx, self.vy) = r_xy(random.uniform(0.5, pi-0.5),
                             random.uniform(10.0, 150.0))


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        pygame.draw.rect(screen, self.color,
                         (x+int(self.sx/2), y+int(self.sy/2),
                          int(self.sx), int(self.sy)), 0)


    # Sirpaleet liikkuvat samalla dynamiikalla kuin kuulatkin
    def liiku(self):
        if self.y > 2.0:


            self.sx = min(12, max(2, self.sx + random.randint(-2, 2)))
            self.sy = min(12, max(2, self.sy + random.randint(-2, 2)))
            self.color = hehku(self.color)


            (self.x, self.y, self.vx, self.vy) = integ_euler(
                        self.x, self.y, self.vx, self.vy, self.A, self.M)


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Morssari
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_morssari:
    def __init__(self):
        self.x = 0.05*Wxmaasto
        self.y = 0.01*Wymaasto
        (w, h) = xy_skaalaus(0.05*Wxmaasto, 0.03*Wymaasto)
        self.w = w
        self.h = h
        self.ruuti = 1.0
        self.KuulanTiheys = 5000.0
        self.kuulanVari = Sin
        self.latausaika = Nayttotaajuus
        self.kulma = pi/7.0  # piipun asento
        self.p1x = 0.0  # piipun pään x-koordinaatti
        self.p1y = 0.0  # piipun pään y-koordinaatti
        self.vaunu = pygame.Rect(0, 0, w, h)


    def piirra(self):
        (x, y) = xy_ruudulla(self.x, self.y)
        # piirretään mörssärin piippu
        (sx, sy) = r_xy(self.kulma, 200.0)
        self.p1x = self.x + sx
        self.p1y = self.y + sy
        if self.latausaika > 0:
            self.latausaika = self.latausaika - 1
            vari = Musta
        else:
            vari = Pun
        pygame.draw.line(screen, vari, (x, y),
                         xy_ruudulla(self.p1x, self.p1y), 10)
        # Ajopanosten ruutimäärän osoitin
        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
        # vaunu
        self.vaunu.center = (x-self.w/4, y)
        pygame.draw.ellipse(screen, Musta, self.vaunu, 0)


    # Vaunun liikuttelua z ja x näppäimillä.
    # Vaunulla ei pääse vuoren läpi ;-)
    def liiku_x(self, dx):
        self.x = self.x + dx
        if self.x > vuori.x1:
            self.x = 0.0


# num+ ja num- näppäimillä kutsutaan tätä metodia
    def add_ruuti(self, m):
        self.ruuti = min(11.0, max(1.0, self.ruuti + 0.5*m))


        self.ruuti = min(11.0, max(1.0, self.ruuti + 2.0*m))


# num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
    def valitseKuula(self, tyyppi):
        if tyyppi ==  'puu':
            self.KuulanTiheys = 1000.0
            self.kuulanVari = Kelt
        elif tyyppi ==  'rauta':
            self.KuulanTiheys = 5000.0
            self.kuulanVari = Sin
        elif tyyppi ==  'lyijy':
            self.KuulanTiheys = 12000.0
            self.kuulanVari = Musta


# Käännetään piippua nuolinäppäimillä
    def asento(self, dkulma):
        self.kulma = self.kulma + dkulma


# laukaistaan mörssäri välilyöntinäppäimellä
    def ammu(self):


        morssari_snd.play()


        self.latausaika = random.randint(Nayttotaajuus/2, 2*Nayttotaajuus)
        M = kuulanMassa(self.KuulanTiheys)
        (vx, vy) = self.kuulan_alkunopeus(M)
        return Cl_ammus(self.p1x, self.p1y, vx, vy, M, self.kuulanVari)


    def kuulan_alkunopeus(self, M):
        Cv0 = 600.0
        v0 = Cv0*sqrt(self.ruuti/M)
        print("ruuti: {:2.1f}".format(self.ruuti),
              "kulma: {:2.1f}".format(self.kulma*180.0/pi),
              "massa: {:4.1f}".format(M),
              "nopeus: {:4.0f}".format(v0))
        return (v0*cos(self.kulma),
                v0*sin(self.kulma))


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Ammus
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
class Cl_ammus:
    def __init__(self, x, y, vx, vy, M, vari):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.M = M  # kg/m**3
        self.A = pi*RKuula**2
        self.vari = vari


        self.rajahdys = False
        self.rajahdysvaihe = 0
        self.rajahtanyt = 0


    def piirra(self):
        if self.rajahdys:
            vari = Pun
        else:
            vari = self.vari
        if not self.rajahtanyt:
            pygame.draw.circle(screen, vari,
                               xy_ruudulla(self.x, self.y), 5, 0)
            (a, b) = xy_ruudulla(self.x, self.y)
            (c, d) = xy_ruudulla(self.x-self.vx, self.y-self.vy)
            pygame.draw.line(screen, Kelt, (a, b), (c, d), 2)


    def liiku(self):
        self.rajahdys = (vuori.osuma(self.x, self.y) or (self.y < 20) or
                         maali.osuma(self.x, self.y))
        if not self.rajahdys:
            (self.x, self.y, self.vx, self.vy) = integ_euler(
                    self.x, self.y, self.vx, self.vy, self.A, self.M)


        else:
            self.vx = 0.0
            self.vy = 0.0


            self.rajahda()


    def rajahda(self):
        if self.rajahdysvaihe < Nayttotaajuus/2:


            if self.rajahdysvaihe ==  0:
                kuula_snd.play()
                kuula_snd.fadeout(600)


            self.rajahdysvaihe = self.rajahdysvaihe + 1
            for i in range(200):
                (p0x, p0y) = xy_ruudulla(self.x, self.y)
                (dx, dy) = r_xy(random.uniform(0, 2*pi),
                           self.rajahdysvaihe*random.uniform(0.25, 2.5))
                p1x = p0x + int(dx)
                p1y = p0y - int(dy)
                vari = (random.randint(160, 255),
                        random.randint(80, 255), 0)
                pygame.draw.line(screen, vari, (p0x, p0y), (p1x, p1y), 1)
        else:
            self.rajahtanyt = True


# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
print("alku")
pygame.init()


pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
osuma_snd = pygame.mixer.Sound(
    'Explosion_Ultra_Bass-Mark_DiAngelo-1810420658.wav')
morssari_snd = pygame.mixer.Sound('morssari.wav')
kuula_snd = pygame.mixer.Sound('kuula.wav')


pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
# pelimaailman leveys "metreinä"
Wxmaasto = 5000.0
scale = Wx/Wxmaasto
# Korkeus pelimaailmassa
Wymaasto = Wy/scale
screen = pygame.display.set_mode((Wx, Wy))
pygame.display.set_caption(
    "Morssari")
clock = pygame.time.Clock()
random.seed()
screen.fill(Tausta)
# Luodaan mörssäri ja vuori
vuori = Cl_vuori()
morssari = Cl_morssari()
kuulat = []


maali = Cl_maali(vuori.x1+2.0*vuori.leveys)


sirpaleet = []


loppu = False
MuistaVanhat = False
while not loppu:
    clock.tick(Nayttotaajuus)
    if not MuistaVanhat:
        screen.fill(Tausta)
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type ==  pygame.QUIT:
            loppu = True
        if event.type ==  pygame.KEYDOWN:
            if event.key ==  pygame.K_ESCAPE:
                loppu = True
            # Pyyhitäänkö peli-ikkuna puhtaaksi joka kierroksella vai ei
            if event.key ==  pygame.K_F1:
                MuistaVanhat = not MuistaVanhat
            # säädellään ajopanoksen ruutimäärää
            if event.key ==  pygame.K_KP_PLUS:
                morssari.add_ruuti(1)
            if event.key ==  pygame.K_KP_MINUS:
                morssari.add_ruuti(-1)
            # valitaan kuulan tyyppi
            if event.key ==  pygame.K_KP1:
                morssari.valitseKuula('puu')
            if event.key ==  pygame.K_KP2:
                morssari.valitseKuula('rauta')
            if event.key ==  pygame.K_KP3:
                morssari.valitseKuula('lyijy')
    # Suunnataan ja siirrellään mörssäriä
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        morssari.asento(0.005*saatoaskel)
    if pressed[pygame.K_DOWN]:
        morssari.asento(-0.005*saatoaskel)
    if pressed[pygame.K_x]:
        morssari.liiku_x(saatoaskel)
    if pressed[pygame.K_z]:
        morssari.liiku_x(-saatoaskel)


    # Laukaistaan mörssäri
    if pressed[pygame.K_SPACE] and morssari.latausaika ==  0:
        kuulat.append(morssari.ammu())


    # Piirretään kaikki oliot peli-ikkunaan ja lasketaan kuulille ja
    # sirpaleille uudet paikat peli-ikkunassa
    morssari.piirra()
    vuori.piirra()


    if maali.ehja:
        maali.piirra()


#            kuula.piirra()
#            kuula.liiku()


    if not kuulat ==  []:
        for k, kuula in enumerate(kuulat):


            kuula.liiku()
            kuula.piirra()


            if kuula.rajahtanyt:
                del kuulat[k]


    if not sirpaleet ==  []:
        for sirpale in sirpaleet:
            sirpale.liiku()
            sirpale.piirra()


    pygame.display.flip()
    # while silmukka päättyy tähän
pygame.quit()


Se­li­tyk­siä ylläolevaan

 #  Peli-ik­ku­na päi­vi­te­tään Nayttotaajuus ker­taa se­kun­nis­sa
Nayttotaajuus = 48



Tuuli = random.uniform(-50.0, 50.0)
 #  Tuuli = -50.0



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: morssari_6.py

Seu­raa­vas­sa pe­lis­sä len­ne­tään Kuu­hun. Viodella ento Kuun kier­to­ra­dal­le pik­kui­sen rä­pis­tel­len

video varalle, jos yo. ei toimi


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > KuuAlus.py (edit: )

KuuAlus.py

Teh­tä­vä­nä on oh­ja­ta ava­ruus­alus Maata kiertävältä radalta Kuuta kiertävälle radalle.

Alus­ta oh­ja­taan nuoli­näp­päi­mil­lä sekä z- ja x-näp­päi­mil­lä. x kiih­dyt­tää alus­ta isol­la ra­ke­til­la, z jar­rut­taa isol­la ra­ke­til­la. Nuo­let va­sem­mal­le ja oi­keal­le hi­das­ta­vat ja kiih­dyt­tä­vät pie­nem­mäl­lä voi­mal­la. Ylä- ja ala­nuo­lil­la ra­ket­tia voi oh­ja­ta sivu­suun­nas­sa liike­ra­taan näh­den. Jos tekee kiih­dy­tyk­set ja hi­das­tuk­set oi­kein, sivu­suun­tais­ta oh­jaus­ta ei tar­vit­se.
F2 tuo näkyviin ohjaamista helpottavia suureita.
Numeronäppäimistön 0, 1 ja enter vaikuttavat pelin nopeuteen.
Numeronäppäimistön + ja - zoomaavat näyttöä
m- ja k- näppäimet siirtävät näytön keskipisteeseen joko Maan tai Kuun F1 jättää aluksen radan näkyviin.

Pe­reh­dy oh­jel­man toi­min­taa tar­kem­min lu­ke­mal­la koo­dia.

Ava­ruus­lento ei ole help­poa. Seu­raa­vat peu­ka­lo­sään­nöt saattavat auttaa.

Wed Apr 22 09:45:29 2020


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > KuuAlus.py > Ohjelman runko, Maa ja Kuu (edit: )

Oh­jel­man runko, Maa ja Kuu

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Harmaa = (128, 128, 128)
Keula = (0, 128, 255)
Pera = (255, 200, 0)
# Gravitaatiovakio
G = 6.674e-11
# Etäisyydellä r suunnassa kulma olevan pisteen
# x- ja y-koordinaatit
def rw_xy(r, kulma):
    return (r*cos(kulma), r*sin(kulma))


# Pelin ja simuloinnin ajoitus
# Nopeutus: Montako kertaa todellista nopeammin pelin
# halutaan etenevän
# Tscalea käytetään nopeutuksen sovittamisessa oikeaksi
class cl_Ajat:
    def __init__(self):
        self.Nopeutus = 2000.0
        self.Nayttotaajuus = 24
        self.Dt = 10.0
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))


    def Hidasta(self):
        if self.Nopeutus > 4000:
            self.Nopeutus -= 4000
            self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
            self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def Nopeuta(self):
        self.Nopeutus += 4000
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
        self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def Reset(self):
        self.Nopeutus = 1
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
        self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def aikaraportti(self, Tsc, Dt, Nfrek):
        print('Aika kuluu ' + str(int(Tsc*Dt*Nfrek)) +
         ' kertaa todellista nopeammin')


# Pelimaailman kuvaaminen näytölle peli-ikkunaan.
# Kuvakulman ja zoomauksen säätelyä.
class Cl_Kamera:
    def __init__(self):
        self.focus = 'maa'
        self.Wx = 1.0e+9
        self.Wy = self.Wx
        self.Wx0 = 0.0
        self.Wy0 = 0.0
# Lasketaan mitä pikseliä näytöllä vastaa pelimaailman piste.
    def xy_naytolla(self, xy):
        scale = Wxnaytto/self.Wx
        (x, y) = xy
        xpikseli = int(Wxnaytto/2 + (x-self.Wx0)*scale)
        ypikseli = int(Wynaytto/2-(y-self.Wy0)*scale)
        return (xpikseli, ypikseli)
    def xy_skaalaus(self, w, h):
        scale = Wxnaytto/self.Wx
        xpikseli = int(w*scale)
        ypikseli = int(h*scale)
        return (xpikseli, ypikseli)
    def skaalaus(self, x):
        scale = Wxnaytto/self.Wx
        xpikseli = int(x*scale)
        return xpikseli
# Sidotaan näytön keskipiste joko Maahan tai Kuuhun
    def liiku(self):
        if self.focus == 'kuu':
            (self.Wx0, self.Wy0) = Kuu.paikka
        else:
            (self.Wx0, self.Wy0) = (0.0, 0.0)
    def zoom(self, z):
        self.Wx *= z
        self.Wy = self.Wx


# Maa. Käytetään todellisia fysikaalisia arvoja.
# Maa pysyy paikallaan pelimaailman keskipisteessä.
class Cl_Maa:
    def __init__(self):
        self.R = 6.37e+6
        self.M = 5.9737e+24
# Sininen ympyrä symboloi maata.
    def piirra(self):
        (xi, yi) = Kamera.xy_naytolla((0, 0))
        ri = Kamera.skaalaus(self.R)
        pygame.draw.circle(screen, Sin, (xi, yi), ri, 0)
# http://fi.wikipedia.org/wiki/Painovoima#Newtonin_painovoimalaki
    def vetovoima(self, xy, m):
        (x, y) = xy
        d = sqrt(x**2 + y**2)
        F = G*m*self.M/d**2
        Fx = -F*x/d
        Fy = -F*y/d
        return (Fx, Fy)
    def crash(self, pxy):
        return (dist(pxy, (0.0, 0.0)) < self.R)
# Kuu. Käytetään todellisia arvoja, paitsi massalle, jonka kasvatin
# moninkertaiseksi, että alus olisi helpompi ohjata Kuun kiertoradalle.
class Cl_Kuu:
    def __init__(self):
        self.R = 1.74e+6
#        self.M = 7.35e+22
        self.M = 18.0e+23  # moninkertainen massa
        self.distMaa = 3.844e+8
        self.kulma = 0.0  # Kuun paikka radallaan radiaaneina
        self.paikka = rw_xy(self.distMaa, self.kulma)
        self.w = 2*pi/(27.3*24.0*3600.0)  # kiertonopeus rad/s
# Kuuta symboloi keltainen ympyrä
    def piirra(self):
        (xi, yi) = Kamera.xy_naytolla(self.paikka)
        pygame.draw.circle(screen, Kelt, (xi, yi),
                           Kamera.skaalaus(self.R), 0)
# piirretään Kuun rata helpottamaan navigointia
        pygame.draw.circle(screen, Harmaa, Kamera.xy_naytolla((0, 0)),
                           Kamera.skaalaus(self.distMaa), 1)
# Kuu liikettä ei lasketa vetovoimalakien mukaan vaan se kiertää
# pakotettua ympyrärataa Maan ympäri.
    def liiku(self):
        self.kulma += self.w*Aika.Dt*Aika.Tscale
        self.paikka = rw_xy(self.distMaa, self.kulma)
    def vetovoima(self, xy, m):
        (x0, y0) = self.paikka
        (x1, y1) = xy
        dx = x1-x0
        dy = y1-y0
        d = sqrt(dx**2 + dy**2)
        F = G*m*self.M/d**2
        Fx = -F*dx/d
        Fy = -F*dy/d
        return (Fx, Fy)
    def crash(self, pxy):
        return (dist(pxy, self.paikka) < self.R)


##= = = = = = = = = = = = = = = = = = = = = = =
#
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = =
# peli-ikkunan leveys näytöllä pikseleinä
pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wynaytto = int(1.0*D_Info.current_h)
Wxnaytto = Wynaytto
screen = pygame.display.set_mode((Wxnaytto, Wynaytto))
pygame.display.set_caption(
    "Matka Kuuhun")
pygame.init()
clock = pygame.time.Clock()
random.seed()
Kamera = Cl_Kamera()
Maa = Cl_Maa()
Kuu = Cl_Kuu()
Aika = cl_Ajat()


loppu = False
MuistaVanhat = False


while not loppu:


    if not MuistaVanhat:
        screen.fill(Musta)  # pyyhitään peli-ikkuna tyhjäksi
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_F1:
                MuistaVanhat = not MuistaVanhat


            if event.key == pygame.K_ESCAPE:
                loppu = True
            if event.key == pygame.K_KP_PLUS:
                Kamera.zoom(0.9)
            if event.key == pygame.K_KP_MINUS:
                Kamera.zoom(1.1)
            if event.key == pygame.K_m:
                Kamera.focus = 'maa'
            if event.key == pygame.K_k:
                Kamera.focus = 'kuu'
            if event.key == pygame.K_KP1:
                Aika.Nopeuta()
            if event.key == pygame.K_KP0:
                Aika.Hidasta()
            if event.key == pygame.K_KP_ENTER:
                Aika.Reset()


    Maa.piirra()
    Kuu.piirra()


    Kuu.liiku()
    Kamera.liiku()


    pygame.display.flip()
    clock.tick(Aika.Nayttotaajuus)
pygame.quit()


Se­li­tyk­siä ylläolevaan

Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Kuu­Alus_0.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > KuuAlus.py > Alus ja sen liikkuminen (edit: )

Alus ja sen liik­ku­mi­nen

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Harmaa = (128, 128, 128)
Keula = (0, 128, 255)
Pera = (255, 200, 0)
# Gravitaatiovakio
G = 6.674e-11
# Etäisyydellä r suunnassa kulma olevan pisteen
# x- ja y-koordinaatit
def rw_xy(r, kulma):
    return (r*cos(kulma), r*sin(kulma))


# Kahden pisteen välinen etäisyys
def dist(p1, p2):
    (x1, y1) = p1
    (x2, y2) = p2
    return sqrt((x2-x1)**2 + (y2-y1)**2)
# kiihtyvyys = Voima/massa, xy-tasossa
def Newton(F1, F2, F3, m):
    (f1x, f1y) = F1
    (f2x, f2y) = F2
    (f3x, f3y) = F3
    ax = (f1x+f2x+f3x)/m
    ay = (f1y+f2y+f3y)/m
    return (ax, ay)


# Kuun ja Maan vetovoimakentässä liikkuvan kappaleen
# kiihtyvyys. Riippu kappaleen paikasta, rakettimoottorin
# synnyttämästä voimasta ja kappaleen massasta
def kiihtyvyys(paikka, F, m):
    (x, y) = paikka
    Fmaa = Maa.vetovoima((x, y), m)
    Fkuu = Kuu.vetovoima((x, y), m)
    return Newton(Fmaa, Fkuu, F, m)
# paikan (x, y) ja nopeuden (vx, vy) derivaatat ajan suhteen
# (f1, f2, f3, f4)
# Ohjelma ei pysähdy, vaikka alus törmäisi Maahan tai Kuuhun.
# "Koukkaus" läheltä Maan ydintä aiheuttaa niin suuria kiihtyvyyksiä,
# että simulointi saattaa antaa epäfysikaalisia tuloksia.
# Siksi niistä varoitetaan.
def dfdt(yy, F, m):
    [x, y, vx, vy] = yy
    (ax, ay) = kiihtyvyys((x, y), F, m)
    if ax**2 + ay**2 > 300.0:
            Alus.warning((ax, ay))
    f1 = vx
    f2 = vy
    f3 = ax
    f4 = ay
    return [f1, f2, f3, f4]
# Paikan ja nopeuden muutokset ajassa Dt
# lasketaan Runge-Kutta algoritmilla
def rk(yy, m, F):
    yk1 = [0, 0, 0, 0]
    yk2 = [0, 0, 0, 0]
    yk3 = [0, 0, 0, 0]
    yy1 = [0, 0, 0, 0]
    kk1 = dfdt(yy, F, m)
    for i in range(4):
        yk1[i] = yy[i]+kk1[i]*Aika.Dt/2.0
    kk2 = dfdt(yk1, F, m)
    for i in range(4):
        yk2[i] = yy[i]+kk2[i]*Aika.Dt/2.0
    kk3 = dfdt(yk2, F, m)
    for i in range(4):
        yk3[i] = yy[i]+kk3[i]*Aika.Dt
    kk4 = dfdt(yk3, F, m)
    for i in range(4):
        yy1[i] = yy[i] + Aika.Dt/6.0*(kk1[i] +
                2.0*kk2[i] + 2.0*kk3[i] + kk4[i])
    return(yy1)


# Pelin ja simuloinnin ajoitus
# Nopeutus: Montako kertaa todellista nopeammin pelin
# halutaan etenevän
# Tscalea käytetään nopeutuksen sovittamisessa oikeaksi
class cl_Ajat:
    def __init__(self):
        self.Nopeutus = 2000.0
        self.Nayttotaajuus = 24
        self.Dt = 10.0
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))


    def Hidasta(self):
        if self.Nopeutus > 4000:
            self.Nopeutus -= 4000
            self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
            self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def Nopeuta(self):
        self.Nopeutus += 4000
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
        self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def Reset(self):
        self.Nopeutus = 1
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
        self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def aikaraportti(self, Tsc, Dt, Nfrek):
        print('Aika kuluu ' + str(int(Tsc*Dt*Nfrek)) +
         ' kertaa todellista nopeammin')


# Pelimaailman kuvaaminen näytölle peli-ikkunaan.
# Kuvakulman ja zoomauksen säätelyä.
class Cl_Kamera:
    def __init__(self):
        self.focus = 'maa'
        self.Wx = 1.0e+9
        self.Wy = self.Wx
        self.Wx0 = 0.0
        self.Wy0 = 0.0
# Lasketaan mitä pikseliä näytöllä vastaa pelimaailman piste.
    def xy_naytolla(self, xy):
        scale = Wxnaytto/self.Wx
        (x, y) = xy
        xpikseli = int(Wxnaytto/2 + (x-self.Wx0)*scale)
        ypikseli = int(Wynaytto/2-(y-self.Wy0)*scale)
        return (xpikseli, ypikseli)
    def xy_skaalaus(self, w, h):
        scale = Wxnaytto/self.Wx
        xpikseli = int(w*scale)
        ypikseli = int(h*scale)
        return (xpikseli, ypikseli)
    def skaalaus(self, x):
        scale = Wxnaytto/self.Wx
        xpikseli = int(x*scale)
        return xpikseli
# Sidotaan näytön keskipiste joko Maahan tai Kuuhun
    def liiku(self):
        if self.focus == 'kuu':
            (self.Wx0, self.Wy0) = Kuu.paikka
        else:
            (self.Wx0, self.Wy0) = (0.0, 0.0)
    def zoom(self, z):
        self.Wx *= z
        self.Wy = self.Wx


# Maa. Käytetään todellisia fysikaalisia arvoja.
# Maa pysyy paikallaan pelimaailman keskipisteessä.
class Cl_Maa:
    def __init__(self):
        self.R = 6.37e+6
        self.M = 5.9737e+24
# Sininen ympyrä symboloi maata.
    def piirra(self):
        (xi, yi) = Kamera.xy_naytolla((0, 0))
        ri = Kamera.skaalaus(self.R)
        pygame.draw.circle(screen, Sin, (xi, yi), ri, 0)
# http://fi.wikipedia.org/wiki/Painovoima#Newtonin_painovoimalaki
    def vetovoima(self, xy, m):
        (x, y) = xy
        d = sqrt(x**2 + y**2)
        F = G*m*self.M/d**2
        Fx = -F*x/d
        Fy = -F*y/d
        return (Fx, Fy)
    def crash(self, pxy):
        return (dist(pxy, (0.0, 0.0)) < self.R)
# Kuu. Käytetään todellisia arvoja, paitsi massalle, jonka kasvatin
# moninkertaiseksi, että alus olisi helpompi ohjata Kuun kiertoradalle.
class Cl_Kuu:
    def __init__(self):
        self.R = 1.74e+6
#        self.M = 7.35e+22
        self.M = 18.0e+23  # moninkertainen massa
        self.distMaa = 3.844e+8
        self.kulma = 0.0  # Kuun paikka radallaan radiaaneina
        self.paikka = rw_xy(self.distMaa, self.kulma)
        self.w = 2*pi/(27.3*24.0*3600.0)  # kiertonopeus rad/s
# Kuuta symboloi keltainen ympyrä
    def piirra(self):
        (xi, yi) = Kamera.xy_naytolla(self.paikka)
        pygame.draw.circle(screen, Kelt, (xi, yi),
                           Kamera.skaalaus(self.R), 0)
# piirretään Kuun rata helpottamaan navigointia
        pygame.draw.circle(screen, Harmaa, Kamera.xy_naytolla((0, 0)),
                           Kamera.skaalaus(self.distMaa), 1)
# Kuu liikettä ei lasketa vetovoimalakien mukaan vaan se kiertää
# pakotettua ympyrärataa Maan ympäri.
    def liiku(self):
        self.kulma += self.w*Aika.Dt*Aika.Tscale
        self.paikka = rw_xy(self.distMaa, self.kulma)
    def vetovoima(self, xy, m):
        (x0, y0) = self.paikka
        (x1, y1) = xy
        dx = x1-x0
        dy = y1-y0
        d = sqrt(dx**2 + dy**2)
        F = G*m*self.M/d**2
        Fx = -F*dx/d
        Fy = -F*dy/d
        return (Fx, Fy)
    def crash(self, pxy):
        return (dist(pxy, self.paikka) < self.R)


# Kuualus
# Sijoitetaan Maan kiertoradalle ja lasketaan nopeus,
# jolla se pysyy kiertoradalla.
class Cl_Alus:
    def __init__(self):
        self.m = 10000.0
        self.F = (0.0, 0.0)  # ohjausrakettien aiheuttama voima
        self.paikka = (Maa.R + 10000.0e+3, 0.0)
        self.nopeus = (0.0, self.alkunopeus())


# Lasketaan sopiva nopeus yhtälöstä
# Keskipakoisvoima = Maan vetovoima
    def alkunopeus(self):
        (x, y) = self.paikka
        return sqrt(x*9.81*(Maa.R/x)**2)
# Piirretään alukseksi kaksivärinen jana,
# josta näkee aluksen asennon ja suunnan
# Huomaa, että näytöllä y kasvaa alaspäin.
    def piirra(self):
        (vx, vy) = self.nopeus
        v = sqrt(vx**2+vy**2)
        lx = int(vx/v*12.0)
        ly = int(vy/v*12.0)
        (xi, yi) = Kamera.xy_naytolla(self.paikka)
        pygame.draw.line(screen, Pera, (xi-lx, yi+ly), (xi, yi), 3)
        pygame.draw.line(screen, Keula, (xi, yi), (xi+lx, yi-ly), 3)


    def liiku(self):
        (x, y) = self.paikka
        (vx, vy) = self.nopeus
        for i in range(Aika.Tscale):
            [x, y, vx, vy] = rk([x, y, vx, vy], self.m, self.F)
        self.paikka = (x, y)
        self.nopeus = (vx, vy)
# Ohjaus tapahtuu näppäimistön nuolilla.
# z ja x näppäimillä boostattu ohjaus
# Ohjauksilla 'vasemmalle' ja 'oikealle' ei yleensä ole
# käyttöä
    def ohjaus(self, suunta):
        (vx, vy) = self.nopeus
        v = sqrt(vx**2+vy**2)
        F = 100.0
        if suunta == 'vas':  # Jarruta
            self.F = (-vx/v*F, -vy/v*F)
        if suunta == 'oik':  # Kiihdytä
            self.F = (vx/v*F, vy/v*F)
        if suunta == 'ylos':  # vasemmalle
            self.F = (-vy/v*F, vx/v*F)
        if suunta == 'alas':  # oikealle
            self.F = (vy/v*F, -vx/v*F)
        if suunta == 'z':  # Jarruta lujaa
            self.F = (-10.0*vx/v*F, -10.0*vy/v*F)
        if suunta == 'x':  # Kiihdytä lujaa
            self.F = (10.0*vx/v*F, 10.0*vy/v*F)
# Törmäys! Simulointi laskee ehkä väärin tämän tilanteen.
    def warning(self, a):
        print('Törmäys?')
        screen.fill(Pun)
        pygame.time.wait(10)


##= = = = = = = = = = = = = = = = = = = = = = =
#
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = =
# peli-ikkunan leveys näytöllä pikseleinä
pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wynaytto = int(1.0*D_Info.current_h)
Wxnaytto = Wynaytto
screen = pygame.display.set_mode((Wxnaytto, Wynaytto))
pygame.display.set_caption(
    "Matka Kuuhun")
pygame.init()
clock = pygame.time.Clock()
random.seed()
Kamera = Cl_Kamera()
Maa = Cl_Maa()
Kuu = Cl_Kuu()
Aika = cl_Ajat()


Alus = Cl_Alus()


loppu = False
MuistaVanhat = False


while not loppu:


    Alus.F = (0.0, 0.0)


    if not MuistaVanhat:
        screen.fill(Musta)  # pyyhitään peli-ikkuna tyhjäksi
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_F1:
                MuistaVanhat = not MuistaVanhat


            if event.key == pygame.K_ESCAPE:
                loppu = True
            if event.key == pygame.K_KP_PLUS:
                Kamera.zoom(0.9)
            if event.key == pygame.K_KP_MINUS:
                Kamera.zoom(1.1)
            if event.key == pygame.K_m:
                Kamera.focus = 'maa'
            if event.key == pygame.K_k:
                Kamera.focus = 'kuu'
            if event.key == pygame.K_KP1:
                Aika.Nopeuta()
            if event.key == pygame.K_KP0:
                Aika.Hidasta()
            if event.key == pygame.K_KP_ENTER:
                Aika.Reset()


    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_RIGHT]:
        Alus.ohjaus('oik')
    if pressed[pygame.K_LEFT]:
        Alus.ohjaus('vas')
    if pressed[pygame.K_UP]:
        Alus.ohjaus('ylos')
    if pressed[pygame.K_DOWN]:
        Alus.ohjaus('alas')
    if pressed[pygame.K_z]:
        Alus.ohjaus('z')
    if pressed[pygame.K_x]:
        Alus.ohjaus('x')


    Maa.piirra()
    Kuu.piirra()


    Alus.piirra()


    Kuu.liiku()
    Kamera.liiku()


    Alus.liiku()
    if Maa.crash(Alus.paikka) or Kuu.crash(Alus.paikka):
        print('!!! Törmäys !!!')
        screen.fill(Pun)
        pygame.time.wait(10)
#        loppu = True


    pygame.display.flip()
    clock.tick(Aika.Nayttotaajuus)
pygame.quit()


Se­li­tyk­siä ylläolevaan

 #  Kuun ja Maan veto­voima­ken­täs­sä liik­ku­van kap­pa­leen
 #  kiih­ty­vyys. Riippu kap­pa­leen pai­kas­ta, ra­ket­ti­moot­to­rin
 #  syn­nyt­tä­mäs­tä voi­mas­ta ja kap­pa­leen mas­sas­ta
def kiihtyvyys(paikka, F, m):
    (x, y) = paikka
    Fmaa = Maa.vetovoima((x, y), m)
    Fkuu = Kuu.vetovoima((x, y), m)
    return Newton(Fmaa, Fkuu, F, m)


 #  pai­kan (x, y) ja no­peu­den (vx, vy) de­ri­vaa­tat ajan suh­teen
 #  (f1, f2, f3, f4)
 #  Oh­jel­ma ei py­säh­dy, vaik­ka alus tör­mäi­si Maa­han tai Kuu­hun.
 #  "Kouk­kaus" lä­hel­tä Maan ydin­tä ai­heut­taa niin suu­ria kiih­ty­vyyk­siä,
 #  että si­mu­loin­ti saat­taa antaa epä­fy­si­kaa­li­sia tu­lok­sia.
 #  Siksi niis­tä va­roi­te­taan.
def dfdt(yy, F, m):
    [x, y, vx, vy] = yy
    (ax, ay) = kiihtyvyys((x, y), F, m)
    if ax**2 + ay**2 > 300.0:
            Alus.warning((ax, ay))
    f1 = vx
    f2 = vy
    f3 = ax
    f4 = ay
    return [f1, f2, f3, f4]


 #  Pai­kan ja no­peu­den muu­tok­set ajas­sa Dt
 #  las­ke­taan Runge-Kutta al­go­rit­mil­la
def rk(yy, m, F):
    yk1 = [0, 0, 0, 0]
    yk2 = [0, 0, 0, 0]
    yk3 = [0, 0, 0, 0]
    yy1 = [0, 0, 0, 0]
    kk1 = dfdt(yy, F, m)
    for i in range(4):
        yk1[i] = yy[i]+kk1[i]*Aika.Dt/2.0
    kk2 = dfdt(yk1, F, m)
    for i in range(4):
        yk2[i] = yy[i]+kk2[i]*Aika.Dt/2.0
    kk3 = dfdt(yk2, F, m)
    for i in range(4):
        yk3[i] = yy[i]+kk3[i]*Aika.Dt
    kk4 = dfdt(yk3, F, m)
    for i in range(4):
        yy1[i] = yy[i] + Aika.Dt/6.0*(kk1[i] +
                2.0*kk2[i] + 2.0*kk3[i] + kk4[i])
    return(yy1)



Mörs­sä­rin kuu­lan radan las­kin Eu­le­rin me­ne­tel­mäl­lä, mutta kuu­aluk­sen liikeiden las­ke­mi­seen käy­tän Runge-Kutta me­ne­tel­mää.

 #  Pelin ja si­mu­loin­nin ajoi­tus
 #  No­peu­tus: Mon­ta­ko ker­taa to­del­lis­ta no­peam­min pelin
 #  ha­lu­taan ete­ne­vän
 #  Tscalea käy­te­tään no­peu­tuk­sen so­vit­ta­mi­ses­sa oi­keak­si
class cl_Ajat:
    def __init__(self):
        self.Nopeutus = 2000.0
        self.Nayttotaajuus = 24
        self.Dt = 10.0
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))



Dt on in­teg­roin­ti­askel Runge-Kutta al­go­rit­mis­sa. Mitä isom­pi Dt, sitä epä­tar­kem­paa las­ken­ta, mutta sitä vä­hem­män peli kuor­mit­taa tieto­ko­net­ta.
Alus = Cl_Alus()


    Alus.F = (0.0, 0.0)


    Alus.piirra()


    Alus.liiku()

    if Maa.crash(Alus.paikka) or Kuu.crash(Alus.paikka):
        print('!!! Törmäys !!!')
        screen.fill(Pun)
        pygame.time.wait(10)
 #         loppu = True



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Kuu­Alus_1.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä > KuuAlus.py > Ohjauksen apuvälineitä (edit: )

Oh­jauk­sen apu­vä­li­nei­tä

# -*- coding: utf-8 -*-
import pygame
import random
from math import pi, cos, sin, sqrt
Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Harmaa = (128, 128, 128)
Keula = (0, 128, 255)
Pera = (255, 200, 0)
# Gravitaatiovakio
G = 6.674e-11
# Etäisyydellä r suunnassa kulma olevan pisteen
# x- ja y-koordinaatit
def rw_xy(r, kulma):
    return (r*cos(kulma), r*sin(kulma))


# Kahden pisteen välinen etäisyys
def dist(p1, p2):
    (x1, y1) = p1
    (x2, y2) = p2
    return sqrt((x2-x1)**2 + (y2-y1)**2)
# kiihtyvyys = Voima/massa, xy-tasossa
def Newton(F1, F2, F3, m):
    (f1x, f1y) = F1
    (f2x, f2y) = F2
    (f3x, f3y) = F3
    ax = (f1x+f2x+f3x)/m
    ay = (f1y+f2y+f3y)/m
    return (ax, ay)


# Piirretään voiman suuntaa ja suuruutta kuvastava jana.
def nuoli(p0, Voima):
    (ix0, iy0) = p0
    (Fx, Fy) = Voima
    F = sqrt(Fx**2 + Fy**2)
# "ylipitkä" jana katkaistaan ja piirretään punaisella.
    if F > 600:
        color = Pun
        Fx = Fx/F*600.0
        Fy = Fy/F*600.0
    else:
        color = Vihr
    Fx = Fx*0.7
    Fy = Fy*0.7
# Huomaa, että näytöllä y kasvaa alaspäin, siksi -Fy.
    p1 = (ix0+Fx, iy0-Fy)
    pygame.draw.line(screen, color, p0, p1, 2)


# Kuun ja Maan vetovoimakentässä liikkuvan kappaleen
# kiihtyvyys. Riippu kappaleen paikasta, rakettimoottorin
# synnyttämästä voimasta ja kappaleen massasta
def kiihtyvyys(paikka, F, m):
    (x, y) = paikka
    Fmaa = Maa.vetovoima((x, y), m)
    Fkuu = Kuu.vetovoima((x, y), m)
    return Newton(Fmaa, Fkuu, F, m)
# paikan (x, y) ja nopeuden (vx, vy) derivaatat ajan suhteen
# (f1, f2, f3, f4)
# Ohjelma ei pysähdy, vaikka alus törmäisi Maahan tai Kuuhun.
# "Koukkaus" läheltä Maan ydintä aiheuttaa niin suuria kiihtyvyyksiä,
# että simulointi saattaa antaa epäfysikaalisia tuloksia.
# Siksi niistä varoitetaan.
def dfdt(yy, F, m):
    [x, y, vx, vy] = yy
    (ax, ay) = kiihtyvyys((x, y), F, m)
    if ax**2 + ay**2 > 300.0:
            Alus.warning((ax, ay))
    f1 = vx
    f2 = vy
    f3 = ax
    f4 = ay
    return [f1, f2, f3, f4]
# Paikan ja nopeuden muutokset ajassa Dt
# lasketaan Runge-Kutta algoritmilla
def rk(yy, m, F):
    yk1 = [0, 0, 0, 0]
    yk2 = [0, 0, 0, 0]
    yk3 = [0, 0, 0, 0]
    yy1 = [0, 0, 0, 0]
    kk1 = dfdt(yy, F, m)
    for i in range(4):
        yk1[i] = yy[i]+kk1[i]*Aika.Dt/2.0
    kk2 = dfdt(yk1, F, m)
    for i in range(4):
        yk2[i] = yy[i]+kk2[i]*Aika.Dt/2.0
    kk3 = dfdt(yk2, F, m)
    for i in range(4):
        yk3[i] = yy[i]+kk3[i]*Aika.Dt
    kk4 = dfdt(yk3, F, m)
    for i in range(4):
        yy1[i] = yy[i] + Aika.Dt/6.0*(kk1[i] +
                2.0*kk2[i] + 2.0*kk3[i] + kk4[i])
    return(yy1)


# Pelin ja simuloinnin ajoitus
# Nopeutus: Montako kertaa todellista nopeammin pelin
# halutaan etenevän
# Tscalea käytetään nopeutuksen sovittamisessa oikeaksi
class cl_Ajat:
    def __init__(self):
        self.Nopeutus = 2000.0
        self.Nayttotaajuus = 24
        self.Dt = 10.0
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))


    def Hidasta(self):
        if self.Nopeutus > 4000:
            self.Nopeutus -= 4000
            self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
            self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def Nopeuta(self):
        self.Nopeutus += 4000
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
        self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def Reset(self):
        self.Nopeutus = 1
        self.Tscale = max(1, int(self.Nopeutus/self.Nayttotaajuus/self.Dt))
        self.aikaraportti(self.Tscale, self.Dt, self.Nayttotaajuus)
    def aikaraportti(self, Tsc, Dt, Nfrek):
        print('Aika kuluu ' + str(int(Tsc*Dt*Nfrek)) +
         ' kertaa todellista nopeammin')


# Pelimaailman kuvaaminen näytölle peli-ikkunaan.
# Kuvakulman ja zoomauksen säätelyä.
class Cl_Kamera:
    def __init__(self):
        self.focus = 'maa'
        self.Wx = 1.0e+9
        self.Wy = self.Wx
        self.Wx0 = 0.0
        self.Wy0 = 0.0
# Lasketaan mitä pikseliä näytöllä vastaa pelimaailman piste.
    def xy_naytolla(self, xy):
        scale = Wxnaytto/self.Wx
        (x, y) = xy
        xpikseli = int(Wxnaytto/2 + (x-self.Wx0)*scale)
        ypikseli = int(Wynaytto/2-(y-self.Wy0)*scale)
        return (xpikseli, ypikseli)
    def xy_skaalaus(self, w, h):
        scale = Wxnaytto/self.Wx
        xpikseli = int(w*scale)
        ypikseli = int(h*scale)
        return (xpikseli, ypikseli)
    def skaalaus(self, x):
        scale = Wxnaytto/self.Wx
        xpikseli = int(x*scale)
        return xpikseli
# Sidotaan näytön keskipiste joko Maahan tai Kuuhun
    def liiku(self):
        if self.focus == 'kuu':
            (self.Wx0, self.Wy0) = Kuu.paikka
        else:
            (self.Wx0, self.Wy0) = (0.0, 0.0)
    def zoom(self, z):
        self.Wx *= z
        self.Wy = self.Wx


# Kiintotahtiä taustaksi ja kiintopisteiksi
class cl_Tahdet:
    def __init__(self):
        self.tt = []
        for i in range(1000):
            self.tt.append((random.uniform(-8.0e+8, 8.0e+8),
                       random.uniform(-8.0e+8, 8.0e+8)))
    def piirra(self):
        for tahti in self.tt:
            p = Kamera.xy_naytolla(tahti)
            color = (random.randint(100, 255),
                     random.randint(100, 255),
                     random.randint(100, 255))
            # jana, jonka loppupiste ja alkupiste ovat samoja,
            # piirtyy näytölle pisteenä
            pygame.draw.line(screen, color, p, p, 1)


# Maa. Käytetään todellisia fysikaalisia arvoja.
# Maa pysyy paikallaan pelimaailman keskipisteessä.
class Cl_Maa:
    def __init__(self):
        self.R = 6.37e+6
        self.M = 5.9737e+24
# Sininen ympyrä symboloi maata.
    def piirra(self):
        (xi, yi) = Kamera.xy_naytolla((0, 0))
        ri = Kamera.skaalaus(self.R)
        pygame.draw.circle(screen, Sin, (xi, yi), ri, 0)
# http://fi.wikipedia.org/wiki/Painovoima#Newtonin_painovoimalaki
    def vetovoima(self, xy, m):
        (x, y) = xy
        d = sqrt(x**2 + y**2)
        F = G*m*self.M/d**2
        Fx = -F*x/d
        Fy = -F*y/d
        return (Fx, Fy)
    def crash(self, pxy):
        return (dist(pxy, (0.0, 0.0)) < self.R)
# Kuu. Käytetään todellisia arvoja, paitsi massalle, jonka kasvatin
# moninkertaiseksi, että alus olisi helpompi ohjata Kuun kiertoradalle.
class Cl_Kuu:
    def __init__(self):
        self.R = 1.74e+6
#        self.M = 7.35e+22
        self.M = 18.0e+23  # moninkertainen massa
        self.distMaa = 3.844e+8
        self.kulma = 0.0  # Kuun paikka radallaan radiaaneina
        self.paikka = rw_xy(self.distMaa, self.kulma)
        self.w = 2*pi/(27.3*24.0*3600.0)  # kiertonopeus rad/s
# Kuuta symboloi keltainen ympyrä
    def piirra(self):
        (xi, yi) = Kamera.xy_naytolla(self.paikka)
        pygame.draw.circle(screen, Kelt, (xi, yi),
                           Kamera.skaalaus(self.R), 0)
# piirretään Kuun rata helpottamaan navigointia
        pygame.draw.circle(screen, Harmaa, Kamera.xy_naytolla((0, 0)),
                           Kamera.skaalaus(self.distMaa), 1)
# Kuu liikettä ei lasketa vetovoimalakien mukaan vaan se kiertää
# pakotettua ympyrärataa Maan ympäri.
    def liiku(self):
        self.kulma += self.w*Aika.Dt*Aika.Tscale
        self.paikka = rw_xy(self.distMaa, self.kulma)
    def vetovoima(self, xy, m):
        (x0, y0) = self.paikka
        (x1, y1) = xy
        dx = x1-x0
        dy = y1-y0
        d = sqrt(dx**2 + dy**2)
        F = G*m*self.M/d**2
        Fx = -F*dx/d
        Fy = -F*dy/d
        return (Fx, Fy)
    def crash(self, pxy):
        return (dist(pxy, self.paikka) < self.R)


# Kuualus
# Sijoitetaan Maan kiertoradalle ja lasketaan nopeus,
# jolla se pysyy kiertoradalla.
class Cl_Alus:
    def __init__(self):
        self.m = 10000.0
        self.F = (0.0, 0.0)  # ohjausrakettien aiheuttama voima
        self.paikka = (Maa.R + 10000.0e+3, 0.0)
        self.nopeus = (0.0, self.alkunopeus())


        self.hanta = [self.paikka for i in range(201)]
        self.ptr = 0


# Lasketaan sopiva nopeus yhtälöstä
# Keskipakoisvoima = Maan vetovoima
    def alkunopeus(self):
        (x, y) = self.paikka
        return sqrt(x*9.81*(Maa.R/x)**2)
# Piirretään alukseksi kaksivärinen jana,
# josta näkee aluksen asennon ja suunnan
# Huomaa, että näytöllä y kasvaa alaspäin.
    def piirra(self):
        (vx, vy) = self.nopeus
        v = sqrt(vx**2+vy**2)
        lx = int(vx/v*12.0)
        ly = int(vy/v*12.0)
        (xi, yi) = Kamera.xy_naytolla(self.paikka)
        pygame.draw.line(screen, Pera, (xi-lx, yi+ly), (xi, yi), 3)
        pygame.draw.line(screen, Keula, (xi, yi), (xi+lx, yi-ly), 3)


# Ohjaamisen helpottamiseksi piirretään alukseen kohdistuvia voimia
# kuvaavat nuolet ja "häntä", joka auttaa hahmottamaan aluksen liikettä
        if not MuistaVanhat and PiirraVoimat:
            self.nuolet(self.paikka, self.F, self.m)
            self.piirraHanta()
    def nuolet(self, p0, F, m):
            Fmaa = Maa.vetovoima(p0, m)
            Fkuu = Kuu.vetovoima(p0, m)
            ip0 = Kamera.xy_naytolla(p0)
            nuoli(ip0, F)
            nuoli(ip0, Fmaa)
            nuoli(ip0, Fkuu)
# Hännän piirtämiseksi talletaan aluksen kulloinenkin paikka listaan
# ja piirretään piste kuhunkin listan pisteeseen.
    def piirraHanta(self):
        for p in self.hanta:
            ip = Kamera.xy_naytolla(p)
            pygame.draw.line(screen, Pera, ip, ip, 1)
        self.hanta[self.ptr] = self.paikka
        self.ptr += 1
        if self.ptr > 200:
            self.ptr = 0


    def liiku(self):
        (x, y) = self.paikka
        (vx, vy) = self.nopeus
        for i in range(Aika.Tscale):
            [x, y, vx, vy] = rk([x, y, vx, vy], self.m, self.F)
        self.paikka = (x, y)
        self.nopeus = (vx, vy)
# Ohjaus tapahtuu näppäimistön nuolilla.
# z ja x näppäimillä boostattu ohjaus
# Ohjauksilla 'vasemmalle' ja 'oikealle' ei yleensä ole
# käyttöä
    def ohjaus(self, suunta):
        (vx, vy) = self.nopeus
        v = sqrt(vx**2+vy**2)
        F = 100.0
        if suunta == 'vas':  # Jarruta
            self.F = (-vx/v*F, -vy/v*F)
        if suunta == 'oik':  # Kiihdytä
            self.F = (vx/v*F, vy/v*F)
        if suunta == 'ylos':  # vasemmalle
            self.F = (-vy/v*F, vx/v*F)
        if suunta == 'alas':  # oikealle
            self.F = (vy/v*F, -vx/v*F)
        if suunta == 'z':  # Jarruta lujaa
            self.F = (-10.0*vx/v*F, -10.0*vy/v*F)
        if suunta == 'x':  # Kiihdytä lujaa
            self.F = (10.0*vx/v*F, 10.0*vy/v*F)
# Törmäys! Simulointi laskee ehkä väärin tämän tilanteen.
    def warning(self, a):
        print('Törmäys?')
        screen.fill(Pun)
        pygame.time.wait(10)


##= = = = = = = = = = = = = = = = = = = = = = =
#
# Pääohjelma alkaa tästä
# = = = = = = = = = = = = = = = = = = = = = = =
# peli-ikkunan leveys näytöllä pikseleinä
pygame.display.init()
# Selvitetään peli-ikkunan koko näytöllä pikseleinä
D_Info = pygame.display.Info()
Wynaytto = int(1.0*D_Info.current_h)
Wxnaytto = Wynaytto
screen = pygame.display.set_mode((Wxnaytto, Wynaytto))
pygame.display.set_caption(
    "Matka Kuuhun")
pygame.init()
clock = pygame.time.Clock()
random.seed()
Kamera = Cl_Kamera()
Maa = Cl_Maa()
Kuu = Cl_Kuu()
Aika = cl_Ajat()


Alus = Cl_Alus()


tahdet = cl_Tahdet()


loppu = False
MuistaVanhat = False


PiirraVoimat = True


while not loppu:


    Alus.F = (0.0, 0.0)


    if not MuistaVanhat:
        screen.fill(Musta)  # pyyhitään peli-ikkuna tyhjäksi
    for event in pygame.event.get():
        # Lopetetaan peli sulkemalla ikkuna tai painamalla ESC
        if event.type == pygame.QUIT:
            loppu = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_F1:
                MuistaVanhat = not MuistaVanhat


            if event.key == pygame.K_F2:
                PiirraVoimat = not PiirraVoimat


            if event.key == pygame.K_ESCAPE:
                loppu = True
            if event.key == pygame.K_KP_PLUS:
                Kamera.zoom(0.9)
            if event.key == pygame.K_KP_MINUS:
                Kamera.zoom(1.1)
            if event.key == pygame.K_m:
                Kamera.focus = 'maa'
            if event.key == pygame.K_k:
                Kamera.focus = 'kuu'
            if event.key == pygame.K_KP1:
                Aika.Nopeuta()
            if event.key == pygame.K_KP0:
                Aika.Hidasta()
            if event.key == pygame.K_KP_ENTER:
                Aika.Reset()


    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_RIGHT]:
        Alus.ohjaus('oik')
    if pressed[pygame.K_LEFT]:
        Alus.ohjaus('vas')
    if pressed[pygame.K_UP]:
        Alus.ohjaus('ylos')
    if pressed[pygame.K_DOWN]:
        Alus.ohjaus('alas')
    if pressed[pygame.K_z]:
        Alus.ohjaus('z')
    if pressed[pygame.K_x]:
        Alus.ohjaus('x')


    Maa.piirra()
    Kuu.piirra()


    Alus.piirra()


    tahdet.piirra()


    Kuu.liiku()
    Kamera.liiku()


    Alus.liiku()
    if Maa.crash(Alus.paikka) or Kuu.crash(Alus.paikka):
        print('!!! Törmäys !!!')
        screen.fill(Pun)
        pygame.time.wait(10)
#        loppu = True


    pygame.display.flip()
    clock.tick(Aika.Nayttotaajuus)
pygame.quit()


Se­li­tyk­siä ylläolevaan

 #  Piir­re­tään voi­man suun­taa ja suu­ruut­ta ku­vas­ta­va jana.
def nuoli(p0, Voima):
    (ix0, iy0) = p0
    (Fx, Fy) = Voima
    F = sqrt(Fx**2 + Fy**2)
 #  "yli­pitkä" jana kat­kais­taan ja piir­re­tään pu­nai­sel­la.
    if F > 600:
        color = Pun
        Fx = Fx/F*600.0
        Fy = Fy/F*600.0
    else:
        color = Vihr
    Fx = Fx*0.7
    Fy = Fy*0.7
 #  Huo­maa, että näy­töl­lä y kas­vaa alas­päin, siksi -Fy.
    p1 = (ix0+Fx, iy0-Fy)
    pygame.draw.line(screen, color, p0, p1, 2)



Piir­re­tään voi­man suun­taa ja suu­ruut­ta ku­vas­ta­va 'nuoli' läh­te­mään pis­tees­tä p0. Len­non ai­ka­na aluk­seen vai­kut­taa sekä hyvin suu­ria, että hyvin pie­niä voi­mia. Voi­mia ei ole skaa­lat­tu, joten yksi pik­se­li vas­taa yhtä New­to­nia, mikä on sat­tu­mal­ta riit­tä­vän hyvä skaa­laus. Tär­kein­tä on nähdä Maan ja Kuun veto­voi­mat Kuuta lähestyttäessä.

 #  Kiintotahtiä taus­tak­si ja kiin­to­pis­teik­si
class cl_Tahdet:
    def __init__(self):
        self.tt = []
        for i in range(1000):
            self.tt.append((random.uniform(-8.0e+8, 8.0e+8),
                       random.uniform(-8.0e+8, 8.0e+8)))

    def piirra(self):
        for tahti in self.tt:
            p = Kamera.xy_naytolla(tahti)
            color = (random.randint(100, 255),
                     random.randint(100, 255),
                     random.randint(100, 255))
            #  jana, jonka loppu­piste ja alku­piste ovat sa­mo­ja,
            #  piir­tyy näy­töl­le pis­tee­nä
            pygame.draw.line(screen, color, p, p, 1)



Pe­liin tulee ai­dom­pi tun­nel­ma, kun lait­taa taus­tak­si kiin­to­täh­tiä. Kun fo­ku­soi ka­me­ran Kuu­hun, taus­tan kiin­to­täh­det tuo­vat liik­keen tun­tua.

Annan täh­den vä­riin sa­tun­nai­ses­ti pu­nais­ta, si­nis­tä ja vih­reää. Se saa täh­det tuik­ki­maan. To­del­li­suu­des­sa täh­tien tuik­keen ai­heut­taa ilma­kehän vä­rei­ly, joten ava­ruu­des­ta kat­soen täh­det eivät tuiki. Voit pois­taa tuik­keen si­joit­ta­mal­la vä­rik­si color = Valk.

        self.hanta = [self.paikka for i in range(201)]
        self.ptr = 0



tahdet = cl_Tahdet()



PiirraVoimat = True



            if event.key == pygame.K_F2:
                PiirraVoimat = not PiirraVoimat


    tahdet.piirra()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Kuu­Alus_2.py



Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Lisäharjoituksia (edit: 2015-05-26)

Lisä­har­joi­tuk­sia

In­nos­tu­nut har­ras­ta­ja — ehkä it­se­kin in­nos­tun — voi täy­den­tää pe­le­jä ja tehdä uusia.

Mörs­sä­ri pe­lis­sä maa­lin voisi lait­taa sa­haa­maan edes­ta­kai­sin maali-alueella. Ehkä tuu­len suun­ta­kin voisi vaih­del­la, mutta siten jär­ke­väs­ti, että pe­laa­mi­nen ei mene sat­tu­man kau­pak­si.

Maan pin­taan voisi lait­taa ker­rok­sen pen­saik­koa tms., johon tu­li­si kuop­pia kuu­lien osu­mis­ta.

Kuu­lat voisi lait­taa rä­jäh­tä­mään sa­mal­la peri­aat­teel­la kuin maa­lin­kin. Sil­loin ehkä voisi olla pe­rus­tel­tua ke­rä­tä ros­kat, eli deletetoida sir­pa­leet, ellei siten halua jät­tää niitä lo­ju­maan maa­han.

Pelin käyn­nis­tyt­tyä voisi aluk­si näyt­tää peli­oh­jeet tms. Hyt­tys­pe­lis­sä voisi vii­mei­sen hyt­ty­sen hil­jen­nyt­tyä soit­taa loppu­fan­faa­rin.

Mörs­sä­rin kuula ja Kuu­Alus liik­ku­vat fy­sii­kan la­ke­ja nou­dat­taen. So­pi­va har­joi­tus voisi olla sel­vit­tää, miten ne on to­teu­tet­tu oh­jel­mas­sa. Sa­mal­la voisi sel­vit­tää, miten eri ta­voin geo­met­riaa on hyö­dyn­net­ty oh­jel­mas­sa.

Haa­vee­ni on ehtiä de­monst­roi­da mu­sii­kin tuot­ta­mis­ta oh­jel­mal­li­ses­ti pythonilla. Aiem­pi ko­kei­lu­ni mu­sii­kin tuot­ta­mi­ses­ta oh­jel­mal­li­ses­ti ei tuottanut järin mukavaa kuunneltavaa, mutta opetti minulle koulussa hämäräksi jääneitä musiikin perusteita. Aloitan ehkä perehtymällä, mitä jythonmusic.org tarjoaa.


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Miten ylläolevat dokumentit tuotettiin (edit: 2015-05-13)

Miten ylläolevat do­ku­men­tit tuo­tet­tiin

Oh­jel­man vai­heit­tai­ses­ta ke­hit­te­lys­tä ei voi tehdä yllä ole­van kal­tai­sia do­ku­ment­te­ja käsi­työnä. Vir­heil­tä ei voisi vält­tyä. Ker­ran do­ku­men­toi­tua oh­jel­maa ei liioin voisi pa­ran­nel­la eikä täy­den­tää, koska do­ku­ment­tien kor­jaa­mi­nen vas­taa­maan muu­tok­sia olisi aivan liian työ­läs­tä ja virhe­al­tis­ta.

On­nek­si pythonilla oli melko help­po au­to­ma­ti­soi­da vai­heit­tai­sen ke­hit­te­lyn do­ku­ment­ti.

Touko­kuu 2015

Au­to­ma­ti­soin­nin to­teu­tin li­sää­mäl­lä oh­jel­mie­ni ke­hi­tys­ver­sioon kom­ment­te­ja, jotka ker­to­vat oh­jel­man ke­hi­tys­vai­heen ja mitä se­li­tyk­siä ku­hun­kin vai­hee­seen kuu­luu.

Kun­kin vai­heen ot­si­kot ovat oh­jel­ma­tie­dos­ton alus­sa perä perää. # shd 0-0:n tarkoittaa, että seuraava kommentti on vaiheen 0 otsikko

# -*- coding: utf-8 -*-
"""
Rakennetaan vaihe vaiheelta videopeli, jossa jahdataan hyttysiä.
Hyttyslätkää ohjataan näppäimillä z,x, nuoli ylös ja nuoli alas.
Ohjelma pysäytetään joko esc-näppäimellä tai sulkemalla peli-ikkuna.
"""
# shd 0-0:m

'''
Vaihe 0: Peliohjelman perusrakenne
'''

# shd 1-1:m
'''
Vaihe 1: Luodaan lätkä hyttysten pyydystämiseen
'''

# shd 2-2:m
'''
Vaihe 2: Luodaan hyttynen
'''

# shd 3-3:m
'''
Vaihe 3: Laitetaan hyttynen liikkeelle
'''

Esi­merk­ki moni­mut­kai­sim­mas­ta koh­das­ta oh­jel­maa. Tämä tuot­taa vai­hei­siin 3 ja 4 so­pi­van koo­din.

# txt 3-3:m
'''
Korvataan ylläoleva monipuolisemmalla liikkeellä.
'''
# cod 3-3:v 4-100:z
def liiku(self):
# cod 4-4:v 5-100:z
  self.isku() # Onko lätkä osunut?
  if self.elossa:
# cod 3-3:v 4-100:z
    self.nopeus = lenna(self.paikka, self.nopeus)
# cod 4-4:v 5-100:z
  else:
    self.nopeus = self.putoa()
# cod 3-3:v 4-100:z
# Lasketaan uusi paikka eli siirrytään matka nopeus kertaa aika
  self.paikka = dfdt(self.paikka, self.nopeus)
# cod 4-4:v 5-100:z

# Jos Latka on lähellä tätä hyttystä sekä x- että y-suunnassa
# lätkä osuu, hyttynen kuolee ja muuttuu punaiseksi.
def isku(self):
  if (etaisyys(self.paikka, Latka.paikka) < Latka.koko and
            Latka.liikkuu):
    self.elossa = False
    self.color = Pun
# txt 4-4:m
'''
Kuolleena putoavan hyttysen liike:
Nopeus x-suuntaan on pieni satunnaisluku, eli hyttynen leijailee alas
lähes pystysuoraan.
Ellei hyttynen jo ole maassa, sillä on pieni satunnainen nopeus alaspäin
(muista, että y kasvaa epäloogisesti alaspäin).
Jos hyttynen on jo maassa, y-suuntainen nopeus asetetaan nollaksi
'''
# cod 4-4:v 5-100:z
def putoa(self):
  vx = random.uniform(-10, 10)
  if self.paikka[1] < Wy-self.koko:
    vy = random.uniform(20, 70)
  else:
    vx = 0
    vy = 0
  return (vx, vy)

Oh­jel­man ke­hi­tys­ver­sio on pal­jois­ta kom­men­teis­ta huo­li­mat­ta luet­ta­vis­sa ja ennen kaik­kea suo­ri­tet­ta­vis­sa, niin että voin aina tar­kis­taa, toi­mii­ko se. Koska voin yh­del­lä ko­men­nol­la tuot­taa do­ku­men­tin li­säk­si myös ku­ta­kin vai­het­ta vas­taa­van suo­ri­tet­ta­van oh­jel­man, voin var­mis­taa, että do­ku­men­tis­sa esi­tel­ty oh­jel­ma toi­mii.

Tein oh­jel­man yritys-ja-erehdys me­ne­tel­mäl­lä, koska vain ko­kei­le­mal­la alan ym­mär­tää, mitä oh­jel­man pitää tehdä ja miten sen voi to­teut­taa. Nyt osai­sin tehdä pal­jon ele­gan­tim­man oh­jel­man. Sitä odo­tel­les­sa lai­tan näy­til­le tämän­het­ki­sen proto­tyy­pin . Se tuottaa xml-koodia, koska se sopii web-kehitysympäristööni, mutta pienellä vaivalla sen saanee tuottamaan html-koodia. Koodilaatikoiden css-tyylinä minulla on seuraava

pre{
  background-color: #F8FCFF;
  margin-left: 2em; margin-right: 2em;
  margin-top: 0em; margin-bottom: 0em;
  padding-left: 1em; padding-right: 1em;
  padding-top: 0.5em; padding-bottom: 0.5em;
  font-family: 'Lucida Console', 'Courier New', monospace;
  font-weight: bold; white-space: pre-wrap;
}

Oh­jel­moi­mal­la voi tehdä mah­dot­to­man — ei hel­pok­si, mutta — mah­dol­li­sek­si. Jos­kus homma on mah­dol­lis­ta tehdä käsin — ehkä jopa no­peam­min kuin laa­ti­mal­la sitä var­ten oh­jel­man. Oh­jel­moin­nin hyöty tulee siitä, että saman hom­man voi tois­taa napin pai­nal­luk­sel­la. Usein homma ei ole lain­kaan mah­dol­lis­ta ilman oh­jel­moin­tia.


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Miten ylläolevat dokumentit tuotettiin > Python2html.py (edit: )

Python2html.py

Tällä oh­jel­mal­la on tuo­tet­tu tämän si­vus­ton python-ohjelmien esit­te­lyt.

Wed May 24 13:09:20 2023


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Miten ylläolevat dokumentit tuotettiin > Python2html.py > Yhdestä python-ohjelmasta eri ohjelmaversioita ja niiden dokumentaatioita (edit: )

Yh­des­tä python-ohjelmasta eri oh­jel­ma­ver­sioi­ta ja nii­den do­ku­men­taa­tioi­ta

# -*- coding: utf-8 -*-
# = = = = = = = = = = = = = = = = = = = = = = = = = =
# import sys
# print "This is the name of the script: ", sys.argv[0]
# print "Number of arguments: ", len(sys.argv)
# print "The arguments are: " , str(sys.argv)
# = = = = = = = = = = = = = = = = = = = = = = = = = =


import re
import os
import sys
import ast
import time
import random
from pathlib import Path, PurePath
def tagline(tag, vari, vaihe, ln):
    if tag == 'shd':
        unique_id = projName + str(vaihe) + str(ln)
        tglin = '\n<section>\n<title id = "' + unique_id + '">'
    else:
        tglin = '\n<' + tag + ' mod = "' + vari + '">\n'
    return tglin
def tagline_s(tag, vari):
    tglin = '\n<' + tag + ' mod = "' + vari + '">\n'
    return tglin


def tarkistaVaihe_s(line, tag, vaihe):
    if not tag.group(1) == 'shd':
        attrs = re.findall(r"([0-9]*)-([0-9]*):([a-z])", line)
        for attr in attrs:
            if int(attr[0]) <= vaihe <= int(attr[1]):
                if attr[2] == 'z':
                    txt = False
                    code = True
                    vari = 'm'
                elif attr[2] == 'p':
                    txt = True
                    code = False
                    vari = 'p'
                else:
                    txt = True
                    code = True
                    vari = str(attr[2])
                tglin = tagline_s(tag.group(1), vari)
                return (txt, code, tglin)
    return (False, False, '')


def tarkistaVaihe_c(line, tag, vaihe, ln):
    if not tag.group(1) == 'txt':
        attrs = re.findall(r"([0-9]*)-([0-9]*):([a-z])", line)
        for attr in attrs:
            if int(attr[0]) <= vaihe <= int(attr[1]):
                if attr[2] == 'z':
                    code = True
                    vari = 'm'
                elif attr[2] == 'p':
                    code = True
                    vari = 'p'
                else:
                    code = True
                    vari = str(attr[2])
                tglin = tagline(tag.group(1), vari, vaihe, ln)
                return (code, tglin)
    return (False, '')


def tarkistaVaihe_py(line, tag, vaihe, ln):
    if tag.group(1) == 'cod':
        attrs = re.findall(r"([0-9]*)-([0-9]*):([a-z])", line)
        for attr in attrs:
            if int(attr[0]) <= vaihe <= int(attr[1]):
                if attr[2] == 'z':
                    code = True
                    vari = 'm'
                elif attr[2] == 'p':
                    code = False
                    vari = 'p'
                else:
                    code = True
                    vari = str(attr[2])
                tglin = tagline(tag.group(1), vari, vaihe, ln)
                return (code, tglin)
    return (False, '')


def endtag(tag, fout):
    if tag == 'shd':
        fout.write('\n</title>')
    elif not tag == '':
        fout.write('\n</' + tag + '>')
    return ''
def endtag_s(tag, fout):
    if not tag == '':
        fout.write('\n</' + tag + '>')
    return ''


def ilman_selityksia(fout, vaihe):
    with open(infil, 'r') as fin:
        ln = 0
        current = ''
        pr_cod = False
        for line in fin:
            ln = ln + 1
            tag = re.search(r"(?<=\# )(txt|cod|shd)", line)
            if tag:
                if pr_cod:
                    current = endtag(current, fout)
                (pr_cod, tagLine) = tarkistaVaihe_c(
                    line, tag, vaihe, ln)
                if pr_cod:
                    current = tag.group(1)
                    fout.write(tagLine)
            elif pr_cod:
                    fout.write(repl(line))
        current = endtag(current, fout)


def selitykset(fout, vaihe):
    with open(infil, 'r') as fin:
        ln = 0
        current = ''
        pr_txt = False
        for line in fin:
            ln = ln + 1
            tag = re.search(r"(?<=\# )(txt|cod|shd)", line)
            if tag:
                if pr_txt:
                    current = endtag_s(current, fout)
                (pr_txt, pr_cod, tagLine) = tarkistaVaihe_s(
                    line, tag, vaihe)
                if pr_txt:
                    current = tag.group(1)
                    fout.write(tagLine)
            elif pr_txt:
                    if current == 'txt':
                        fout.write(line)
                    elif current == 'cod':
                        alkuloppu = re.split(r"^#| #", line, maxsplit=1)
                        fout.write(repl(alkuloppu[0]))
                        if len(alkuloppu) > 1:
                            fout.write('<orange> # ' + repl(alkuloppu[1]) +
                                       '</orange>')
        current = endtag_s(current, fout)


def vain_koodi(progfil, vaihe):
    with open(infil, 'r') as fin:
        ln = 0
        pr_cod = False
        for line in fin:
            ln = ln + 1
            tag = re.search(r"(?<=\# )(txt|cod|shd)", line)
            if tag:
                (pr_cod, tagLine) = tarkistaVaihe_py(line, tag, vaihe, ln)
            elif pr_cod:
                    progfil.write(line)


def repl(line):
    return re.sub('<', '<', re.sub('>', '>', line))
def summary():
    with open(infil) as fd:
        file_contents = fd.read()
        module = ast.parse(file_contents)
        return ast.get_docstring(module)
def lisafileet(fout, curdir):
    narg = len(sys.argv)
    if narg > 3:
        fout.write(
            '\n<sp><iso>Lataa myös seuraavat, ellet ole vielä ladannut</iso></sp>')
        for i in range(3, narg):
            webref = PurePath('/webPy/', curdir, str(sys.argv[i]))
            fout.write(
                '\n<p><weblink id="l' + str(random.randint(100, 9999)) +
                '" webref="' + webref + ' type="downLoad">lisätiedosto: ' + str(sys.argv[i]) +
                '</weblink></p>')


def main():
    with open(xmlf, 'w') as fout:
        fout.write('<section>\n<title anchor="' + xmlf.as_posix() + '">' +
                   projName + '.py\n</title>\n')
        fout.write('\n<summary>')
        fout.write(summary())
        fout.write('\n<paivays>')
        fout.write(time.asctime(time.localtime(time.time())))
        fout.write('\n</paivays>')
        fout.write('\n</summary>')
        for vaihe in range(maxVaihe):
            ilman_selityksia(fout, vaihe)
            fout.write('<shd uid="h' + str(random.randint(100, 9999)) +
                       '">Selityksiä ylläolevaan</shd>')
            selitykset(fout, vaihe)
            pfname = projName+'_'+str(vaihe)+'.py'
            with open(pfname, 'w') as progfil:
                vain_koodi(progfil, vaihe)
            webroot = Path(os.path.expandvars("$WEBPY"))
            curdir = Path.cwd().relative_to(webroot)
            webref = PurePath('/webPy/', curdir, pfname).as_posix()
            fout.write(
                '<sp><iso><weblink  id="v' + str(random.randint(100, 9999)) +
                '" webref= "' + webref + '" type= "downLoad">Lataa tästä ohjelman tämän hetkinen versio: ' +
                pfname + '</weblink></iso></sp>')
            lisafileet(fout, curdir)
            fout.write('\n</section>\n')
        fout.write('\n</section >')


if len(sys.argv) > 1:
    projName = str(sys.argv[1])
    print(projName)
else:
    projName = 'ProjektiX'
if len(sys.argv) > 2:
    maxVaihe = int(sys.argv[2])
else:
    maxVaihe = 6
xmlf = PurePath(projName + '_code.xml')
infil = Path(projName + '.py')
summary()
if __name__ == "__main__":
    main()


Se­li­tyk­siä ylläolevaan

Ha­lu­sin esi­tel­lä miten ke­hit­te­lin peli­oh­jel­mia vaihe vai­heel­ta. Esi­mer­kik­si hyt­tys­jahti­pelin en­sim­mäi­nen ver­sio ei tee muuta kuin luo näy­töl­le ik­ku­nan. Seu­raa­vas­sa vai­hees­sa pe­lis­sä on nuoli­näp­päi­mil­lä siir­rel­tä­vä hyt­tys­lätkä jne.

Peliä ke­hit­täes­sä huo­ma­sin usein tar­peen muut­taa ai­kai­sem­pien vai­hei­den koo­dia. Opin esi­mer­kik­si vasta pelin val­mis­tut­tua, miten avata näyt­tö­lait­teen ruu­dun ko­koi­nen peli-ik­ku­na. Jos oli­sin teh­nyt oi­keas­ti kuusi eri ver­sioi­ta pe­lis­tä, oli­sin jou­tu­nut muut­ta­maan niitä kaik­kia. Niin­pä tein tämän oh­jel­man, jolla voin "poi­mia" lo­pul­li­ses­ta ver­sios­ta oh­jel­man eri ke­hi­tys­vai­hei­ta vas­taa­vat oh­jel­ma­ver­siot. Sa­mal­la teen oh­jel­mas­ta xml-tiedoston, josta luon tämän­kal­tai­sia do­ku­ment­te­ja.

Ge­ne­roin yh­des­tä python-ohjelmasta do­ku­men­tin ja eri ver­sioi­ta itse oh­jel­mas­ta.

Li­sään python oh­jel­maan seu­raa­van­lai­sia kom­ment­te­ja:

 #shd n1-n1:v

shd-tagia seuraava kommentti näkyy dokumenttitiedostossa otsikkona.


 #cod n1-n2:v n3-n4:m n5-100:z
  # tähän tulee ohjelmakoodia, joka on ohjelmaversioissa n1:stä ylöspäin.
  # Lisäksi tämä koodi näkyy dokumentissa vaiheissa n1-n2 ja n3-n4
  # (Käytän kommenttirivejä, koska tämä teksti on siis suoritettavassa
  # ohjelmassa.)
 #txt n1-n2:v n3-n4:m

  Tähän tulevat rivit eivät näy ohjelmassa,
  mutta näkyvät vaiheiden n1-n2 ja n3-n4 dokumenteissa.

 

ylem­pi tar­koit­taa, että sitä seu­raa­vat rivit kir­joi­te­taan sekä do­ku­ment­ti­tie­dos­toon että oh­jel­ma­tie­dos­toon. Vai­heis­sa n1-n2 kir­joi­te­taan do­ku­ment­ti­tie­dos­toon vih­reäl­lä ja vas­taa­van vai­heen oh­jel­ma­tie­dos­toon. Vai­heis­sa n3-n4 rivit kir­joi­te­taan mus­tal­la ja vai­heis­sa n5-100 ne kir­joi­te­taan pel­käs­tään oh­jel­ma­tie­dos­toon. Vai­heis­sa ennen n1:tä, seu­raa­via ri­ve­jä ei kir­joi­te­ta do­ku­ment­tiin eikä itse oh­jel­maan.

<orange> #  -*- coding: utf-8 -*-
</orange>
<orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange><orange> #  import sys
</orange><orange> #  print "This is the name of the script: ", sys.argv[0]
</orange><orange> #  print "Number of arguments: ", len(sys.argv)
</orange><orange> #  print "The arguments are: " , str(sys.argv)
</orange><orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange>



-




import re
import os
import sys
import ast
import time
import random
from pathlib import Path, PurePath
def tagline(tag, vari, vaihe, ln):
    if tag == 'shd':
        unique_id = projName + str(vaihe) + str(ln)
        tglin = '\n<section>\n<title id = "' + unique_id + '">'
    else:
        tglin = '\n<' + tag + ' mod = "' + vari + '">\n'
    return tglin


def tagline_s(tag, vari):
    tglin = '\n<' + tag + ' mod = "' + vari + '">\n'
    return tglin




a


def tarkistaVaihe_s(line, tag, vaihe):
    if not tag.group(1) == 'shd':
        attrs = re.findall(r"([0-9]*)-([0-9]*):([a-z])", line)
        for attr in attrs:
            if int(attr[0]) <= vaihe <= int(attr[1]):
                if attr[2] == 'z':
                    txt = False
                    code = True
                    vari = 'm'
                elif attr[2] == 'p':
                    txt = True
                    code = False
                    vari = 'p'
                else:
                    txt = True
                    code = True
                    vari = str(attr[2])
                tglin = tagline_s(tag.group(1), vari)
                return (txt, code, tglin)
    return (False, False, '')




a


def tarkistaVaihe_c(line, tag, vaihe, ln):
    if not tag.group(1) == 'txt':
        attrs = re.findall(r"([0-9]*)-([0-9]*):([a-z])", line)
        for attr in attrs:
            if int(attr[0]) <= vaihe <= int(attr[1]):
                if attr[2] == 'z':
                    code = True
                    vari = 'm'
                elif attr[2] == 'p':
                    code = True
                    vari = 'p'
                else:
                    code = True
                    vari = str(attr[2])
                tglin = tagline(tag.group(1), vari, vaihe, ln)
                return (code, tglin)
    return (False, '')




a


def tarkistaVaihe_py(line, tag, vaihe, ln):
    if tag.group(1) == 'cod':
        attrs = re.findall(r"([0-9]*)-([0-9]*):([a-z])", line)
        for attr in attrs:
            if int(attr[0]) <= vaihe <= int(attr[1]):
                if attr[2] == 'z':
                    code = True
                    vari = 'm'
                elif attr[2] == 'p':
                    code = False
                    vari = 'p'
                else:
                    code = True
                    vari = str(attr[2])
                tglin = tagline(tag.group(1), vari, vaihe, ln)
                return (code, tglin)
    return (False, '')




a


def endtag(tag, fout):
    if tag == 'shd':
        fout.write('\n</title>')
    elif not tag == '':
        fout.write('\n</' + tag + '>')
    return ''


def endtag_s(tag, fout):
    if not tag == '':
        fout.write('\n</' + tag + '>')
    return ''




a


def ilman_selityksia(fout, vaihe):
    with open(infil, 'r') as fin:
        ln = 0
        current = ''
        pr_cod = False
        for line in fin:
            ln = ln + 1
            tag = re.search(r"(?<=\# )(txt|cod|shd)", line)
            if tag:
                if pr_cod:
                    current = endtag(current, fout)
                (pr_cod, tagLine) = tarkistaVaihe_c(
                    line, tag, vaihe, ln)
                if pr_cod:
                    current = tag.group(1)
                    fout.write(tagLine)
            elif pr_cod:
                    fout.write(repl(line))
        current = endtag(current, fout)




a


def selitykset(fout, vaihe):
    with open(infil, 'r') as fin:
        ln = 0
        current = ''
        pr_txt = False
        for line in fin:
            ln = ln + 1
            tag = re.search(r"(?<=\# )(txt|cod|shd)", line)
            if tag:
                if pr_txt:
                    current = endtag_s(current, fout)
                (pr_txt, pr_cod, tagLine) = tarkistaVaihe_s(
                    line, tag, vaihe)
                if pr_txt:
                    current = tag.group(1)
                    fout.write(tagLine)
            elif pr_txt:
                    if current == 'txt':
                        fout.write(line)
                    elif current == 'cod':
                        alkuloppu = re.split(r"^#|<orange> # ", line, maxsplit=1)
</orange>                        fout.write(repl(alkuloppu[0]))
                        if len(alkuloppu) > 1:
                            fout.write('<orange><orange> #  ' + repl(alkuloppu[1]) +
</orange>                                       '</orange>')
        current = endtag_s(current, fout)




a


def vain_koodi(progfil, vaihe):
    with open(infil, 'r') as fin:
        ln = 0
        pr_cod = False
        for line in fin:
            ln = ln + 1
            tag = re.search(r"(?<=\# )(txt|cod|shd)", line)
            if tag:
                (pr_cod, tagLine) = tarkistaVaihe_py(line, tag, vaihe, ln)
            elif pr_cod:
                    progfil.write(line)




a


def repl(line):
    return re.sub('<', '<', re.sub('>', '>', line))


def summary():
    with open(infil) as fd:
        file_contents = fd.read()
        module = ast.parse(file_contents)
        return ast.get_docstring(module)


def lisafileet(fout, curdir):
    narg = len(sys.argv)
    if narg > 3:
        fout.write(
            '\n<sp><iso>Lataa myös seuraavat, ellet ole vielä ladannut</iso></sp>')
        for i in range(3, narg):
            webref = PurePath('/webPy/', curdir, str(sys.argv[i]))
            fout.write(
                '\n<p><weblink id="l' + str(random.randint(100, 9999)) +
                '" webref="' + webref + ' type="downLoad">lisätiedosto: ' + str(sys.argv[i]) +
                '</weblink></p>')




a


def main():
    with open(xmlf, 'w') as fout:
        fout.write('<section>\n<title anchor="' + xmlf.as_posix() + '">' +
                   projName + '.py\n</title>\n')
        fout.write('\n<summary>')
        fout.write(summary())
        fout.write('\n<paivays>')
        fout.write(time.asctime(time.localtime(time.time())))
        fout.write('\n</paivays>')
        fout.write('\n</summary>')
        for vaihe in range(maxVaihe):
            ilman_selityksia(fout, vaihe)
            fout.write('<shd uid="h' + str(random.randint(100, 9999)) +
                       '">Selityksiä ylläolevaan</shd>')
            selitykset(fout, vaihe)
            pfname = projName+'_'+str(vaihe)+'.py'
            with open(pfname, 'w') as progfil:
                vain_koodi(progfil, vaihe)

            webroot = Path(os.path.expandvars("$WEBPY"))
            curdir = Path.cwd().relative_to(webroot)
            webref = PurePath('/webPy/', curdir, pfname).as_posix()
            fout.write(
                '<sp><iso><weblink  id="v' + str(random.randint(100, 9999)) +
                '" webref= "' + webref + '" type= "downLoad">Lataa tästä ohjelman tämän hetkinen versio: ' +
                pfname + '</weblink></iso></sp>')
            lisafileet(fout, curdir)
            fout.write('\n</section>\n')
        fout.write('\n</section >')




a


if len(sys.argv) > 1:
    projName = str(sys.argv[1])
    print(projName)
else:
    projName = 'ProjektiX'

if len(sys.argv) > 2:
    maxVaihe = int(sys.argv[2])
else:
    maxVaihe = 6

xmlf = PurePath(projName + '_code.xml')
infil = Path(projName + '.py')

summary()

if __name__ == "__main__":
    main()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: Python2html_0.py