Merge pull request #49327 from nekomatata/sync-to-physics-2d

Move sync to physics to StaticBody2D
This commit is contained in:
Rémi Verschelde 2021-07-15 23:18:08 +02:00 committed by GitHub
commit 7aa44bef1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 77 deletions

View File

@ -110,9 +110,6 @@
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4"> <member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide]. Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
</member> </member>
<member name="motion/sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false">
If [code]true[/code], the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method move_and_slide] or [method PhysicsBody2D.move_and_collide] functions.
</member>
<member name="snap" type="Vector2" setter="set_snap" getter="get_snap" default="Vector2(0, 0)"> <member name="snap" type="Vector2" setter="set_snap" getter="get_snap" default="Vector2(0, 0)">
When set to a value different from [code]Vector2(0, 0)[/code], the body is kept attached to slopes when calling [method move_and_slide]. When set to a value different from [code]Vector2(0, 0)[/code], the body is kept attached to slopes when calling [method move_and_slide].
As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code]Vector2(0, 0)[/code]. As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code]Vector2(0, 0)[/code].

View File

@ -29,6 +29,9 @@
The physics material override for the body. The physics material override for the body.
If a material is assigned to this property, it will be used instead of any other physics material, such as an inherited one. If a material is assigned to this property, it will be used instead of any other physics material, such as an inherited one.
</member> </member>
<member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false">
If [code]true[/code] and [member kinematic_motion] is enabled, the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method PhysicsBody2D.move_and_collide].
</member>
</members> </members>
<constants> <constants>
</constants> </constants>

View File

@ -75,7 +75,7 @@ private:
int total_subshapes = 0; int total_subshapes = 0;
Map<uint32_t, ShapeData> shapes; Map<uint32_t, ShapeData> shapes;
bool only_update_transform_changes = false; //this is used for sync physics in CharacterBody2D bool only_update_transform_changes = false; // This is used for sync to physics.
void _apply_disabled(); void _apply_disabled();
void _apply_enabled(); void _apply_enabled();

View File

@ -229,6 +229,13 @@ void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) {
set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
} }
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
return;
}
#endif
_update_kinematic_motion(); _update_kinematic_motion();
} }
@ -236,8 +243,75 @@ bool StaticBody2D::is_kinematic_motion_enabled() const {
return kinematic_motion; return kinematic_motion;
} }
void StaticBody2D::set_sync_to_physics(bool p_enable) {
if (sync_to_physics == p_enable) {
return;
}
sync_to_physics = p_enable;
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
return;
}
#endif
if (kinematic_motion) {
_update_kinematic_motion();
}
}
bool StaticBody2D::is_sync_to_physics_enabled() const {
return sync_to_physics;
}
void StaticBody2D::_direct_state_changed(Object *p_state) {
if (!sync_to_physics) {
return;
}
PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state);
ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument");
last_valid_transform = state->get_transform();
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
}
TypedArray<String> StaticBody2D::get_configuration_warnings() const {
TypedArray<String> warnings = PhysicsBody2D::get_configuration_warnings();
if (sync_to_physics && !kinematic_motion) {
warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled."));
}
return warnings;
}
void StaticBody2D::_notification(int p_what) { void StaticBody2D::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
// Used by sync to physics, send the new transform to the physics...
Transform2D new_transform = get_global_transform();
real_t delta_time = get_physics_process_delta_time();
new_transform.translate(constant_linear_velocity * delta_time);
new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// ... but then revert changes.
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) { if (Engine::get_singleton()->is_editor_hint()) {
@ -247,19 +321,23 @@ void StaticBody2D::_notification(int p_what) {
ERR_FAIL_COND(!kinematic_motion); ERR_FAIL_COND(!kinematic_motion);
real_t delta_time = get_physics_process_delta_time();
Transform2D new_transform = get_global_transform(); Transform2D new_transform = get_global_transform();
real_t delta_time = get_physics_process_delta_time();
new_transform.translate(constant_linear_velocity * delta_time); new_transform.translate(constant_linear_velocity * delta_time);
new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
if (sync_to_physics) {
// Propagate transform change to node.
set_global_transform(new_transform);
} else {
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// Propagate transform change to node. // Propagate transform change to node.
set_block_transform_notify(true); set_block_transform_notify(true);
set_global_transform(new_transform); set_global_transform(new_transform);
set_block_transform_notify(false); set_block_transform_notify(false);
}
} break; } break;
} }
} }
@ -276,10 +354,14 @@ void StaticBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override); ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody2D::set_sync_to_physics);
ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody2D::is_sync_to_physics_enabled);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
} }
StaticBody2D::StaticBody2D() : StaticBody2D::StaticBody2D() :
@ -303,14 +385,24 @@ void StaticBody2D::_update_kinematic_motion() {
} }
#endif #endif
if (kinematic_motion && sync_to_physics) {
PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody2D::_direct_state_changed));
set_only_update_transform_changes(true);
set_notify_local_transform(true);
} else {
PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable());
set_only_update_transform_changes(false);
set_notify_local_transform(false);
}
bool needs_physics_process = false;
if (kinematic_motion) { if (kinematic_motion) {
if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) { if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) {
set_physics_process_internal(true); needs_physics_process = true;
return;
} }
} }
set_physics_process_internal(false); set_physics_process_internal(needs_physics_process);
} }
void RigidBody2D::_body_enter_tree(ObjectID p_id) { void RigidBody2D::_body_enter_tree(ObjectID p_id) {
@ -1206,45 +1298,6 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) {
return slide_colliders[p_bounce]; return slide_colliders[p_bounce];
} }
void CharacterBody2D::set_sync_to_physics(bool p_enable) {
if (sync_to_physics == p_enable) {
return;
}
sync_to_physics = p_enable;
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
if (p_enable) {
PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &CharacterBody2D::_direct_state_changed));
set_only_update_transform_changes(true);
set_notify_local_transform(true);
} else {
PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable());
set_only_update_transform_changes(false);
set_notify_local_transform(false);
}
}
bool CharacterBody2D::is_sync_to_physics_enabled() const {
return sync_to_physics;
}
void CharacterBody2D::_direct_state_changed(Object *p_state) {
if (!sync_to_physics) {
return;
}
PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state);
ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument");
last_valid_transform = state->get_transform();
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
}
void CharacterBody2D::set_safe_margin(real_t p_margin) { void CharacterBody2D::set_safe_margin(real_t p_margin) {
margin = p_margin; margin = p_margin;
} }
@ -1304,8 +1357,6 @@ void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {
void CharacterBody2D::_notification(int p_what) { void CharacterBody2D::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_ENTER_TREE: { case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
// Reset move_and_slide() data. // Reset move_and_slide() data.
on_floor = false; on_floor = false;
on_floor_body = RID(); on_floor_body = RID();
@ -1314,16 +1365,6 @@ void CharacterBody2D::_notification(int p_what) {
motion_results.clear(); motion_results.clear();
floor_velocity = Vector2(); floor_velocity = Vector2();
} break; } break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
// Used by sync to physics, send the new transform to the physics.
Transform2D new_transform = get_global_transform();
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// But then revert changes.
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
} break;
} }
} }
@ -1356,9 +1397,6 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody2D::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody2D::get_slide_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);
ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &CharacterBody2D::set_sync_to_physics);
ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &CharacterBody2D::is_sync_to_physics_enabled);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled");
@ -1367,7 +1405,6 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
} }

View File

@ -69,6 +69,11 @@ class StaticBody2D : public PhysicsBody2D {
Ref<PhysicsMaterial> physics_material_override; Ref<PhysicsMaterial> physics_material_override;
bool kinematic_motion = false; bool kinematic_motion = false;
bool sync_to_physics = false;
Transform2D last_valid_transform;
void _direct_state_changed(Object *p_state);
protected: protected:
void _notification(int p_what); void _notification(int p_what);
@ -84,6 +89,8 @@ public:
Vector2 get_constant_linear_velocity() const; Vector2 get_constant_linear_velocity() const;
real_t get_constant_angular_velocity() const; real_t get_constant_angular_velocity() const;
virtual TypedArray<String> get_configuration_warnings() const override;
StaticBody2D(); StaticBody2D();
private: private:
@ -93,6 +100,9 @@ private:
void set_kinematic_motion_enabled(bool p_enabled); void set_kinematic_motion_enabled(bool p_enabled);
bool is_kinematic_motion_enabled() const; bool is_kinematic_motion_enabled() const;
void set_sync_to_physics(bool p_enable);
bool is_sync_to_physics_enabled() const;
}; };
class RigidBody2D : public PhysicsBody2D { class RigidBody2D : public PhysicsBody2D {
@ -243,7 +253,7 @@ public:
TypedArray<Node2D> get_colliding_bodies() const; //function for script TypedArray<Node2D> get_colliding_bodies() const; //function for script
TypedArray<String> get_configuration_warnings() const override; virtual TypedArray<String> get_configuration_warnings() const override;
RigidBody2D(); RigidBody2D();
~RigidBody2D(); ~RigidBody2D();
@ -276,7 +286,6 @@ private:
bool on_floor = false; bool on_floor = false;
bool on_ceiling = false; bool on_ceiling = false;
bool on_wall = false; bool on_wall = false;
bool sync_to_physics = false;
Vector<PhysicsServer2D::MotionResult> motion_results; Vector<PhysicsServer2D::MotionResult> motion_results;
Vector<Ref<KinematicCollision2D>> slide_colliders; Vector<Ref<KinematicCollision2D>> slide_colliders;
@ -285,9 +294,6 @@ private:
bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result); bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result);
Transform2D last_valid_transform;
void _direct_state_changed(Object *p_state);
void set_safe_margin(real_t p_margin); void set_safe_margin(real_t p_margin);
real_t get_safe_margin() const; real_t get_safe_margin() const;
@ -329,9 +335,6 @@ public:
int get_slide_count() const; int get_slide_count() const;
PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const; PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const;
void set_sync_to_physics(bool p_enable);
bool is_sync_to_physics_enabled() const;
CharacterBody2D(); CharacterBody2D();
~CharacterBody2D(); ~CharacterBody2D();
}; };