Fix physics glitch with TileMap moving platforms

Added a parameter in test_body_motion to exclude attached objects from
collision, used to avoid collision with all TileMap tiles with moving
platform motion instead of just the one tile the character touches.

Same changes made in 3D for consistency, and handling potential similar
cases.
This commit is contained in:
PouleyKetchoupp 2021-09-30 11:05:30 -07:00
parent 5b270278c8
commit 4f8d761be6
12 changed files with 99 additions and 0 deletions

View file

@ -16,6 +16,9 @@
<member name="exclude_bodies" type="Array" setter="set_exclude_bodies" getter="get_exclude_bodies" default="[]">
Optional array of body [RID] to exclude from collision.
</member>
<member name="exclude_objects" type="Array" setter="set_exclude_objects" getter="get_exclude_objects" default="[]">
Optional array of object unique instance ID to exclude from collision. See [method Object.get_instance_id].
</member>
<member name="from" type="Transform2D" setter="set_from" getter="get_from" default="Transform2D(1, 0, 0, 1, 0, 0)">
Transform in global space where the motion should start. Usually set to [member Node2D.global_transform] for the current body's transform.
</member>

View file

@ -16,6 +16,9 @@
<member name="exclude_bodies" type="Array" setter="set_exclude_bodies" getter="get_exclude_bodies" default="[]">
Optional array of body [RID] to exclude from collision.
</member>
<member name="exclude_objects" type="Array" setter="set_exclude_objects" getter="get_exclude_objects" default="[]">
Optional array of object unique instance ID to exclude from collision. See [method Object.get_instance_id].
</member>
<member name="from" type="Transform3D" setter="set_from" getter="get_from" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
Transform in global space where the motion should start. Usually set to [member Node3D.global_transform] for the current body's transform.
</member>

View file

@ -1089,6 +1089,9 @@ 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.exclude_bodies.insert(platform_rid);
if (platform_object_id.is_valid()) {
parameters.exclude_objects.insert(platform_object_id);
}
PhysicsServer2D::MotionResult floor_result;
if (move_and_collide(parameters, floor_result, false, false)) {
@ -1125,9 +1128,11 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Vector2 prev_floor_normal = floor_normal;
RID prev_platform_rid = platform_rid;
ObjectID prev_platform_object_id = platform_object_id;
int prev_platform_layer = platform_layer;
platform_rid = RID();
platform_object_id = ObjectID();
floor_normal = Vector2();
platform_velocity = Vector2();
@ -1199,6 +1204,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
}
on_floor = true;
platform_rid = prev_platform_rid;
platform_object_id = prev_platform_object_id;
platform_layer = prev_platform_layer;
platform_velocity = p_prev_platform_velocity;
floor_normal = prev_floor_normal;
@ -1285,6 +1291,7 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
Vector2 motion = motion_velocity * p_delta;
platform_rid = RID();
platform_object_id = ObjectID();
floor_normal = Vector2();
platform_velocity = Vector2();
@ -1402,6 +1409,7 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu
void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) {
platform_rid = p_result.collider;
platform_object_id = p_result.collider_id;
platform_velocity = p_result.collider_velocity;
platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid);
}
@ -1620,6 +1628,7 @@ void CharacterBody2D::_notification(int p_what) {
// Reset move_and_slide() data.
on_floor = false;
platform_rid = RID();
platform_object_id = ObjectID();
on_ceiling = false;
on_wall = false;
motion_results.clear();

View file

@ -368,6 +368,7 @@ private:
Vector2 real_velocity;
RID platform_rid;
ObjectID platform_object_id;
bool on_floor = false;
bool on_ceiling = false;
bool on_wall = false;

View file

@ -1131,6 +1131,9 @@ 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.exclude_bodies.insert(platform_rid);
if (platform_object_id.is_valid()) {
parameters.exclude_objects.insert(platform_object_id);
}
PhysicsServer3D::MotionResult floor_result;
if (move_and_collide(parameters, floor_result, false, false)) {
@ -1169,6 +1172,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Vector3 prev_floor_normal = floor_normal;
platform_rid = RID();
platform_object_id = ObjectID();
platform_velocity = Vector3();
platform_ceiling_velocity = Vector3();
floor_normal = Vector3();
@ -1416,6 +1420,7 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
Vector3 motion = motion_velocity * p_delta;
platform_rid = RID();
platform_object_id = ObjectID();
floor_normal = Vector3();
platform_velocity = Vector3();
@ -1611,6 +1616,7 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
void CharacterBody3D::_set_platform_data(const PhysicsServer3D::MotionCollision &p_collision) {
platform_rid = p_collision.collider;
platform_object_id = p_collision.collider_id;
platform_velocity = p_collision.collider_velocity;
platform_layer = PhysicsServer3D::get_singleton()->body_get_collision_layer(platform_rid);
}
@ -1833,6 +1839,7 @@ void CharacterBody3D::_notification(int p_what) {
// Reset move_and_slide() data.
collision_state.state = 0;
platform_rid = RID();
platform_object_id = ObjectID();
motion_results.clear();
platform_velocity = Vector3();
} break;

View file

@ -383,6 +383,7 @@ private:
int max_slides = 6;
int platform_layer = 0;
RID platform_rid;
ObjectID platform_object_id;
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
real_t floor_snap_length = 0.1;

View file

@ -615,6 +615,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const PhysicsServer2D::Motion
if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
continue;
}
if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {
continue;
}
int shape_idx = intersection_query_subindex_results[i];
@ -747,6 +750,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const PhysicsServer2D::Motion
if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
continue;
}
if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {
continue;
}
int col_shape_idx = intersection_query_subindex_results[i];
Shape2DSW *against_shape = col_obj->get_shape(col_shape_idx);
@ -896,6 +902,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const PhysicsServer2D::Motion
if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
continue;
}
if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {
continue;
}
int shape_idx = intersection_query_subindex_results[i];

View file

@ -704,6 +704,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const PhysicsServer3D::Motion
if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
continue;
}
if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {
continue;
}
int shape_idx = intersection_query_subindex_results[i];
@ -795,6 +798,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const PhysicsServer3D::Motion
if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
continue;
}
if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {
continue;
}
int shape_idx = intersection_query_subindex_results[i];
@ -916,6 +922,10 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const PhysicsServer3D::Motion
if (p_parameters.exclude_bodies.has(col_obj->get_self())) {
continue;
}
if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {
continue;
}
int shape_idx = intersection_query_subindex_results[i];
rcd.object = col_obj;

View file

@ -430,6 +430,26 @@ void PhysicsTestMotionParameters2D::set_exclude_bodies(const Vector<RID> &p_excl
}
}
Array PhysicsTestMotionParameters2D::get_exclude_objects() const {
Array exclude;
exclude.resize(parameters.exclude_objects.size());
int object_index = 0;
for (ObjectID object_id : parameters.exclude_objects) {
exclude[object_index++] = object_id;
}
return exclude;
}
void PhysicsTestMotionParameters2D::set_exclude_objects(const Array &p_exclude) {
for (int i = 0; i < p_exclude.size(); ++i) {
ObjectID object_id = p_exclude[i];
ERR_CONTINUE(object_id.is_null());
parameters.exclude_objects.insert(object_id);
}
}
void PhysicsTestMotionParameters2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_from"), &PhysicsTestMotionParameters2D::get_from);
ClassDB::bind_method(D_METHOD("set_from"), &PhysicsTestMotionParameters2D::set_from);
@ -446,11 +466,15 @@ void PhysicsTestMotionParameters2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_exclude_bodies"), &PhysicsTestMotionParameters2D::get_exclude_bodies);
ClassDB::bind_method(D_METHOD("set_exclude_bodies"), &PhysicsTestMotionParameters2D::set_exclude_bodies);
ClassDB::bind_method(D_METHOD("get_exclude_objects"), &PhysicsTestMotionParameters2D::get_exclude_objects);
ClassDB::bind_method(D_METHOD("set_exclude_objects"), &PhysicsTestMotionParameters2D::set_exclude_objects);
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"), "set_exclude_bodies", "get_exclude_bodies");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_objects"), "set_exclude_objects", "get_exclude_objects");
}
///////////////////////////////

View file

@ -472,6 +472,7 @@ public:
real_t margin = 0.08;
bool collide_separation_ray = false;
Set<RID> exclude_bodies;
Set<ObjectID> exclude_objects;
MotionParameters() {}
@ -609,6 +610,9 @@ public:
Vector<RID> get_exclude_bodies() const;
void set_exclude_bodies(const Vector<RID> &p_exclude);
Array get_exclude_objects() const;
void set_exclude_objects(const Array &p_exclude);
};
class PhysicsTestMotionResult2D : public RefCounted {

View file

@ -380,6 +380,26 @@ void PhysicsTestMotionParameters3D::set_exclude_bodies(const Vector<RID> &p_excl
}
}
Array PhysicsTestMotionParameters3D::get_exclude_objects() const {
Array exclude;
exclude.resize(parameters.exclude_objects.size());
int object_index = 0;
for (ObjectID object_id : parameters.exclude_objects) {
exclude[object_index++] = object_id;
}
return exclude;
}
void PhysicsTestMotionParameters3D::set_exclude_objects(const Array &p_exclude) {
for (int i = 0; i < p_exclude.size(); ++i) {
ObjectID object_id = p_exclude[i];
ERR_CONTINUE(object_id.is_null());
parameters.exclude_objects.insert(object_id);
}
}
void PhysicsTestMotionParameters3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_from"), &PhysicsTestMotionParameters3D::get_from);
ClassDB::bind_method(D_METHOD("set_from"), &PhysicsTestMotionParameters3D::set_from);
@ -399,12 +419,16 @@ void PhysicsTestMotionParameters3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_exclude_bodies"), &PhysicsTestMotionParameters3D::get_exclude_bodies);
ClassDB::bind_method(D_METHOD("set_exclude_bodies"), &PhysicsTestMotionParameters3D::set_exclude_bodies);
ClassDB::bind_method(D_METHOD("get_exclude_objects"), &PhysicsTestMotionParameters3D::get_exclude_objects);
ClassDB::bind_method(D_METHOD("set_exclude_objects"), &PhysicsTestMotionParameters3D::set_exclude_objects);
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");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_collisions"), "set_max_collisions", "get_max_collisions");
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"), "set_exclude_bodies", "get_exclude_bodies");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude_objects"), "set_exclude_objects", "get_exclude_objects");
}
///////////////////////////////

View file

@ -491,6 +491,7 @@ public:
int max_collisions = 1;
bool collide_separation_ray = false;
Set<RID> exclude_bodies;
Set<ObjectID> exclude_objects;
MotionParameters() {}
@ -805,6 +806,9 @@ public:
Vector<RID> get_exclude_bodies() const;
void set_exclude_bodies(const Vector<RID> &p_exclude);
Array get_exclude_objects() const;
void set_exclude_objects(const Array &p_exclude);
};
class PhysicsTestMotionResult3D : public RefCounted {