c++/SDL Multi_Surfaces und dessen Kollision



  • Hallo

    Seit einiger Zeit arbeite ich jetzt mit SDL
    und bin damit auch schon recht weit gekommen.

    ich schreibe momentan an einen sehr einfachen Raumschiff Spiel
    und bin gerade bei der erstellung der Feinde und dessen Collision..

    vor etwa ein paar wochen war ich zufällig via google/bilder auf einer spanischen Seite,
    gelandet, wo mit ein fast ausführbaren Code-Schnippsel gezeigt wurde,
    wie man ein Bild mehrmals darstellen kann, indem man ein Array ans Surface bindet und die
    Position in int umwandelt

    das ganze habe ich in mein code integriert, abgespeckt und verändert und das klappt auch 🙂

    nur hab ich jetzt ein problem mit der Collisions erkennung.
    die Collision ist zwar da, aber nur sehr vereinzeld und nicht überall

    sprich: manche Feinde haben keine Collision oder nur ein einziger 😞

    Die Kollisions-Abfrage hab ich aus 3d Gamestudio Aum 83 (2d spiel tutorial)
    und diese lief auch als ich ein einzigen feind hatte (bin ehrmaliger Lite C benutzer)

    -> die defines bestimmen die bildgröße, sonst keine Kollision ! - glaub ich ...

    hier ist der Code:

    Hintergrund
    Raumschiff
    Schuss
    Feinde

    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
    #include <SDL/SDL_mixer.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <iostream>
    
    #define shoot_width 6
    #define shoot_height 12
    
    #define ship_width 32
    #define ship_height 32
    
    #define enemy_width 32
    #define enemy_height 32
    
      struct position
      {
       int x;
       int y;
      };
    
    int main(int argc, char* argv[])
    {
     SDL_Init(SDL_INIT_EVERYTHING);
    
     SDL_Surface *screen;
     SDL_Surface *background_a;
     SDL_Surface *ship;
     SDL_Surface *laser;
     SDL_Surface *astro;
    
     SDL_Surface **multi_pic;
    
     struct position *multipic_position;
    
     SDL_Event event;
     SDL_Rect position;
    
     int multipic_num=30;
     int i,j;
    
     int shoot = 0;
     int enemy_var = 1;
    
     srand(time(NULL));
     // SDL_mixer starten.
     const int samplingFrequency = 44100;	// 44100 Hz Abtastfrequenz
     Uint16 audioFormat = AUDIO_S16SYS;		// 16 Bits pro Sample
     const int numChannels = 2;				// 2 Kan?le = Stereo
     const int chunkSize = 4096;			// ein guter Wert ...
    
     if(SDL_Init(SDL_INIT_VIDEO) < 0 )
     {
      return -1;
     }
    
     if(Mix_OpenAudio(samplingFrequency, audioFormat, numChannels, chunkSize) == -1)
     {
      return -1;
     }
    
     // convert sdl surface** to sdl_surface*  -> for array  <-> to multi_pic[i]
     // convert struct position to rect for all surfaces... very amazing o.O   !!!
     // i think this is the array container , isnt it ?
    
     multi_pic = (SDL_Surface**)malloc(sizeof (SDL_Surface*)*multipic_num); // SDL_Surface to Array
     multipic_position = (struct position*)malloc(sizeof (struct position)*multipic_num); // rect to int
    
      // create 20 enemys and 20 x,y rand positions...
     for (i=0; i<multipic_num; i++)
     {
      multipic_position[i].x =  rand()%800+1;
      multipic_position[i].y =  rand()%100+1;
     }
    
     screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
     SDL_WM_SetCaption("Space Shooter v.1.0", "Space Shooter v.1.0");
    
     background_a = IMG_Load("background_a.jpg");
     ship = IMG_Load("ship.jpg");
     laser = IMG_Load("laser.bmp");
     astro = IMG_Load("astro.bmp");
    
     Mix_Chunk* sound1 = Mix_LoadWAV("laser.wav");
     Mix_Chunk* sound2 = Mix_LoadWAV("explo.wav");
     Mix_Music* music = Mix_LoadMUS("background.mp3");
     Mix_PlayMusic(music, -1);
    
     SDL_Rect imagePosition;
     imagePosition.x = 100;
     imagePosition.y = 500;
    
     SDL_Rect laserPosition;
     laserPosition.x = imagePosition.x;
     laserPosition.y = imagePosition.y-10;
    
     bool run = true;
     Uint8* keyPressed;
    
     while(run)
     {
      SDL_Event event;
    
      while(SDL_PollEvent(&event))
      {
       if(event.type == SDL_QUIT || event.key.keysym.sym == SDLK_ESCAPE)
       run = false;
      }
    
     keyPressed = SDL_GetKeyState(NULL);
    
     if(keyPressed[SDLK_LEFT])   imagePosition.x -= 5;
     if(keyPressed[SDLK_RIGHT])  imagePosition.x += 5;
     if(keyPressed[SDLK_UP])   imagePosition.y -= 5;
     if(keyPressed[SDLK_DOWN])  imagePosition.y += 5;
    
     if(keyPressed[SDLK_SPACE] && shoot == 0)
     {
      laserPosition.x = imagePosition.x;
      laserPosition.y = imagePosition.y-10;
      Mix_PlayChannel(-1, sound1, 0);
      shoot = 1;
     }
    
     SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 255));
     SDL_BlitSurface(background_a, 0, screen, NULL);
     SDL_BlitSurface(ship, 0, screen, &imagePosition);
    
     if (shoot == 1)
     {
      laserPosition.y -= 5;
      SDL_BlitSurface(laser, 0, screen, &laserPosition);
      if (laserPosition.y < 10)
      {
      shoot = 0;
      }
     }
    
       // NOW we draw 20 enemys and make 20 differents x,y position
     if (enemy_var == 1 )
     {
      for (i=0; i<multipic_num; i++)
      {
       position.x = multipic_position[i].x;
       position.y = multipic_position[i].y;
       SDL_BlitSurface(astro,NULL,screen ,&position);
      }
     }
    
    /*  for (i=0; i<multipic_num; i++)
      {
       multipic_position[i].y +=rand()%2+1;
      }
    */
    
    // collision
     if ((((laserPosition.x + shoot_width >= multipic_position[i].x) &&
            (laserPosition.x + shoot_width <= multipic_position[i].x + enemy_width)) ||
    
            ((multipic_position[i].x + enemy_width >= laserPosition.x) &&
             (multipic_position[i].x + enemy_width <= laserPosition.x + shoot_width))) &&
    
           (((laserPosition.y + shoot_height >= multipic_position[i].y) &&
            (laserPosition.y + shoot_height <= multipic_position[i].y + enemy_height)) ||
    
    		((multipic_position[i].y + enemy_height >= laserPosition.y) &&
    	    (multipic_position[i].y + enemy_height <= laserPosition.y + shoot_height))))
    
              {
               shoot = 0;
               enemy_var = 0;
    
               laserPosition.y = imagePosition.y;
               laserPosition.x = imagePosition.x +10;
              // EnemyPosition.x = rand() %  700 + 100;
              // EnemyPosition.y = 20;
               Mix_PlayChannel(-1, sound2, 0);
              }
    
     if ((((imagePosition.x + ship_width >= position.x) &&
            (imagePosition.x + ship_width <= position.x + enemy_width)) ||
    
            ((position.x + enemy_width >= imagePosition.x) &&
             (position.x + enemy_width <= imagePosition.x + ship_width))) &&
    
           (((imagePosition.y + ship_height >= position.y) &&
            (imagePosition.y + ship_height <= position.y + enemy_height)) ||
    
    		((position.y + enemy_height >= imagePosition.y) &&
    	    (position.y + enemy_height <= imagePosition.y + ship_height))))
    
              {
               shoot = 0;
               laserPosition.y = imagePosition.y;
               laserPosition.x = imagePosition.x +10;
              // EnemyPosition.x = rand() %  700 + 100;
              // EnemyPosition.y = 20;
               Mix_PlayChannel(-1, sound2, 0);
              }
    
      SDL_Flip(screen);
     }//end of main
    
     Mix_FreeChunk(sound1);
     Mix_FreeChunk(sound2);
     Mix_FreeMusic(music);
     Mix_CloseAudio();
    
     SDL_FreeSurface(background_a);
     SDL_FreeSurface(ship);
     SDL_FreeSurface(astro);
    
     SDL_Quit();
     return 0;
    }
    

    im Spieleprogrammierforum meint man: der code sei veraltet
    ich sollte std::Array benutzen

    FRAGE:

    -> was mache ich bei der Collision falsch ?

    -> könnt ihr mir zeigen wie das gehen soll mit "std::array" (einfaches ausührbeispiel)

    -> wie lösche ich denn jetzt das Surface[i] wieder, ohne das gleich alle verschwinden, wenn ich es abschieße ?

    -> wie bekomme ich es hin , dass nich alle Feinde aufenmal gezeichnet werden , sondern nach und nach und auch immer wieder ?
    -> hab schon alles in ausprobiert , desswegen benutze ich auch ganz einfach gerade variablen und frage sie ab 😛



  • Hmm...fehlt da nicht eine Schleife in Z. 159?

    for (int i=0; i<multipic_num; ++i)  // hinzugefügt
      if ((((laserPosition.x + shoot_width >= multipic_position[i].x) &&
            (laserPosition.x + shoot_width <= multipic_position[i].x + enemy_width)) || ...
    

    Schließlich ist da ein i, aber es wird nicht iteriert.

    Übrigens passiert sowas nicht, wenn Du Deine Variablen so lokal wie möglich deklarierst. Ein i wäre dann gar nicht im Scope.

    Wie und wo da jetzt std::array besser sein soll: keine Ahnung. Evtl um multipic_position zu ersetzen...?!
    Das ist schöner Spaghetticode - da blickt niemand durch...

    Aber eins ist mir aufgefallen: was hat es denn mit der Variable multi_pic auf sich? Die benutzt Du doch gar nicht?!



  • Jaaa!!! 🙂 😃 YOU MADE MY DAY

    endlich kollision , nur weil ich for vergessen hatte 😛

    so und jetzt erstmal zum multi_pic[i]

    multi_pic[i] = img_load ("feind")
    

    war ein surface welches über die aller erste for-schleife hochgerechnet wurde , machte aber kein sinn , da ja schon mit for eine anzahl an surfaces erstellt wird

    -> und hier ist mein neues problem ...

    das ganze spiel läuft super bis auf eine sache ...

    wie lösche ich die erstellten Surfaces wieder ?

    -> wenn ich SDL_FreeSurface benutze, dann geht mein programm einfach aus !

    -> muss ich i wieder zurückrechnen ?

    hier das neue ...

    ...
    int enemy_var = 1 // MUSS !!!
    while(run)
    {
     ... 
     // draw Position , draw enemy
     if (enemy_var == 1)
     {
      for (i=0; i<multipic_num; i++)
      {
       multipic_position[i].x =  rand()%800+1;
       multipic_position[i].y =  rand()%100+1;
       // multi_pic[i] = IMG_Load ("feind.bmp");
       enemy_var = 2;
      }
     }
    
     // draw on the screen 
     if (enemy_var == 2 )
     {
      for (i=0; i<multipic_num; i++)
      {
       position.x = multipic_position[i].x;
       position.y = multipic_position[i].y;
       SDL_BlitSurface(astro,NULL,screen ,&position);
      }
     }
    
     // enemy go down ...
     for (i=0; i<multipic_num; i++)
      {
       multipic_position[i].y +=rand()%2+1;
      }
     ...  
     //collision
     for (i=0; i<multipic_num; i++)
     {
      if ((((laserPosition.x + shoot_width >= multipic_position[i].x) &&
            (laserPosition.x + shoot_width <= multipic_position[i].x + enemy_width)) ||
    
            ((multipic_position[i].x + enemy_width >= laserPosition.x) &&
             (multipic_position[i].x + enemy_width <= laserPosition.x + shoot_width))) &&
    
           (((laserPosition.y + shoot_height >= multipic_position[i].y) &&
            (laserPosition.y + shoot_height <= multipic_position[i].y + enemy_height)) ||
    
    		((multipic_position[i].y + enemy_height >= laserPosition.y) &&
    	    (multipic_position[i].y + enemy_height <= laserPosition.y + shoot_height))))
    
              {
               shoot = 0;
               enemy_var = 0;
    
               laserPosition.y = imagePosition.y;
               laserPosition.x = imagePosition.x +10;
               SDL_FreeSurface(mutli_pic[i]);            //<- hier
               enemy_var = 1                            // sonst geht das programm aus !
               Mix_PlayChannel(-1, sound2, 0);
              } 
    
      }
     }
    ...
    

    wie geht das ?



  • patty1991 schrieb:

    so und jetzt erstmal zum multi_pic[i]

    multi_pic[i] = img_load ("feind")
    

    war ein surface welches über die aller erste for-schleife hochgerechnet wurde , machte aber kein sinn , da ja schon mit for eine anzahl an surfaces erstellt wird

    -> und hier ist mein neues problem ...

    das ganze spiel läuft super bis auf eine sache ...

    wie lösche ich die erstellten Surfaces wieder ?

    Du hast doch gar keine Surfaces erstellt.

    Ich meine: Ja, Du allozierst Speicher für einen Haufen SDL_Surface* , aber machst nie was damit. Dann darfst Du auch nicht SDL_FreeSurface darauf aufrufen.

    An welcher Stelle meinst Du denn "eine anzahl an surfaces erstellt" zu haben?



  • ... 
    if (enemy_var == 2 )
     {
      for (i=0; i<multipic_num; i++) 
     { 
      multipic_position[i].x =  rand()%800+1; 
      multipic_position[i].y =  rand()%100+1; 
      multi_pic[i] = IMG_Load("astro.bmp");     //  <- hier 
     } 
    
       enemy_var = 1;
     } 
    
     if (enemy_var == 1 ) 
     { 
      for (i=0; i<multipic_num; i++) 
      { 
       position.x = multipic_position[i].x; 
       position.y = multipic_position[i].y; 
       SDL_BlitSurface(multi_pic[i],NULL,screen ,&position); 
      } 
     } 
    
    If collision ect..
    {
     SDL_FreeSurface(multi_pic[i]);
    }
    ...
    

    hää ? Hab ich doch, ich habs nur mit // erstmal raus genommen und stattdessen
    astro benutzt aber selbst so funktioniert es nicht



  • patty1991 schrieb:

    hää ? Hab ich doch, ich habs nur mit // erstmal raus genommen und stattdessen
    astro benutzt aber selbst so funktioniert es nicht

    Und das soll ich riechen? Oder wie stellst Du Dir das vor?

    Wie dem auch sei:
    Meinen Tipp mit den lokal erstellten Variablen fandest Du nicht so gut, was? Aber Du verstehst, was Dir das bringt, wenn Du for(int i=...);
    stattt int i; for(i=....); schreibst, ja?

    Zu Deinem aktuellen Problem:
    Wenn ich das richtig verstehe löscht Du im Gameloop einfach eine SDL_Surface . Aber im nächsten Durchlauf wird doch wieder über das gesamte multi_pic Array iteriert - auch über die gelöschten SDL_Surface* s. Richtig?

    Das wäre doch ein Fehler, oder?



  • if (enemy_var == 2 )
     {
     	for (i=0; i<multipic_num; i++) 
     { 
      multipic_position[i].x =  rand()%800+1; 
      multipic_position[i].y =  rand()%100+1; 
      multi_pic[i] = IMG_Load("game/astro.bmp"); 
     } 
    
       enemy_var = 1;
     } 
    
     if (enemy_var == 1 ) 
     { 
      for (i=0; i<multipic_num; i++) 
      { 
       position.x = multipic_position[i].x; 
       position.y = multipic_position[i].y; 
       SDL_BlitSurface(multi_pic[i],NULL,screen ,&position); 
      } 
     } 
    
      for (i=0; i<multipic_num; i++) 
      { 
       multipic_position[i].y +=rand()%2+1; 
      } 
    
    if (Live == 0)
    {
      //SDL_BlitSurface(game_over, 0, screen, NULL); 	
      imagePosition.x = 100; 
      imagePosition.y = 500; 
      Live = 3;
      Score = 0;
      enemy_var = 2;
    } 
    
    // collision 
    for (i=0; i<multipic_num; i++)  // hinzugefügt 
    {
     if ((((laserPosition.x + shoot_width >= multipic_position[i].x) && 
            (laserPosition.x + shoot_width <= multipic_position[i].x + enemy_width)) || 
    
            ((multipic_position[i].x + enemy_width >= laserPosition.x) && 
             (multipic_position[i].x + enemy_width <= laserPosition.x + shoot_width))) && 
    
           (((laserPosition.y + shoot_height >= multipic_position[i].y) && 
            (laserPosition.y + shoot_height <= multipic_position[i].y + enemy_height)) || 
    
            ((multipic_position[i].y + enemy_height >= laserPosition.y) && 
            (multipic_position[i].y + enemy_height <= laserPosition.y + shoot_height)))) 
    
              { 
               shoot = 0; 
              // enemy_var = 0; 
    
               laserPosition.y = imagePosition.y; 
               laserPosition.x = imagePosition.x +10; 
               Score++; 
    
              SDL_FreeSurface(multi_pic[i]);
    
              enemy_var = 1;
    
               Mix_PlayChannel(-1, sound2, 0); 
              } 
    }
    

    Also das mit dem lokalen variabeln hab ich jetzt noch nicht verstanden und dass ich mich in der for-schleife vertippt habe, kann ja mal vorkommen
    ich will ja das ein Surface gelöscht wird, nicht alle

    Ich weiss beim besten echt nicht wie ...

    Manchmal kann ich eins abschießen und die anderen laufen weiter und wenn ich dann ein zweites abschieße stürtz das program ab

    ODER

    ich scheiße eins ab und es geht sofort aus ...

    und warum wird alles, was ich an code einfüge auf dem tablet hier in forum immmer nach oben an erster stelle eingefügt ?? 😕

    [/cpp]



  • patty1991 schrieb:

    ich will ja das ein Surface gelöscht wird, nicht alle

    Ich weiss beim besten echt nicht wie ...

    Spieleprogrammieren ist schon cool, aber schnell mal eben ein wenig C++ hingetippt ist auf jeden Fall die Straße in's nirgendwo.

    Das zentrale Hilfsmittel in C und C++ sind doch Klassen(bzw. structs). Hast Du schonmal selbst eins geschrieben? Mit der SDL benutzt du sie ja den lieben langen Tag, aber schonmal eins erdacht?

    Z.B. Deine sprites könnten so ausshen (Ich weiß dafür komme ich in die C++-Hölle...)

    struct sprite{
      SDL_Rect pos;
      SDL_Surface* surf;
      bool alive;
    };
    

    Das heisst ein sprite hat eine position, eine surface und ein flag, ob es getroffen wurde.
    Davon brauchst Du einen Haufen und schon kanns losgehen. Beispielsweise hier ein paar der wichtigsen Funktionen um diese sprite-Klasse:

    #include <iterator>  // std::begin, std::end
    #include <SDL/SDL.h>
    #include <SDL/SDL_image.h>
    
    SDL_Surface *screen;
    
    struct sprite{
      SDL_Rect pos;
      SDL_Surface* surf;
      bool alive;
    };
    
    sprite new_sprite(const char* s){
      sprite ret;
      ret.pos.x=0;
      ret.pos.y=0;
      ret.surf=IMG_Load(s);
      ret.alive=true;
      return ret;
    }
    
    void delete_sprite(sprite& s){
      SDL_FreeSurface(s.surf);
    }
    
    void render(sprite& s){
      SDL_BlitSurface(s.surf, nullptr, screen, &s.pos);
    }
    
    void render(sprite* first, sprite* last){
      for(; first!=last; ++first)
        if(first->alive)
          render(*first);
    }
    
    bool is_in_sprite(int x, int y, const sprite& s){
      int left   = s.pos.x;
      int right  = left + s.surf->w;
      int bottom = s.pos.y;
      int top    = bottom + s.surf->h;
      return left<x && x<right && bottom<y && y<top;
    }
    
    void shoot(int x, int y, sprite* first, sprite* last){
      for(;first!=last; ++first)
        if(first->alive && is_in_sprite(x, y, *first)){
          first->alive=false;
          break;
        }
    }
    
    int main(){
      const int num_enemies = 20;
      sprite enemies[num_enemies]={0};
    
      for(int i=0; i<num_enemies; ++i)
        enemies[i]= new_sprite("enemy.bmp");
    
      shoot(...);
      //....
      render(std::begin(enemies), std::end(enemies));
    
      //....
      for(int i=0; i<num_enemies; ++i)
        delete_sprite(enemies[i]);  
    }
    

    Das ist jetzt eigentlich pures C mit ein paar C++ features. In reinem C++ sähe das noch besser aus, aber ich habe nicht das Gefühl, dass Dir das viel bringen würde...

    Schau Dir das mal an - Du könntest mit neuen Ideen nochmal anfangen. Aber Du brauchst z.B. noch eine Funktion, die abfragt, ob zwei Sprites kollidieren und wahrscheinlich noch viel mehr.

    Das mit dem neu anfangen ist mein ernst: Dein Code ist glaube ich nicht großartig zu retten! 😞

    Aber das gehört dazu! 🙂



  • ja ersteinmal wieder ein dickes dankeschön , ich werde mir die sachen heute mal angucken und anfangen versuchen alles neu zu machen - gerade kam mir so die idee:

    warum nicht einfach bei kollision das surface nach "out of screen" positionieren ,dann ist es weg , aber doch noch da 😛

    Danke für so viel information und codeschnippsel/beispielen



  • patty1991 schrieb:

    Danke für so viel information und codeschnippsel/beispielen

    Moin patty1991.
    Nimm die Beispiele nicht zu wörtlich - wie ich schon schrieb kann das auch ganz anders aussehen.
    Wichtig wäre nur, dass Du mitnimmst, wie komplexe Funktionalität aus kurzen Klassen und Funktionen zusammengesetzt werden kann.

    Ich bin mir übrigens gar nicht sicher, ob Du C oder C++ schreiben möchtest.
    Je nachdem was für Probleme auftreten, kann auch ein Besuch im C++ Subforum, bzw. C Subforum lohnend sein.
    Allerdings wirst Du dort wahrscheinlich nach Deinem Lehrbuch gefragt - und ich hoffe, dass Du dann nicht antwortest aus Internettutorials und -foren zu lernen! 😉

    Bleib auf jeden Fall am Ball!

    Gruß,
    FW


Anmelden zum Antworten