Godot version:
Godot 3 Beta 2
OS/device including version:
Windows 10
Issue description:
If using move_and_slide to move downwards against a slope when your normal is (0, -1), the lower end of the slope doesn't respect the slope_stop_min_velocity. If using a tilemap and a rotated slope, the disrespecting end is rotated accordingly.
Steps to reproduce:
Download the project below, go completely against the wall and then completely downwards. On the left, it'll ignore slope_stop_min_velocity, on the right, it won't, since it's a rotated slope and the disrespecting end is rotated.
Minimal reproduction project:
Test.zip
I think the problem may be related to decimal errors on computing move_and_collide, as it computes angles equals to the p_floor_max_angle, and the if-conditional branches in a non-homogeneous way. I'll try to explain it better:
The result obtained changing the p_floor_direction and moving onto both slopes is:
p_floor_direction | move direction | left slope | right slope
--------------------- | --------------------|-------------|--------------
(0,-1) | vertical | wall (sliding) | floor (not sliding)
(0,1) | vertical | wall (sliding) | ceiling (sliding)
(0,-1) | horizontal | wall (sliding) | wall (sliding)
(0, 1) | horizontal | wall (sliding) | wall (sliding)
The if-block is this:
if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
[...]
} else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
[...]
} else {
on_wall = true;
}
The movement direction is (0, 1), and the slope is 45潞. The angle between both is also 45潞. Being p_floor_max_angle 0.785398 (45潞), its cosine is 0.70710678118655. The computed collision.normal.dot (for vertical movements) equals to 0.706955612 (wall), 0.707182169 (floor), -0.706825793 (wall) and -0.707106829 (ceiling). As all are the same slope being colliding in the same direction, I would hope all the collision be resolved the same way (all walls, or ceilings/floors depending the signs), but I think the small deviation of the calculations at the limit angle make this happening.
If you change the p_floor_max_angle a bit, you obtain all being walls or floor/ceilings.
@robfram Umm... does this mean... it's fixable? Sorry, I lost you at "robfram commented".
@mateusak Sorry! I was describing what I found to any developer with better understanding of the collision system than me, in order to make faster the fixing of this bug. I'm afraid this post will follow the same line.
I don't know if I'm going the wrong way, but adding a "safety margin" to the angle comparison of the p_floor_max_angle seems to fix this specific issue. I read through the Math class and find the CMP_EPSILON const, but it is smaller than the precision error of this case, and cos function is not linear. So, I used a proportional safety margin. 1/1000 of p_floor_max_angle was enough for this 45潞 slope case. Thus, changing Math::cos(p_floor_max_angle) for Math::cos(p_floor_max_angle * 1.001) in the if-block makes the attached project behave as expected.
@robfram Mind if you make a PR? It seems like a nice possible change, and it would probably get reviewed much faster that way.
Most helpful comment
@robfram Umm... does this mean... it's fixable? Sorry, I lost you at "robfram commented".