# Calculating the new direction after collision

Started by Floru, Aug 13 2001 11:39 PM

9 replies to this topic

###
#1
Members - Reputation: **124**

Posted 13 August 2001 - 11:39 PM

Hi!
I previously asked about collision detection with lines and circles and I got it work. Thanks for help! Now I''d like to know how should I calculate the new direction after a collision with line or circle? I have following variables for the objects:
double dMovX;
double dMovY;
I made it work so that if the object hits something when going to right I just multiply the dMovX with -1 so that it changes direction etc. But it doesn''t look realistic with lines.Could someone tell how to do this? At most I''m intrested in collision with circles and lines.
Floru

###
#2
Members - Reputation: **351**

Posted 14 August 2001 - 12:06 AM

The way a collision causes an object to change it''s motion is through an impulse. This causes an instantaneous change in momentum, with the change given by the size and magnitude of the impulse. For a single moving object you can ignore the mass and work with velocity rather than momentum.

In practice this means you need to:

1) work out the direction of the collision. e.g. for a circle/ball hitting a line/surface without friction it will be the perpendicular/normal direction.

2) work out the impact speed, the speed in the direction of the collision, i.e. the speed towards the line/surface.

3) work out the impulse. If there''s no rotation it will be simply between 1 and 2 times the impact speed, with 1 being no bounce and 2 being maximum bounce. It is in the direction as the normal/perpendicular, away from the edge/surface.

4) Add it to the object velocity to get the velocity after the collision.

This is for an object hitting a fixed edge/surface, in 2D or 3D, ignoring friciton and rotation. These can be added in to the above proceedure but they make the calculations a lot more complex, especially in 3D.

In practice this means you need to:

1) work out the direction of the collision. e.g. for a circle/ball hitting a line/surface without friction it will be the perpendicular/normal direction.

2) work out the impact speed, the speed in the direction of the collision, i.e. the speed towards the line/surface.

3) work out the impulse. If there''s no rotation it will be simply between 1 and 2 times the impact speed, with 1 being no bounce and 2 being maximum bounce. It is in the direction as the normal/perpendicular, away from the edge/surface.

4) Add it to the object velocity to get the velocity after the collision.

This is for an object hitting a fixed edge/surface, in 2D or 3D, ignoring friciton and rotation. These can be added in to the above proceedure but they make the calculations a lot more complex, especially in 3D.

###
#3
Members - Reputation: **134**

Posted 14 August 2001 - 12:09 AM

You need to find the surface normal, which is pretty much a line at 90 degrees to the point at which you are colliding with. Then, the angle between the ball''s original vector and the ball''s new vector are equal, but on opposite sides of the normal.

*Trying is the first step towards failure.*###
#4
Members - Reputation: **124**

Posted 14 August 2001 - 12:14 AM

Thanks! But could you please give some kind of example because I''m not so good at maths? If lines are described with two points (x1,y1 and x2,y2) and I know when for example that a ball hits the line. And I know what is ball''s direction. And there is no need to calculate friction and rotation.

Floru

###
#5
Anonymous Poster_Anonymous Poster_*
Guests - Reputation:

Posted 15 August 2001 - 06:18 PM

One way is to reflect the component of the ball''s velocity in the direction of the normal to the line.

I.e:

Using (dMovX, dMovY) as the ball''s initial velocity, and points (x1,y1) and (x2,y2) as defining the line.

; Find line normal to line (as (nx, ny))

nx = y2 - y1 ; Find direction

ny = -(x2 - x1)

length = SquareRoot (nx*nx + ny*ny)

nx = nx / length ; Normalise (scale to length 1)

ny = ny / length

; Find component of ball''s velocity in direction of normal

dotProd = dMovX * nx + dMovY * ny

componentX = nx * dotProd

componentY = ny * dotProd

; Reflect this component of the ball''s velocity

newDMovX = dMovX - 2 * componentX

newDMovY = dMovY - 2 * componentY

This is off the top of my head, and hasn''t been tested.

I suggest looking up normals, vector normalization and dot product (or simply 2D vector arithmetic in general).

I.e:

Using (dMovX, dMovY) as the ball''s initial velocity, and points (x1,y1) and (x2,y2) as defining the line.

; Find line normal to line (as (nx, ny))

nx = y2 - y1 ; Find direction

ny = -(x2 - x1)

length = SquareRoot (nx*nx + ny*ny)

nx = nx / length ; Normalise (scale to length 1)

ny = ny / length

; Find component of ball''s velocity in direction of normal

dotProd = dMovX * nx + dMovY * ny

componentX = nx * dotProd

componentY = ny * dotProd

; Reflect this component of the ball''s velocity

newDMovX = dMovX - 2 * componentX

newDMovY = dMovY - 2 * componentY

This is off the top of my head, and hasn''t been tested.

I suggest looking up normals, vector normalization and dot product (or simply 2D vector arithmetic in general).

###
#6
Members - Reputation: **161**

Posted 15 August 2001 - 07:46 PM

I have some code for calculation the new directions.

// vDelta = Distance between the two actors:

float fContactAngle,

fXClosingVelocity, fYClosingVelocity, fResVelocity, fXContactVelocity,

fYContactVelocity, fClosingAngle, fMassFactor, fMass1, fMass2, fAdiff, fPortion;

// Determine the angle of contact

if(vDelta.fX != 0)

{

fContactAngle = (float) atan(vDelta.fY/vDelta.fX);

if(fContactAngle < 0)

fContactAngle = -fContactAngle;

}

else

fContactAngle = (float) PId2;

// To determine how the objects will rebound off of each

// other, we are not concerned with the speed of each

// object, but rather the relative, or closing, speeds

// of the two objects.

fXClosingVelocity = pActor2->vWorldVelocity.fX-pActor1->vWorldVelocity.fX;

fYClosingVelocity = pActor2->vWorldVelocity.fY-pActor1->vWorldVelocity.fY;

// Calculate the x & y speed in the direction of contact

fResVelocity = ASFastSqrt(fXClosingVelocity*fXClosingVelocity+fYClosingVelocity*fYClosingVelocity);

fXContactVelocity = (float) cos(fContactAngle)*fResVelocity;

fYContactVelocity = (float) sin(fContactAngle)*fResVelocity;

// Now, determine the closing angle.

if(fXClosingVelocity != 0)

{

fClosingAngle = (float) atan(fYClosingVelocity/fXClosingVelocity);

if(fClosingAngle < 0)

fClosingAngle = -fClosingAngle;

}

else

fClosingAngle = (float) PId2;

// Hmmmm...

// With equal masses,

// the max rebound speed is 1/2 the closing speed.

// With unequal masses,

// the max rebound speed is the closing speed.

// Okay then,

// Normalize the two masses to be: mass1 + mass2 = 2.0

fMassFactor = 2/(pActor1->fMass+pActor2->fMass);

fMass1 = pActor1->fMass*fMassFactor;

fMass2 = pActor2->fMass*fMassFactor;

// Quadrant-specific stuff

if(pActor1->vWorldPos.fX > pActor2->vWorldPos.fX)

{

if(pActor1->vWorldPos.fY < pActor2->vWorldPos.fY)

{

// pActor1 is contacting upper right quadrant of pActor2

if(fYClosingVelocity < 0)

{

if(fXClosingVelocity < 0)

fAdiff = (float) PI-fContactAngle-fClosingAngle;

else

fAdiff = fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass1;

}

else

{

// pActor1 is contacting lower right quadrant of pActor2

if(fYClosingVelocity > 0)

{

if(fXClosingVelocity < 0)

fAdiff = (float) PI-fContactAngle-fClosingAngle;

else

fAdiff = fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass1;

}

}

else

{

if(pActor1->vWorldPos.fY < pActor2->vWorldPos.fY)

{

// pActor1 is contacting upper left quadrant of pActor2

if(fYClosingVelocity < 0)

{

if(fXClosingVelocity < 0)

fAdiff = fContactAngle-fClosingAngle;

else

fAdiff = (float) PI-fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass1;

}

else

{

// pActor1 is contacting lower left quadrant of pActor2

if(fYClosingVelocity > 0)

{

if(fXClosingVelocity < 0)

fAdiff = fContactAngle-fClosingAngle;

else

fAdiff = (float) PI-fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass1;

}

}

At the moment it only works for 2D. Could someone help me to add the third dimension??

// vDelta = Distance between the two actors:

float fContactAngle,

fXClosingVelocity, fYClosingVelocity, fResVelocity, fXContactVelocity,

fYContactVelocity, fClosingAngle, fMassFactor, fMass1, fMass2, fAdiff, fPortion;

// Determine the angle of contact

if(vDelta.fX != 0)

{

fContactAngle = (float) atan(vDelta.fY/vDelta.fX);

if(fContactAngle < 0)

fContactAngle = -fContactAngle;

}

else

fContactAngle = (float) PId2;

// To determine how the objects will rebound off of each

// other, we are not concerned with the speed of each

// object, but rather the relative, or closing, speeds

// of the two objects.

fXClosingVelocity = pActor2->vWorldVelocity.fX-pActor1->vWorldVelocity.fX;

fYClosingVelocity = pActor2->vWorldVelocity.fY-pActor1->vWorldVelocity.fY;

// Calculate the x & y speed in the direction of contact

fResVelocity = ASFastSqrt(fXClosingVelocity*fXClosingVelocity+fYClosingVelocity*fYClosingVelocity);

fXContactVelocity = (float) cos(fContactAngle)*fResVelocity;

fYContactVelocity = (float) sin(fContactAngle)*fResVelocity;

// Now, determine the closing angle.

if(fXClosingVelocity != 0)

{

fClosingAngle = (float) atan(fYClosingVelocity/fXClosingVelocity);

if(fClosingAngle < 0)

fClosingAngle = -fClosingAngle;

}

else

fClosingAngle = (float) PId2;

// Hmmmm...

// With equal masses,

// the max rebound speed is 1/2 the closing speed.

// With unequal masses,

// the max rebound speed is the closing speed.

// Okay then,

// Normalize the two masses to be: mass1 + mass2 = 2.0

fMassFactor = 2/(pActor1->fMass+pActor2->fMass);

fMass1 = pActor1->fMass*fMassFactor;

fMass2 = pActor2->fMass*fMassFactor;

// Quadrant-specific stuff

if(pActor1->vWorldPos.fX > pActor2->vWorldPos.fX)

{

if(pActor1->vWorldPos.fY < pActor2->vWorldPos.fY)

{

// pActor1 is contacting upper right quadrant of pActor2

if(fYClosingVelocity < 0)

{

if(fXClosingVelocity < 0)

fAdiff = (float) PI-fContactAngle-fClosingAngle;

else

fAdiff = fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass1;

}

else

{

// pActor1 is contacting lower right quadrant of pActor2

if(fYClosingVelocity > 0)

{

if(fXClosingVelocity < 0)

fAdiff = (float) PI-fContactAngle-fClosingAngle;

else

fAdiff = fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass1;

}

}

else

{

if(pActor1->vWorldPos.fY < pActor2->vWorldPos.fY)

{

// pActor1 is contacting upper left quadrant of pActor2

if(fYClosingVelocity < 0)

{

if(fXClosingVelocity < 0)

fAdiff = fContactAngle-fClosingAngle;

else

fAdiff = (float) PI-fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass1;

}

else

{

// pActor1 is contacting lower left quadrant of pActor2

if(fYClosingVelocity > 0)

{

if(fXClosingVelocity < 0)

fAdiff = fContactAngle-fClosingAngle;

else

fAdiff = (float) PI-fContactAngle-fClosingAngle;

}

else

fAdiff = fContactAngle+fClosingAngle;

fPortion = (float) cos(fAdiff);

pActor1->vWorldVelocity.fX -= fXContactVelocity*fPortion*fMass2;

pActor1->vWorldVelocity.fY += fYContactVelocity*fPortion*fMass2;

pActor2->vWorldVelocity.fX += fXContactVelocity*fPortion*fMass1;

pActor2->vWorldVelocity.fY -= fYContactVelocity*fPortion*fMass1;

}

}

At the moment it only works for 2D. Could someone help me to add the third dimension??

###
#7
Members - Reputation: **122**

Posted 16 August 2001 - 02:33 AM

A realy simple technique,

which looks pretty good is to swap the velocity vectors of the colliding bodies.

//store the velocity of the first body in a seperate variable

velocity_body1 = intermediate_storage;

//set velocity body 1 = velocity of body 2

velocity_body1 = velocity_body2;

//set velocity of body 2 = velocity body 1 original value

velocity_body2 = intermediate_storage;

and you''ve swapped there velocity vectors!!

its not physically perfect, but its simple ao give it a shot

which looks pretty good is to swap the velocity vectors of the colliding bodies.

//store the velocity of the first body in a seperate variable

velocity_body1 = intermediate_storage;

//set velocity body 1 = velocity of body 2

velocity_body1 = velocity_body2;

//set velocity of body 2 = velocity body 1 original value

velocity_body2 = intermediate_storage;

and you''ve swapped there velocity vectors!!

its not physically perfect, but its simple ao give it a shot

###
#8
Members - Reputation: **124**

Posted 27 August 2001 - 01:58 AM

Thanks for help!

I have now a working collision detection and I also know how to calculate new direction. I have a problem and I don''t know how common it is. I''ve seen demos where balls bounce in box or in area but sometimes the balls don''t work correctly. For example they stick into walls or go through them or bounce funny. I have same kind on problems. Rarely but I''d like to my system work perfectly. I also have a object which is controlled by the user (like in pinball game the flipper). But usually the balls go through. So how can I make my system perfect? Is this a common problem?

Floru

###
#9
Members - Reputation: **122**

Posted 27 August 2001 - 02:31 AM

I think the sticking-together problem is caused by the fact, that you only do coll-detection in finite time steps, i.e. you can only detect collisions if objects stick together already.

(Also if sometimes only a little bit)

I''ve had and still have some problems with that penetration problem, but you can solve it.

Try to find out "how much" the one object is inside the other one and then move your object away along the collision normal by your measured penetration. This will create a non-penetration state of the bodies.

A little bit hard to explain, but perhaps have a look into the FastCar source code.

http://www.oxforddynamics.co.uk

Ciao!

Roppi

http://www.moonbyte.de

(Also if sometimes only a little bit)

I''ve had and still have some problems with that penetration problem, but you can solve it.

Try to find out "how much" the one object is inside the other one and then move your object away along the collision normal by your measured penetration. This will create a non-penetration state of the bodies.

A little bit hard to explain, but perhaps have a look into the FastCar source code.

http://www.oxforddynamics.co.uk

Ciao!

Roppi

http://www.moonbyte.de

###
#10
Members - Reputation: **124**

Posted 28 August 2001 - 04:11 AM

Thanks for the link but the documentation was quite complicated although the demo was quite impressive. Is there any simpler sample or documentation? I think some info how flipper games are made could also be useful. What about the sticking problem?

Floru