0
\$\begingroup\$

I'm trying to implement player physics with arbitrary objects, in this case, a bunch of cubes.

I wanted to add a "floor" so in my cube collision function, I added the following following lines:

//direction = direction of movement, 
//position = initial position
//new_collided_normals = vector of normals collided with
//closest_distance = closest distance so far
glm::vec3 ground_normal = glm::vec3(0.0, 1.0, 0.0);
glm::vec3 ground_origin = glm::vec3(0.0,0.0,0.0);
float direction_plane_product = glm::dot(direction, ground_normal);
//equation for plane ray intersection
float ground_distance = ((ground_origin - position) * ground_normal) / direction_plane_product;

if (direction_plane_product != 0.0 && ground_distance >= 0.0){
    if(closest_distance > ground_distance){
        closest_distance = ground_distance;
        new_collided_normals.clear();
        new_collided_normals.append(ground_normal);
    }
}

return std::tuple(closest_distance, new_collided_normals);

If a collision the player's position is set back by doing the following:

//collision set back
float ratio_distance = (player_moved_distance - player_collided_distance)/player_moved_distance;
glm::vec3 projected_velocity = glm::dot(player_velocity, collision_normal) * collision_normal ; 
player_position += projected_velocity * ratio_distance;

This is all find and good for a bit, until I jump a bunch, and eventually I fall through the floor...

What I found was happening is that my "collision setback" logic above will sometimes not exactly get rid of the portions of the position that would have resulted in being below the floor (or inside of a cube for that matter). This caused no collision to be found with the floor (since our position was initially beneath it at the start).

for example, if my position was 0, 0, 0, and my velocity caused me to end up at 0,-1,0 within the same frame, we are colliding with the floor. When removing the component (-1 in y) that caused us to go through the floor, we would have the following situation:

float ratio_distance = (1.0 - 0.0)/1.0;
glm::vec3 projected_velocity = glm::dot(glm::vec3(0.0,-1.0,0.0), glm::vec3(0.0,1.0,0.0) * glm::vec3(0.0,1.0,0.0);
player_position += glm::vec3(0.0, -1.0, 0.0)* 1.0;

Mathematically, the players position should reset back to glm::vec3(0.0, 0.0, 0.0), but if we were to print out the value, what I've found is that sometimes this value ends up being something like glm::vec3(0.0, -5.6e-20, 0.0). This means on the next iteration this line:

float ground_distance = ((ground_origin - position) * ground_normal) / direction_plane_product;

will actually be negative so this line:

if (direction_plane_product != 0.0 && ground_distance >= 0.0){

will be false, so no collision check with ground is made, and we'll fall through the floor.

Now I've outlined what happens with the floor for simplicity, but this applies to all collisions in my game. What can I do to avoid this problem? I've seen this: How to resolve collision between a dynamic AABB and static AABB? and that will solve the issue for the floor, but not any arbitrarily oriented body that I collide with. Moving to double precision doesn't help, it only delays the issue, and I have constraints that require my physics to be done mostly in floating point.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.