2
\$\begingroup\$

I've been wondering about simple collision in C++ for a while now, and I've tried to make up my own code.

The code posted below works when walls are placed in a perfect rectangle around the player. However, once you randomly place them in the array, as in the code, weird inconsistencies seem to occur. Sometimes the player collides, sometimes the player doesn't.

If anyone would be as kind as to point out what the error is, I'd be much appreciated. In a clear step by step manner, if you would: I'm very new to this and chances are small that I can make the logical steps of thought necessary to successfully implement your suggestions.

#include "string.h" 
#include "surface.h" 
#include "stdlib.h" 
#include "template.h" 
#include "game.h" 

using namespace Tmpl8; 

int pacmanPosX = 32; 
int pacmanPosY = 32; 
int indexX; 
int indexY; 

bool colR; 
bool colL; 
bool colU; 
bool colD; 


Sprite pacman (new Surface( "assets/pacmanLeft.png"), 1);  

Surface* tileSet[3];  

int levelMap[8][8] =  
{{1,1,1,1,1,1,1,1}, 
 {1,0,0,0,0,0,0,1}, 
 {1,0,0,0,0,1,0,1}, 
 {1,0,0,1,0,0,0,1}, 
 {1,0,0,0,1,0,0,1},  
 {1,1,0,0,1,1,1,1}, 
 {1,0,0,0,0,0,1,1}, 
 {1,1,1,1,1,1,1,1}}; 

void Game::Init()  
{  
   // put your initialization code here; will be executed once  
   tileSet[0] = new Surface( "assets/pacmanFloor.png" ); // Sets up data for tileSet  
   tileSet[1] = new Surface( "assets/pacmanWall.bmp" );  
   tileSet[2] = new Surface( "assets/pacmanCookie.png" ); 

}  

void Game::Tick( float a_DT )  
{  
    m_Screen->Clear( 0 );  

     for (indexX = 0; indexX < 8; indexX++) 
    { 
        for (indexY = 0; indexY < 8; indexY++) 
        { 
            int tile = levelMap[indexY][indexX]; 
            tileSet[tile]->CopyTo( m_Screen, indexX * 32, indexY * 32 ); 
        } 
    } 



    pacman.Draw(pacmanPosX, pacmanPosY, m_Screen); 


    //Defining collision requirements for walls// 

    if(levelMap[(pacmanPosX+32)/32][pacmanPosY/32] == 1) //Right collision 
    { 
        colR = true; 
    }else colR = false; 

    if(levelMap[(pacmanPosX-2)/32][pacmanPosY/32] == 1) //Left collision 
    { 
        colL = true; 
    }else colL = false; 

    if(levelMap[pacmanPosX/32][(pacmanPosY-2)/32] == 1) //Upward collision 
    { 
        colU = true; 
    }else colU = false; 

    if(levelMap[pacmanPosX/32][(pacmanPosY+32)/32] == 1) //Downward collision 
    { 
        colD = true; 
    }else colD = false; 


    //Setting Up Controls// 

    if (GetAsyncKeyState(VK_RIGHT) && colR == false){pacmanPosX += 2;} 
    if (GetAsyncKeyState(VK_LEFT) && colL == false){pacmanPosX -= 2;} 
    if (GetAsyncKeyState(VK_UP) && colU == false){pacmanPosY -= 2;} 
    if (GetAsyncKeyState(VK_DOWN) && colD == false){pacmanPosY += 2;} 

    Sleep( 10 );  
}  
\$\endgroup\$

closed as off-topic by Jamal May 3 '14 at 1:19

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions containing broken code or asking for advice about code not yet written are off-topic, as the code is not ready for review. Such questions may be suitable for Stack Overflow or Programmers. After the question has been edited to contain working code, we will consider reopening it." – Jamal
If this question can be reworded to fit the rules in the help center, please edit the question.

1
\$\begingroup\$

Bug in your code

Here you are accessing the levelMap by 'Y' then 'X'

        int tile = levelMap[indexY][indexX]; 

In all other places you are accessing the levelMap by 'X' then 'Y'

if(levelMap[(pacmanPosX+32)/32][pacmanPosY/32] == 1) //Right collision 

So it looks like you are running around a maze that has been printed incorrectly on the screen. and thus it looks like collision detection is not working (it probably is) but the detection does not match the visual cues.

Reviewing your code:

These expressions

if(levelMap[(pacmanPosX+32)/32][pacmanPosY/32] == 1) //Right collision 
{ 
    colR = true; 
}else colR = false; 

Can be simplifies too:

colR = (levelMap[(pacmanPosX+32)/32][pacmanPosY/32] == 1); //Right collision 

While these

if (GetAsyncKeyState(VK_RIGHT) && colR == false){pacmanPosX += 2;} 

can be simplified to:

if (GetAsyncKeyState(VK_RIGHT) && !colR)
{    pacmanPosX += 2;
} 

Putting the statement on the same line as the test makes it harder to debug (when stepping through with the debugger) as you can not see when then the statements are executed. By putting it on its own line stepping through with the de-bugger does become easier as you will see it skip the statement when the condition fails.

Global state is the easy way to do things.

int pacmanPosX = 32; 
int pacmanPosY = 32; 
int indexX; 
int indexY; 

bool colR; 
bool colL; 
bool colU; 
bool colD; 

But in the long run makes your code more brittle and harder to update. Pass things as parameters to make it easier to maintain and extend your code (or create an object that wraps the information).

Also it looks like some of these properties would be better held in arrays rather than individual variables.

Edit base on comments:

This only detects a collision if the top right corner hits a wall.

colR = (levelMap[(pacmanPosX+32)/32][pacmanPosY/32] == 1); //Right collision 

Change too:

colR =    (levelMap[(pacmanPosX+33)/32][(pacmanPosY+00)/32] == 1)  // Top right
       || (levelMap[(pacmanPosY+33)/32][(pacmanPosY+31)/32] == 1); // bottom right

// Use +33 because moving left you use -2
// When you move right +32 puts you in the first pixel of the next block
//                     +33 puts you in the second pixel just like move left detection

// Use +31 on the Y access because this is still inside the current sprite.

Note If you have a hole in the wall that is exactly one block wide you need to hit it dead center to pass through the gap. To give you some wiggle room you can move the corners in slightly.

colR =    (levelMap[(pacmanPosX+33)/32][(pacmanPosY+02)/32] == 1)  // Top right
       || (levelMap[(pacmanPosY+33)/32][(pacmanPosY+29)/32] == 1); // bottom right

Here you do not need to hit the whole in the wall dead center and you get some wiggle room passing through.

\$\endgroup\$
  • \$\begingroup\$ Thanks a lot for your useful feed-back. How silly of me to swap the X and the Y! :o It works a lot better now! Something still bothers me, though. Hopefully I can explain it... When you push the 32x32 character against a 32x32 wall which has no wall above or below it, it collides only when the player hits it in the middle. When you rub against the wall and move down, the collision stays okay. When you move up while pushing against the wall, though, you slide through it. This problem goes for every block that isn't surrounded by other blocks. Uhm... Could you follow that? \$\endgroup\$ – Turbosheep Oct 17 '11 at 13:55
  • \$\begingroup\$ That's because you are only looking for a collision in the top right or top left corner. In your collision code change it so that when you move right check for a collision in the top and bottom right corners. \$\endgroup\$ – Martin York Oct 17 '11 at 14:05
  • \$\begingroup\$ Hey, I did it, sort of. I used this: if((levelMap[(pacmanPosX+34)/32][pacmanPosY/32] == 1) || (levelMap[(pacmanPosX+34)/32][(pacmanPosY+32)/32] == 1)) //Right collision { colR = true; }else colR = false; You can see I used x+34 instead of x+32. That's because movement gets stuck somehow. This way, though, the character doesn't fit in 'alleys', like in pacman. Suggestions? :) \$\endgroup\$ – Turbosheep Oct 17 '11 at 14:43
  • \$\begingroup\$ Sorry, I didn't saw your edit. Thanks again for all your help! \$\endgroup\$ – Turbosheep Oct 17 '11 at 14:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.