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.

2 kommenttia:

  1. Wanhassa Elitessähän myöskin maailmankaikkeus kirjaimellisesti pyöri pelaajan ympärillä, eli pelaajan alus istui aina origossa, ja sivulle tai taakse katsottaessa maailmankaikkeus (ja sen liikevektori) pyörähtivät 90 tai 180 astetta. Tosin nykykoneilla voi huijata, koska laskentateho riittää oikeasti laskemaan trigonometrisiä funktioita eikä tarvitse huijata logaritmitaulukoita yhteenlaskemalla.

    Nykykoneilla voi huijata sikälikin, kun pelimaailmaa ei tarvitse koostaa palasista (eli nepan 2D-pelejä esimerkkinä käyttääkseni, määritellään vaikka 8x8 merkin, tai edistyneemmillä koneilla 64x64 px blokkeja muutama kappale, tai vieläkin isompia, joskus jopa blokkien muodostamia blokkeja, ja sitten vain lasketaan mitkä näistä näkyvät kulloinkin ruudulla ja lätkitään ne kuvaruutumuistiin, ja sama uudestaan ruutua scrollattaessa, tai no, taisivat itse asiassa Mercenary ja vastaavat 3D-pelit turvautua myös vastaavaan kikkaan) tai proseduraalisesti (koko Eliten galaksi vaihtuvine hintatasoineen päivineen syntyi kahdesta n. 20 merkin jonosta. joita sitten veivattiin laskukaavoilla tai syötettiin puppugeneraattorin valintamuuttujaksi; kahdeksan galaksia selittyi sillä, että bittejä rollattiin, pelaajan puolittuva etsintäkuulutustasokin selittyi sillä, että bittejä siirrettiin yksi oikealle).

    Muistaakseni Oolite-wikissä on enemmänkin juttua Eliten koodauksesta (tai ainakin linkit relevantteihin lähteisiin), jos kiinnostaa tilaa, prosessoriaikaa ja koodirivejä säästävät kikkakolmoset.

    VastaaPoista
  2. Minä en ole vielä niin sairas ja perverssi, että harkitsisin minkään kolmiulotteisen kanssa sormieni (ja hermojeni) polttamista. Pysyn siis kiltisti 2d:ssä ;)

    Tietysti, koska en ole yhtä ankarasti kahlittu kuin David aikanaan, en ole pakotettu huijaamaan ja olemaan yhtä ovela. Randomshittiä tietysti, muttei varmaankaan missään noin (itselleni) älyvapaassa mittakaavassa :) Mutta totta himmelissä käyn kurkkimassa kunhan vaan maltan! Kikat ovat aina jees.

    Jos meinaa tehdä pinta-alaltaan ei-rajattuja leveleitä, niin jonkunsorttiset lohkot tulevat varmasti vastaan. Esimerkkinä rajattomista kartoista käyköön rakas Minecraftimme, jonka äärettömyys on muistaakseni Java.lang.Doublen matkan päästä päähän. Ja chunkeissa senkin datat säästetään - mutta menee ehkä ohi aiheesta.

    VastaaPoista