Perjantaina varmistin koulussa että projektini on toimintakunnossa, ja niinhän se oli. Kotonakaan ei mitään ongelmia ollut, eikä ole vieläkään. Mutta sen sijaan koulussa Windows-käännöstä tehdessä ongelmia ilmenee, ja eilen vielä varmistin kotona, eikä Linuxilla ollut mitään ongelmaa. Enhän minä kuitenkaan viikonloppuna muuttanut kuin vähän grafiikkaa, ja etenin itse "tarinan" kanssa ja lisäilin uusia ominaisuuksia joilla ei ollut kuitenkaan graafikan kanssa mitään tekemistä, mikä tässä nyt oikein meni vikaan? Mutta Windowsilla niitä vikoja nyt vain ilmeisesti tulee vaikka ei tarvitsekkaan.
Ohjelma käynnistyy oikein. "Päävalikko" tulee esiin, ja sen jälkeen aloitan itse pelin ja päädyn tallentamaani kohtaan. Sitten menen huoneesta pois, ja ohjelma sammuu, antaen Segmentation Faultin. Kun saman teen Linuxilla, pääsen toiseen huoneeseen ongelmitta ja kokonaisuus toimii moitteettomasti. Huoneen vaihtuessa kaikki esineet, viholliset, luodit ja muut efektit poistetaan, ja luodaan uudet huoneen mukaan (toteutettu olio-kokoelmilla), eikä itse poistaessa parin testaamisen jälkeen jää mitään ylimääräistä häiritsemään. Tässä tilanteessa virhe ilmeisesti kuitenkin tulee jos debuggeriin on uskominen. Itse valitus tulee erään tietyn esineen luonnin yhteydessä. Myöskin muuttelin juuri kyseisen esineen grafiikkaa viikonloppuna, ja epäilenkin hieman että tässä olisi se vika.
Läpinäkyvyys tietylle värille on varmaan jollain tapaa tuttu temppu SDL:n käyttäjille? Eli näin:
SDL_SetColorKey(image, SDL_SRCCOLORKEY, SDL_MapRGB(image->format, 0, 0, 0));
Image on siis SDL_Surface jonka tässä tapauksessa mustat kohdat haluan muuttaa läpinäkyviksi, ja tähän debugger osoittaa virheen. Kommentointi tietysti auttaa asiaa, mutta läpinäkyvyys on ehdottomasti välttämätön tässä projektissa. Onko ongelma siis kuvassa? Tutkin tätä asiaa, ja sain vastaukseksi että pinta oli NULL. Sepä outoa, reitti on kirjoitettu täsmälleen oikein, eikä kuvan hakemiseenkaan tarvita muuta kuin lukuoikeudet, ainakaan tietääkseni. Kuvan reitti annetaan olion constructorille parametrinä, ja itse olion constructorissa kuva ladataan ja läpinäkyvyys tehdään.
Vielä oudommaksi tämän tekee se että kun laitan tämä kyseisen esineen tuohon ensimmäiseen huoneeseen mihin pääsen (ja sehän toimii), tämä esine näkyy hyvin eikä virheitä tule. Mutta kun vaihtaa huonetta, Segmentation Fault.
Olen kuormittanut SDL:n oman IMG_Load funktion näin, mutta kyllä se tähän mennessä on aina toiminut, eli en uskoisi että vika on täällä. Tai sitten g++:lla on jokin outo tapa huomata virheitä monta kuukautta sen teon jälkeen.
SDL_Surface *IMG_Load(char *file) { SDL_Surface *temp = NULL, *optimized = NULL; if((temp = SDL_LoadBMP(file)) == NULL) { return NULL; } optimized = SDL_DisplayFormatAlpha(temp); SDL_FreeSurface(temp); return optimized; }
Jos jotain vielä pitäisi koodina nähdä niin saa kysyä. Itse olen aika neuvoton, eniten tässä ärsyttää se että kotona ei mitään vikaa ole, mutta koulussa taas on, koulun koneilla projekti pyörii paljon miellyttävämmin, ja jostain syystä motivaatio tekemiseenkin on myöskin koulussa paljon korkeampi.
No eikö ole mahdollisuutta näyttää koodia enempää? Esim vaikkapa, niiden olioiden vapautus ja varaamis rutiinit.
Aja softaa debuggerissa ja kato, missä kohassa se kaatuu.
Esine-olioiden luonti:
void InitItems(int room) { if(room == 1) { items.push_back(Item("gfx/tree1.bmp", 19, "Tree", 5, 100, 390, true)); } }
Eli, kun ollaan room 1:sessä, työnnetään items-kokoelmaan esineitä. Tämä funktio tietysti kutsutaan vain kerran.
Parametrit ovat: string, int, string, int, int, int, bool. Ensimmäinen on kuvan reitti, josta se ladataan. Seuraava int-muuttuja on esineen id-numero, jonka avulla se tunnistetaan. Seuraava string-muuttuja on taas esineen nimi, mutta on välttämätön vain tiettyjen esineiden kanssa. Seuraava int-muuttuja on esineen tyyppi, 5 on tässä tapauksessa puu, tällä erotellaan esineitä esimerkiksi piirtämisvaihteessa, tai että pysähtyykö niihin kun törmää, ja pystyykö niiden läpi kävelemään. Kaksi seuraavaa int-muuttujaa ovat x ja y, ja viimeinen boolean on taas totuusarvo sille että näytetäänkö esine.
Esine-olioden constructorissa ei ole mielestäni mitään erityistä nähtävää (siis oikeasti), joten en näe tarpeelliseksi sen näyttämistä.
Esine-kokoelman tyhjennys tapahtuu taas seuraavasti:
void RemoveItems() { for(item = items.begin(); item != items.end(); ++item) { if(item->GetType() != 0 && item->GetType() != 2) { item->FreeData(); item = items.erase(item); } } }
Selataan siis koko esinekokoelma läpi ja vapautetaan ja poistetaan halutut esineet pois. GetType() funktioita tarvitaan siihen ettei poisteta 0 ja 2 tyypin omaavia esineitä, jotka ovat inventoryssa käytettäviä esineitä, eikä niitä tietenkään kannata poistaa joka kerta kun huone vaihtuu. Tämäkin funktio kutsutaan vain kerran juuri siinä tilanteessa kun huone vaihtuu.
FreeData funktio taas sisältää simppelisti:
SDL_FreeSurface(image); image = NULL;
image tietysti esineen kuvan sisältävä SDL_Surface.
erakko- kirjoitti:
Esine-olioiden luonti:
void RemoveItems() { for(item = items.begin(); item != items.end(); ++item) { if(item->GetType() != 0 && item->GetType() != 2) { item->FreeData(); item = items.erase(item); } } }
Tämä tuskin on kaatumistesi syynä, mutta tuo silmukka näyttää mielestäni vialliselta. ++item pitäisi suorittaa ainoastaan jos edellistä itemiä ei ole poistettu, sillä jos poisto tapahtui, item osoittaa jo valmiiksi seuraavaan tarkastettavaan elementtiin.
Suosittelen muuten testaamaan ohjelmaasi Linux-puolella valgrind-ohjelmalla. Valgrind usein paljastaa sellaisiakin bugeja jotka eivät aina johda kaatumiseen.
Tarkistatko ollenkaan, onnistuuko kuvan lataaminen? Usein koodissa, jossa funktiot kertovat virheistä paluuarvoilla, ei muisteta tai jakseta huomioida noita virhetilanteita. Siksi kannattaakin käyttää todellisten virheiden ilmoittamiseen poikkeuksia, jolloin ongelmakohta selviää usein nopeammin. Esimerkiksi kuvanlatausfunktiosi voisi korjata näin:
#include <stdexcept> // Loogisempi nimi sekä parametriin const. SDL_Surface *SDL_LoadAndOptimize(const char *file) { SDL_Surface *temp, *optimized; if (!file) { throw std::runtime_error(std::string("Tiedostonimi puuttuu!")); } // Kun funktion nimi on vaihdettu, voit helposti vaihtaa SDL_LoadBMP:n // paikalle IMG_Load-funktion, joka osaa ladata muitakin kuin BMP-kuvia. if (!(temp = SDL_LoadBMP(file))) { throw std::runtime_error(std::string("Kuvaa ei löydy: ") + file); } optimized = SDL_DisplayFormatAlpha(temp); SDL_FreeSurface(temp); if (!optimized) { throw std::runtime_error(std::string("Virhe muunnoksessa: ") + file); } return optimized; }
Poikkeuksia kannattaa heittää ainakin kaikista niistä kohdista, joiden epäonnistuminen selvästi johtaa vakaviin ongelmiin – kuten nyt kuvan puuttuminen. Ongelmia voi sitten korjata catch-lohkoissa: esimerkiksi puuttuvan kuvan tilalle voi yrittää ladata jonkin oletuskuvan, vaikka perinteisen kieltomerkin, josta näkee ohjelman aikana, että kuva on puuttunut. Koskaan ei pidä kirjoittaa catch-lohkoa vain siksi, ettei ohjelma kaatuisi virheeseen, jos ei ole aivan varmaa, ettei ohjelma sitten kaadu hetken kuluttua virheen seurauksiin (kuten tyhjään osoittimeen).
Tein muutamia ilmoituksia tiettyihin tilanteisiin, ja tälläisiä tuloksia saan kun yritän ladata kuvaa:
stdout kirjoitti:
SDL_GetError: Couldn't open gfx/ceiling2.bmp
TTF_GetError: Couldn't open Tahoma.ttf
Jokainen esine-olio myös sisältää pinnan johon tehdään teksti, ja ilmeisesti myös tarvittavan fontin latauskaan ei onnistu. TTF_Init() on kutsuttu ennen fontin latausta, joten se ei ole ongelma. Ensimmäiseksi tulisi mieleen käyttöoikeudet, mutta tästä huolimatta onnistun menemään ensimmäiseen huoneeseen sujuvasti ja pystyn tekemään tuon saman esineen siihen huoneeseen.
Eniten pistää mietityttämään että miksi juuri näin satunnaiset esineet, eikä sitten kaikki. Kuitenkaan eroa toiseen samanlaiseen toimivaan esineeseen ei ole yhtään.
Sisuaski kirjoitti:
Suosittelen muuten testaamaan ohjelmaasi Linux-puolella valgrind-ohjelmalla. Valgrind usein paljastaa sellaisiakin bugeja jotka eivät aina johda kaatumiseen.
Teinpä näin, mutta sain ainoastaan yhden errorin jonka sain korjattua saman tien eikä se edes liittynyt tähän asiaan ollenkaan. Muuten toimi aivan normaalisti.
Ovatko nuo tiedostot aivan varmasti paikoillaan? Onko ohjelman työskentelyhakemisto oikea? (Tulostavatko system("dir")
ja system("dir gfx")
oikeat hakemistolistaukset?)
Metabolix kirjoitti:
Ovatko nuo tiedostot aivan varmasti paikoillaan? Onko ohjelman työskentelyhakemisto oikea? (Tulostavatko
system("dir")
jasystem("dir gfx")
oikeat hakemistolistaukset?)
Kyllä, ja kyllä. Myöskin nuo komennot tulostavat ihan oikeat listaukset.
Vika on ilmeisesti käyttöoikeuksissa, nyt en saa ladattua yhtään kuvaa. Toisaalta meillä on koulussa "profiilit", joihin saa laitettua suunnilleen 10 Mt tavaraa. Tosin teen työni muistitikulla, en sitten tiedä...
Mutta ilmeisesti tämä nyt on jotain mitä ei saa korjattua, vituttaa kyllä kun koulu on tällä hetkellä ainoa paikka missä joudun... noh, "voin" Windowsia käyttää, sekä motivaatiota olisi enemmän eikä olisi tylsää. Kiitän kuitenkin avusta.
Voit testata Windows-käännöksiä alustavasti Winellä. Jos hallussasi on Windowsin asennuslevy, voit asentaa koko järjestelmän emulaattoriin (esim. VirtualBox); grafiikkojen nopeus kärsii aika paljon, mutta ainakin pääset testaamaan, että ohjelma käynnistyy ja toimii. Voit myös kokeilla ReactOS-käyttöjärjestelmää, joka pyrkii Windows-yhteensopivuuteen ja perustuu suurelta osin Wine-koodiin. (Toisaalta siinä luultavasti toimivat suunnilleen samat asiat kuin Winelläkin.) Itselläni kyseinen järjestelmä kaatui minuutin käytön jälkeen, kun yritin avata Start-valikon; yhteensopivuus on siis korkealla tasolla! ;)
Kääntämiseen voit käyttää Linux-kääntäjää, joka tuottaa Windows-ohjelmia (löytyy usein paketinhallinnasta nimellä mingw32). Tähän joudut luultavasti asentamaan käsin kaikki tarvitsemasi kirjastot (SDL, SDL_image, mitä lie). Toinen tapa on ajaa Windows-kääntäjää Winellä.
Optimointi onkin aika kätevä keksintö. Olin jo pitkän aikaa ajatellut vähentää muistinkäyttöä juuri dialogien kanssa (eli siis tekstinpätkien mitä näytetään tietyissä tilanteissa), sillä ne jokainen latasivat oman fonttinsa erikseen. Tämä näkyikin usean sadan rivinä stdout.txt tiedostossa jossa valiteltiin että fonttia ei saatu ladattua (kenties profiilin rajatun tilan tai nimenomaan koulun koneella työskentelyn vuoksi), ja kun nyt loin fontit näiden dialogien käsittelijälle huolehdittavaksi, joka taas antaa tarvittavan fontin parametrinä dialogille sen luonnin yhteydessä.
Tämä olikin enemmän kuin hyödyllinen asia tehdä, nimittäin nyt kaikki toimii hyvin.
Aihe on jo aika vanha, joten et voi enää vastata siihen.