C++ Kollisionsabfrage Problem



  • Guten Tag an die Community,
    Im Sinne meines Abschlussprojektes programmiere ich ein 2D-Platformer mit Microsoft Visual Studio 2010, C++, Der Bibliothek "Allegro 4". Das ganze natürlich objektorientiert.

    Ich habe folgendes Problem:

    mit dieser Methode:

    void Collision::LoadCollisionMap(const char *filename)
    {
       ifstream openfile(filename);
       if(openfile.is_open())
       {
         openfile >> mapSizeX >> mapSizeY;
         while (!openfile.eof())
         {
           openfile >> ColMapFile[loadCounterX][loadCounterY];
           loadCounterX++;
           if(loadCounterX >= mapSizeX)
           {
             loadCounterX = 0;
             loadCounterY ++;
           }
         }
         loadCounterX = loadCounterY = 0;
       }
    }
    

    lade ich meine Map:

    10 8
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 1 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 1 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0

    in das zweidimensionale Array "ColMapFile[loadCounterX][loadCounterY]"
    die ersten zwei Werte sollen inr mapSizeX und mapSizeY geladen werden.

    Das funktioniert auch, da ich genau die selbe Logik angewandt habe, um meine Map mittels Tiles zu gestalten.

    Nun zu dem Problem:

    mit folgender Methode:

    void Collision:: PlatformCollision(BITMAP *buffer, Player &player)
    {
       for (int i = 0; i < mapSizeX; i++)
       {
         for (int j = 0; j < mapSizeY; j++)
         {
    
           if(ColMapFile[I][j] != 0)
           {
             if(player.y+player.animation->h<j*BlockSize || player.x > i*BlockSize+BlockSize || player.y > j*BlockSize+BlockSize || player.x+player.animation->w < i* BlockSize)
             {
               player.Platform = false;
    
             }
             else
             {
               player.Platform = true;
    
             }
           }
         }
       } 
    }
    

    prüfe ich das o.g. Array auf einsen, wenn eine gefunden wird, soll die oben zusehende if-Abfrage durchgeführt werden usw.

    Auch das funktioniert soweit, ich habe aus Testzwecken erstmals ein einziges Tile, also eine einzige 1, geladen, die Kollisions-Erkennung funktionierte dann auch einwandfrei, aber sobald ich eine zweite eins in die Map packe, wird nur eine eins erkannt und auf Kollision überprüft. Es scheint mir so als würde sich mein Code immer auf die eins festlegen, welche am nächsten zum Spieler spawn ist.

    Syntaktischer oder logischer Fehler?

    schonmal vielen lieben Dank,
    mit freundlichen Grüßen,
    flohdieter


  • Mod

    das duerfte ein logik problem sein. bei deiner schleife vom letzten code snipple entscheidet quasi nur das letzte feld mit einer 1, ob player.Platform true oder false ist. du muesstest also entweder
    -entweder player.Platform noch zur bedingung hinzufuegen
    oder
    -sofort rausspringen wenn die entscheidung dass es collision gibt stattfindet

    wenn du eine bedingung aufstellst und darunter true/false zuweisungen hast, dann schreibst du quasi sowas wie

    if(true)
     foo=true;
    else
     foo=false;
    

    du kannst dir das vereinfachen indem du direkt zuweist.

    du solltest vielleicht auch player.Platform anfangs einen wert zuweisen, falls nie ein 1ser feld gefunden wird. sonst steht da undefiniertes drinnen vielleicht.



  • d.h. Ich soll aus der Schleife rausspringen, sobald eine Kollision stattfindet?
    EDIT: Tatsache ist, wenn ich auf den einen Block zusteuere und ihn dann berühre, wird player.platform = true, wenn ich mich wieder von ihm wegbewege, dann wird player.platform auch wieder false...



  • Habs geschafft, für alle anderen hier noch den Snipple den ich veränder hab :p

    void Collision:: PlatformCollision(BITMAP *buffer, Player &player)
    {
    player.Platform = false;
       for (int i = 0; i < mapSizeX; i++)
       {
         for (int j = 0; j < mapSizeY; j++)
         {
    
           if(ColMapFile[i][j] != 0)
           {
             if(!(player.y+player.animation->h<j*BlockSize || player.x > i*BlockSize+BlockSize || player.y > j*BlockSize+BlockSize || player.x+player.animation->w < i* BlockSize))
             {
               player.Platform = true;
    
             }
    
           }
         }
       }   
    }
    


  • flohdieter schrieb:

    Habs geschafft, für alle anderen hier noch den Snipple den ich veränder hab :p

    void Collision:: PlatformCollision(BITMAP *buffer, Player &player)
    {
    player.Platform = false;
       for (int i = 0; i < mapSizeX; i++)
       {
         for (int j = 0; j < mapSizeY; j++)
         {
           
           if(ColMapFile[i][j] != 0)
           {
             if(!(player.y+player.animation->h<j*BlockSize || player.x > i*BlockSize+BlockSize || player.y > j*BlockSize+BlockSize || player.x+player.animation->w < i* BlockSize))
             {
               player.Platform = true;
               
             }
    
           }
         }
       }   
    }
    

    Auch wenn ich mir den Code nicht ganz genau angesehen habe, wirkt er ein wenig auf mich, als würdest du prüfen ob der Spieler den aktuell zu prüfenden Block "nicht nicht berührt". Vielleicht ist es der Code-Verständlichkeit etwas förderlicher, wenn du das umformulierst 😃

    Ansonsten noch ein Tip (falls ich richtig verstanden habe, was du vorhast):
    Kann man sich die Schleife über alle Map-Felder nicht eventuell sparen, indem man die Player-Koordinaten auf die Map-Indizes abbildet, und dann dort direkt prüft, ob diese ungleich 0 sind?

    Also für das simple Beispiel eines punktförmigen Spielers so etwas wie

    player_i = player.x / BlockSize;
      player_j = player.y / BlockSize;
      player.Platform = (ColMapFile[player_i][player_j] != 0);
    

    Für eine "Player-Animation", bei der w und h <= BlockSize sind, sollte es reichen die 4 Eckpunkte dieser Animations-Bounding-Box zu prüfen ([x, y], [x + w, y], [x, y + h] und [x + w, y + h]).
    Bei größeren Player-Animationen benötigt man natürlich mehr Test-Punkte in BlockSize-Abständen:
    `[x, y], [x + BlockSize, y], [x + 2 * BlockSize, y], ..., [x + w, y]

    [x, y + BlockSize], [x + BlockSize, y + BlockSize], [x + 2 * BlockSize, y + BlockSize], ..., [x + w, y + BlockSize]

    ...

    [x, y + h], [x + BlockSize, y + h], ..., [x + w, y + h].`

    Auf diese Weise wird jeder Map-Block, der von der Player-Animation überlappt wird geprüft: [x, y] bis [x + w, y + h] decken die ganze Player-Animation ab und die Test-Punkte haben alle BlockSize -Abstände, dadurch ist der in jeder Richtung nächste Test-Punkt auch immer im nächsten Map-Block in der selben Richtung. Die Test-Punkte kann man z.B. in einer zweifach geschachtelten Schleife berechnen, die dann allerdings nur über die Box der Player-Animation läuft, und nicht über die gesamte Map.

    Das hätte dann den Vorteil, auch bei einer sehr großen Map noch effizent zu laufen (Konstante Laufzeit bezogen auf Map-Größe).
    Ich kann mir vorstellen, dass dieser Kollisionstest wahrscheinlich für jeden Frame einmal durchgeführt werden soll (~60fps?) und dann vielleicht auch nicht nur für den Spieler, sondern auch für andere Objekte (z.B. Gegner, Projektile, etc.). Es könnte sich also lohnen den Algorithmus wie vorgeschlagen zu verbessern... kannst es dir ja mal merken und wieder dran denken wenn es später an der Performance hapert, ist sicher erstmal wichtig das Projekt überhaupt fertig zu bekommen 😉

    Gruss,
    Finnegan



  • vielen Dank für die Antwort, finde deinen Vorschlag sehr gut, wenn ich noch Puffer haben sollte, werde ich Laufzeitoptimierungen vornehmen und deinen Aspekt mit einfließen lassen 🙂

    mit freundlichen Grüßen,
    flohdieter



  • flohdieter schrieb:

    vielen Dank für die Antwort, finde deinen Vorschlag sehr gut, wenn ich noch Puffer haben sollte, werde ich Laufzeitoptimierungen vornehmen und deinen Aspekt mit einfließen lassen 🙂

    Richtige Einstellung! Ich bin zwar auch ein Freund von elegantem und effizientem Code, bei Projekten mit Deadline habe ich allerdings auch schon so manche Unausprechlichkeiten produziert, die ich am liebsten wieder vergessen will 😉
    ... eigentlich ist Softwareentwicklung ja eine Kunst, aber im realen projektgetriebenen Einsatz artet es leider oft in grobschlächtige Fließbandarbeit aus 😞

    Finnegan


Anmelden zum Antworten