Näytetään tekstit, joissa on tunniste Pygame. Näytä kaikki tekstit
Näytetään tekstit, joissa on tunniste Pygame. Näytä kaikki tekstit

8.1.2014

Oma koodailuprojekti, osa IX

Elämää etsimässä

Pelkät asteroidit ja alukset eivät olisi kovin viihdyttäviä pelialueen sisällön kannalta. Tässä vaiheessa näppäilyä päätin, että yksi planeetta per sektori olisi ihan riittävä. Varsinkin, kun sektori on nyt vain 1024x1024 pikselin kokoinen, ei sinne kovin montaa planeettaa mahtuisi. Todennäköisesti jatkossa skaala on isompi, katsellaan.

Peliprojektini ykkösiteraatiossa planeetta piirrettiin pygamen circlenä, mutta se näytti jotenkin ahdistavalta, kun kaikki muut olivat antialiasoituja polygoneja. Päätin siis sangen ennakoitavasti, että minun planeettani ovat (ainakin tässä vaiheessa) 16-kulmaisia. Planeetat ovat muuten kuin asteroidit, mutta ne pysyvät paikallaan, eivätkä pyöri.

Epämääräisen muistikuvani mukaan Lasse kyseli aiemmin kommenteissa jotain planeettatyyppeihin liittyen, joten vetäisin olemattomasta hatustani muutaman tyypin niille (industrial, farming näin esimerkiksi) testailua varten. Jatkossa näille tulee tietysti lisää attribuutteja, pelkkä tyyppi, massa ja väestö ei vielä riitä mihinkään. Niin ja niiden kobolttikuoristen pommien vaikutusten takia joku (tausta)säteilykin pitäisi varmaan pistää talteen... kiäh kiäh kiäh.



Mitä seuraavaksi?

Fysiikkaa

Kaipa siirryn seuraavaksi takaisin QuadTreen ja ainakin yksinkertaisen törmäystarkistelun pariin. Törmäysten toteamisen jälkeen pitää viritellä kimpoamisia niin, että ne toimisivat jotenkin tolkullisesti. Samoin aluksen kiihdytys pitää korjata niin, että lentäminen tuntuisi oikeammalta kuin nykyinen nykiminen.

Aseita, tuhoa ja kuolemaa

Kun törmäystarkistelut sun muut on edes aloitettu, voinen siirtyä toteuttamaan ainakin jonkunlaisen asejärjestelmän. Kuolemansäteet eivät ehkä ole se ensimmäinen, vaan taidan testata ensin konetykkejä tai ohjuslavetteja. Saa nyt nähdä, mikä sillä hetkellä huvittaa eniten.
Johonkinhan  niitä on myös voitava testata joko asteroideihin tai dummy-avaruusaluksiin. Ensin varmasti asteroideihin, koska ne ovat jo lähestulkoon valmiit, tai niin ainakin uskottelen itselleni.

Epämääräisiä ideoita

Miten pelaaja voi telakoitua avaruusasemalle? Tai miten pelaaja voi tehdä yhtään mitään planeetan kanssa? Sain ajatuksen vanhasta, mainiosta Zone 66:sta. Muistin, että jos laskeutumisalustan yli lensi tarpeeksi hitaasti, sille laskeuduttiin, muuten yli vain lennettiin. Samalla idealla tarpeeksi matalanopeuksinen avaruusaseman kohdalle lentäminen siirtäisi pelin telakointimoodiin. Tai planeetan päällä (ei, planeettaan ei voi törmätä aluksella, ne sijaitsevat z-akselilla eri tasoilla) hidastellessaan siirrytään kiertoratatilaan, jossa voi sitten tehdä omiaan.

Tähän mennessä ainoa ehdoton vaatimukseni on tunnettu kiertoratapommituksena. Tietenkin pelaajalla tulee olla mahdollisuus kylvää verivihollisensa kotiplaneetta täyteen vaikkapa terästettyä paiseruttoa, jos siltä kerran tuntuu. Luonnollisesti tämä edellyttää sitä, että planeetoilla pitäisi olla jonkunsorttinen tapa puolustaa itseään kaikenmaailman psykopaatteja vastaan. Ohjussiiloja kuin piikkejä siilissä, ionikanuunat tulivalmiudessa, hävittäjäparvet laukaisualustoillaan ja kiertoradat aseistettuja tiesmitä täynnä. Näistä on hyvä lähteä.

Kaikki (minä ainakin) inhoavat telepaattisia osapuolia peleissä. Nämä kaikille tutut hulluthan tietävät oitis, jos olet vaikkapa ryöstänyt heidän veljensä toisella puolella universumia ilman, että kukaan on nähnyt tai kuullut mitään ja suuttuvat silmittömästi. Tämän kun saisin vältettyä, olisin ultraonnellinen.

Turha haave?


Ajatusleikkiesimerkkinä: hyökkäät rahtialuksen kimppuun ja nirhaat sen yksittäisen saattajan ripeällä yllätyshyökkäyksellä. Murhaat miehistön, rosmoat rahdin ja kaikkoat paikalta. Kukaan ei todistanut tapahtumaa, hätälähetyksiä ei joko ehditty edes lähettää tai häirintälaitteesi jyräsi halvan radiolaitteen signaalin. Aiempi loistava maineesi on edelleen tahraantumaton ja miksei olisi? Galaksin silmissä olet edelleen nuhteeton pyhimys.
Ainakin siihen asti, kunnes lähdet tekemään kauppaa mahdollisesti tunnistettavalla rahdilla...


Tällaisia minä olen miettinyt sairaassa mielessäni.

1.1.2014

Oma koodailuprojekti, osa VIII

Liikettä!

Kun ruudulle oli saatu jotain satunnaista möllöttämään, jatkoin lentelytoimintojen parissa. Pienen pyörittelyn jälkeen päätin pysyä entisessä nopeusvektorilähestymistavassani ja käyttää sen kaverina heading-tietoa, josta aluksen nokan osoittaman suunnan saisi irti suoraan. Koska näytettävä pelialue oli sidottu alukseen, aluksen liikkuminen vieritti automaattisesti näkymää ympäri sektoria.

Aluksen pyörittely akselinsa ympäri oli hyvin yksinkertaista, eikä siitä ja sen rotate-metodeista sen enempää. Nopeuden muutos oli iso osaprojekti aiemmallakin kerralla, joten ei siitä nytkään päästy helpolla. Tätä kirjoittaessani kaasuttelu oli vieläkin keskeneräinen, joten siihen on palattava myöhemmin. Nyt riitti, että sain sen kulkemaan ympäri sektoria edes puoliymmärrettävästi, joskaan en elegantisti.



Ensin lasketaan aluksen nokan (heading) ja kiihdytyksen perusteella dx ja dy -arvot. Sitten katsotaan, mihin suuntaan nokka osoittaa ja asetetaan sen perusteella dx:lle ja dy:lle etumerkki. Mikäli uusi nopeus olisi ylittämässä aluksen maksiminopeuden, kiihdyttäminen estetään. Kuten sanoin, nykytoteutuksessani on jotain omituista.

Sektorilta toiselle

Tässä vaiheessa esiin nousi seuraava kysymys: mitäs sitten, kun sektorin rajat ylitetään? Jatketaan seuraavaan tietenkin. Koska en ollut aiemmin asettanut ektoreille naapuriviittauksia, jouduin palaamaan pelimaailman inittiin. Samoin, koska sektorit luodaan vain yksinkertaisessa silmukassa, naapurien selvittäminen vaati vähän lisätöitä.

Loppujen lopuksi tähän meni muutama tunti muutamana iltana ja refaktorointia siellä sun täällä (ainakin luokissa Player, Sector, World ja Game). Maailmanluonti oli hidastumassa huomattavasti: kun sektorit on ensin luotu, ne käydään sitten uudelleen läpi ja kunkin sektorin pohjoinen, eteläinen, itäinen ja läntinen naapuri käydään kaivamassa ja asettamassa "koordinaattiensa" perusteella.

Sektorinvaihdon yhteydessä pelaajalle on asetettava uusi sektoritieto, alus on heitettävä uuden sektorin vastakkaiseen päähän (ylöspäin mennessä siirrytään siis pohjoisen naapurin eteläpäähän), irrotettava entisen sektorin rakenteista ja heitettävä uuden sektorin vastaavaan ja vielä ladattava maailman nelipuuhun uuden sektorin peliobjektit.





Olin taas kovin ylpeä tekeleestäni tässä vaiheessa. Kaiken muun lisäksi virittelin maailman inittiä nätimmäksi. Tässä vaiheessa sektoreille luotiin vain satunnaisia määriä asteroideja, vihannesviholliset kommentoin mäkeen perustoimintojen kanssa puljaamisen selventämiseksi.


25.12.2013

Oma koodailuprojekti, osa VII

Pitkän tauon jälkeen

Pygame-projektini on edennyt enemmän kuin mitä postaustahdista voisi olettaa. Pääasiassa olen räveltänyt taustapalikoiden kanssa, mitään kovin näkyvää tai käyttäjälle suoraan (ainakaan vielä) näkyvää en niinkään.
Toisaalta, jos edellispostausta aiheen tiimoilta katsoo, aikalaillahan tässä on muuttunut.

Viimeksi siis pelimaailma sektoreineen luotiin ja sen perusteella käyttäjälle (tässä vaiheessa ei ole mitään pelattavaa, joten en saata kutsua sitä "pelaajaksi") näytettiin kartta. Kartan sai avata/sulkea ja pelin pystyi pistämään tauolle. Perustoimintoja, muttei mitään kovin hienoa tai ihmeellistä.

Jatkoaskeleet

Ensimmäisen iteraation pelinäkymä oli kiinteä ja alus lensi siinä vapaasti. Ensimmäinen asia oli siis aluksen kiinnittäminen näytettävän alueen (viewport) keskelle ja sen vierittäminen eestaas ympäri pelialuetta (sektoria). Koska sektorin koko olisi aina näyttöaluetta laajempi, ei olisi myöskään mitään tolkkua piirrättää sellaisia objekteja, jotka eivät ole näyttöalueen sisällä. Sektorilla olisi siis oltava jonkunlainen tietorakenne peliobjektejaan varten, josta tuonkin tiedon saisi kaivettua esiin.

QuadTree

QuadTreen idea on, että jos sen yksittäisen solun kapasiteetti ylitetään, se jakautuu neljään alisoluun, johon kaikki sen sisältämät objektit jaetaan edelleen alisolujen sisään. Ja niin edelleen, kunnes puun maksimisyvyys on saavutettu. Mikään selitykseni ei voi olla parempi kuin jo olemassaolevat, joten vaikkapa wikipediasta voi lukea lisää.
Oleellista on se, että toteutin oman (tai tässä vaiheessa osan siitä) versioni tuosta. Alkuvaiheessa, kun sen pelkkä olemassaolo riittää hyvin, kaikki sektoriin kuuluva romu lisätään puuhun. Yllämainittu tarve näyttöalueella olevien peliobjektien hakemisesta taas oli ensimmäinen oikea käyttötapaus. Koska sektorit ovat neliöitä, kaikki peliobjektini toimivat ainakin tässä vaiheessa neliöinä ja nelipuun kaikki solut ovat luonnollisesti neliöitä, pygamen colliderect-metodit ratkaisevat kaiken.

def get_contained_objects(self, rectangle):
  contained_objects = []
  if self.gameobjects:
    for gameobject in self.gameobjects:
      if rectangle.colliderect(gameobject.get_rect()):
        contained_objects.append(gameobject)
      if self.top_left:
        contained_objects.extend(self.top_left.get_contained_objects(rectangle))
        contained_objects.extend(self.top_right.get_contained_objects(rectangle))
        contained_objects.extend(self.bottom_right.get_contained_objects(rectangle))
        contained_objects.extend(self.bottom_left.get_contained_objects(rectangle))
  return contained_objects

Oleellista ylläolevassa on välitetty rectangle, joka on siis pelinäkymä (viewport). Pelinäkymän pinta-ala taas kaivetaan pelaajan aluksen keskipisteen ympäriltä. Samalla vaivalla lasketaan aluksen keskipisteen ja kuvapinta-alan 0,0-pisteen välinen poikkeama, jota taas käytetään palautettujen peliobjektien renderöintiin. Kaikkien peliobjektien sijainti kartalla (sektorissa) on suhteessa sen vasempaan yläkulmaan ja siksi tuota poikkeamaa tarvitaan, jotta ko. peliobjekti voidaan piirtää oikeaan paikkaan suhteessa pelaajan alukseen.

Ehkä selitin tämänkin taas turhan oudosti, mutta minkäs teet. Koko ongelman selvittämiseen meni muutamakin istunto ja tuokio. Olin siis hyvin ylpeä, kun sain asiat vihdoin toimimaan oikein.



Peliobjektien paluu

Tietysti minulla piti olla jotain piirrettävää, jotta sain nelipuuni toiminnan testattua edes alkeellisesti. Alkuvaiheessa minulla oli nuo viimeksikin mainitut kolme aliavaruuspiraattien alusta heitettynä vakiopisteisiin sektorissa. Niiden lisäksi halusin tietysti palauttaa Asteroidit mukaan kuvaan, mutta tälläkin kerralla vähän paremmin. Samalla vaivalla puun insert- ja jakautumismetodeja voisi testata satunnaismäärällä sisältöä.

Asteroidikoodi siirtyi lähes sellaisenaan uuteen projektiin. Inittiin tuli lähinnä lisää satunnaisuutta. 2d6-heiton tuloksella 9-12 syntyy jääasteroidi, muuten kivinen murikka. Samoin 2d6-heitolla määritellään murkulan kokoluokka vanhoilla tutuilla pieni / keskikokoinen / iso / suunnaton -vaihtoehdoilla. Materiaali vaikuttaa sekä väriin, satunnaisarvottuun massaan että pyörimisnopeuteen. Kiviset ovat sekä raskaampia että hitaampia kuin jäiset asteroidit.



3.7.2013

Oma koodailuprojekti, osa VI

Toisesta suunnasta lähestyen

Aiemmin aloitin suoraan peliolioiden ja niiden kontrolloinnin parissa. Koetin jossain välissä tehdä välistä jäänyttä muuta osuutta, mutta jotenkin tuon .py-filun sörkkiminen ei oikein tuntunut hyvältä ratkaisulta. Olemassaoleva koodi oli kokonsa puolesta vähän ikävää käpälöitävää IDLEllä (enkä ole laiskana käyttänyt muuta), joten aloitin sivussa sivuprojektin sille kaikelle muulle. Myöhemmin voisin sitten yhdistää tekemäni viritelmät ja nauttia/kärsiä lopputuloksesta.

Tilasta toiseen

Päätin aloittaa määrittelemällä tekeleelleni muutaman eri tilan, jossa se voi olla. Näiden mukaan eventtien (tapahtumien) käpälöinti olisi jaoteltavissa aika siististi erikseen:
  • päävalikossa
  • kartassa
  • pelissä
  • taukotilassa (ihmisten kielellä ihan vaan pause)
Näin. Päävalikkoidea lienee peräisin jostain Apogeen tasohyppelypeleistä sun muista, tai niitä muistellessani minä tuota hahmottelin. Oleellista on, että pelin voi aloittaa ja siitä voidaan poistua - muut vaihtoehdot, kuten tallenna, lataa/jatka (riippuen siitä, onko mitään tallennusviritelmiä olemassa) ja asetukset voidaan toteuttaa sitten joskus mutta ruudulle ne voitaisiin silti syöstä.



Kun peli aloitetaan, pelimaailma alustetaan annettujen ja tässä vaiheessa täysin kovakoodattujen parametrien mukaisesti. Java-taustani voi vaikuttaa siihen, että olin alusta alkaen takomassa paisuvaa määrää luokkia ja perintää sinne tänne.

Maailmojen rakentaja

Mietiskelin, mitä kaikkea oleellista tähän voitaisiin tarvita. Game luonnollisesti sisältää ja pyörittää peliin itseensä yleisesti liittyviä asioita, kuten tilansa, asetuksensa ja erinäköisten näkymien renderöinnin. Gameen sisältyy sitä varten luotu World, pelimaailma, jossa taas pyöritellään Osapuolia (Faction), niiden asioita ja pelimaailman kokoa (testivaiheessa 3x3 -maailma). Maailma koostuu Sektoreista eli loppujen lopuksi niistä "tasoista", joissa pelaaja avaruuslaivallaan koheltaa. Sektorilla on tässä vaiheessa lähinnä koko (pelialueen x,y  -koko) ja omistaja (joko ei ketään tai jokin Osapuoli). Jatkossa sillä on lista romusta, jota se sisältää.

"Politiikka"

Osapuolilla taas on nykyhetkellä nimi, tunnusväri, rotu, politiikka ja nollasta useampaan laivastoa. Rotuoppina on hatusta vedetty humanoidi / insektoidi  / robotoidi tai sekalainen - en ollut kovinkaan innovatiivisella tuulella noita raapustaessani. Rotujen ainoa vaikutus tulee politiikan kautta, yleispoliittisia maailmankatsontoja on kolme: neutraali, ksenofobi ja aggressiivinen. Ideana on, että neutraaleja ei kiinnosta, mikä olet, ksenofobit suuttuvat oitis, jos olet erirotuinen ja viimeistä ryhmää ei sitäkään kiinnosta, kuka ja miksi olet, kunhan et ole kauaa.

Ehkä typeränä ideana raapustelin vielä osapuolien välisiä suhteita varten listan asetuksia. Liittolaiset, ystävällismieliset, neutraalit, epäystävälliset ja viholliset - siinä viisi tapaa, jolla kaksi eri osapuolta voivat toisiinsa suhtautua, jos suinkaan kykenevät.

Politiikan jatke

Kukkien jakaminen kanssaolioille ei varmaan kiinnosta ketään, joten sotaa tulee käymän. Sodankäyntiin mietin Laivastoja (Fleet) eri Osapuolille. Laivasto voisi koostua yhdestä tai useammasta emoaluksesta (Capital Ship), joilla voi tai ei voi olla uumenissaan hävittäjäkokoluokan (Fighter) aluksia sun muita rotteloita.
Testimielessä ajettava alustin rakentaa pelaajan kaveriksi muutaman Osapuolen, joista tasan yksi on nimetty: Subspace Pirates (rotu: sekalainen, politiikka: valitaan aina satunnaisesti) ja sillä on aloitussektorissa Laivasto "Hammer of the gods" kolmella aluksellaan ("Thunder", "Wind", "Rain"). Riemukasta. Loput nimet avain kaikelle ja kaikille on tarkoitus kaivaa jostain puppugeneraattorista, epämääräisellä lopputuloksella. Satunnaisuus on hieno asia ja tekee mm. testaamisesta huomattavasti mielenkiintoisempaa, kun kaikki ei tapahdu aina 1:1 samassa järjestykessä.
Laivastoille voi antaa kohdesektorin eli mikään niistä ei olisi sidottu tiettyyn sektoriin, vaan voisi siirtyä tarpeen vaatiessa kiusaamaan pelaajaa tai toista Osapuolta. Noin ainakin ideatasolla sen pitäisi toimia. Tekoälyttömyyden arpominen onkin sitten ihan oma lukunsa, joka pelottaa lievästi jo nyt.

Kartalla vai kujalla?

Pelin aloitettuaan ja maailman alustajaa ehkä odoteltuaan peluuttelija pääsee ihastelemaan karttanäkymää. Nollasektorissa näkyvä rastiympyrä osoittaa pelaajan purtilon suhteellisen paikan maailmassa. Samaa kuviota meinasin harkita käyttäväni minikartassa, jos sellainen tuntuu tulevaisuudessa tarpeelliselta.

3x3

5x5


Tässä vaiheessa, kun kartasta poistuu, ei olekaan sitten mitään tekemistä. Ruudulle jää tyhjä näkymä ja pois pääsee joko karttaan tai välimenuun (jatka/asetukset/poistu), jollaisen väkästelin kasaan tuossa pari päivää sitten. Niin ja k-näppäintä hivelemällä kuolee ja pääsee kuolemanäkymään, josta taas jatketaan päävalikkoon. Oleellista sekin.

Seuraavaksi varmaan koetan saada vanhat luokkani käyttöön ja toimintaa ruudulle. Yksi iso mietittävä asia on vielä tuo peliolion nopeus... haluanko pitää sen entisellään (x- ja y-nopeudet) vaiko nopeus ja kulma -arvot erillisinä muuttujina. Joka tapauksessa joudun sörkkimään asioita, eli ihan helpolla en pääsisi missään nimessä.

11.1.2013

Oma koodailuprojekti, osa V

Pikkumuutoksia sinne tänne

Saavutukseni näiden viikkojen aikana ovat aika vähäiset, kiitos vajaan kolmeviikkoisen loman ja kaiken muun pienen. Koetin vähän sörkkiä 'roidien törmäilyä siten, että massaerolla olisi vaikutusta - isompaan törmääminen vaikuttaa siihen isoon kiveen vähemmän kuin pieneen jne. Viilailuahan tuo vaatii, mutta on askel vähän normaalimpaan suuntaan. Nyt megamurkula ei lähde kiitämään tuhatta ja sataa kun pikkumurikka kolahtaa siihen. Ja jos oikein hassusti käy, se halkeaa kahteen suureen murikkaan.

Huvikseni kokeilin väkertää vaaleansinisen gridin taustalle. Ikäänkuin koko roska olisi tuherrettu ruutupaperille. Ehkei se toimi niin hyvin. Katsotaan nyt.
Raapusteluefektin tavoittelua


Sataa sataa ropisee...

Samoin pykäsin kevyen planeettaluokan. Ruudulla se on ympyrä ja taustalla sillä on myös nimi, massa, populaatio sun muuta epäoleellista. Kun kivet kopsahtelevat tonttiin, populaatio vähenee niin kauan kun sitä vielä on. Yllättävän hauskaa tuotakin oli testata :P
Candyland II


Paukkuraudat, kuolemansäteet ja n-vaiheiset raketit ovat tietysti vielä vain ideatasolla. Aloittelin tuossa ennen lomalle lähtöäni risteävien janojen tutkimista mutten päässyt puusta pitkään. Vielä.


Muu touhu

Ensi viikolla saattaisin ehkä ehtiä tuon tankinkin kimppuun, jos kaikki muu kiirus sallii. Sekin on ruvennut vähitellen taas kuumottelemaan, usean viikon tauon jälkeen.

2.1.2013

Oma koodailuprojekti, osa IV

Toiminnallisuutta mukaan

Sain ruudulla näkymään sitä ja tätä, mutta millään ei ollut mitään vaikutusta mihinkään. Tämä ei käynyt laatuun, joten seuraavat GameObjectiin lisättävät luokkamuuttujat olivat mm. hitpoints eli osumapisteet. Tungin minä sinne kaikkea muutakin hienoa, kivaa ja tulevaisuudessa oleellistakin, mutta ei niistä ole hirveästi iloa tässä vaiheessa. Kun kerran ollaan jonkunsorttisen Asteroids-viritelmän kanssa tekemisissä (en meinannut jäädä siihen, vaan lisätä jotain todella sekavaa mutta ainakin omasta mielestäni mielenkiintoista), törmäily on kai aika olennaista. Tätä raapustaessani olen tehnyt pohjaa pidemmälle kuin mistä nyt on kertoa.

Törmäilymietteitä

  • Murkulat voivat törmäillä pelaajan purtiloon ja päinvastoin
  • Murkulat voivat törmäillä toisiinsa
  • Nopeuseroilla pitäisi olla merkitystä törmäystilanteessa
  • Massaeroilla pitäisi olla merkitystä törmäystilanteessa
  • Törmäyskulmakaan ei ole yhdentekevä

Kaksivaiheinen törmäystestaus

Kun kullakin hetkellä listoilla voi olla aika hyvä määrä olioita, ei ole mitään tolkkua käydä kaikkien törmäyksiä läpi supertarkasti, vaan voidaan ensin käydä kaikki läpi ja katsoa edes summittaisesti, josko kolisee ja sitten, tarpeen vaatiessa, tarkemmin. Ykkösvaiheen toteutuksessa käyn kaikki 'roidit ja pelaajan aluksen läpi ja vertaan toisiinsa, josko niiden summittaiset pinta-alat risteävät. Tähän puuhaan pygamen rectangle.colliderect() riittää mukavasti:

def collides_with(self, other):
        collides = False
        if self and other:
            if self.get_rect().colliderect(other.get_rect()):
                collides = self.accurate_collision(other)
        return collides
 
def accurate_collision(self, other):
        return True


Jälkimmäinen metodi on luonnollisesti stubi, jonka toteutus tulee kohdille kun ehtii. Tämänhetkinen vain toteaa, että "näyttää siltä, että törmäävät ja se kelpaa minulle" ja sen jälkeen molemmille törmääville elementeille heitetään 2d6-vauriot, kunnes toinen tai molemmat tuhoutuvat. Koska murkuloita on neljää kokoluokkaa, isommat hajoavat n kappaleeksi pienempiä, jos sille päälle sattuvat.
Kyllästyin aika pian siihen, että alukseni hajosi aika ripeästi testailun tiimellyksessä, joten lisäsin mukaan automaattisen Megatron-huijauksen. Peli sanoo Afterdeath ja kaikki jatkuu pelaajan kannalta kuin mitään ei olisi tapahtunut.

Yhtenä aamuna juttelin tästä projektinkuvatuksesta yhden työkaverini kanssa (oli huomannut jotain mutinoitani g+:ssa). Muiden seassa esiin nousi kommentti antialiasoinnin puutteesta, olinko miettinyt käyttäväni sitä vai aioinko tehdä tarkoituksella sahalaitaista. Vastasin, että meinasin korjata tilanteen nätimmäksi kun saan oleellisemmat pulmat ratkottua. Siinä samalla katseltiin pikaisesti dokumentaatiota ja hops, jos vaihtaa drawpolygon-metodikutsun aalines-kutsuksi ja muistaa lisätä yhden True-parametrin, muuta ei tarvita. Kokeilin kurillani samana iltana ja näyttäähän se nätimmältä näin, vai mitä?
Antialiasoituna

Törmäyksen jälkeen

Koska olisi aika naurettavaa, että avaruuskivet poikkeuksetta jatkaisivat törmäämisensä jälkeen toistensa sisään syöksymistä, mietin, että seuraavaksi pitäisi toteuttaa deflect-metodi, joka laskisi jotenkin molemmille törmäyksen osapuolille uudet nopeusvektorit. Jos saisin aikaan jotain edes etäisesti biljardipöydän toimia jäljittelevän toteutuksen, olisin onnellinen. Kun tuon yhdistää kriittisiin osumiin (pieni ja nopea kivi voisi sopivasti osuessaan halkaista suunnattoman kiven kahtia), lopputulos voisi olla vänkä. Niin ja ne planeetat ja ties mitkä... ei tässä ole vielä mitään tehtynä!

"Sota. Sota ei koskaan muutu."

Kaikille tätä lässytystä jonkun aikaa seuranneelle lienee kai ihan päivänselvää, että kaiken muun omituisen lisäksi Star Wars ja Battletech ovat minulle isoja juttuja. Vallankin jälkimmäisestä olen miettinyt lähteväni rosmoamaan aseistukseen liittyvää kamaa. Pääideana on, että purkkiinsa saisi valikoida jonkun tai jokusen väkivallan välineen riippuen vähän aluksen sloteista. Ohjuksia/raketteja, kuolemansäteitä (pulssittaisina tai ei), automaattikanuunoita ja taivaansinisiä salamoita yskiviä partikkelikanuunoita. Muahaha! Ja jos kerran aseistusta voi muuttaa, miksei moottoriakin voisi vaihtaa?

Kunhan mietin, myöhemminhän se selviää, mitä hemmettiä oikein haluan.

25.12.2012

Oma koodailuprojekti, osa III

Minne nokka näyttää

Nyt kun olin vihdoin saanut sekä kuvioni kierron kuntoon ja sen suunnankin vihdoin selville, perustoimista jäljellä oli liikkuminen. Ensimmäinen yritelmäni oli vallankin jälkikäteen ajateltuna naurettava: jos kulma osoitti johonkin pääilmansuuntaan, kyseinen suunta sai täyden nopeusmuutoksen - jos taas sille välille niin jonkun puolikkaan muutoksen kahteen pääsuuntaan jaettuna. Testatessa se näytti ensin hienolta, mutta hetken päästä huomasin, että "Heeeeei... tämähän nykii kääntyessä ja kaasuttaessa ihan omituisesti". Pienikin poikkeama 3π/2:sta muutti nopeutta aina täysillä johonkin väli-ilmansuuntaan. Hupsis.

Realistisempi nopeusmuutos

Hyvin pienen ruuduntuijottelun jälkeen pääparassani syttyi lamppu: jos vaikka käyttäisin noita hiton trigonometrisia funktioita kertomaan juuri tuon: tunnettu kulma ja nopeus voidaan kääntää kertomaan aiemmin ihmettelemäni Δx ja Δy -arvot... Eli sama temppu, mutta päinvastaiseen suuntaan. Onneksi tuon äkkäämiseen ei mennyt muutamaa minuuttia enempää.

tempspeed = Vector(self.speed.x, self.speed.y)
dy = math.fabs((math.sin(self._angle))*self._acceleration)
dx = math.fabs((math.cos(self._angle))*self._acceleration)

# ...


Toki tuossa pitää vielä tarkastella polygonin suunnan perusteella, ovatko Δx ja Δy positiivisia vai negatiivisia kukin, jotta kiihdytyksen voi asettaa oikein. Pääasia, että se näyttää hyvältä ainakin tällä hetkellä.

Partikkeliefektejä!

Pelkän kuvion "lennättely" tyhjällä ruudulla käy äkkiä tylsäksi. Näpertelin siis Star Control (tai Auts tai  V-Wing tai Wings) -henkisen kaasutteluefektin. Sen tarkemmin moottoripaikkoja miettimättä asetin* ne polygonini alakulmiin. Aina, kun kaasua painetaan, senhetkisiin koordinaatteihin pudotetaan palikat, jotka kuolevat pois muutaman syklin päästä ja elellessään haalistuvat vähitellen. Tekaisin samalla vaivalla testimielessä warppinapin, jota painamalla palikka siirretään keskelle pelialuetta. Aiempaan paikkaan luodaan räjähdyksenomaiseti kahdeksan eri suuntiin lentävää partikkelia. Efekti on hauska, vaikka itse sanonkin. Räjähdyksiin minä tuota meinasin oikeasti käyttää.

*) Teinpä kaukoviisaana sentään sellaisen toteutuksen, että heti halutessani voin nakata ne mihin lystää, niissä lukumäärissä kuin haluan. Noin suunnilleen. Ehkä vältän tyhmyysrefaktorointia tällä tavoin.

Kavereita ruudulle

Mitäpä sitä suurinpiirtein kolmion muotoisella roikaleellaan tekisi 2d-avaruudessa ja varppailisi eestaas, jollei siellä ole seuraa? Tylsistyisi äkkiä, sanon minä. Mahdollisesti typerä ideanikin lienee jo tässä vaiheessa kaikille selvä kuin pläkki. Sekaan heitettäisiin tietenkin asteroideja!
Olin kovin riemastunut, kun sain kahdeksankulmaiset satunnaisepämuodot ruudulle neljässä eri kokoluokassa, pyörimään hiljakseen joko vasta- tai myötäpäivään ja parin iteraation jälkeen myös lentämään omaan suuntaansa. Se oli upeaa.
Pröp pröp pröp
Minä ainakin olin tuosta ylpeä.

18.12.2012

Oma koodailuprojekti, osa II

Taisin viimeksi sanoakin, etten ole koskaan koettanut tehdä mitään kovin graafista, jollei opiskeluaikojen nelikulmioita tai palloja ruudulle oksentelevaa TurboC++ -tekelettä lasketa mukaan (ei, ei sitä lasketa). Ykkösongelmani iski siis silmille jo tässä vaiheessa. Nelikärkisen polygonini piirtäminen keskelle piirtoaluetta oli simppeliä, mutta sen siirtely...

Mutisen tässä omista oivallusaskeleistani kutakuinkin niin kuin niitä muistan ja missä on olevinaan edes jonkunlaista tolkkua. Laatua en takaa, kuten en yleensäkään ;)

Sijainti, kierto, siirto, piirto

Ensimmäinen yritykseni oli siis kovakoodattuja koordinaattipisteitä käyttävä roipe. Kuvittelin, että siitä olisi hyvä lähteä mutta voi voi, miten väärässä olinkaan. Ehkä vika oli omissa ensimmäisissä kiertometodeissani ja toimivilla olisi toiminutkin, mutta ehkä ei. Alkutilanne piirtyi ruudulle kauniisti, mutta yksikin kiertoliike hajoitti koko muodon ihan miten sattuu.

Nollapisteen kautta

Keksin jostain (ja työkaverin kanssa puhuttuani oletukseni osoittautui oikeanlaiseksi), että jospa kaikkien piirrettävien kuvioiden kulmapisteet asetettaisiin aina (0,0):n suhteen. Tällöin polygonin kierto toimisi aina samalla tavalla, kun ei tarvitsisi arpoa sijainnin kanssa pätkääkään. Ensin siis napataan polygonille kulmat aina samaan paikkaan, sitten niiden arvot siirretään kohdalleen, tässä tapauksessa keskelle piirtoaluetta ja piirretään näkyviin. Yksinkertaista ja varmaan lapsellisen alkeellistakin, mutta kukas tuon olisi minulle käynyt kertomassa?

Kaksiulotteisen koordinaatiston kierto

Minun matemattiset opintoni ovat vuosien takana ja olin ehdottomasti niitä rasittavia kakaroita jo peruskoulussa, jotka julistivat, etteivät ikinä tarvitse matematiikkaa, saati sitten trigonometriaa koulun jälkeen. Näin se taas nähtiin, ettei se ihan noin mene :)
Muutaman omituisen ja hölmön "teen itse"-henkisen yrityksen jälkeen törmäsin affiiniin kuvaukseen ja sehän olikin näppärä ja toteutus kutakuinkin suoraan kirjoitettava. Sille nakataan kunkin piirrettävän polygonin jokaisen kulman koordinaatit vuorollaan ja haluttu kiertokulma (luonnollisesti se on kaikille sama), paluuarvona saadaan uudet koordinaatit.

def rotate(self, angle):
        rotated_points = []
        for point in self._points:
            new_point = self.affine_transform(Vector(point.x, point.y), angle)
            rotated_points.append(new_point)       
        return rotated_points


def affine_transform(self, point, angle):
        temp_x = ((point.x * math.cos(angle)) + (point.y * math.sin(angle)))
        temp_y = ((-point.x * math.sin(angle)) + (point.y * math.cos(angle)))
        return Vector(temp_x, temp_y )



Hah! Seuraavat kannot kaskessani olivatkin tuo kulma (kohelsin asteiden ja radiaanien kanssa, omaa pohjatonta tyhmyyttäni siis). Suurin ja melkoista päänvaivaa aiheuttanut ongelma oli siis polygonini liikuttelu piirtopinnalla. Kääntely toimi, mutta kun koetin saada sitä liikkumaan kolmion kärjen suuntaan, se lähti liitämään kiinteästi toiseen yläkulmaan ja sen ohi. Jos yritin kääntää liikkuvaa kolmiota, se liikkui edelleen oikeaa yläkulmaa kohti, mutta kuin syöksykierteessä... gnaah.

Miten lasken, mihin suuntaan polygonini katsoo?

Typeränä virheenä numero 74 olin unohtanut paikalleen kovakoodatut "piirrä tuo keskelle kuvaruutua"-arvot ja toisena jäätyneenä käpynä oli aina sama kulma. Olin jostain saanut aikaan kuvion kiertokulman (eli mihin sen "nokka" osoittaa) laskentaan toteutuksen, joka oli vähän väärä. Laskin nimittäin jotain eri kylkien pituuksien ja niiden välisten kulmien perusteella - muuten kai ihan hienoa toimintaa, muttei se tässä auttanut.

Pienen googlettelun ja kevyen pään pöytään hakkaamisen perusteella ratkaisu olikin helpompi kuin mitä sopisi olettaa. Tai ainakin näin tyhmälle. Kun tiedetään kärkipisteen koordinaatit ja keskipisteen koordinaatit, voidaan niiden x- ja y-suuntaisten välimatkojen (Δx, Δy) perusteella laskea arctanilla koko roskan kulma radiaaneina.

Kai se jotain selventää, vaikka vähän epäilenkin :p


def calculate_angle(self, center, nose):
        dx = center.x - nose.x
        dy = center.y - nose.y       
        radian_angle = math.atan2(dy, dx)
        return radian_angle


Nerokkaan yksinkertaista, sanon minä. Tähän väliin näyttää tosin siltä, että kukaan ei jaksa lukea tästä enää enempää yhdellä istumalla, joten jatkan taas seuraavalla rundilla.

11.12.2012

Koodailuprojektin taustaa

Taustaa

Kaipa "kaikki" missään määrin ohjelmointia edes kokeilleet ovat miettineet, että oman pelin tekeminen olisi hienoa. Joskus aikanaan räpelsin jotain sekavia tekstiviritelmiä senaikaisen 486:n mukana tulleella QuickBasicilla, muttei niistä syntynyt ikinä mitään sen mainittavampaa. Suurin kanto kaskessani on tuossa vaiheessa ollut se, etten vaan osannut mitään mutten myöskään keksinyt etsiä mitään Basic-ohjekirjaa hyödyllisempää lähdettä, josta opetella.
Jostain kummallisesta syystä, varmaankin laiskuudestani, johtuen en myöskään ruvennut kikkailemaan pelien tekemistä myöskään sen jälkeen, kun olin opiskellut ohjelmointia ja jopa oppinutkin jotain. Ajoittain mielessä kävi kyllä, että pitäisi tehdä jotain. Jos ei muuta niin ihan vaan huvin vuoksi.

Python - Pygame

Työkaveri vinkkasi joskus Ruby-pohjaisesta Shoooes-palikasta, jota meinasin kokeillakin, mutten taas vaihteeksi saanut mitään oleellista aikaan. Sitten sekin jäi monen muun "hei kokeillaas tätä"-tyyppisen testin kanssa nurkkaan pölyttymään. Törmäsin joskus vuosi, puolitoista sitten rss-feedeissäni Pygame-kirjastoon Pythonille. Perinteitä kunnioittaen testailin jotain pientä oman aikani kunnes sekin vain jäi hyvien (lue: toteuttamiskelpoisten) ideoiden puutteen takia.

for idea in ideas:

Kun tyystin oman idiksen toteuttaminen ei ole se helpoin, mitäs sitten? Jäljitellään muita eli kiivetään niiden jättiläisten hartioille huitomaan. Työkaverini oli ruvennut sörkkimään jotain Roguelikeä kasaan joten se siitä tähän väliin (vaikken ollutkaan miettinyt mitään perusfantasiateemaa). Lapsuudessani pelasin kovasti epätoivoisen vaikeaa Xenon II: Megablastia ja myöhemmin loistavaa Tyriania.
Xenon II: Megablast

Siinä mielessä klassishenkisen shoot'em upin pysty- tai sivuskrollaavana kiinnosti kovasti sekin. Noissa ensimmäinen esiinnoussut kysymysmerkki oli tasojen kanssa tuunaaminen, joten jätin idikset edelleen korvan taakse kaiken muun saastan sekaan.

Tyrian
Hiekkalaatikkoilu sekä tietyntyyppinen avoimuus, valinnanvapaus ja yleinen satunnaisuus (= kaikki ei aina tapahdu samalla tavalla ja samassa järjestyksessä) on aina kutkuttanut. Totaalinen vaihtoehdottomuus ja eritoten (tiukat) aikarajat kiehuttavat kerrasta toiseen. Ei sillä, että tuosta olisi helpointa lähteä liikkeelle, mutta jostain on aloitettava.

Kiepsis

Rupesinpa sitten lähestymään asiaa toisesta päästä eli aiemmasta top-downista bottom-upiin, vaikken ehkä ihan tietoisesti vaan ennemminkin puolivahingossa. Näppäilin siis ihan uteliaisuuttani ympäriinsä ja katselin, mitä kaikkea tuolla Pygamella voi tehdä ja miten se tehdään. Parilla hassulla koordinaattipisteellä ja kevyellä päänrapsuttelulla pygame.draw.Polygon(...) toi kiehtovia tuloksia piirtopinnalle. "Hei, tuotahan voisi vaikka liikutella noiden näppäineventtien perusteella! Mites sen tekiskään..."

Mitä juoninkaan? Siitä kerron teille ensi viikolla.
Olen ilkeä tiiseri, tiedän. Hähä.