Browse Source

Merge pull request #53272 from fabriceci/bug-platform-ceiling

Fix #53255 when a body gets stuck when it hits a descending platform in 2D/3D
Camille Mohr-Daurat 3 years ago
parent
commit
95432893e2
3 changed files with 38 additions and 2 deletions
  1. 16 1
      scene/2d/physics_body_2d.cpp
  2. 20 1
      scene/3d/physics_body_3d.cpp
  3. 2 0
      scene/3d/physics_body_3d.h

+ 16 - 1
scene/2d/physics_body_2d.cpp

@@ -1131,6 +1131,8 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 	bool sliding_enabled = !floor_stop_on_slope;
 	// Constant speed can be applied only the first time sliding is enabled.
 	bool can_apply_constant_speed = sliding_enabled;
+	// If the platform's ceiling push down the body.
+	bool apply_ceiling_velocity = false;
 	bool first_slide = true;
 	bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
 	Vector2 last_travel;
@@ -1147,6 +1149,19 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 			motion_results.push_back(result);
 			_set_collision_direction(result);
 
+			// If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one.
+			if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) {
+				// If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.
+				if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) {
+					apply_ceiling_velocity = true;
+					Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity);
+					Vector2 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity);
+					if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
+						motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction);
+					}
+				}
+			}
+
 			if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
 				Transform2D gt = get_global_transform();
 				if (result.travel.length() <= margin + CMP_EPSILON) {
@@ -1200,7 +1215,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 				motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length());
 			}
 			// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
-			else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
+			else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) {
 				Vector2 slide_motion = result.remainder.slide(result.collision_normal);
 				if (slide_motion.dot(motion_velocity) > 0.0) {
 					motion = slide_motion;

+ 20 - 1
scene/3d/physics_body_3d.cpp

@@ -1168,14 +1168,18 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 
 	platform_rid = RID();
 	platform_velocity = Vector3();
+	platform_ceiling_velocity = Vector3();
 	floor_normal = Vector3();
 	wall_normal = Vector3();
+	ceiling_normal = Vector3();
 
 	// No sliding on first attempt to keep floor motion stable when possible,
 	// When stop on slope is enabled or when there is no up direction.
 	bool sliding_enabled = !floor_stop_on_slope;
 	// Constant speed can be applied only the first time sliding is enabled.
 	bool can_apply_constant_speed = sliding_enabled;
+	// If the platform's ceiling push down the body.
+	bool apply_ceiling_velocity = false;
 	bool first_slide = true;
 	bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
 	Vector3 total_travel;
@@ -1193,6 +1197,19 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 			CollisionState result_state;
 			_set_collision_direction(result, result_state);
 
+			// If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one.
+			if (collision_state.ceiling && platform_ceiling_velocity != Vector3() && platform_ceiling_velocity.dot(up_direction) < 0) {
+				// If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.
+				if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (ceiling_normal + up_direction).length() < 0.01) {
+					apply_ceiling_velocity = true;
+					Vector3 ceiling_vertical_velocity = up_direction * up_direction.dot(platform_ceiling_velocity);
+					Vector3 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity);
+					if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
+						motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction);
+					}
+				}
+			}
+
 			if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
 				Transform3D gt = get_global_transform();
 				if (result.travel.length() <= margin + CMP_EPSILON) {
@@ -1304,7 +1321,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
 
 			if (apply_default_sliding) {
 				// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
-				if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
+				if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) {
 					const PhysicsServer3D::MotionCollision &collision = result.collisions[0];
 
 					Vector3 slide_motion = result.remainder.slide(collision.normal);
@@ -1526,6 +1543,8 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
 			if (ceiling_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
 				r_state.ceiling = true;
 				if (p_apply_state.ceiling) {
+					platform_ceiling_velocity = collision.collider_velocity;
+					ceiling_normal = collision.normal;
 					collision_state.ceiling = true;
 				}
 				continue;

+ 2 - 0
scene/3d/physics_body_3d.h

@@ -392,8 +392,10 @@ private:
 	Vector3 motion_velocity;
 	Vector3 floor_normal;
 	Vector3 wall_normal;
+	Vector3 ceiling_normal;
 	Vector3 last_motion;
 	Vector3 platform_velocity;
+	Vector3 platform_ceiling_velocity;
 	Vector3 previous_position;
 	Vector3 real_velocity;