Elikkäs...
Miten saan tehtyä törmäystarkistukset ruutuihin, jotka on piirretty draw_sprite() -funktiolla näytölle taulukon antamien tietojen perusteella? Mikä olisi hyvä ja kätevä tapa tehdä se?
Mapin luonti, läpikäynti ja seinien ja lattian piirto 30pix (ruutu) välein:
//0=tyhjää, 1=seinä int map[15][15] = {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; for(int box_y = 0; box_y < 15; box_y++) { for(int box_x = 0; box_x < 15; box_x++) { switch(map0001[box_x][box_y]) { case 0: draw_sprite(buffer, lattia, box_x * 30, box_y * 30); //lattiaa break; case 1: draw_sprite(buffer, seina, box_x * 30, box_y * 30); //seinää break; } } }
Ja Tuohon seinään siis pitäisi saada Collision Detectionia =)
öh, onpas mielenkiintoinen(ko?) kysymys..
Tälläisessä 2D-spriteilyssä törmäystarkistuksen ideana on katsoa, että osuisiko hahmoa liikkuessaan tileen, johon ei ole tarkoitus osua.
Jos hahmo on kohdassa hahmo_x = 2, hahmo_y = 2 niin sehän on sama kuin map[1][1], eli (jos koordinaatisto alkaa kohdasta map[0][0], jossa ollaan pisteessä 1,1) silloin ollaan lattian päällä. nyt, jos hahmo liikkuu ylöspäin, tullaan kohtaan map[1][0], eli hahmo_x = 2, hahmo_y = 1, jossa onkin arvona 1, eli seinässä. Nyt todetaan, että tämä on törmäys, eikä anneta hahmon liikkua sinne vaan palautetaan hahmon koordinaatit siihen, missä ne olivat ennen liikkumista.
Toivottavasti selkeytti - en halua antaa suoraa vastausta, kun silloin on vaarana, ettei ymmärretä jutun jujua syvällisemmin ^^
EDIT: Erittäin pseudona koodina:
if (hahmo.liikkuu == true) if (map[hahmo.x + liikkumissuuntaX][hahmo.y + liikkumissuuntaY] == "SEINÄ") älä.liiku; else liiku.uuteen.kohtaan;
En saanut oikein käytännössä pähkäiltyä tuota sanomaasi.
Olisi niin helppoa jos saisi revittyä noi forin sisällä olevat box_x ja box_y muuttujien arvot veke. Siis ne arvot, mihin seinä piirretään.
Tämä alkaa jo tuottamaan harmaita hiuksia.
Piirtämisellä ja törmäystarkistuksella ei (nykyään) ole mitään tekemistä keskenään. Erota ne nyt ensiksi toisistaan.
Metabolix kirjoitti:
Piirtämisellä ja törmäystarkistuksella ei (nykyään) ole mitään tekemistä keskenään. Erota ne nyt ensiksi toisistaan.
Eihän toki. Onneksi olen tämä sentään tiedostanut :)
No miksi sitten haluat tuolta piirtosilmukasta muuttujan jonnekin? Ainahan voit sijoittaa sen ihan normaaliin tapaan johonkin muuhun muuttujaan. Ainoa järkevä tapa törmäystarkistukseen on kuitenkin tuo Tzaerun tarjoama, eli jo hahmon liikuttamisen yhteydessä pitäisi tarkistaa, voiko se liikkua.
Metabolix kirjoitti:
No miksi sitten haluat tuolta piirtosilmukasta muuttujan jonnekin? Ainahan voit sijoittaa sen ihan normaaliin tapaan johonkin muuhun muuttujaan. Ainoa järkevä tapa törmäystarkistukseen on kuitenkin tuo Tzaerun tarjoama, eli jo hahmon liikuttamisen yhteydessä pitäisi tarkistaa, voiko se liikkua.
Voisin tehdä normaalin törmäystarkistuksen myöhemmin. Tai normaalin ja normaalin...
Mutta nythän en voi.
Toki tuo annettu tapa olisi paras... täytyy vielä vaan saada toimimaan doh.
Tuplapostaan että huomattaisiin;
eli pitipä sanomani, että liikkuminen on sulavaa, ei ruutu ruudulta pomppimista.
Jos jaksaisit vähänkään panostaa etsimiseen, löytäisit vastauksen saman tien. Kirjoittamalla tuohon sivun oikeassa laidassa majailevaan hakukenttään "törmäystarkistus", saat listan aiheita, joista moni käsittelee ongelmaasi.
Idea vielä kerran, vaikka se on jo kerrottu pariin otteeseen:
Jos siirto on mahdollinen
-> Siirretään
Eli:
if(map[hahmo.x][hahmo.y+1] != este) MoveTo(x, y);
Jos jaksaisit vähänkään katsoa viestien päivämääriä, niin huomaisit, että olen pari päivää koittanut etsiä ja koittaa tehdä kysymättä mitään.
No sori... ja kiitos niille jotka jaksaa jotenkin edes yrittää auttaa. Koitanpa vielä... j en ole tyhmä; asioita ei tarvitse toistaa... tai voitte toistaa ihan rauhassa mutta se ei vie suuntaan eikä toiseen. Sen sijaan kuluttaisitte ajan mielummin uusien näkökantojen luomiseen =)
Ja mainittakoon tuosta vielä, että sulavallakin liikkumisella tarkistus toimii samalla tavalla, luvut täytyy vain pyöristää. Tietenkin ukkelin pyöreys tekee laskelmista paljon mutkikkaammat. Kynä käteen ja piirtelemään ja laskemaan! Ei ohjelmoinnissa saa kaikkea valmiina, ei ainakaan if_tormaa-funktiota.
uusi_sijainti = sijainti + nopeus * aika; if (tyhja(map[(int)uusi_sijainti.x][(int)uusi_sijainti.y])) { sijainti = uusi_sijainti; }
Jos haluat pistemäisen seinääntörmäystarkistuksen tilalle pyöreämmän, joudut laskemaan ensiksi, mihin ruutuihin uusi sijainti osuisi (eli laske ruutujen kulmien ja reunojen etäisyydet ukkoon), ja tämän jälkeen voit tarkistaa kaikki ruudut.
Et ole tyhmä, mutta voisit silti kertoa, mikä tuossa nyt on ongelmana. Miksi oikein kysyt neuvoa, jos kerran olet tässä asiat ymmärtänyt? Paha näihin on kovin erilaisia näkökantoja heittää. Liikkuessa tarkistat, törmääkö, ja se tarkistaminen on puhdasta matematiikkaa.
Metabolix kirjoitti:
Et ole tyhmä, mutta mikä tuossa nyt on ongelmana? Miksi oikein kysyt neuvoa, jos kerran olet tässä asiat ymmärtänyt? Paha näihin on kovin erilaisia näkökantoja heittää. Liikkuessa tarkistat, törmääkö, ja se tarkistaminen on puhdasta matematiikkaa.
En ole sanonut että olen ymmärtänyt, mutta asian toistaminen ei auta. Ymmärrät kai sen?
No mutta kerro edes, mitä kohtaa et ymmärrä.
Olen itseasiassa tehnyt ruutuun törmäystarkistuksen (puoliksi) jo jokin aika sitten, mutta se toimii vain, jos piirtää erikseen esim yhden seinän...
Nyt siis, kun ne piirretään taulukon perusteella forilla, niin se ei toimi mitenkään.
Huomioikaa että tuo on vain puoliksi tehty, eli nyt se tunnistaa pelaajan törmäyksen ruutuun vain alhaalta ja oikealta. Nyt, kun en pääse testaamaan ja koodaamaan, niin en viitsi edes yrittää tehdä sitä toista puolta. Kunhan koneelleni pääsen, niin jatkan taas (np), mutta tässä nyt olisi aluksi se pätkä, jos siitä voisi jotenkin lähteä...
Eli
// p = pelaaja, // x, y = seinän (30*30 = ruutu) koordinaatit // x3, y3 = pelaaja oikea alakulma // x2 ja y2 ovat epäoleellisia tässä tapauksessa //pelaaja tulee oikealta, pelaaja ei pääse läpi. if(p_x >= x + 30 && p_x <= x + 30 && p_y >= y && p_y <= y + 30){p_x++, p_x2++, p_x3++;} if(p_y3 <= y + 30 && p_x <= x + 30) {p_x++, p_x2++, p_x3++;} //pelaaja tulee alhaalta, pelaaja ei pääse läpi if(p_y >= y + 30 && p_y <= y + 30 && p_x >= x && p_x <= x + 30){p_y++, p_y2++, p_y3++;} if(p_x3 <= x + 30 && p_y <= y + 30) {p_y++, p_y2++, p_y3++;}
Siis suoraan kysyttynä: Miten saisin tuon toimimaan tässä tapauksessa, kun seinien piirto tapahtuu taulukon ja forin avulla?
=)
E: Niin ja x ja y ovat seinän (30*30) koordinaatteja.
E: Metabolix: palataan asiaan jos tämä ei tule toimimaan =)
http://koti.mbnet.fi/tzaeru/explanation.png
OK! En todellakaan jaksa alkaa lukemaan noin sekavan oloista koodia, joten toivon, että tuo selkeyttää sen 2D:ssä käytetyimmän ja varmasti helpoimman mallin ^_^
Monimutkaisimmissa jutuissa voidaan sitten esim. pitää listaa kaikkien objektien sijainneista, eikä varsinaisesta kartasta, koska karttaa ei enää jaeta tileihin. Sitten tehdään ovelia yhtälöitä ja katotaan, törmättiinkö vai ei, mutta ne on vähän monimutkasempia juttuja sit!
Älä tee siitä törmäystarkistuksesta mörköä!
Sillä, mistä suunnasta pelaaja tulee tai mihin suuntaan hän menee, ei ole merkitystä. Ainoastaa se, pyrkiikö hän liikkumaan sallitulle vai kielletylle alueelle. Eli ennen liikuttamista tarkistetaan, onko tuleva sijainti sallittu.
Pistän vielä koodiesimerkin, jotta ymmärrät varmasti. Ei sitä kukaan jaksa kovin montaa päivää pähkäillä yhtä asiaa putkeen.
const int TILE_KORKEUS = 32; const int TILE_LEVEYS = 32; const int KARTTA_KORKEUS = 100; const int KARTTA_LEVEYS = 100; int ukko_x = 50, ukko_y = 50; // Sankarin sijainti(pixelin tarkka) int liiku_x = 0; liiku_y = 0; // Sankarin liike(pixelin tarkka) bool este[KARTTA_KORKEUS][KARTTA_LEVEYS]; // Estetaulukko(ruudun/tilen tarkka) // Luetaan käyttäjän syötteet ja päätellään mihin suuntaan hän haluaa liikkua liiku_x = -7; // Setitsemän pixeliä vasempaan liiku_y = 13; // Kolmetoista pixeliä ala // Kokonaisluvuilla laskettaessa lopputulos pyöristetään aina alaspäin(3.9 -> 3) // Jos yritetään liikkua sallitulle alueelle if(este[(ukko_x+liiku_x)/TILE_KORKEUS][(ukko_y+liiku_y)/TILE_LEVEYS] == false) { // Liikutaan ukko_x += liiku_x; ukko_y += liiku_y; } else { // Ei liikuta, nollataan vaikka liikuttaminen liiku_x = 0; liiku_y = 0; }
Jos ongelmasi on, että ukko menee puolittain seinän sisää, muutat luonnollisesti törmäystarkistuksen ehdon laskuja.
Tzaeru (ja yleisestikin), ne piilovittuilut saa jättää pois. Ei tee hirveesti mieli kysyä mitään, kun saa kaikki paskat niskaan.
Kaikesta huolimatta kiitos vastanneille. Enköhän näillä eväillä jotenkin selviä. (Ja jos en selviä, niin ainakaan täältä en aijo kysyä enään mitään)
Tzaerun tapa kävisi, jos mappeja olisi vain yksi. Entäs jos mappeja on satoja? Funktioon... mutta sitten joutuu määrittelemään 15*15 (225) eri muuttujaa... =/
Kariejr kirjoitti:
Entäs jos mappeja on satoja? Funktioon... mutta sitten joutuu määrittelemään 15*15 (225) eri muuttujaa... =/
Miksi joutuisi? Siksi juuri pitää tehdä tällä yleispätevämmällä tavalla, jossa ei todellakaan tarvita kuin muutama muuttuja. Luuletko, että täällä neuvoisimme, jos emme itse ymmärtäisi asiaa?
Metabolix kirjoitti:
Miksi joutuisi? Siksi juuri pitää...
Hmm... siis ymmärrätkö mitä tarkoitin vai et? :D
Ensimmäiseen lauseeseesi vastaan että, siks kun taulukossa on ne 225 eri ruutua. Toiseen lauseeseesi vastaan vain että, niinpä niin =P ja niinpä teenkin.
Kariejr kirjoitti:
Hmm... siis ymmärrätkö mitä tarkoitin vai et? :D
En, siksi kysyinkin. En keksi yhtäkään niin tyhmää tapaa törmäystarkistuksen tekemiseen, että tarvitsisin siihen erillisen muuttujan jokaiselle ruudulle – ellei luovuta taulukosta kokonaan ja tehdä muuttujia tyyliin ruutu_x5_y7 jne.
Voisit esitellä miten nyt oot sitä sit tekemässä. C++:aa vääntäessä on parasta käyttää stl:n vector-luokkaa dynaamisia taulukoita varten. Siihen voi helposti lisätä alkioita sitä mukaan ku tarttee. Näin ei tarvitse edes karttojen määrää tietää etukäteen, eikä sitä koodia tuu yhtään lisää, oli karttoja 1 tai 100 000.
Asiasta voisi ehkä vääntää isomman luokan esimerkin jos vain jaksais. Ei pitäisi olla kovin ylivoimaista, kuitenkaan.
Kariejr kirjoitti:
Tzaeru (ja yleisestikin), ne piilovittuilut saa jättää pois. Ei tee hirveesti mieli kysyä mitään, kun saa kaikki paskat niskaan.
Kaikesta huolimatta kiitos vastanneille.
Ei se mitään piilovittuilua ollut kun se kerran huomattiin. ^^;
Yleisesti ottaen vaan ärsyttää kun aletaan inttämään vastaan ja tarjoamaan sitä samaa ratkasumallia uusiksi ja uusiksi vaikka uutta ehdotettu tilalle. Ja ei nyt kukaan voi oikeasti loukkaantua tuollaisesta leikittelystä, ihan samanlaista oli minulla ja varmaan kaikilla muillakin ekoina kertoina ohjelmointifoorumeilla.
Tzaeru kirjoitti:
Voisit esitellä miten nyt oot sitä sit tekemässä.
Tuo Gaxxin ratkaisumalli... kunhan saisin vaan jotain aikaiseksi...
(en tajua miten saa boolean este -taulukon tietoon niiden esteruutujen sijainnit *_*)
Tzaeru kirjoitti:
Ei se mitään piilovittuilua ollut kun se kerran huomattiin. ^^;
Hmm
Tzaeru kirjoitti:
Yleisesti ottaen vaan ärsyttää kun aletaan inttämään vastaan...
En ole inttänyt vastaan (ainakaan tietoisesti)
Tzaeru kirjoitti:
ja tarjoamaan sitä samaa ratkasumallia uusiksi ja uusiksi vaikka uutta ehdotettu tilalle.
Tehän te toistelette samoja ja minä pyydän uusia... tai... nvm.
Tzaeru kirjoitti:
Ja ei nyt kukaan voi oikeasti loukkaantua tuollaisesta leikittelystä...
Kyllä voi ;D
Kariejr kirjoitti:
Tuo Gaxxin ratkaisumalli... kunhan saisin vaan jotain aikaiseksi...
(en tajua miten saa boolean este -taulukon tietoon niiden esteruutujen sijainnit *_*)
C++:ssa nolla tulkitaan boolen arvona false ja nollasta poikkeava arvo truena.
Sinullahan on esteet tiedossa map-taulukossa. Oletan, että ykkönen tarkoittaa seinää(eli estettä) ja 0 tarkoittaa liikumatilaa(eli ei estettä). Voit vaikka kopioida map-taulukon arvot estetaulukkoon tai korvata este-taulukon map-taulukolla.
Olen vaan niin palikka etten saa toimimaan.
Käyttäisin tuota törmäystarkistusta minkä näytinkin, mutta mikä siinä on kun se ei suostu reagoimaan mitenkään forista otettuihin tilen x ja y muuttujiin? =/
Edes kokeilun vuoksi... kyllä sen silti toimia pitäisi vaikka hirviö olisikin.
Aihe on jo aika vanha, joten et voi enää vastata siihen.