Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Outo ongelma

Sivun loppuun

vehkis91 [09.11.2008 17:09:02]

#

Miksiköhän tämä funktio ei vähennä vihollisen energiaa, vaan energia pysyy aina saman. Pelaajan energia vähennetään samanlaisella funktiolla, mutta se toimii. :S

void enemy::DecreaseEnergy(int maara)
{

    //Debuggausta varten tulostuksia...
    std::cout<<"Vähennetään vihollisen ennergiaa"<<std::endl;
    std::cout<<"Määrä ennen vähennystä"<<this->energy<<std::endl;

    //Vähennetään vihollisen energiaa...
    this->energy-=maara;

    std::cout<<"tallennetaan vihollisen verta"<<std::endl;
    vX.push_back(this->x);
    vY.push_back(this->y);
    vLaskuri++;

    std::cout<<"Vihollisen energia vähennetty"<<std::endl;
    std::cout<<"ENERGIA on nyt: "<<energy<<std::endl;
    std::cout<<"ENERGIAA vahennettiin :"<<maara<<std::endl;
}

Metabolix [09.11.2008 17:56:01]

#

Teet jotain muuta väärin. On aivan selvää, että tuo funktio toimii kyllä.

vehkis91 [09.11.2008 18:00:34]

#

Edit: Alempana lisää koodia, josta saattais löytyä virhe.

Tossa pätkä siitä tiedostosta:

Määrä ennen vähennystä40
tallennetaan vihollisen verta
Vihollisen energia vähennetty
ENERGIA on nyt: 30
ENERGIAA vahennettiin :10
Vähennetään vihollisen ennergiaa
Määrä ennen vähennystä40
tallennetaan vihollisen verta
Vihollisen energia vähennetty
ENERGIA on nyt: 30
ENERGIAA vahennettiin :10
Vähennetään vihollisen ennergiaa
Määrä ennen vähennystä40
tallennetaan vihollisen verta
Vihollisen energia vähennetty
ENERGIA on nyt: 30
ENERGIAA vahennettiin :10

Lisää koodia...

void CheckCollinsion()
{
    for(int j=0;j<AMMUKSIA;j++)
    {
        for(int i=0;i<ENEMIES;i++)
        {
            if(viholliset[i].GetState()==true)
            {
                TarkistaYksi(viholliset[i], j);
            }
        }
    }
}

void TarkistaYksi(enemy a, int j)
{
    if(TormaysPelaaja(a, ukko))
    {
            ukko.DecreaseEnergy(1);
    }

    if(TormaysPanos(a, ammukset[j]))
    {
        a.DecreaseEnergy(10);
        ammukset[j].SetX(-1);
        ammukset[j].SetY(-1);
        ammukset[j].SetSpeed(0);
    }
}

Metabolix [09.11.2008 18:42:18]

#

void TarkistaYksi(enemy a, int j)

Funktion kutsuminen luo siis aina uuden vihollisen. Opettelisit käyttämään niitä viittauksia (tai edes osoittimia, vaikka ne ovatkin huonommin sopivia).

Lisää debug-tulostus luokan muodostimeen ja tuhoajaan, niin huomaat.

enemy() { cout << "enemy() osoitteessa " << this << endl; }
~enemy() { cout << "~enemy() osoitteessa " << this << endl; }

Mahdatko ymmärtää tuon silmukkarakenteesi seurauksia? Tarkistat ukon ja vihollisen törmäyksen kerran jokaista ammusta kohden, vaikkei se ammus liity millään tavalla asiaan.

vehkis91 [09.11.2008 18:46:39]

#

EDIT: peli jökkää jostain syystä satunnaisesti vihollisia tapettaessa, yleensä kun tappaa 2 vihollista ja tämän jälkeen ampuu kerran vihollista päin koko homma kaatuu... :S

Metabolix [09.11.2008 19:19:02]

#

vehkis91 kirjoitti:

Edit: teeknö sitten toisen silmukan missä tarkastan törmäykset pelaajaan? eikös se hidasta pelin kulkua jos on hirveesti eri silmukoita?

Mietipä itse, kumpi on hitaampi:

AMMUKSIA kertaa (AMMUKSIA):
   ENEMIES kertaa (ENEMIES):
      tarkista(ukko, vihollinen[i]); // AMMUKSIA * ENEMIES kertaa
      tarkista(ammus[j], vihollinen[i]); // AMMUKSIA * ENEMIES kertaa
// yhteensä A*E + A*E tarkistusta, esim. A = 100, E = 10 tekee jo 2000
ENEMIES kertaa (ENEMIES):
   tarkista(ukko, vihollinen[i]); // ENEMIES kertaa
   AMMUKSIA kertaa (AMMUKSIA):
      tarkista(ammus[j], vihollinen[i]); // AMMUKSIA * ENEMIES kertaa
// yhteensä E + A*E tarkistusta, esim. A = 100, E = 10 tekee vain 1010, noin puolet

Ylimääräistä silmukkaakaan ei tarvittu, mutta sekään ei juuri vaikuttaisi asioihin. Yleensä silmukan sisällä tehtävät asiat (tässä tarkista-funktio) vievät paljon enemmän aikaa kuin itse silmukka. Olennaista on siis opetella laskemaan, montako kertaa jotain oikeasti tehdään. Silmukat kannattaa suunnitella järjen kanssa niin, että kaikki tehdään vain kerran. Esimerkiksi vihollisten keskinäisissä törmäyksissä on turha tarkistaa erikseen tapauksia 1-2 ja 2-1, kannattaa tarkistaa vain ne, joita ei ole vielä tarkistettu: 1-2, 1-3, 1-4, ..., 2-3, 2-4, ..., 3-4, ... jne.

joka viholliselle Vi:
  tarkista ukko, Vi
joka ammukselle Ai:
  tarkista ukko, Ai

joka viholliselle Vi:
  joka viholliselle Vj (kun j > i):
    tarkista Vi, Vj
  joka ammukselle Ai:
    tarkista Vi, Ai

Edit. Opettele myös käyttämään debuggeria, se osaa kertoa, millä rivillä ohjelma kaatuu, ja voit sitten tutkiskella arvoista, mikä ehkä meni pieleen.

vehkis91 [09.11.2008 19:51:03]

#

No debuggasin niin tulee vaan jotain muistipaikkoja ja ?-merkkejä. :S

Edit: tai siis funktion kohdalla on vaan ??(). :S

Metabolix [09.11.2008 21:31:27]

#

Et varmaankaan ole asettanut debug-infoa päälle. Jos käännät GCC:llä komentorivillä, ota optimoinnit (-O) pois ja lisää debug-info (-g).

vehkis91 [11.11.2008 22:10:29]

#

Juu eli debuggasin tätä ja debuggerista tuli kaikkea ulos, mutt tämä pisti silmään...

#0 6812850c SDL_UpperBlit() (I:/..../SDL.dll:??)

tarkoittaako tämä, että sdl.dll tiedostossa on se vika? Kaatuminen on aika satunnaista, mutt yleensä kaatuu juuri kun ampuu vihollista. :S

Kray [11.11.2008 22:14:26]

#

Ei, tuo vain kertoo että jossain välissä käytät SDL_BlitSurfacea, joka on tuon UpperBlitin "kopio", ja että se löytyy tiedostosta SDL.dll

Metabolix [11.11.2008 22:31:49]

#

Tuo tarkoittaa, että kyseisessä funktiossa tapahtuu lopullinen kaatuminen. Tarkemmalla tutkimisella voit kuitenkin selvittää, miten sinne päädyttiin ja millaisia arvoja se sai parametrinaan. Tässä on pieni esimerkki, jossa ohjelma kaatuu, pyydetään tiedot aiemmista kutsuista (bt, backtrace), siirrytään tutkittavaan kohtaan omassa ohjelmassa (frame 1 bt:n tulosteen mukaisesti) ja tulostetaan sieltä joitakin arvoja (print C++-ilmaus):

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7d2f6c0 (LWP 3659)]
0xb7ee4d61 in SDL_UpperBlit () from /usr/lib/libSDL-1.2.so.0

(gdb) bt
#0  0xb7ee4d61 in SDL_UpperBlit () from /usr/lib/libSDL-1.2.so.0
#1  0x08048638 in main () at koe.c:15

(gdb) frame 1
#1  0x08048638 in main () at koe.c:15
15              SDL_BlitSurface(ruutu, 0, kuva, 0); // piirretään

(gdb) print ruutu
$1 = (SDL_Surface *) 0xa01e750

(gdb) print kuva
$2 = (SDL_Surface *) 0xa02d1b0

(gdb) print *ruutu
$3 = {flags = 0, format = 0xa023f78, w = 640, h = 480, pitch = 2560, pixels = 0xb7954000, offset = 0, hwdata = 0x0, clip_rect = {x = 0,
    y = 0, w = 640, h = 480}, unused1 = 0, locked = 0, map = 0xa021830, format_version = 2, refcount = 1}

(gdb) print *kuva
Cannot access memory at address 0xa02d1b0

Viimeisestä ilmoituksesta selviää siis, että SDL_Surface *kuva osoittaa muistiin, jota ei voida käyttää. Se on siis ehkä jo vapautettu. Sen sijaan ruutu oli aivan kelvollinen SDL-pinta, josta saatiinkin hyvin tietoa irti. Tulostettavaksi kelpaavat C++:n normaalit muuttujat ja merkinnät eli esimerkiksi luokka->taulujasen[i+j].

vehkis91 [12.11.2008 09:27:01]

#

Siis tulostetaanko niitä tietoja komennolla: print? vai? :O Pitää näyttävästi tutkia, että vapautanko muistin jossain paikoissa liian aikaisin... :S

Tässä vielä se koko debug-printti:

#0 6812850C	SDL_UpperBlit() (I:\School\Ohjelmointi\\bin\Debug\SDL.dll:??)
#1 004018F6	enemy::Piirra(this=0x44a530, index=0, X=459, Y=197) (I:/School/Ohjelmointi/Enemy.cpp:184)
#2 00403DA6	PiirraKuva() (I:/School/Ohjelmointi/main.cpp:343)
#3 00403989	SDL_main(argc=1, argv=0x9512b0) (I:/School/Ohjelmointi/main.cpp:221)
#4 00404A77	console_main(argc=1, argv=0x9512b0) (./src/main/win32/SDL_win32_main.c:217)
#5 00404C55	WinMain(hInst=0x400000, hPrev=0x0, szCmdLine=0x291da6 "", sw=10) (./src/main/win32/SDL_win32_main.c:353)
#6 004044D4	main() (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/iostream:77)

Jos te saisitte siitä enemmän selkoa kun minä, en ole meinaa hirveesti softia debugannut. :S

Sisuaski [12.11.2008 11:54:29]

#

Backtraceissa normaalisti mielenkiintoisin osa on ylin omaan koodiin viittaava kohta, tuossa siis Enemy.cpp:n rivi 184. (Tuo siis tulkitaan alhaalta ylöspäin niin, että main on kutsunut WinMainia, joka on kutsunut console_mainia, joka on kutsunut SDL_Mainia, joka on kutsunut PiirraKuvaa jne.)

Tuon perusteella voisi olettaa, että tuolla kutsuttavalle SDL_BlitSurfacelle (joka nyt näkyy tuossa SDL_UpperBlittinä, oletan että et kutsu tämän nimistä funktiota suoraan koodistasi) annetaan parametrina rikkinäinen surface (esim. jo vapautettu tai joku on ylikirjoittanut sen tietoja) lähde- tai kohdeparametriksi.

Täysin varmaa ei kuitenkaan ole, että virhe tulee juuri tuossa kohdassa, sillä aiemmin sattunut virhe voi kaataa ohjelman vasta vähän myöhemmin.

vehkis91 [12.11.2008 13:34:25]

#

Edit:: Öö, kopioin wikipediasta itoa funktion, mikä muuttaa char taulukon int:ksi.

Huomasin vain että se on hirveän raskas, joten onko c++ mitään omaa tyyliä tähän. eli tarkoitus olisi muuttaa pelaajan energian määrä char-taulukoksi tai stringiksi, että voin tulostaa sen sdl_ttf:llä esiin.

Tässä kopioimani funkkarit:

void reverse(char s[])
{
    int c, i, j;

    for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

void itoa(int n, char s[])
{
    int i, sign;

    if ((sign = n) < 0)  // record sign
        n = -n;          // make n positive
    i = 0;
    do {       /* generate digits in reverse order */
        s[i++] = n % 10 + '0';   /* get next digit */
    } while ((n /= 10) > 0);     /* delete it */
    if (sign < 0)
        s[i++] = '-';
    s[i] = '\0';
    reverse(s);
}

TsaTsaTsaa [12.11.2008 15:09:35]

#

#include <sstream>
#include <iostream>
#include <string>

void int2str(int i, std::string &s) {
   std::ostringstream os;
   os << i;
   s = os.str();
}

std::string int2str2(int i) {
   std::ostringstream os;
   os << i;
   return os.str();
}

int main() {
   int a = 84;
   int q = 823;
   std::string b;
   int2str(a, b);
   std::cout << b << std::endl;
   std::cout << int2str2(q) << std::endl;
   return 0;
}

Sisuaski [12.11.2008 15:10:19]

#

Tuon homman saa C:ssä hoidettua sprintf-funktiolla ja c++:ssa stringstreameillä:

#include <stdio.h>

...

char str[64];
int luku=25;
sprintf(str, "%d", luku);
#include <sstream>
#include <string>

...

std::ostringstream os;
int luku=25;
os << luku;
std::string str = os.str();

vehkis91 [12.11.2008 15:26:13]

#

Juu kiitos, tuo ostringstream toimii paljon luotettavammin kuin tuo itoa funkkari. :D

Metabolix [12.11.2008 15:36:49]

#

Omallakin funktiolla tuon voi tehdä tehokkaammin takaperin:

std::string format_integer(int n, bool aina_etumerkki = false) {
  char buf[15], etumerkki = aina_etumerkki ? '+' : 0;
  int i = sizeof(buf);

  // lopetusmerkki ja erikoistapaukset (0 ja negatiiviset)
  buf[--i] = 0;
  if (n < 0) {
    etumerkki = '-';
    n = -n;
  }

  // hoidetaan luku alhaalta alkaen
  do {
    buf[--i] = '0' + (n % 10);
  } while (n /= 10);

  // lisätään etumerkki alkuun
  if (etumerkki) {
    buf[--i] = etumerkki;
  }
  return std::string(&buf[i]);
}

Toki standardikirjaston välineet ovat paljon paremmat, kun voi helposti säätää halutessaan kaikenlaisia formatointitemppuja kuten etumerkkiä, tasausta jne.

vehkis91 [12.11.2008 18:29:15]

#

Juu nuo viat saatu korjattua. Nyt vasta outo vika tulikin, kun tossa Code::Blockissa on silleen että saa valita että kääntääkö release version vai debug version, niin kun kääntää debug version niin kaikki pelaa hyvin, mutta kun kääntää release version niin tuo peli ukkon nopeus on tyyliin 100pxl vaikka sen pitäis olla 5. :S

Edit: Ja tyypin liikkuma-alue pienenee, eli tyyppi ei voi liikkua ku n1/2 siitä alueesta mitä se voi liikkua debug versiossa. :S

Metabolix [12.11.2008 22:13:42]

#

Jotain olet sitten tehnyt väärin, välineissä harvoin on vikaa. Miten rajoitat liikenopeutta, miten säilytät koordinaatit, miten hoidat tuon alueen reunaan törmäämisen? Ei auta kuin selvittää, missä menee pieleen. Tulostele vaikka ukon koordinaatteja ja vertaa, ovatko ne erilaiset ja muuttuvatko ne eri tavalla eri versioissa vai onko vika kuitenkin jossain muualla.

vehkis91 [13.11.2008 09:26:35]

#

Siis en ole muuttanut koodia ollenkaan, se toimii kun kääntä "debug" version, mut release versio ei toimi. :( No pitääpä tutkia...

Tässä ukon ohjausfunkkari, missä on samassa se törmäystarkistus seiniin.

void player::Move()
{
    if(player::state==true)
    {
        // Liikutetaan pelaajaa, jos mahdollista
        if(this->keyboard[SDLK_LEFT] && this->x - this->speed >= 0)
        {
            this->suunta=1;
            this->x -= speed;
        }
        else
        if(this->keyboard[SDLK_RIGHT] && (this->x + IMAGE_SIZE) + this->speed < SCREEN_WIDTH)
        {
            this->suunta=2;
            this->x += speed;
        }
        else
        if(this->keyboard[SDLK_UP] && this->y - this->speed >= 0)
        {
            this->suunta=3;
            this->y -= speed;
        }
        else
        if(this->keyboard[SDLK_DOWN] && (this->y + IMAGE_SIZE) + this->speed < SCREEN_HEIGHT)
        {
            this->suunta=4;
            this->y += speed;
        }

    }

}

//Pelaajan luonti

player ukko(1000,400,300,5); //player pelaanannimi(energia,x,y,nopeus);

vehkis91 [13.11.2008 14:35:29]

#

No se alkoi toimiin ku käänsin win xp:llä vistan sijasta. :S aika outoo...

Edit: Samalla tuli 0.6 versio ulos. Osoite on mun profiilissa.

groovyb [20.11.2008 02:25:18]

#

onpas mahdoton peli :)

oikein hermot meni kun ne ötökät tulee niin nopiaa päälle että elinikä aina max 10sek ;/

Laitinen [20.11.2008 02:49:05]

#

Ei lainkaan mahdoton :), vaikka muutama minuutti läpipääsyyn vaadittiinkin. Toisaalta peli ei edes lopu vaikka kaikki viholliset tappaa, olisihan se kiva että jotenkin pelaamisesta palkittaisiin :/. Muutenkin peli olisi kiva aloittaa alusta ilman että se sulkeutuu välissä, sen lisäksi olisi kiva ehtiä reagoimaan alussa, ts. ettei peli käynnistyisi heti.

vehkis91 [20.11.2008 09:59:23]

#

Eli, seuraavaan versioon tulee menu. Olen miettinyt, että miten voisin rajoittaa vihollisten iskunopeutta. Olen kyll yrittänytkin sitä, mutta se ei toiminut. Eli ehdotuksia saa laittaa... :D

Edit: Tuolla on "keskustelua" pelistä: http://www.suomipelit.com/index.php?c­=keskusteluviestit&id=9315&s=1


Sivun alkuun

Vastaus

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

Tietoa sivustosta