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 (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 (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 ()

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

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

  • 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 ()

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 2 19:59:23 2025


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

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 = 48  # 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ä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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

<orange> #  -*- coding: utf-8 -*-
</orange>

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ää.

<orange> #  Tässä pelissä läiskitään ympäriinsä lenteleviä hyttysiä
</orange>

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-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.
import pygame
import random
from math import sqrt
Musta = (0, 0, 0) <orange> #  Mustassa ei ole mitään valoa
</orange>Sin = (0, 0, 255) <orange> #  vain sinistä
</orange>Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Valk = (255, 255, 255) <orange> #  valkoisessa on kaikenväristä valoa
</orange>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.

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


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 = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>

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

<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



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()
<orange> #  Selvitetään peli-ikkunan koko näytöllä pikseleinä. Pikseli on piste näytöllä.
</orange>D_Info = pygame.display.Info()
Wx = D_Info.current_w
Wy = int(0.9*D_Info.current_h)
Wxy = (Wx, Wy) <orange> #  peli-ikkunan korkeus ja leveys
</orange>


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()



<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

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)




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

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 ()

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 = 48  # 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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy
        # 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, 30)


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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_RIGHT]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_LEFT]:
        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
    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka
    # 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




Nayttotaajuus = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>

<orange> #  Peliin liittyviä muuttujia.
</orange>VLatka = 200.0 <orange> #  Lätkän nopeus
</orange>

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



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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy

       <orange> #  Lisäämällä tähän väliin muutaman if lauseen, voit korjata ohjelmaa
</orange>       <orange> #   niin, että Lätkä ei karkaa peli-ikkunan reunan ulkopuolelle
</orange>       <orange> #  jos x on suurempi kuin niin Wx x.stä kannattaa vähentää Wx jne...
</orange>        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.

<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



. . .
<orange> #  Luodaan 'olio' tyyppiä Cl_latka
</orange>Latka = Cl_Latka(Wx-50, Wy-50, 30)



<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

while not loppu:



    Latka.liikkuu = False

<orange> #    Tarkistetaan, onko joku tai jotkin lätkää ohjaavista näppäimistä
</orange><orange> #  painettuna. Liikutetaan lätkää vastaavasti.
</orange>    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_RIGHT]:
        Latka.liiku((0.0, -VLatka)) <orange> #  ylös
</orange>    if pressed[pygame.K_LEFT]:
        Latka.liiku((0.0, VLatka)) <orange> #  alas
</orange>    if pressed[pygame.K_z]:
        Latka.liiku((-VLatka, 0.0)) <orange> #  vasemmalle
</orange>    if pressed[pygame.K_x]:
        Latka.liiku((VLatka, 0.0)) <orange> #  oikealle
</orange>    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka

   <orange> #  Piirretään lätkä uudelle paikalleen
</orange>    Latka.piirra()


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

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 ()

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 = 48  # 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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy
        # 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, 30)


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


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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_RIGHT]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_LEFT]:
        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
    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka
    # 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




Nayttotaajuus = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>

<orange> #  Peliin liittyviä muuttujia.
</orange>VLatka = 200.0 <orange> #  Lätkän nopeus
</orange>

Vmax = 250.0
MaxKiihtyvyys = 400.0


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


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

        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)
       <orange> #  poista seuraavista kommenttimerkki,
</orange>       <orange> #  niin saat hyttysen pysymään ruudulla
</orange><orange> #         if (x > Wx and vx > 0) or (x < 0 and vx < 0):
</orange><orange> #             vx = -vx
</orange><orange> #         elif (y > Wy and vy > 0) or (y < 0 and vy < 0):
</orange><orange> #             vy = -vy
</orange>        self.paikka = (x, y)
        self.nopeus = (vx, vy)



<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



. . .
<orange> #  Luodaan 'olio' tyyppiä Cl_latka
</orange>Latka = Cl_Latka(Wx-50, Wy-50, 30)



<orange> #  Luodaan yksi hyttynen
</orange>hyttynen = Cl_Hyttynen(50, 50)


<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

while not loppu:



    hyttynen.liiku_suoraan()


    hyttynen.piirra()


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

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ä ()

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 = 48  # 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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy
        # 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, 30)


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


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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_RIGHT]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_LEFT]:
        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
    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka
    # 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




Nayttotaajuus = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>


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


<orange> #  Siirrytään eteenpäin nopeus kertaa aikaväli
</orange>        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

<orange> #  matka = nopeus*aika
</orange>

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
   <orange> #  ei päästetä hyttystä reunojen ulkopuolelle
</orange>    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
   <orange> #  pysäytetään hyttynen, mikäli se lentää tolkuttoman lujaa
</orange>    elif sqrt(vx**2 + vy**2) > Vmax:
        vx, vy = 0.0, 0.0
    return (vx, vy)



<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



. . .
<orange> #  Luodaan 'olio' tyyppiä Cl_latka
</orange>Latka = Cl_Latka(Wx-50, Wy-50, 30)



<orange> #  Luodaan yksi hyttynen
</orange>hyttynen = Cl_Hyttynen(50, 50)


<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

while not loppu:



    hyttynen.liiku_suoraan()


    hyttynen.liiku()


    hyttynen.piirra()


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

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 ()

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 = 48  # 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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy
        # 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
            liiskaus.play()


    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, 30)


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


#
# Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
# tai painaa ESC-iä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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_RIGHT]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_LEFT]:
        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
    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka
    # 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




Nayttotaajuus = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>

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


        else:
            self.nopeus = self.putoa()


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



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)




<orange> #  kahden pisteen p0 ja p1 välinen etäisyys xy-tasossa
</orange>

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

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


<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



. . .
<orange> #  Luodaan 'olio' tyyppiä Cl_latka
</orange>Latka = Cl_Latka(Wx-50, Wy-50, 30)



<orange> #  Luodaan yksi hyttynen
</orange>hyttynen = Cl_Hyttynen(50, 50)


<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

while not loppu:



    hyttynen.liiku()


    hyttynen.piirra()


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

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ä ()

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 = 48  # 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 = 200


# __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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy
        # 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
            liiskaus.play()


    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, 30)


# 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ä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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_RIGHT]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_LEFT]:
        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
    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka
    # 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




Nayttotaajuus = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>

<orange> #  Peliin liittyviä muuttujia.
</orange>VLatka = 200.0 <orange> #  Lätkän nopeus
</orange>

Vmax = 250.0
MaxKiihtyvyys = 400.0


N_hyttyset = 200


<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



. . .
<orange> #  Luodaan 'olio' tyyppiä Cl_latka
</orange>Latka = Cl_Latka(Wx-50, Wy-50, 30)



<orange> #  Luodaan yksi hyttynen
</orange>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.

https://heikki-valisuo.fi/kuva/templates/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)



<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

while not loppu:



    hyttynen.liiku()


    hyttynen.piirra()


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



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

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 > Python hyttysjahdissa > HyttysJahti.py > Vaihe 6: ()

Vaihe 6:

# -*- 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 = 48  # 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 = 200


# __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)
        if x > Wx:
            x = 0
        elif x < 0:
            x = Wx
        if y > Wy:
            y = 0
        elif y < 0:
            y = Wy
        # 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
            liiskaus.play()


    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, 30)


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ä
#
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')
loppu = False
pygame.mixer.music.play(-1)
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)


#  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_RIGHT]:
        Latka.liiku((0.0, -VLatka))  # ylös
    if pressed[pygame.K_LEFT]:
        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
    if pressed[pygame.K_KP_PLUS]:
        VLatka = 1.1*VLatka
    if pressed[pygame.K_KP_MINUS]:
        VLatka = 0.9*VLatka
    # 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




Nayttotaajuus = 48 <orange> #  Voit kokeilla eri näyttötaajuuksia
</orange>

<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>



. . .
<orange> #  Luodaan 'olio' tyyppiä Cl_latka
</orange>Latka = Cl_Latka(Wx-50, Wy-50, 30)



<orange> #
</orange><orange> #  Toistetaan allaolevaa while-silmukkaa, kunnes käyttäjä sulkee peli-ikkunan
</orange><orange> #  tai painaa ESC-iä
</orange><orange> #
</orange>
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

inina = pygame.mixer.music.load('Hyttynen_tasainen_inina.wav')
liiskaus = pygame.mixer.Sound('Hyttysen_liiskaus.wav')

loppu = False

pygame.mixer.music.play(-1)

while not loppu:



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

pygame.quit()



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


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Pelejä > Muutama python-kielinen videopeli > Voimia ja liikettä (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ä.


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

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 2 20:00:02 2025


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

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

<orange> #  Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
</orange>Nayttotaajuus = 48



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



<orange> #  Nopeutus = 1.0 ->  oikea "filmin" nopeus
</orange>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 <orange> #  putoamiskiihtyvyys
</orange>RKuula = 0.1 <orange> #  kuulan säde metreinä
</orange>




<orange> #  Lasketaan, mihin tietty pelimaailman piste kuuluu peli-ikkunassa
</orange>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.

<orange> #  Vuori ja "tuuliviiri"
</orange>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):
       <orange> #  Vuori
</orange>        (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ä



<orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange><orange> #  Morssari
</orange><orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange>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 <orange> #  piipun asento
</orange>        self.p1x = 0.0 <orange> #  piipun pään x-koordinaatti
</orange>        self.p1y = 0.0 <orange> #  piipun pään y-koordinaatti
</orange>        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)
       <orange> #  piirretään mörssärin piippu
</orange>        (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)
       <orange> #  Ajopanosten ruutimäärän osoitin
</orange>        pygame.draw.line(screen, Sin, (10, Wy),
                         (10, Wy-int(self.ruuti/12.0*Wy)), 8)
       <orange> #  vaunu
</orange>        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


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




<orange> #  num+ ja num- näppäimillä kutsutaan tätä metodia
</orange>    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.

<orange> #  num-1, num-2 ja num-3 näppäimillä valitaan kuulan tyyppi
</orange>
    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.

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




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



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



    pygame.display.flip()
   <orange> #  while silmukka päättyy tähän
</orange>
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 ()

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(int(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

<orange> #  Siirtymä ja nopeuden muutos ajassa dt
</orange>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)




https://heikki-valisuo.fi/kuva/templates/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ä.

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


        self.latausaika = random.randint(int(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


<orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange><orange> #  Ammus
</orange><orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange>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 <orange> #  kg/m**3
</orange>        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)



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



<orange> #     if not kuulat =  []:
</orange><orange> #         for kuula in kuulat:
</orange>

<orange> #             kuula.liiku0()
</orange><orange> #             kuula.piirra0()
</orange>

    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. ()

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(int(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)
<orange> #  Tuuli = -50.0
</orange>


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.
       <orange> #  Tuuliviirin masto
</orange>        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)
       <orange> #  Tuuliviiri
</orange>        pygame.draw.line(screen, Sin, (a+w/2, b-mastonKorkeus),
                         (a+w/2+tuulix, b-mastonKorkeus), 12)


   <orange> #  Tarkistetaan, onko kuula, jonka koordinaatit ovat x ja y osunut vuoreen
</orange>
    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)

<orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange><orange> #  Maali
</orange><orange> #  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
</orange>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()



<orange> #             kuula.liiku0()
</orange><orange> #             kuula.piirra0()
</orange>

            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. ()

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(int(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.

<orange> #             kuula.liiku0()
</orange><orange> #             kuula.piirra0()
</orange>

<orange> #             kuula.piirra()
</orange><orange> #             kuula.liiku()
</orange>


            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. ()

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(int(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ä. ()

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(int(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 <orange> #  Kuulan vaakanopeus ilman suhteen
</orange>    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ää.

https://heikki-valisuo.fi/kuva/templates/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

<orange> #  Sirpale
</orange>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)



   <orange> #  Sirpaleet liikkuvat samalla dynamiikalla kuin kuulatkin
</orange>    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 > ??? ()

???

# -*- 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(int(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

<orange> #  Peli-ikkuna päivitetään Nayttotaajuus kertaa sekunnissa
</orange>Nayttotaajuus = 48



Tuuli = random.uniform(-50.0, 50.0)
<orange> #  Tuuli = -50.0
</orange>


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


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

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 2 20:28:32 2025


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

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 ()

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

<orange> #  Kuun ja Maan vetovoimakentässä liikkuvan kappaleen
</orange><orange> #  kiihtyvyys. Riippu kappaleen paikasta, rakettimoottorin
</orange><orange> #  synnyttämästä voimasta ja kappaleen massasta
</orange>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)


<orange> #  paikan (x, y) ja nopeuden (vx, vy) derivaatat ajan suhteen
</orange><orange> #  (f1, f2, f3, f4)
</orange><orange> #  Ohjelma ei pysähdy, vaikka alus törmäisi Maahan tai Kuuhun.
</orange><orange> #  "Koukkaus" läheltä Maan ydintä aiheuttaa niin suuria kiihtyvyyksiä,
</orange><orange> #  että simulointi saattaa antaa epäfysikaalisia tuloksia.
</orange><orange> #  Siksi niistä varoitetaan.
</orange>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]


<orange> #  Paikan ja nopeuden muutokset ajassa Dt
</orange><orange> #  lasketaan Runge-Kutta algoritmilla
</orange>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ää.

<orange> #  Pelin ja simuloinnin ajoitus
</orange><orange> #  Nopeutus: Montako kertaa todellista nopeammin pelin
</orange><orange> #  halutaan etenevän
</orange><orange> #  Tscalea käytetään nopeutuksen sovittamisessa oikeaksi
</orange>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)
<orange> #         loppu = True
</orange>


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ä ()

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

<orange> #  Piirretään voiman suuntaa ja suuruutta kuvastava jana.
</orange>def nuoli(p0, Voima):
    (ix0, iy0) = p0
    (Fx, Fy) = Voima
    F = sqrt(Fx**2 + Fy**2)
<orange> #  "ylipitkä" jana katkaistaan ja piirretään punaisella.
</orange>    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
<orange> #  Huomaa, että näytöllä y kasvaa alaspäin, siksi -Fy.
</orange>    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ä.

<orange> #  Kiintotahtiä taustaksi ja kiintopisteiksi
</orange>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))
           <orange> #  jana, jonka loppupiste ja alkupiste ovat samoja,
</orange>           <orange> #  piirtyy näytölle pisteenä
</orange>            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 (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 (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 ()

Python2html.py

Tällä oh­jel­mal­la on tuo­tet­tu tämän si­vus­ton python-ohjelmien esit­te­lyt.

Wed Apr 2 20:29:41 2025


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 ()

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)
# = = = = = = = = = = = = = = = = = = = = = = = = = =


from pathlib import Path, PurePath
import random
import time
import ast
import sys
import re
import os
def tagline(projName, 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(projName, 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(projName, tag.group(1), vari, vaihe, ln)
                return (code, tglin)
    return (False, '')


def tarkistaVaihe_py(projName, 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(projName, 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(projName, infil, 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(
                    projName, 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(infil, 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(projName, infil, 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(
                    projName, line, tag, vaihe, ln)
            elif pr_cod:
                    progfil.write(line)


def repl(line):
    return re.sub('<', '<', re.sub('>', '>', line))
def summary(infil):
    print('infil ', infil)
    with open(infil, 'r') 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(curdir, '/', str(sys.argv[i]))
            fout.write(
                '\n<p>< anchor="l' + str(random.randint(100, 9999)) +
                '" file="' + webref + ' type="download">lisätiedosto: ' + str(sys.argv[i]) +
                '</download></p>')


def main():
    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(infil)
    with open(xmlf, 'w') as fout:
        fout.write('<section>\n<title id="' + xmlf.as_posix() + '">' +
                   projName + '.py\n</title>\n')
        fout.write('\n<summary>')
        fout.write(summary(infil))
        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(projName, infil, fout, vaihe)
            fout.write('<shd uanchor="h' + str(random.randint(100, 9999)) +
                       '">Selityksiä ylläolevaan</shd>')
            selitykset(infil, fout, vaihe)
            pfname = projName+'_'+str(vaihe)+'.py'
            with open(pfname, 'w') as progfil:
                vain_koodi(projName, infil, progfil, vaihe)
            args = sys.argv
            abs_path = os.path.abspath(args[1])
            print("\n!!!!!")
            print(abs_path)
            top_dir = PurePath("/home/heikki/0Pilvi/vps/FastAPI/templates")
            print(top_dir)
            abs_path_pp = PurePath(abs_path)
            print(abs_path_pp)
            subdir = abs_path_pp.relative_to(top_dir).parent
            print(subdir)
            # webroot = Path(os.path.expandvars("$WEBPY"))
            # curdir = Path.cwd().relative_to(webroot)
            webref = f'{subdir}/{pfname}'
            print(webref)
            print("\n----------------")
            # PurePath(curdir, pfname).as_posix()
            # print(f' paths {webroot} \n {curdir} \n {pfname} \n {webref}')
            fout.write(
                f'<sp><iso><download file="{webref}" type="download"> Lataa tästä ohjelman tämän hetkinen versio: ' +
                f'{pfname} </download></iso></sp>'
            )
            # lisafileet(fout, subdir)
            fout.write('\n</section>\n')
        fout.write('\n</section >')
        print('\n!!!! lisäfileet ohitettiin !!!')


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>



-




from pathlib import Path, PurePath
import random
import time
import ast
import sys
import re
import os
def tagline(projName, 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(projName, 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(projName, tag.group(1), vari, vaihe, ln)
                return (code, tglin)
    return (False, '')




a


def tarkistaVaihe_py(projName, 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(projName, 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(projName, infil, 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(
                    projName, 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(infil, 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(projName, infil, 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(
                    projName, line, tag, vaihe, ln)
            elif pr_cod:
                    progfil.write(line)




a


def repl(line):
    return re.sub('<', '<', re.sub('>', '>', line))


def summary(infil):
    print('infil ', infil)
    with open(infil, 'r') 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(curdir, '/', str(sys.argv[i]))
            fout.write(
                '\n<p>< anchor="l' + str(random.randint(100, 9999)) +
                '" file="' + webref + ' type="download">lisätiedosto: ' + str(sys.argv[i]) +
                '</download></p>')




a


def main():
    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(infil)

    with open(xmlf, 'w') as fout:
        fout.write('<section>\n<title id="' + xmlf.as_posix() + '">' +
                   projName + '.py\n</title>\n')
        fout.write('\n<summary>')
        fout.write(summary(infil))
        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(projName, infil, fout, vaihe)
            fout.write('<shd uanchor="h' + str(random.randint(100, 9999)) +
                       '">Selityksiä ylläolevaan</shd>')
            selitykset(infil, fout, vaihe)
            pfname = projName+'_'+str(vaihe)+'.py'
            with open(pfname, 'w') as progfil:
                vain_koodi(projName, infil, progfil, vaihe)

            args = sys.argv
            abs_path = os.path.abspath(args[1])
            print("\n!!!!!")
            print(abs_path)

            top_dir = PurePath("/home/heikki/0Pilvi/vps/FastAPI/templates")
            print(top_dir)
            abs_path_pp = PurePath(abs_path)
            print(abs_path_pp)

            subdir = abs_path_pp.relative_to(top_dir).parent
            print(subdir)
           <orange> #  webroot = Path(os.path.expandvars("$WEBPY"))
</orange>           <orange> #  curdir = Path.cwd().relative_to(webroot)
</orange>            webref = f'{subdir}/{pfname}'
            print(webref)
            print("\n----------------")
           <orange> #  PurePath(curdir, pfname).as_posix()
</orange>           <orange> #  print(f' paths {webroot} \n {curdir} \n {pfname} \n {webref}')
</orange>            fout.write(
                f'<sp><iso><download file="{webref}" type="download"> Lataa tästä ohjelman tämän hetkinen versio: ' +
                f'{pfname} </download></iso></sp>'
            )
           <orange> #  lisafileet(fout, subdir)
</orange>            fout.write('\n</section>\n')
        fout.write('\n</section >')
        print('\n!!!! lisäfileet ohitettiin !!!')




-


if __name__ == "__main__":
    main()



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