diff --git a/doc/classes/PhysicsTestMotionParameters2D.xml b/doc/classes/PhysicsTestMotionParameters2D.xml index c22a49edf72..00f21a2058c 100644 --- a/doc/classes/PhysicsTestMotionParameters2D.xml +++ b/doc/classes/PhysicsTestMotionParameters2D.xml @@ -28,5 +28,9 @@ Motion vector to define the length and direction of the motion to test. + + If set to [code]true[/code], any depenetration from the recovery phase is reported as a collision; this is used e.g. by [method CharacterBody2D.move_and_slide] for improving floor detection when floor snapping is disabled. + If set to [code]false[/code], only collisions resulting from the motion are reported; this is used e.g. by [method PhysicsBody2D.move_and_collide]. + diff --git a/doc/classes/PhysicsTestMotionParameters3D.xml b/doc/classes/PhysicsTestMotionParameters3D.xml index 4ff07de7aaa..5b07796a10d 100644 --- a/doc/classes/PhysicsTestMotionParameters3D.xml +++ b/doc/classes/PhysicsTestMotionParameters3D.xml @@ -31,5 +31,9 @@ Motion vector to define the length and direction of the motion to test. + + If set to [code]true[/code], any depenetration from the recovery phase is reported as a collision; this is used e.g. by [method CharacterBody3D.move_and_slide] for improving floor detection when floor snapping is disabled. + If set to [code]false[/code], only collisions resulting from the motion are detected; this is used e.g. by [method PhysicsBody3D.move_and_collide]. + diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index f345d8c3c9e..9d9db0d6b43 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -56,13 +56,11 @@ PhysicsBody2D::~PhysicsBody2D() { Ref PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. PhysicsServer2D::MotionResult result; - bool collided = move_and_collide(parameters, result, p_test_only); - - // Don't report collision when the whole motion is done. - if (collided && result.collision_safe_fraction < 1) { + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -143,15 +141,9 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distan } PhysicsServer2D::MotionParameters parameters(p_from, p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. - bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); - - if (colliding) { - // Don't report collision when the whole motion is done. - return (r->collision_safe_fraction < 1.0); - } else { - return false; - } + return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); } TypedArray PhysicsBody2D::get_collision_exceptions() { @@ -1145,6 +1137,7 @@ bool CharacterBody2D::move_and_slide() { if (!current_platform_velocity.is_equal_approx(Vector2())) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { parameters.exclude_objects.insert(platform_object_id); @@ -1203,6 +1196,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. Vector2 prev_position = parameters.from.columns[2]; @@ -1359,6 +1353,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer2D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1405,6 +1400,7 @@ void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_ real_t length = MAX(floor_snap_length, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer2D::MotionResult result; @@ -1440,6 +1436,7 @@ bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_f real_t length = MAX(floor_snap_length, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer2D::MotionResult result; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 94cd5400db5..9a89bf8ce6f 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -94,13 +94,11 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { Ref PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, int p_max_collisions) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); parameters.max_collisions = p_max_collisions; + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. PhysicsServer3D::MotionResult result; - bool collided = move_and_collide(parameters, result, p_test_only); - - // Don't report collision when the whole motion is done. - if (collided && result.collision_safe_fraction < 1) { + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -184,15 +182,9 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distan } PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. - bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); - - if (colliding) { - // Don't report collision when the whole motion is done. - return (r->collision_safe_fraction < 1.0); - } else { - return false; - } + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); } void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { @@ -1214,6 +1206,8 @@ bool CharacterBody3D::move_and_slide() { if (!current_platform_velocity.is_equal_approx(Vector3())) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { parameters.exclude_objects.insert(platform_object_id); @@ -1277,6 +1271,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, !sliding_enabled); @@ -1521,6 +1516,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1575,6 +1571,7 @@ void CharacterBody3D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_ PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -1610,6 +1607,7 @@ bool CharacterBody3D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_f PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; diff --git a/servers/physics_2d/godot_space_2d.cpp b/servers/physics_2d/godot_space_2d.cpp index 731eab2dfee..0a7859d86a9 100644 --- a/servers/physics_2d/godot_space_2d.cpp +++ b/servers/physics_2d/godot_space_2d.cpp @@ -874,7 +874,7 @@ bool GodotSpace2D::test_body_motion(GodotBody2D *p_body, const PhysicsServer2D:: bool collided = false; - if (recovered || (safe < 1)) { + if ((p_parameters.recovery_as_collision && recovered) || (safe < 1)) { if (safe >= 1) { best_shape = -1; //no best shape with cast, reset to -1 } diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp index 9c051b23f64..2bdaf581a13 100644 --- a/servers/physics_3d/godot_space_3d.cpp +++ b/servers/physics_3d/godot_space_3d.cpp @@ -906,7 +906,7 @@ bool GodotSpace3D::test_body_motion(GodotBody3D *p_body, const PhysicsServer3D:: } bool collided = false; - if (recovered || (safe < 1)) { + if ((p_parameters.recovery_as_collision && recovered) || (safe < 1)) { if (safe >= 1) { best_shape = -1; //no best shape with cast, reset to -1 } diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 4aeaa7497f4..3259bd6ef94 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -505,12 +505,16 @@ void PhysicsTestMotionParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_exclude_objects"), &PhysicsTestMotionParameters2D::get_exclude_objects); ClassDB::bind_method(D_METHOD("set_exclude_objects", "exclude_list"), &PhysicsTestMotionParameters2D::set_exclude_objects); + ClassDB::bind_method(D_METHOD("is_recovery_as_collision_enabled"), &PhysicsTestMotionParameters2D::is_recovery_as_collision_enabled); + ClassDB::bind_method(D_METHOD("set_recovery_as_collision_enabled", "enabled"), &PhysicsTestMotionParameters2D::set_recovery_as_collision_enabled); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "from"), "set_from", "get_from"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion"), "set_motion", "get_motion"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_separation_ray"), "set_collide_separation_ray_enabled", "is_collide_separation_ray_enabled"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_bodies", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_exclude_bodies", "get_exclude_bodies"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_objects"), "set_exclude_objects", "get_exclude_objects"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recovery_as_collision"), "set_recovery_as_collision_enabled", "is_recovery_as_collision_enabled"); } /////////////////////////////// diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index bd537649323..745ee38ee19 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -485,6 +485,7 @@ public: bool collide_separation_ray = false; RBSet exclude_bodies; RBSet exclude_objects; + bool recovery_as_collision = false; MotionParameters() {} @@ -727,6 +728,9 @@ public: Array get_exclude_objects() const; void set_exclude_objects(const Array &p_exclude); + + bool is_recovery_as_collision_enabled() const { return parameters.recovery_as_collision; } + void set_recovery_as_collision_enabled(bool p_enabled) { parameters.recovery_as_collision = p_enabled; } }; class PhysicsTestMotionResult2D : public RefCounted { diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index d05f5128b27..63085cf2756 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -527,6 +527,9 @@ void PhysicsTestMotionParameters3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_exclude_objects"), &PhysicsTestMotionParameters3D::get_exclude_objects); ClassDB::bind_method(D_METHOD("set_exclude_objects", "exclude_list"), &PhysicsTestMotionParameters3D::set_exclude_objects); + ClassDB::bind_method(D_METHOD("is_recovery_as_collision_enabled"), &PhysicsTestMotionParameters3D::is_recovery_as_collision_enabled); + ClassDB::bind_method(D_METHOD("set_recovery_as_collision_enabled", "enabled"), &PhysicsTestMotionParameters3D::set_recovery_as_collision_enabled); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "from"), "set_from", "get_from"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion"), "set_motion", "get_motion"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin"), "set_margin", "get_margin"); @@ -534,6 +537,7 @@ void PhysicsTestMotionParameters3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_separation_ray"), "set_collide_separation_ray_enabled", "is_collide_separation_ray_enabled"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_bodies", PROPERTY_HINT_ARRAY_TYPE, "RID"), "set_exclude_bodies", "get_exclude_bodies"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_objects"), "set_exclude_objects", "get_exclude_objects"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recovery_as_collision"), "set_recovery_as_collision_enabled", "is_recovery_as_collision_enabled"); } /////////////////////////////// diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index c707f621b4d..6a721f75a35 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -522,6 +522,7 @@ public: bool collide_separation_ray = false; RBSet exclude_bodies; RBSet exclude_objects; + bool recovery_as_collision = false; MotionParameters() {} @@ -941,6 +942,9 @@ public: Array get_exclude_objects() const; void set_exclude_objects(const Array &p_exclude); + + bool is_recovery_as_collision_enabled() const { return parameters.recovery_as_collision; } + void set_recovery_as_collision_enabled(bool p_enabled) { parameters.recovery_as_collision = p_enabled; } }; class PhysicsTestMotionResult3D : public RefCounted {