0
votes

I'm developing a physics-engine in Python (I know, it's mostly for "learning") and have it come along quite well. I've been following a lot of online documentation on the matter, and this series of articles was especially helpful: How to Create a Custom Physics Engine.

I've got materials working, that act as a class that holds restitution and density of a body. What happens next is that bodies that have a low density and a high restitution (e.g. 0.3, 0.8 respectively), they bounce off each other with such high velocity it doesn't take long for them to shoot off into the "void".

I have a test scene set up with 4 walls that encloses the bodies (ball). When they go at such high velocity, a collision is never registered as the next physics simulation happens after they already passed the "walls".

What I'm really interested in is why they get such high velocity when bouncing off each other, while I don't believe they should. In my example recording, I have a small ball (red) which is controller by the player (me). It has a density of 0.3 and a restitution of 0.8, with a volume of 100 (r * r or 10 * 10), its mass is 30 KGs (100 * 0.3). Another larger ball (green) is not controller by the player, but can be bounced by hitting it with the red ball. It has the same material, therefor has the same density and restitution. However, its mass is higher, because of the higher radius (30). The actual mass of the green ball is 270 (900 * 0.8).

In the recording it's noticeable the balls bounce off of each other in an unnatural way. Shouldn't the small ball get inflicted a higher impulse than the big ball? I tried to work around this by swapping the mass values used when calculating the impulse. So the small ball's impulse is calculated using the mass of the big ball, and vice-versa. You'd think this is due to the bigger ball having a higher mass, but to me they bounce off of each other rather quickly?

Is this normal behaviour for the density and restitution I have on both of these balls? I will share code if needed, but as this text is already getting a bit long, I won't waste space if not needed. My code is almost exactly following what is covered in the articles, only "converted" into Python code.

Bouncing Balls (Red = Player)Bouncing Balls (Red = Player)

Code on calculating impulse

# Relative velocity
rv = b.velocity - a.velocity

# Velocity along the normal direction
vel_along_normal = rv.dot(contact_normal)

# EPSILON (use lowest restitution value)
e = min(a.restitution, b.restitution)

# Calculate j, which will be used to get the impulse
j = -(1 + e) * vel_along_normal
j /= a_inv_mass + b_inv_mass

impulse = contact_normal * j

# Using ratio to have lighter bodies bounce with a higher impulse than heavier bodies
mass_sum = a_mass + b_mass
ratio = (b_mass / mass_sum)
a.rigid_body.velocity -= (impulse * ratio) * dt

ratio = (a_mass / mass_sum)
b.rigid_body.velocity += (impulse * ratio) * dt
2
Restitution e is a value 0 to 1. Assuming e is a positive value after e = min(a.restitution, b.restitution) then the next line j = -(1 + e) * vel_along_normalis incorrect as it makes (1 + e) > 1 which will add energy to the collision (which should never happen in the real world). As it is unclear how you define e I would assume the standard definition and say that the line calculating j should be j = -e * vel_along_normalBlindman67
When changing the code like this, the ball just smashes against a collider and does nothing. A restitution value is 0 to 1, as you stated. The ball has a e of 0.8, so it should bounce quite high. But it does not.Qlii256
I managed to get it working, I was not multiplying by dt. They still behave weirdly though. With a stationary object, it seems to work fine, but as soon as both are moving, they suddenly start to go super fast.Qlii256
It seems like the heavier the body, the faster it goes but only against stationary objects. I believe it's because stationary objects have a mass of 0 (infinite mass). So j is not properly calculated because of the 0 inverse_mass. What value should I use if one of the bodies has infinite mass?Qlii256
Now that I think of it, when a wall has infinite mass, isn't it supposed to send back a high impulse just like we see happening? So, I guess the reason why there's kinematics is to solve this issue. So you can still have a mass, but keep the body from being affected by forces.Qlii256

2 Answers

0
votes

It seems that there is a sign problem with velocity difference after collision. Rather than slowing red ball down it speeds it up.

0
votes

The whole function does not look right to me, and as it does not seam to work as expected there is something wrong. Rather than try and workout what is wrong (too many unknowns in your code, dt, a_inv_mass, b_inv_mass, contact_normal) the following snippet will do the collision math for you

// A and B are balls at time of collision
mA = A.mass
mB = B.mass
mm = mA + mB

// vector between balls
x = A.x - B.x
y = A.y - B.y  

// restitution
e1 = A.restitution
e2 = B.restitution

// distance sqr between balls
d = x * x + y * y;

// v velocities
uAB = (A.v.x * x + A.v.y * y) / d  // normal component force A into B
u2  = (A.v.y * x - A.v.x * y) / d  // tangent component force A

uBA = (B.v.x * x + B.v.y * y) / d  // normal component force B into A
u4  = (B.v.y * x - B.v.x * y) / d  // tangent component force B

// Force from B into A along normal, scaled for mass ratio and restitution
uA = ((mA - mB) / mm * uAB + (2 * mB) / mm * uBA) * e1

// Force from A into B along normal, scaled for mass ratio and restitution
uB = ((mB - mA) / mm * uBA + (2 * mA) / mm * uAB) * e2

// new velocities 
A.v.x = x * uA - y * u2
A.v.y = y * uA + x * u2

B.v.x = x * uB - y * u4
B.v.y = y * uB + x * u4

Note that dt is ignored (assuming dt to be delta time)

Note both objects must have some mass. This will not work if one or both have 0 mass.