nphysics icon indicating copy to clipboard operation
nphysics copied to clipboard

Some issues in collision resolving.

Open n0uk opened this issue 6 years ago • 6 comments

Hi!

Characters in my game presented in physics world as a Ball, I call rigidbody.set_linear_velocity() every frame, to control them. And sometimes characters can pass between 2 cuboid walls like this:

Image

For example, there is 2 cuboids, on (-0.5, 0) and (0.5, 0), timestep 1/10 and ball with radius 0.3 on position (0.0, 1.2), setting it linear velocity after every step to (0, -5.0):

Ball: Translation { vector: Matrix { data: [0.0, 1.2] } } Ball: Translation { vector: Matrix { data: [0.0, 0.70000005] } } // Collision happens on next frame Ball: Translation { vector: Matrix { data: [0.0, 0.95000005] } } // Collision happens and resolved Ball: Translation { vector: Matrix { data: [0.0, 0.45000005] } } // Going through cuboids Ball: Translation { vector: Matrix { data: [0.0, -0.049999952] } } // Going through cuboids Ball: Translation { vector: Matrix { data: [0.0, -0.54999995] } } // Going through cuboids Ball: Translation { vector: Matrix { data: [0.0, -1.05] } } // Finally passing through Ball: Translation { vector: Matrix { data: [0.0, -1.55] } }

If i change a start position of a ball slightly to (0.0, 1.3) - it can't pass through:

Ball: Translation { vector: Matrix { data: [0.0, 1.3] } } Ball: Translation { vector: Matrix { data: [0.0, 0.79999995] } } Ball: Translation { vector: Matrix { data: [0.0, 1.05] } } Ball: Translation { vector: Matrix { data: [0.0, 0.54999995] } } Ball: Translation { vector: Matrix { data: [0.0, 0.81401926] } } Ball: Translation { vector: Matrix { data: [0.0, 1.0640192] } }

Also, if move it a little right to (0.01, 1.2) and increase speed to 5.01, ball can't pass too.

I understand that behavior depends on timestep and speed, but it looks like that position can be correctly resolved on this steps of first example: Ball: Translation { vector: Matrix { data: [0.0, 0.95000005] } } // Resolved Ball: Translation { vector: Matrix { data: [0.0, 0.45000005] } } // Going through cuboids Ball: Translation { vector: Matrix { data: [0.0, -0.049999952] } } // Going through cuboids

And in the game, this things can happens on low velocities. I'm playing with integration parameters (and setting ERP *iterations to max values), and nothing helps.

UPD:

Also, I iterate through all contact events during every frame, and there is a really strange things: Cuboid(0) - 1.0x1.0 static Cuboid placed on -0.5, 0 Cuboid(1) - 1.0x1.0 static Cuboid placed on 0.5 0 Ball(2) - Ball with radius(0.3) placed on 0.0, 1.2 Default integration parameters, without gravity, timestep - 0.1 Every frame after step linear velocity setted to (0.0, -0.5) on the ball rigidbody

Frame 0- Ball: [0.0, 1.2] Frame 1- Ball: [0.0, 0.70000005] Frame 2- Ball: [0.0, 0.95000005] // Collision happens during this frame and have been resolved, but contacts only started and not stopped Contact between Ball(2) and Cuboid(0): Started Contact between Ball(2) and Cuboid(1): Started Frame 3- Ball: [0.0, 0.45000005] // Shapes intersected, but contacts stopped (they must be stopped in previous frame) and not started (?) Contact between Ball(2) and Cuboid(0): Stopped Contact between Ball(2) and Cuboid(1): Stopped Frame 4- Ball: [0.0, -0.049999952] // We are still in collision, but there is no started contacts Frame 5- Ball: [0.0, -0.54999995] // And we are still in collision Frame 6- Ball: [0.0, -1.05] // Our ball passing through cuboids and away from them, contacts started just now Contact between Ball(2) and Cuboid(0): Started Contact between Ball(2) and Cuboid(1): Started Frame 7- Ball: [0.0, -1.55] Contact between Ball(2) and Cuboid(0): Stopped Contact between Ball(2) and Cuboid(1): Stopped

n0uk avatar Oct 02 '18 10:10 n0uk

Hi!

I understand that behavior depends on timestep and speed, but it looks like that position can be correctly resolved on this steps of first example: Ball: Translation { vector: Matrix { data: [0.0, 0.95000005] } } // Resolved Ball: Translation { vector: Matrix { data: [0.0, 0.45000005] } } // Going through cuboids Ball: Translation { vector: Matrix { data: [0.0, -0.049999952] } } // Going through cuboids

That does look suspicious. It appears the collision is actually detected but does not prevent the next motion that leads to a penetration. One thing you could try is to increase the number of iterations max_velocity_iterations. The issue may just be due to slow convergence (though that would be surprising given the simplicity of the scenario here and the fact it works with higher velocities).

There might be an actual bug here so I will try to reproduce this behavior to find a better insight on the problem on saturday. Besides the information you already gave, could you tell what collider margin you are using for all your shapes? Also, I guess gravity is not enabled?

sebcrozet avatar Oct 02 '18 11:10 sebcrozet

@sebcrozet I'm update the post just before you send comment. Looks like there is an issue with contact handling. I try setting max_velocity_iterations to 1000 and it not helps.

Code to reproduce an issue: https://gist.github.com/n0uk/61ccdf4caf6a760572413719b46563da

n0uk avatar Oct 02 '18 11:10 n0uk

Thank you for providing the code to reproduce the issue! I've looked at it and I understand the problem now. I don't think there is a bug here, I pretty confident that the solution would be to enable Continuous Collision Detection (CCD) for the ball (though you won't be able to do this since CCD is currently broken on nphysics…)

Here is the explanation. First, note that during a step(), things happen in the following order:

  1. Collision detection (and generation of the contact started/stopped events).
  2. Computation of various forces and contact response.
  3. Integration and update of the positions of all bodies.

This explains why you get the weird outputs when you print the collision events. When you see the Frame 2, the body was moved out of its collision state, but since collision detection taking those new positions into account will only occur at the next frame, there was no stopped contact events. Those events are actually reported at the frame 3. At the end of the frame 3, the ball is moved again into a configuration where there are contacts. Because collision detection will only be performed at the beginning on the next .step(), the contact started events will be reported only at frame 4. So this weird behavior is actually by-design. But I understand this is very confusing so this will have to be improved in the future.

Now, regarding your issue, it appears that at Frame 2 the ball is moved quite far from the wall because of restitution. Because they are so far apart, the collision detection will not even attempt to detect contacts between the ball and the walls again. Therefore, the ball moves freely at Frame 3 is and now located in a place where penetration is actually impossible to recover correctly (because the ball is half penetrated on one cube side, and has its complete other half on the second cube) so it ends up passing through the wall altogether after some time. The solution to the problem here is to ensure contacts will actually be detected in-between frames 2 and 3, and that's the point of CCD.

As a side note, keep in mind that restitution will almost always play badly with setting the body velocity exactly since the body will always bounce back a little right after you hit a wall.

sebcrozet avatar Oct 02 '18 11:10 sebcrozet

@sebcrozet Thank you for you explanation! Sorry for another comment, but i can't understand something. Same code, zero restitution, speed 7.0

Frame 0: Ball: [0, 1.2] Frame 1: Ball: [0, 0.50000006] // Collision started after frame 1 Frame 2: Ball: [0, -0.19999993] // Collision still here after frame 2, but there is no events Frame 3: Ball: [0, -0.8999999] // Collision finished Frame 4: Ball: [0, -1.5999999]

(No contacts generated)

Ball starting overlapping cuboids after frame 1, so collision events/contacts must be generated during frame 2 (ball still overlapping cuboids before and after frame 2 world.step), but there is no contacts?

n0uk avatar Oct 02 '18 12:10 n0uk

Mmh, the fact no contact is generated is a bug. Probably due to that unhandled case.

sebcrozet avatar Oct 02 '18 12:10 sebcrozet

@n0uk I've merged to the master branch of ncollide a patch to handle the case I mentioned in my previous comment.

sebcrozet avatar Oct 06 '18 17:10 sebcrozet