|
@@ -1030,7 +1030,7 @@ bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only, const Set<RID> &p_exclude) {
|
|
|
|
|
|
+bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) {
|
|
if (sync_to_physics) {
|
|
if (sync_to_physics) {
|
|
ERR_PRINT("Functions move_and_slide and move_and_collide do not work together with 'sync to physics' option. Please read the documentation.");
|
|
ERR_PRINT("Functions move_and_slide and move_and_collide do not work together with 'sync to physics' option. Please read the documentation.");
|
|
}
|
|
}
|
|
@@ -1039,6 +1039,44 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_
|
|
|
|
|
|
bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes, p_exclude);
|
|
bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes, p_exclude);
|
|
|
|
|
|
|
|
+ // Restore direction of motion to be along original motion,
|
|
|
|
+ // in order to avoid sliding due to recovery,
|
|
|
|
+ // but only if collision depth is low enough to avoid tunneling.
|
|
|
|
+ if (p_cancel_sliding) {
|
|
|
|
+ real_t motion_length = p_motion.length();
|
|
|
|
+ real_t precision = 0.001;
|
|
|
|
+
|
|
|
|
+ if (colliding) {
|
|
|
|
+ // Can't just use margin as a threshold because collision depth is calculated on unsafe motion,
|
|
|
|
+ // so even in normal resting cases the depth can be a bit more than the margin.
|
|
|
|
+ precision += motion_length * (result.collision_unsafe_fraction - result.collision_safe_fraction);
|
|
|
|
+
|
|
|
|
+ if (result.collision_depth > (real_t)margin + precision) {
|
|
|
|
+ p_cancel_sliding = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (p_cancel_sliding) {
|
|
|
|
+ // When motion is null, recovery is the resulting motion.
|
|
|
|
+ Vector2 motion_normal;
|
|
|
|
+ if (motion_length > CMP_EPSILON) {
|
|
|
|
+ motion_normal = p_motion / motion_length;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check depth of recovery.
|
|
|
|
+ real_t projected_length = result.motion.dot(motion_normal);
|
|
|
|
+ Vector2 recovery = result.motion - motion_normal * projected_length;
|
|
|
|
+ real_t recovery_length = recovery.length();
|
|
|
|
+ // Fixes cases where canceling slide causes the motion to go too deep into the ground,
|
|
|
|
+ // because we're only taking rest information into account and not general recovery.
|
|
|
|
+ if (recovery_length < (real_t)margin + precision) {
|
|
|
|
+ // Apply adjustment to motion.
|
|
|
|
+ result.motion = motion_normal * projected_length;
|
|
|
|
+ result.remainder = p_motion - result.motion;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
if (colliding) {
|
|
if (colliding) {
|
|
r_collision.collider_metadata = result.collider_metadata;
|
|
r_collision.collider_metadata = result.collider_metadata;
|
|
r_collision.collider_shape = result.collider_shape;
|
|
r_collision.collider_shape = result.collider_shape;
|
|
@@ -1092,7 +1130,7 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|
Collision floor_collision;
|
|
Collision floor_collision;
|
|
Set<RID> exclude;
|
|
Set<RID> exclude;
|
|
exclude.insert(on_floor_body);
|
|
exclude.insert(on_floor_body);
|
|
- if (move_and_collide(current_floor_velocity * delta, p_infinite_inertia, floor_collision, true, false, exclude)) {
|
|
|
|
|
|
+ if (move_and_collide(current_floor_velocity * delta, p_infinite_inertia, floor_collision, true, false, false, exclude)) {
|
|
colliders.push_back(floor_collision);
|
|
colliders.push_back(floor_collision);
|
|
_set_collision_direction(floor_collision, up_direction, p_floor_max_angle);
|
|
_set_collision_direction(floor_collision, up_direction, p_floor_max_angle);
|
|
}
|
|
}
|
|
@@ -1101,14 +1139,17 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|
on_floor_body = RID();
|
|
on_floor_body = RID();
|
|
Vector2 motion = body_velocity * delta;
|
|
Vector2 motion = body_velocity * delta;
|
|
|
|
|
|
- while (p_max_slides) {
|
|
|
|
|
|
+ // No sliding on first attempt to keep floor motion stable when possible,
|
|
|
|
+ // when stop on slope is enabled.
|
|
|
|
+ bool sliding_enabled = !p_stop_on_slope;
|
|
|
|
+ for (int iteration = 0; iteration < p_max_slides; ++iteration) {
|
|
Collision collision;
|
|
Collision collision;
|
|
bool found_collision = false;
|
|
bool found_collision = false;
|
|
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
for (int i = 0; i < 2; ++i) {
|
|
bool collided;
|
|
bool collided;
|
|
if (i == 0) { //collide
|
|
if (i == 0) { //collide
|
|
- collided = move_and_collide(motion, p_infinite_inertia, collision);
|
|
|
|
|
|
+ collided = move_and_collide(motion, p_infinite_inertia, collision, true, false, !sliding_enabled);
|
|
if (!collided) {
|
|
if (!collided) {
|
|
motion = Vector2(); //clear because no collision happened and motion completed
|
|
motion = Vector2(); //clear because no collision happened and motion completed
|
|
}
|
|
}
|
|
@@ -1124,29 +1165,36 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
|
|
found_collision = true;
|
|
found_collision = true;
|
|
|
|
|
|
colliders.push_back(collision);
|
|
colliders.push_back(collision);
|
|
- motion = collision.remainder;
|
|
|
|
|
|
|
|
_set_collision_direction(collision, up_direction, p_floor_max_angle);
|
|
_set_collision_direction(collision, up_direction, p_floor_max_angle);
|
|
|
|
|
|
if (on_floor && p_stop_on_slope) {
|
|
if (on_floor && p_stop_on_slope) {
|
|
- if ((body_velocity_normal + up_direction).length() < 0.01 && collision.travel.length() < 1) {
|
|
|
|
|
|
+ if ((body_velocity_normal + up_direction).length() < 0.01) {
|
|
Transform2D gt = get_global_transform();
|
|
Transform2D gt = get_global_transform();
|
|
- gt.elements[2] -= collision.travel.slide(up_direction);
|
|
|
|
|
|
+ if (collision.travel.length() > margin) {
|
|
|
|
+ gt.elements[2] -= collision.travel.slide(up_direction);
|
|
|
|
+ } else {
|
|
|
|
+ gt.elements[2] -= collision.travel;
|
|
|
|
+ }
|
|
set_global_transform(gt);
|
|
set_global_transform(gt);
|
|
return Vector2();
|
|
return Vector2();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- motion = motion.slide(collision.normal);
|
|
|
|
- body_velocity = body_velocity.slide(collision.normal);
|
|
|
|
|
|
+ if (sliding_enabled || !on_floor) {
|
|
|
|
+ motion = collision.remainder.slide(collision.normal);
|
|
|
|
+ body_velocity = body_velocity.slide(collision.normal);
|
|
|
|
+ } else {
|
|
|
|
+ motion = collision.remainder;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ sliding_enabled = true;
|
|
}
|
|
}
|
|
|
|
|
|
if (!found_collision || motion == Vector2()) {
|
|
if (!found_collision || motion == Vector2()) {
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
-
|
|
|
|
- --p_max_slides;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (!on_floor && !on_wall) {
|
|
if (!on_floor && !on_wall) {
|
|
@@ -1169,7 +1217,7 @@ Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_veloci
|
|
Collision col;
|
|
Collision col;
|
|
Transform2D gt = get_global_transform();
|
|
Transform2D gt = get_global_transform();
|
|
|
|
|
|
- if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) {
|
|
|
|
|
|
+ if (move_and_collide(p_snap, p_infinite_inertia, col, false, true, false)) {
|
|
bool apply = true;
|
|
bool apply = true;
|
|
if (up_direction != Vector2()) {
|
|
if (up_direction != Vector2()) {
|
|
if (Math::acos(col.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
|
|
if (Math::acos(col.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
|
|
@@ -1180,9 +1228,12 @@ Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_veloci
|
|
if (p_stop_on_slope) {
|
|
if (p_stop_on_slope) {
|
|
// move and collide may stray the object a bit because of pre un-stucking,
|
|
// move and collide may stray the object a bit because of pre un-stucking,
|
|
// so only ensure that motion happens on floor direction in this case.
|
|
// so only ensure that motion happens on floor direction in this case.
|
|
- col.travel = up_direction * up_direction.dot(col.travel);
|
|
|
|
|
|
+ if (col.travel.length() > margin) {
|
|
|
|
+ col.travel = up_direction * up_direction.dot(col.travel);
|
|
|
|
+ } else {
|
|
|
|
+ col.travel = Vector2();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
} else {
|
|
} else {
|
|
apply = false;
|
|
apply = false;
|
|
}
|
|
}
|