Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python: Kuusikulmioista muodostuva pelilauta

pipo [19.10.2010 23:27:48]

#

Tämmösen funktion olisi tarkoitus kertoa, missä heksassa annettu piste/pikseli sijaitsee. Ongelma on, että kun piste on heksan reunalla, funktio palauttaa joskus reunan toisen puolen heksan ja joskus toisen puolen. Riippuu käsittääkseni siitä miten ne pisteen desimaalit sattuivat asettumaan tai jotain semmosta.

Käytin ohjeena GameDev.netin Coordinates in Hexagon-Based Tile Maps-artikkelia. Tosin heksa on 90 astetta eri suunnassa.

Käytetyt muuttujat lasketaan heksaluokan konstruktorissa, jonka parametrinä on heksan a-mitta tai s-mitta.

a = 2*r
b = h+s

 -- = h
   ____
  /    \    |
 /      \   | = b
 \      /   |
  \____/    |

   ---- = s

| /   |\    |    |
|/    | \___|    | = ruutukor
|\ A  | / B |    |
|_\___|/____|    |

 ----- = ruutulev

Luulisin että jotain koordinaatteja pitäisi jossain kohtaa pyöristää. Mutta missä ja mitä? Taikka sitten pitäisi käyttää <= ja >= -operaatioita siinä vaiheessa kun tarkastellaan ollaanko suoran vasemmalla vaiko oikealla puolella?

def a_hx(self, x,y):
    # origon siirto
    x += self.h + self.s * 0.5
    y += self.r
    # missä ruudussa ollaan
    rx = x // self.ruutulev
    ry = y // self.ruutukor
    # onko ruutu A-tyyppinen
    # round(rx)?
    Atyyppinen = not( int(rx) & 1 )
    # missä ruudun pisteessä ollaan
    px = x - rx * self.ruutulev
    py = y - ry * self.ruutukor
    # osoittaako piste oikeata heksaa
    # jos ei niin siirrytään viereiseen heksaan

    # A-tyyppinen
    if Atyyppinen:
        # yläosa
        if py > self.r:
            # vasen osa
            if py > self.k * px + self.r:
                return rx-1,ry
            # oikea osa
            else:
                return rx,ry
        # alaosa
        else:
            # vasen osa
            if py < - self.k * px + self.r:
                return rx-1,ry-1
            # oikea osa
            else:
                return rx,ry
    # B-tyyppinen
    else:
        # yläosa
        if py > self.r:
            # vasen osa
            if py < - self.k * px + self.r * 2:
                return rx-1,ry
            # oikea osa
            else:
                return rx,ry
        # alaosa
        else:
            # vasen osa
            if py > self.k * px:
                return rx-1,ry
            # oikea osa
            else:
                return rx,ry-1

Metabolix [19.10.2010 23:46:08]

#

Liukuluvuilla et voi odottaa saavasi tarkkaa tulosta. Vaikka piste liikkuisi täsmälleen rajaa pitkin, eri arvoilla voi tulla erilaisia pyöristysvirheitä, joiden takia algoritmi ilmoittaa vuorotellen kahta yhtä hyvää vaihtoehtoa. En tiedä ohjelmasi lähtökohtia, mutta yksi ratkaisu on sijainnin jatkuva pyöristely niin karkealle tarkkuudelle, ettei pyöristysvirhe muuta tuon laskun tulosta. Toinen vaihtoehto on tallentaa edellinen tulos ja muuttaa vastausta vain, jos ollaan jo selvästi toisen ruudun puolella.

Eksoottisempi ratkaisu olisi vaihtaa ohjelma käyttämään koordinaatistoa, jossa akselit olisivat 120° (tai 60°) kulmassa toisiinsa nähden, mutta tämä täytyisikin sitten huomioida aika monessa paikassa.

pipo [19.10.2010 23:59:35]

#

Ongelman lähtökohta näkyy jotenkin tässä kuvassa.

Jokainen pieni heksa kuuluu johonkin isoon heksaan. Pienten ja isojen heksojen koordinaatiston origot on samassa kohtaa. Tarkoitus olisi, että isot heksat olisivat kaikki samankokoisia esim 5 heksaa per sivu. Pienissä heksoissa näkyvien koordinaattien värit kertovat mihin isoon heksaan ne kuuluvat. Ongelma näkyy mm. heksassa (4,7) joka on siis sininen vaikka voisi luulla sen kuuluvan oikealla alapuolella olevaan isoon heksaan.

Tuli juuri mieleeni että täytyyköhän ison heksan a-mitan olla pienen heksan b-mitan tai (s + h)-mitan kerrannainen. Mutta tuskimpa sentään.

Metabolix kirjoitti:

Eksoottisempi ratkaisu olisi vaihtaa ohjelma käyttämään koordinaatistoa

Onnistuu kohtalaisen nopeasti kun mulla on vielä toi paperisuunnitelma tallessa, mutta luulen ettei koordinaatiston vaihtaminen ratkaise tätä vaan justiin joku pyöristäminen.

Grez [20.10.2010 00:16:37]

#

Sun pitää lähinnä päättää millä säännöillä mikin useampaan isoon kuuluva pieni kuuluu mihinkin. Tai jos et tee selkeää sääntöä niin voit yhtä hyvin arpoa mihin ne kuuluu.

pipo [20.10.2010 00:19:48]

#

Säännöt menee sillain että pieni heksa kuuluu isoon heksaan, jonka sisäpuolella pienen heksan keskipiste sijaitsee.

Grez [20.10.2010 00:31:39]

#

Noitten isojen raja menee noitten pienten keskipisteen kohdalta, joten noi rajalla olevat palat pitäisi sitten olla rajan värisiä?

Eli siirrä jomman kumman origoa pari pikseliä sivuun niin homma toimii säännönmukaisesti. Vaikka 2 pikseliä ylös ja 2 vasemmalle.

Metabolix [20.10.2010 01:04:01]

#

pipo kirjoitti:

Säännöt menee sillain että pieni heksa kuuluu isoon heksaan, jonka sisäpuolella pienen heksan keskipiste sijaitsee.

Pienet kulmiot ovat rajalla, kuten Grez sanoi. Ratkaisusi ei siis toimi ilman ehdotettua kikkailua.

pipo kirjoitti:

Luulen ettei koordinaatiston vaihtaminen ratkaise tätä vaan justiin joku pyöristäminen.

Kyllä se ratkaisisi. Koordinaatisto voisi mennä niin, että y kasvaisi ylävasemmalle ja x kasvaisi yläoikealle. Tällöin oikealle x kasvaisi ja y pienenisi. Ruudun etäisyys toisesta (kulmioita pitkin) laskettaisiin kaavalla max(abs(dx), abs(dy), abs(dx + dy)). Saat etäisyyden kokonaislukuna, jolloin ei tule pyöristysvirheitä. Rajatapaukset voit ratkaista yksinkertaisesti niin, että riitapukareista valitaan ensisijaisesti alempana oleva, tai jos kaksi on yhtä alhaalla, näistä vasemmanpuoleinen.

Testasinkin koordinaatistoa, ks. kuva. Käytin seuraavia muunnosfunktioita ja etäisyysfunktiota (kielenä JavaScript):

// Muunnos kuusikulmioista oikeaksi:
function hex2euc(p) {
	return {
		"x": (p.x - p.y) * 0.5,
		"y": (p.x + p.y) * Math.sqrt(3) / 2
	};
}
// Muunnos oikeasta kuusikulmioiksi:
function euc2hex(p) {
	return {
		"x": p.y / Math.sqrt(3) + p.x,
		"y": p.y / Math.sqrt(3) - p.x
	};
}
// Kuusikulmiopisteiden etäisyys kokonaisina kuusikulmioina:
function hexdist(a, b) {
	var dx = b.x - a.x;
	var dy = b.y - a.y;
	return Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dx + dy));
}

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta