Koordinaatistomuunnos: Karteesinen -> Isometrinen

Oletko tehnyt jotain, mistä muut voisivat hyötyä. Postita vinkit tänne.
Post Reply
SPuntte
Tech Developer
Tech Developer
Posts: 650
Joined: Mon Aug 27, 2007 9:51 pm
Location: Helsinki, Finland
Contact:

Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by SPuntte »

Tuli tässä jääkiekkoa sivusilmällä töllöttäessä ideoitua ja koodailtua kätevähkö funktio mistheman projektin avuksi. Sillä saa helposti muunnettua ruudun koordinaatit isometristen tielejen koordinaateiksi. Itse asiassa muunnos toimii myö muille kuin puhtaasti isometrisille projektioille. Ideaalinen käyttötarkoitus on poimia hiirellä ruudukosta tietty tile.

Tileruudukon koordinaatisto on kiinnitetty siten, että "salmiakin" ylänurkan tile tottelee koordinaatteja (1, 1). X kasvaa oikealle alaviistoon ja Y vastaavasti vasemmalle alaviistoon.

Koodia:

Code: Select all

SCREEN 640, 480, 0, 1
SetWindow "Koordinaatistomuunnos isometriseen ruudukkoon"

Global gMouseTileX, gMouseTileY As Integer

Dim OrigX, OrigY, TileRX, TileRY, W, H As Integer
OrigX = 275 //Ruudukon sijainti
OrigY = 100
TileRX = 20 //Tilen mitat
TileRY = 10
W = 15 //Tileruudukon mitat (tilejä)
H = 12

Dim mx, my, mvx, mvy, i As Integer
Repeat
    mx = MouseX()
    my = MouseY()
    mvx = MouseMoveX()
    mvy = MouseMoveY()
    
    //Hiiren klikkaus siirtää ruudukkoa
    If MouseDown(1) Then
        OrigX = OrigX + mvx
        OrigY = OrigY + mvy
    EndIf
    
    getMouseIsoCoords(OrigX,OrigY,TileRX,TileRY, mx, my)
    
    Color 255, 255, 255
    ClearText
    AddText "MouseX: " + mx
    AddText "MouseY: " + my
    AddText "OrigX: " + OrigX
    AddText "OrigY: " + OrigY
    AddText "TileX: " + gMouseTileX
    AddText "TileY: " + gMouseTileY
    
    //Ruudukon piirto
    For i = 0 To W
        Line OrigX + i*TileRX, OrigY + i*TileRY, OrigX - H*TileRX + i*TileRX, OrigY + H*TileRY + i*TileRY
    Next i
    For i = 0 To H
        Line OrigX - i*TileRX, OrigY + i*TileRY, OrigX + W*TileRX - i*TileRX, OrigY + W*TileRY + i*TileRY
    Next i
    
    //Merkitään tile, jonka päällä hiiri on
    If gMouseTileX > 0 And gMouseTileX <= W And gMouseTileY > 0 And gMouseTileY <= H Then
        Color 255, 0, 0
        Circle OrigX + (gMouseTileX - 1)*TileRX - (gMouseTileY - 1)*TileRX, OrigY + (gMouseTileX - 1)*TileRY + gMouseTileY*TileRY, 3
    EndIf
    
    DrawScreen
Forever

//PARAMTERIT:
//Ruudukon yläkulma on pisteessä (Ax, Ay)
//tileRadiusX ja -Y ovat tilen leveyden ja korkeuden puolikkaat
//(Px, Py) on piste, jolle koordinaatistomuunnos suoritetaan (yleensä hiiren sijainti)
Function getMouseIsoCoords(Ax#, Ay#, tileRadiusX#, tileRadiusY#, Px#, Py#)
    Dim v0x#, v0y#, v1x#, v1y#, v2x#, v2y# As Float
    Dim dot00#, dot01#, dot02#, dot11#, dot12# As Float
    Dim invDenom#, x#, y# As Float
    
    v0x# = -tileRadiusX
    v0y# = tileRadiusY
    
    v1x# = tileRadiusX
    v1y# = tileRadiusY
    
    v2x# = Px# - Ax#
    v2y# = Py# - Ay#
    
    dot00# = v0x#*v0x# + v0y#*v0y#
    dot01# = v0x#*v1x# + v0y#*v1y#
    dot02# = v0x#*v2x# + v0y#*v2y#
    dot11# = v1x#*v1x# + v1y#*v1y#
    dot12# = v1x#*v2x# + v1y#*v2y#
    
    invDenom# = 1.0 / (dot00 * dot11 - dot01 * dot01)
        
    gMouseTileX = RoundUp((dot00 * dot12 - dot01 * dot02) * invDenom)
    gMouseTileY = RoundUp((dot11 * dot02 - dot01 * dot12) * invDenom)
EndFunction
CoolBasic henkilökuntaa
Tech-kehittäjä
CoolBasic Classic, Cool VES

CoolPhysicsEngine | MissileSystem | Jana-ympyrä -törmäys | cbSimpleTexture | CoolCPLX
User avatar
Misthema
Advanced Member
Posts: 312
Joined: Mon Aug 27, 2007 8:32 pm
Location: Turku, Finland
Contact:

Re: Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by Misthema »

Mahtavan kätevä funktio! Itselle oli todella suureksi hyödyksi! Kiitoksia vielä kerran. =)
Jonhu
Active Member
Posts: 186
Joined: Mon Aug 04, 2008 5:45 pm

Re: Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by Jonhu »

Löysin melko samantyyppisen funktion c++:lla kirjoitettuna: http://www.gamedev.net/community/forums ... _id=541377

Tuo laskutapa on melko matemaattinen vektoreiden risti- ja pistetulon avulla, mutta saisiko tuon toimimaan myös isometrisille mapeille, joissa on korkeuserot mukana? Yleensä isometrisille kartoille, joissa on korkeuserot mukana käytetään hiiritilejä apuna, mutta eikö tämä olisi myös mahdollista ratkaista matemaattisesti? Muuten funktio on todella hyödyllinen ja kätevä, mutta kaipaisin tuota korkeuserojen tunnistusta.

Onko cb:ssä muuten merkittävää hyötyä määritellä alussa noita muuttujia?
Tekeillä pikkupelejä ja ohjelmia :)
ristis
Moderator
Moderator
Posts: 101
Joined: Sat Sep 08, 2007 4:32 pm
Location: Espoo
Contact:

Re: Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by ristis »

Korkeuserohan ei oikeastaan muuta tee kuin siirtää sitä salmiakkia ruudulla pystysuunnassa. Ei siis ole kummoisempi operaatio. Ongelmia aiheuttaa ainoastaan päällekkäisyydet kun esimerkiksi etualalla korkeammalla oleva tile on taka-alalla matalammalla olevan tilen edessä. Sekään ei nyt mikään varsinainen probleemi.


Jonhu wrote:Onko cb:ssä muuten merkittävää hyötyä määritellä alussa noita muuttujia?
Isommissa rojekteissa pysyy ehkä hommat paremmin näpeissä jos määrittelee muuttujat. CoolBasic kun ei oo niin ronkeli sen suhteen, että mitä muuttujaan tunkee niin jää kyl vähän hyödyttömäksi ehkäpä.
SPuntte
Tech Developer
Tech Developer
Posts: 650
Joined: Mon Aug 27, 2007 9:51 pm
Location: Helsinki, Finland
Contact:

Re: Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by SPuntte »

Jonhu wrote:Tuo laskutapa on melko matemaattinen vektoreiden risti- ja pistetulon avulla, mutta saisiko tuon toimimaan myös isometrisille mapeille, joissa on korkeuserot mukana? Yleensä isometrisille kartoille, joissa on korkeuserot mukana käytetään hiiritilejä apuna, mutta eikö tämä olisi myös mahdollista ratkaista matemaattisesti? Muuten funktio on todella hyödyllinen ja kätevä, mutta kaipaisin tuota korkeuserojen tunnistusta.
Todella mielenkintoinen kysymys. Nykyinen systeemi käyttää hyväksi isometrisen projektion sitä ominaisuutta, että luonnossa yhtäsuuret pituudet ja yhdensuuntaisuudet säilyvät myös itse projektiossa. Kuitenkaan itse projektiota ei missään kohtaa suoriteta. Tämä johtaa siihen, että algoritmi toimii sellaisenaan vain kaksiulotteisella kartalla.

Luullakseni kolmiulotteisen kartan tilekoordinaattitunnistus vaatisi vähintäänkin karttadatan analysoimista. Lisäksi algoritmin tulisi tietää, miten korkeuserot vaikuttavat itse piirtoon. Ainoa tapa, joka nyt tulee mieleen, olisi käyttää raycastingia.

Tässä ideoimani konsepti: Rakennetaan isometrisen projektion projektiomatriisi ja mapataan haluttu ruudun pikseli sen avulla takaisin kartta-avaruuteen puolisuoraksi (engl. ray). Puolisuora alkaa kamerasta ja jatkuu siitä eteenpäin loputtomiin. Suoran suunta tulee valita niin, että kameraan projisoituna se näyttää yhdeltä ainoalta pikseliltä.

Sitten rakennetaan funktio, joka laskee annetun puolisuoran ja karttadatan primitiivien (kartan nelikulmaisten osien, kuten lattioiden, seinien, viistojen ramppien, tms.) leikkaukset ja palauttaa niistä kameraa lähinnä olevan koordinaatit.

Ei siis mikään helppo nakki, mutta jos lineaarialgebra ja analyyttinen geometria sujuvat, niin mikä ettei.
Jonhu wrote:Onko cb:ssä muuten merkittävää hyötyä määritellä alussa noita muuttujia?
Lähinnä käytän Force Variable Declarationia ennaltaehkäisevänä typofixinä. Kun lähes poikkeuksetta aina tulee riehuttua tyyppi-muistipala-handle -sekamelskojen kanssa, ei huvita spottailla ylimääräisiä kirjoitus- tai copypaste-virheistä johtuvia MAVeja.

Lisäksi tapaan yleensä laskea kaikki useampaan kertaan käytettävät arvot (kuten esimerkiksi vektorien komponentit) etukäteen muuttujaan, ihan vain koodirivien lyhentämiseksi. En tiedä, onko "apumuuttujilla" vaikutusta suorituskykyyn.
CoolBasic henkilökuntaa
Tech-kehittäjä
CoolBasic Classic, Cool VES

CoolPhysicsEngine | MissileSystem | Jana-ympyrä -törmäys | cbSimpleTexture | CoolCPLX
ristis
Moderator
Moderator
Posts: 101
Joined: Sat Sep 08, 2007 4:32 pm
Location: Espoo
Contact:

Re: Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by ristis »

Omissa räpellyksissäni olen tehny hivenen eri lailla. Mielestäni parempi tehdä jotain, mikä toimii suunnilleen oikein ja nopeasti kuin prikulleen mutta hitaasti. Tosin enpä nyt tiedä, mitä tässä tarkalleen mietittiin.
Joka tapauksessa mun systeemissä hiirellä osoittamalla täytyy projektiosta löytää kyseinen tile. Hivenen ärsyttävyyttä tuo tilejen eri koot ja muodot, kun kaikki tilet eivät ole samanlaisia. Sydeemi on vähän rakennettu räätälöiden, että en tiedä toimisiko systeemini irti revittynä suoraan tässä.

...

Tässä kun nyt kirjoitin niin, apinan aivoni ehtivät päivittää tilanteen ajan tasalle, eihän se toimisikaan. :)
Kannattaa siis miettiä tarvitseeko yleistä ratkaisua vai sopiiko jokin erilainen tapa siihen mitä tarvitaan.

Universaaliratkaisu vaatii todellakin kolmesta ulottuvuudesta (lev,syv,kor) tasoprojektiota. Vaikeampia toteuttaa, todennäköisesti myös hitaampikin jos yksityiskohtia (ruutuja) on paljon.

Käyttämässäni tavassa kun ruudulla näkyvä tilanne piirretään (projektio sekin) niin tästä projektiosta (mitä ruudulla näkyy) voidaan "ottaa talteen" tilejen sijainnit. Nopeampaa ja kätevämpää mutta ei toimi välttämässä kaikissa tarpeissa (hiirellä ruutua tökkiessä toimii mainiosti).

Pahoittelen sekavaa viestiä mutta kirjoittaminen ja nenän kaivaminen samanaikaisesti osoittautui yllättävän vaikeaksi.
SPuntte
Tech Developer
Tech Developer
Posts: 650
Joined: Mon Aug 27, 2007 9:51 pm
Location: Helsinki, Finland
Contact:

Re: Koordinaatistomuunnos: Karteesinen -> Isometrinen

Post by SPuntte »

Tuli vielä mieleen yksityiskohtia tuosta raycastingin toteuttamisesta. Jotta "poimintasäteen" (puolisuoran) leikkaustarkistusta ei tarvitse laskea kaikille kartan primitiiveille, kannattaa toimia about seuraavalla tavalla:
1. lasketaan säteelle pisteet, joissa se tulee karttaan sisään ja poistuu siitä.

2. Lasketaan säteen leikkaamat karttasolut bresenhamin algoritmin muunnoksella, joka lisäksi täytyy muuttaa operoimaan pikselien sijaan vokseleilla, eli karttasoluilla.

3. Tarkistetaan edellisessä kohdassa saadun listan soluja vastaava karttadata ja palautetaan kameraa lähinnä oleva leikkaus.
ristis wrote:Käyttämässäni tavassa kun ruudulla näkyvä tilanne piirretään (projektio sekin) niin tästä projektiosta (mitä ruudulla näkyy) voidaan "ottaa talteen" tilejen sijainnit. Nopeampaa ja kätevämpää mutta ei toimi välttämässä kaikissa tarpeissa (hiirellä ruutua tökkiessä toimii mainiosti).
Tuo tosiaan on ihan toimiva ratkaisu. Millaisia (erilaisia) tilejä omassa systeemissäsi on?
ristis wrote:Mielestäni parempi tehdä jotain, mikä toimii suunnilleen oikein ja nopeasti kuin prikulleen mutta hitaasti.
Heh, ettei vain olisi ollut sinun vinkistäsi (tai jopa näppäimistöstäsi) mishtemalla käytössäsi se systeemi, jossa jokainen isometrinen tile jaettiin kahdeksi kolmioksi ja testattiin bruteforce-tyyliin jok'ikinen siltä varalta, että hiiri olisi sen sisällä.
CoolBasic henkilökuntaa
Tech-kehittäjä
CoolBasic Classic, Cool VES

CoolPhysicsEngine | MissileSystem | Jana-ympyrä -törmäys | cbSimpleTexture | CoolCPLX
Post Reply