Page 1 of 1

Collision Testing

Posted: Mon Jun 28, 2010 2:23 pm
by adikid89
I'm having some problems with collision detection. I used the one I found on this forum, and it seems to pretty much work, although I didn't really understand how it works... Here is some code:

Code: Select all

//// INSIDE Player::handle_collision()
      // ...   //

				//Determine overlap for each axis
				int xDist = abs((rect1.x + rect1.w / 2) - (rect2.x + rect2.w / 2)); 
				int yDist = abs((rect1.y + rect2.h / 2) - (rect2.y + rect2.h / 2));
					
				Singleton<Log>::getInstance()->print("Rect2: x(%d), y(%d)", rect2.x, rect2.y);

				//minimal amount of distance that the two can be apart and not collide
				int xMinDist = rect1.w/2 + rect2.w;
				int yMinDist = rect1.h/2 + rect2.h;

				//neither axis is colliding
				if(xDist >= xMinDist || yDist >= yMinDist) {
					continue; 
				}
				int xOverlap = xDist - xMinDist;
				int yOverlap = yDist - yMinDist;
	
				collidedTimes++;

				onObject = false;

				if(abs(xOverlap) < abs(yOverlap)) { 
             //move the player by the amount of the overlap
					if(rect2.x < rect1.x) {
						x += std::abs(xOverlap);
					} else {
						x -= std::abs(xOverlap);
					}
				} else { 
					if(rect2.y < rect1.y){
						y += std::abs(yOverlap);
						onObject = true;
					}
					else { 			
						y -= std::abs(yOverlap);
					}
				}
			}
		}
The problem is that when a collision is detected... the player keeps going up and down like mad when it's a y axis collision and keeps going left-right when it's a x axis collision... I've been trying to fix that for a couple of days, and I got no idea how...
Here some more pieces of code that might help:

Code: Select all

void Player::update() 
{
     //only add gravity if he's not on objects.. this is pretty dumb.. must modify it
	if(!onObject) Singleton<Physics>::getInstance()->apply_gravity(this);	
	update_collision();  //move the collision box to match players coords
	handle_collision();  //handles the collision
	move();                //moves players coords based on the velocities
	handle_input();	   //handle input and updates velocities
	draw();                //renders the player sprite on screen
}
Any idea on where I failed? Also just to mention I didn't understand the algorithm :oops: ... maybe someone could spare some time to explain ? :bow:
P.S. rect1 belongs to the player, and rect2 belongs to the other entity

Re: Collision Testing

Posted: Tue Jun 29, 2010 2:32 pm
by MrDeathNote
Is your x and y in the centre and not the top left?? This sort of 'vibrating' collision normally occurs when you update the centre of the object during every collision check. It could be that your doing your updates in the wrong order, this could cause it. Your update should really be :-

Code: Select all

void Player::update()
{
   handle_input();      //handle input and updates velocities
   move();                //moves players coords based on the velocities
     //only add gravity if he's not on objects.. this is pretty dumb.. must modify it
   if(!onObject) Singleton<Physics>::getInstance()->apply_gravity(this);   
   update_collision();  //move the collision box to match players coords
   handle_collision();  //handles the collision 
   draw();                //renders the player sprite on screen
}
What your doing is checking for collisions then updating the player position, so your always one frame behind.

Re: Collision Testing

Posted: Wed Jun 30, 2010 5:59 am
by adikid89
MrDeathNote wrote:Is your x and y in the centre and not the top left?? This sort of 'vibrating' collision normally occurs when you update the centre of the object during every collision check. For example if I had a list of objects to check for collisions with and I was calculating the centre position before each collision check then I would get this vibrating effect. What you should really do is calculate the centre before the collision checks then do all the check using the same centre. I'm not sure if this is what your doing, it could be that your doing your updates in the wrong order, this could cause it. Your update should really be :-
I didn't really understand what you said, and no, the x and y coords are in the top-left corner of the sprite.. why? It shouldn't be there?
Also I tried changing the order in pretty much every way imaginable.. still vibrates.

Re: Collision Testing

Posted: Wed Jun 30, 2010 9:10 am
by MrDeathNote
For this type of collision the position i.e. the x and y should be in the center of the sprite, this is just a fact of the vector maths which your using in this method. The drawing position WILL be in the top left corner but not the position used in this method. I'll post an example to show you :

Code: Select all

            //Update the player
            player.Update();

            //Reset collision dist
            collisionDist.X = 0;
            collisionDist.Y = 0;

            //Check for collisions
            for (int i = 0; i < map.collisionRects.Count(); i++)
            {
                if(IsColliding(player.collisionRect, map.collisionRects[i]))
                {
                    //If there are multiple collision make sure we only react to the most severe
                    if (normal.X > collisionDist.X)
                        collisionDist.X = normal.X;
                    if (normal.Y > collisionDist.Y)
                        collisionDist.Y = normal.Y;
                }
            }

            //Update the players position
            player.position.X += collisionDist.X;
            player.position.Y += collisionDist.Y;
This is the update for the game.

Code: Select all

        bool IsColliding(Rectangle body1, Rectangle body2)
        {
            //Reset the normal vector
            normal.X = 0;
            normal.Y = 0;

            //Get the centre of each body
            Vector2 body1Centre = new Vector2(body1.X + (body1.Width / 2), body1.Y + (body1.Height / 2));
            Vector2 body2Centre = new Vector2(body2.X + (body2.Width / 2), body2.Y + (body2.Height / 2));

            //Declare 2 local vectors
            Vector2 distance, absDistance;

            //xMag and yMag represent the magnitudes of the x and y components of the normal vector
            float xMag, yMag;

            //Calculate the difference in position of the two bodies
            distance = body1Centre - body2Centre;

            //Get the combined half heights/widths of the rects
            float xAdd = ((body1.Width) + (body2.Width)) / 2.0f;
            float yAdd = ((body1.Height) + (body2.Height)) / 2.0f;

            //Calculate absDistance, according to distance
            absDistance.X = (distance.X < 0) ? -distance.X : distance.X;
            absDistance.Y = (distance.Y< 0) ? -distance.Y : distance.Y;

            //Check if there is a collision
            if (!((absDistance.X < xAdd) && (absDistance.Y < yAdd)))
                return false;

            //The magnitude of the normal vector is determined by the overlap in the rectangles.
            xMag = xAdd - absDistance.X;
            yMag = yAdd - absDistance.Y;

            //Only adjust the normal vector in the direction of the least significant overlap.
            if (xMag < yMag)
                normal.X = (distance.X > 0) ? xMag : -xMag;
            else
                normal.Y = (distance.Y > 0) ? yMag : -yMag;

            //There was a collision, return true
            return true;
        }
This is the actual collision code. normal and collisionDist are Vector2's. This way your not actually moving the position just finding the center then detecting collisions and resolving the most severe.

Re: Collision Testing

Posted: Wed Jun 30, 2010 11:49 am
by adikid89
Thanks :bow: !
I changed the coords to the middle of the sprite and it works now. I just need to tweak the collision boxes a little or something because i'm getting some strange bugs. Debugging skills... don't let me down again...

Re: Collision Testing

Posted: Fri Jul 02, 2010 3:48 am
by MrDeathNote
adikid89 wrote:Thanks :bow: !
I changed the coords to the middle of the sprite and it works now. I just need to tweak the collision boxes a little or something because i'm getting some strange bugs. Debugging skills... don't let me down again...
No prob man, glad i could help :)

Re: Collision Testing

Posted: Tue Jul 13, 2010 12:05 pm
by adikid89
I was having some more problems with collision testing, and since I haven't had internet connection for a week I had to fix it on my own. What I did is probably a hack, but it works! I'm going to show the problem and how I fixed it in order to get some feedback on how I could have done it easier and better :).
Pictures better show the idea:
Image
The problem was that the player couldn't move left, because gravity was pulling the player down by 10, and then when moving left with a velocity of 10, so when moving left, the player would have always collided with the object on the left, and the collision would have been resolved by pushing the player back 10 pixels, then pushing the player up by 10 pixels, as in the picture.

This was my first fix:
I used a onObject flag, so that whenever the player was on a object, gravity would no longer apply :) Something like this:

Code: Select all

if(!onObject) 
    Singleton<Physics>::getInstance()->apply_gravity(this);   
The problems was semi-solved. The player could move to left, but not always. I knew what was the problem but I didn't know how to solve it, so I discarded it, saying I'll fix it later.
It went like this:
Player is not on a object, so gravity will apply, pulling him down by 10... he then collides with an object, that will push him back by 10 and set the onObject flag to true. The problem was that, if at one time the player was on a onObject the next time it wouldn't be, because no gravity was applied to the player, so next time, no penetration was made and thus no collision was detected(which means next time the player wouldn't be onObject, and thus it may collide with a left object). Maybe a picture will better help ?
Image
So you can see that the player gets, in some cases, pushed back.
The way I fixed that, at least for now, is something like this:

Code: Select all

	//special pleading... this is bad.. need a better fix
	if(onObject) {
	//assume not on boxes				
	onObject = false; 
        /* Test collisions */
        if a collision is detected
          if it's a "player is on the left or right" type collision
             if player is above the current object 
                then do nothing;
        } else {
	//assume not on boxes				
	onObject = false; 
        /* Test collisions */
       }
Well this works pretty well, despite the large amount of code I had to duplicate, in order to make it work(which is why I call it a hack, it should be this difficult and ridiculous to test collisions).
I would love to hear a better .. fix, to this problem :). Also I'm having some other problems, when it's a "high-speed collision" at least I think that's the problem. Basically because the penetration is so big, it's considered a left or right type collision and the player is pushed in the wrong direction, it's not pushing him down. How can I fix that ?
P.S. : Sorry for the long post, but it's meant to be easy to understand :) . I hope you like the pictures :P took me a while to make.