Mitenköhän C++ mahdetaan katsoa kuinka monta numeroa (Tarkalleen SDL_TTF:Ssä) niin kuinka monta numeroa löytyy tekstistä. (tai muuttujasta) Esim jos muuttujan arvo on 8000000 niin se voisi lentää eteen näin: 8,000,000 mutta miten tuonne väliin saadaan noita pilkkuja tai sitten pelkkä väli?
Mieti, voisiko 10:llä jakamista hyödyntää tässä jotenkin... Toisaalta tulostusta varten muutat luvun joka tapauksessa tekstiksi, jolloin numeroiden määrä selviää suoraan tekstin pituudesta.
Jep, mutta en tiedä sitä funktiota millä katsotaan tekstin pituus. Enkä kyllä tiedä sitten, että mitä sitten jos se on esimerkiksi 7 kirjainta niin jotenkin näin:
if (numberlengt == 7) { //sitten tähän jotain mitä en tiedä, että ensin ensimmäisen kirjaimen ja toisen väliin pilkku sitten kolme eteenpäin ja siihen sama. }
No eipä tuo eilinen ongelmani kokonaan sitten ratkennutkaan ja taas on mennyt 9h siihen, että koittanut keksiä jotain ja selannut googlea. Ongelma on, että peli tekee uudestaan ja uudestaan saman tekstin erilliseksi muistiksi eikä käytä samaa tai poista vanhaa. Eli koodia:
namespace ohjelma { static SDL_Surface *ruutu; static void KirjoitaTekstia(SDL_Surface *tteksti, int x, int y); namespace tekstien { static SDL_Surface *lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size); static SDL_Surface *teksti, *teksti2, *teksti3, *teksti4, *teksti5, *teksti6, *teksti7, *teksti8, *teksti9, *teksti10, *teksti11; } } /* Tämä on vain siksi tuossa alapuolella, kun en keksinyt muutakaan tapaa millä muuttaa jo tallennetun muuttujan arvo toiseen. Ja tässä se vikakin oikeastaan on. Peli tallentaa sen aina erikseen vaikka annan tismalleen samat nimet. */ void ohjelma::LataaUudestaan(int ladattava, int viikko, int vuosi) { std::clog << "Ladata täytyy: " << ladattava << std::endl; tekstien::teksti = tekstien::lataateksti("nothing", true, ladattava, 255, 0, 0, 16); tekstien::teksti4 = tekstien::lataateksti("nothing", true, viikko, 255, 0, 0, 16); tekstien::teksti5 = tekstien::lataateksti("nothing", true, vuosi, 255, 0, 0, 16); } static SDL_Surface *ohjelma::tekstien::lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size) { TTF_Init(); TTF_Font *font = NULL; SDL_Color textcolor = {r, b, g}; font = TTF_OpenFont("courbd.ttf", size); if (muuttuja) { char mjono[10]; sprintf(mjono, "%i", muuttujan_arvo); SDL_Surface *tex = TTF_RenderText_Blended(font, mjono, textcolor); return tex; } else { SDL_Surface *tex = TTF_RenderText_Blended(font, text, textcolor); return tex; } }
Tuostahan näkee jo pikaisella vilkaisulla, että avaat fonttitiedoston joka kerta uudestaan etkä koskaan sulje etkä myöskään (ainakaan tuossa koodissa) vapauta noita lataateksti-funktion palauttamia tekstejä vaan luot niitä vain lisää ja lisää.
Voisit jatkossa jatkaa keskusteluja siellä, missä olet ne aloittanutkin.
Tämän keskustelun alkuperäiseen ongelmaan: Luo uusi teksti siten, että kun on jäljellä enemmän kuin kolme merkkiä, siirretään viimeiset kolme uuteen tekstiin ja laitetaan alkuun erotinmerkki, ja kun on jäljellä enää kolme tai vähemmän, laitetaan ne uuteen tekstiin ilman erotinta. (Jos et vieläkään osaa tähän tarvittavia tekstinkäsittelyfunktioita, nyt jo vihdoinkin lukemaan string-olion käytöstä ainakin funktiot size, substr ja c_str! Ei ohjelmoinnista tule mitään, jos ei osaa etsiä itse aivan yksinkertaisia tietoja.)
Tässä olisi vielä teksti pätkimiseen soveltuva pätkä koodia. Tämä käyttää sekä std::string ja std::stack kokoelmia. Periaatteessa std::stack ei ole pakollinen, mutta toisaalta se hiukan helpotaa ohjelmointia.
#include <iostream> #include <string> #include <stack> std::stack <std::string> muunna_teksti_pinoksi(std::string teksti) { std::stack <std::string> valipino; int pituus = teksti.length(); for(int i = 0; i < pituus; ++i) { valipino.push(teksti.substr(i, 1)); } return valipino; } std::stack <std::string> lisaa_erottelumerkit(std::stack <std::string> teksti_pinossa, int joukko, std::string erotin) { std::stack <std::string> valipino; while(teksti_pinossa.size()) { valipino.push(teksti_pinossa.top()); teksti_pinossa.pop(); if(valipino.size() % joukko == 0 && teksti_pinossa.size()) { valipino.push(erotin); } } return valipino; } std::string erottele_teksti(std::string teksti, int joukko = 3, std::string erotin=",") { std::stack <std::string> teksti_pinoksi = muunna_teksti_pinoksi(teksti); std::stack <std::string> tekstiin_merkit = lisaa_erottelumerkit(teksti_pinoksi, joukko, erotin); std::string muunnettu; while(tekstiin_merkit.size()) { muunnettu.append(tekstiin_merkit.top()); tekstiin_merkit.pop(); } return muunnettu; } int main() { std::cout << erottele_teksti("800", 3, ",") << std::endl; //800 std::cout << erottele_teksti("8000", 3, ",") << std::endl; //8,000 std::cout << erottele_teksti("80000", 3, ",") << std::endl; //80,000 return EXIT_SUCCESS; }
Minusta tuo Teuron ratkaisu näyttää kaikkea muuta kuin helpolta. Seuraava ratkaisu toimii, kuten itse aiemmin selitin, ja lisäksi parametrit ovat lukujen erottelun kannalta loogisemmassa järjestyksessä: erotinta muutetaan useammin kuin erotusväliä.
std::string erottele_teksti(std::string alku, const std::string& erotin = " ", const int joukko = 3) { std::string loppu; while (alku.length() > joukko) { loppu = erotin + alku.substr(alku.length() - joukko) + loppu; alku = alku.substr(0, alku.length() - joukko); } return alku + loppu; }
Sama ajatus toimii myös yhdellä tekstillä mutta vaatii näin ehkä enemmän hahmottamista:
std::string erottele_teksti(std::string teksti, const std::string& erotin = " ", const int joukko = 3) { for (int kohta = teksti.length() - joukko; kohta > 0; kohta -= joukko) { teksti = teksti.substr(0, kohta) + erotin + teksti.substr(kohta); } return teksti; }
mika132: Älä kopioi mitään näistä vaan mieti, miten algoritmi toimii, ja toteuta se itse!
En ymmärrä. Miksi tästä tulee virhe.
namespace ohjelma { namespace tekstien { static SDL_Surface *lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size, const *fontit); static SDL_Surface *teksti, *teksti2, *teksti3, *teksti4, *teksti5, *teksti6, *teksti7, *teksti8, *teksti9, *teksti10, *teksti11; } namespace fontit { static TTF_Font *LataaFontit(const char *nimi, int size); static TTF_Font *font; } } ohjelma::aja() { fontit::font = fontit::LataaFontit("Courbd.ttf", 16); tekstien::teksti2 = tekstien::lataateksti("Rahaa:", false, 1, 255, 0, 0, 16, fontit::font); //Errori rivi } static SDL_Surface *ohjelma::tekstien::lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size, TTF_Font *font) { TTF_Init(); SDL_Color textcolor = {r, b, g}; if (muuttuja) { char mjono[10]; sprintf(mjono, "%i", muuttujan_arvo); SDL_Surface *tex = TTF_RenderText_Blended(font, mjono, textcolor); return tex; SDL_FreeSurface(tex); } else { SDL_Surface *tex = TTF_RenderText_Blended(font, text, textcolor); return tex; SDL_FreeSurface(tex); } } // Lataa kuvan ja optimoi sen piirtoa varten. static TTF_Font *ohjelma::fontit::LataaFontit(const char *nimi, int size) { TTF_Init(); TTF_Font *fontdata = TTF_OpenFont(nimi, size); return fontdata; }
errori:
71: error: cannot convert `TTF_Font*' to `const int*' for argument `8' to `SDL_Surface* ohjelma::tekstien::lataateksti(const char*, bool, int, int, int, int, int, const int*)'
mika132 kirjoitti:
71: error: cannot convert `TTF_Font*' to `const int*' for argument `8' to `SDL_Surface* ohjelma::tekstien::lataateksti(const char*, bool, int, int, int, int, int, const int*)'
No siinähän se sanotaan ihan selvästi: funktio tahtoo pointterin kokonaislukuun, niin sinä annat sille pointterin TTF_Fontiin.
Tuo oli jäänyt kyllä sinne vahinkossa, mutta en ymmärrä, kun errori ilmoittaa, että jotkin argumentit puuttuvat, mutta mitkä? Mielestäni kaikki on. Vai tekeekö tuo TTF_Font *font jotain ihmeellistä tuossa?
static SDL_Surface *lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size, TTF_Font *font); static SDL_Surface *ohjelma::tekstien::lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size, TTF_Font *font) { TTF_Init(); SDL_Color textcolor = {r, b, g}; if (muuttuja) { char mjono[10]; sprintf(mjono, "%i", muuttujan_arvo); SDL_Surface *tex = TTF_RenderText_Blended(font, mjono, textcolor); return tex; SDL_FreeSurface(tex); } else { SDL_Surface *tex = TTF_RenderText_Blended(font, text, textcolor); return tex; SDL_FreeSurface(tex); } }
Näytä kohta, jossa kutsut tuota funktiota. Mutta mutta miksi TTF_Init() ajetaan aina, kun funktiota kutsutaan? Miksi väri luodaan aina, kun funktiota kutsutaan? Miksei käytetä std::string, vaan mukana on epävarma char muuttuja? Miksei varattua pintaa vapauteta? Miksei funktiota käytetä tekstin lataamiseen, vaan kirjoittamiseen näytölle?
mika132 kirjoitti:
return tex; SDL_FreeSurface(tex);
Miten voit yhä olla näin pahasti pihalla sekä funktioiden toiminnasta että pintojen vapauttamisesta? Selostahan nyt tarkasti, mitä mielestäsi tässä muka tehdään.
No jos tuo vapautus tehdään ennen niin heittää errorin ettei tuollaista kuvaa ole edes olemassa. Tai niin sen ymmärsin, mutta ajattelin, että pitäisikö minun ladata kuvat sittenkin näin:
Ensin se miten nyt:
//Nämä ovat lataus funktiossa tekstien::teksti7 = tekstien::lataateksti("Toimisto", false, 1, 255, 0, 0, 16); tekstien::teksti8 = tekstien::lataateksti("Ohjelmointi", false, 1, 255, 0, 0, 16); tekstien::teksti9 = tekstien::lataateksti("Koulutus", false, 1, 255, 0, 0, 16); //ja nämä ovat värin vaihto funktiossa if (valinta == 1) { tekstien::teksti7 = tekstien::lataateksti("Toimisto", false, 1, 255, 255, 0, 16); tekstien::teksti8 = tekstien::lataateksti("Ohjelmointi", false, 1, 255, 0, 0, 16); tekstien::teksti9 = tekstien::lataateksti("Koulutus", false, 1, 255, 0, 0, 16); }
Vai pitäisikö sittenkin tehdä niin, että värin vaihto funktiossa olevat ladataan normaalissa lataus funktiossa ja nimeksi pistetään teksti 10, 11 ja 12 ja sitten sieltä piirretään niitä aina?
Pistän vielä varuiksi tämän nykyisen tekstin lataus funktion:
static SDL_Surface *ohjelma::tekstien::lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size) { TTF_Init(); SDL_Color textcolor = {r, b, g}; TTF_Font *font = TTF_OpenFont("Courbd.ttf", size); if (muuttuja) { char mjono[10]; sprintf(mjono, "%i", muuttujan_arvo); SDL_Surface *tex = TTF_RenderText_Blended(font, mjono, textcolor); return tex; SDL_FreeSurface(tex); } else { SDL_Surface *tex = TTF_RenderText_Blended(font, text, textcolor); return tex; SDL_FreeSurface(tex); } SDL_FreeSurface(TTF_Font *font); //Heitti erroria joten laitoin TTF_font alkuun niin loppui. En tiedä miksi. }
mika132 kirjoitti:
SDL_Surface *tex = TTF_RenderText_Blended(font, text, textcolor); return tex; SDL_FreeSurface(tex);
Identtinen virhe edellisen koodisi kanssa. Eli jos annat funktiosta ulos pointterin, niin siiten sinun pitää huolehtia sen vapauttamisesta. Tässä vaiheessa voisi tietty kysyä, että miksi tuollainen funktio antaa ulos mitään, sehän voisi olla ihan vaan void-tyyppinen.
Mutta sitten kysyin jo hetki sitten, että miksi TTF_Init() ajetaan aina? Ja miksi kummassa TTF_Font* annetaan funktiolle parametrinä?
Sitten vähän toista asiaa. Voisit luopua noista tekstien::teksti* muuttujista ja siirtyä vaikkapa std::vector<SDL_Surface*> tyyppiseen ratkaisuun.
mika132 kirjoitti:
No jos tuo vapautus tehdään ennen niin heittää errorin ettei tuollaista kuvaa ole edes olemassa.
Mutta ymmärrätkö, mitä koodisi nyt tekee? Vapauttaako se kuvan? Milloin?
Mitä eroa on vectoreilla tuohon?
Sitten mitäs ihmettä. Miten voin pistää funktioon tyypin.
namespace fontit { static TTF_Font *LataaFontit(const char *nimi, int size); static TTF_Font *font; } namespace tekstien { static SDL_Surface *lataateksti(const char *text, bool muuttuja, int muuttujan_arvo, int r, int b, int g, int size, fontit::font); static SDL_Surface *teksti, *teksti2, *teksti3, *teksti4, *teksti5, *teksti6, *teksti7, *teksti8, *teksti9, *teksti10, *teksti11; }
Siellä tekstien namespacessa on muuttujassa viimeisenä fontit::font, mutta heittää erroria.
error: `ohjelma::fontit::font' is not a type
En ymmärrä. Sekään ei toimi, että pistän tuonne funktioon fontit::font tilalle TTF_Font *font
No mikä tyyppi ohjelma::fontit::font sitten sinusta on? Minusta se ei ole mikään tyyppi. Sen sijaan tuon niminen pointteri (font) on olemassa tuossa nimiavaruudessa. std::vector käyttäytyy ohjelmoijan kannalta miellyttävämmin, koska se tuhoutuu automaattisesti. Miksei TTF_Font* tyyppi käy?
No nyt se toimikin noin, mutta vielä kasvaa tekstin vaihtuessa noin 1100kt pelin RAM muistin vienti koko ajan. Eli viekiö tekstin väri jotain muistista? Tarkoitan siis funktiota:
SDL_Color textcolor = {r, b, g};
Että pitäisikö sekin ladata vain kerran kun nyt se on minulla tuolla tekstin piirrossa, että se siis tehdään aina kun tekstiä piirretään.
Oletko jo ratkaissut kysymäni asian tuosta pinnan vapauttamisesta? Eiköhän vika ole edelleen siinä.
Jäänyt kokonaan välistä tuo. Eli juu vapauttaa kuvan vasta jälkeen sen kun sen tulos palautetaan, mutta miten sen sitten saan toimimaan, koska jos kuva (tai oikeastaan teksti) palautetaan ennen return tex kohtaa niin tulee windows errori 3 joka muistaaksen itarkoittaa, että määritettyä polkua ei löydy.
mika132 kirjoitti:
Eli juu vapauttaa kuvan vasta jälkeen sen kun sen tulos palautetaan
Menepä kertaamaan oppaasta funktioiden toiminta (erityisesti siis return-lauseen toiminta) ja yritä sitten uudestaan. Kai nyt tällaiset perusasiat pitäisi jo vähitellen osata. x_x
Eli siis onko sulla edelleen näin:
return tex; SDL_FreeSurface(tex);
koska jos on, niin tämä olisi toiminnan kannalta identtinen.
return tex;
koska return rivin jälkeen funktion suoritus loppuu, kuten oppaissa aivan taatusti kerrotaan.
No missä vapautan tuon tex jutun koska en voi vapauttaa sitä ennen returnia ja jos voin niin miten ihmeessä. :o
Et voi vapauttaa sitä ennen return lausetta, jos aiot returnissa palauttaa tuon osoittimen. Miksi muuten tuo pinta annetaan ulos funktiosta?
Pinta pitää tietenkin vapauttaa juuri ennen uuden lataamista.
vapauta(pinta); pinta = lataa();
Aivan. Kiitos paljon! Nyt toimii. = )
Aihe on jo aika vanha, joten et voi enää vastata siihen.