Game Programming

SO FUCKIN MAD

by Cory on October 31, 2018 1:52 AM

I'M SO FUCKIN MAD!!! >:(

Work is shitty this week and I got into a fight when biking home because some guy passed me so close he almost side swiped me. Anyway that has nothing to do with game programming. I need to fucking quit my job. >:(

I spent a few hours tonight try to fix the collision detection problem because after thinking and observing on and off for a few weeks I think I figured out why it's acting strange when more than 2 objects collide in the same frame.

My collision response is the culprit. What I have here is the "slide" described in this page. I have some temporary code thrown in the physics system that does this after each collision detection. It's definitely not the final thing. Observe this case:

Here I have the character entity and then 2 separate entities that act as the floor. The first section is on the left side and the second section is the raised floor on the right side.

I threw all my code into the update body of the physics system code. Yes it's all in one function right now. It's laid out where I have a for loop that goes through all physics entities. In each iteration of the loop, I take two entities and perform the swept aabb detection algorithm on it and generate a normal vector, and a bunch of other collision information. If the collision information shows that a collision will happen in the frame, I follow it up with a slide collision response to fill out the for loop iteration.

// swept aabb collision test
// ...

// collision response
// 1. move entity to scaled velocity
if (update_final_position) {
final_position.x = e.get<Velocity>()->x() * this->collision_time;
final_position.y = e.get<Velocity>()->y() * this->collision_time;

// stop updating the final position if collision_time isn't 1.f (note: this is a hack. Probably need to replace this for loop with some sort of constraint solver)
update_final_position = (this->collision_time == 1.f);
}

float remaining_time = 1.f - this->collision_time;

// 2. slide the rest of the way
if (remaining_time > 0.f) {
sf::Vector2f response_delta;

this->dotprod = (e.get<Velocity>()->x() * this->normal.y + e.get<Velocity>()->y() * this->normal.x) * remaining_time;

response_delta.x += this->dotprod * this->normal.y;
response_delta.y += this->dotprod * this->normal.x;

if (this->normal.x != 0.f) {
e.get<Velocity>()->x(0);
e.get<Acceleration>()->x(0);
}

if (this->normal.y != 0.f) {
e.get<Velocity>()->y(0);
e.get<Acceleration>()->y(0);
}

// This is another hack (when you have multiple objects colliding at the same time,
// sometimes the collision slide response can slide the entity into one of the colliding
// objects). This section of code tries to catch this.

sf::FloatRect end_pos = this->global_transform(e).transformRect(e.get<Collision>()->volume());

end_pos.left += final_position.x + final_position_delta.x + response_delta.x;
end_pos.top += final_position.y + final_position_delta.y + response_delta.y;

for (std::vector<Handle>::const_iterator other_it = physics_entities.begin(); other_it != physics_entities.end(); ++other_it) {
Entity& third_e = *(this->scene().get_entity(*other_it));
sf::FloatRect third_e_collision = this->global_transform(third_e).transformRect(third_e.get<Collision>()->volume());

if (&third_e == &other_e || &third_e == &e) {
continue;
}

if (third_e_collision.intersects(end_pos)) {
response_delta.x = 0;
response_delta.y = 0;
break;
}
}

final_position_delta += response_delta;
}
// end collision response
}

e.get<Space>()->move(final_position + final_position_delta);
this->clamp_entity(e);

If you move the character so that it's standing on the lower platform and go right until it collides with the right platform, the expected behavior is that the character should just stop in the corner. Instead what happens is that the character clips through the raised platform and is pushed back out and repeats this over and over as long as the walk right button is held down.

  1. Stepping through the physics simulation and using gdb shows that what happens is that in my system loop, the first entity to collide with the player character is the right one. This works as you would expect. The player is prevented from going through the left side of the raised platform.

  2. However, in the same frame, the left floor collision is processed afterwards. Gravity and the walk right button causes the character to move to the lower right. The swept aabb collision prevents the character from going through the floor and my sliding collision response means the character slides to the right.

  3. This is bad because now the character entity is clipping through the rightmost floor entity a littl ebit. Since the rightmost floor entity has already been collision tested, there is nothing that is correcting this oversight and results in the erroneous behavior.
     

My solution was to add a section of code after handling a collision response to loop through all collidable entities again and do a simple aabb intersection test on the final position. (it's the block of code under the huge "this is a hack" comment in the snippet above) If an entity (other than the two that are currently being tested) is found to be colliding, the collision response is nullified. So in the example above, when we slide response on the leftmost floor, before we actually move the character, we check to see if it intersects with the rightmost entity and then cancel it before we end up clipping through it.

Ok so this is obviously a temporary solution. I think there are probably more sophisticated (and performant) ways of doing this, but I would probably need to read a game physics book or two to figure out how to do that.

Now to figure out what to do next...



This Thought is part of Game Programming

Game programming general topic. I eventually hope to split this into separate ideas exclusively about specific games that I make.

back to the

top