diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index b5893e02f4ee..a65fe8f4282c 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -172,7 +172,19 @@ Lock the body's Z axis movement. Deprecated alias for [member axis_lock_motion_z]. + + Sets the behaviour to apply when you leave a moving platform. By default, to be physically accurate, when you leave the last platform velocity is applied. See [enum MovingPlatformApplyVelocityOnLeave] constants for available behaviour. + + + Add the last platform velocity when you leave a moving platform. + + + Add the last platform velocity when you leave a moving platform, but any downward motion is ignored. It's useful to keep full jump height even when the platform is moving down. + + + Do nothing when leaving a platform. + diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index 32eeb9f317cd..24ed1332acfa 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -144,7 +144,19 @@ 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 move_and_collide] functions. + + Sets the behaviour to apply when you leave a moving platform. By default, to be physically accurate, when you leave the last platform velocity is applied. See [enum MovingPlatformApplyVelocityOnLeave] constants for available behaviour. + + + Add the last platform velocity when you leave a moving platform. + + + Add the last platform velocity when you leave a moving platform, but any downward motion is ignored. It's useful to keep full jump height even when the platform is moving down. + + + Do nothing when leaving a platform. + diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index b62361b2e831..a3813cc9ba76 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1250,9 +1250,14 @@ Vector2 KinematicBody2D::_move_and_slide_internal(const Vector2 &p_linear_veloci } } - if (!on_floor) { + if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { // Add last platform velocity when just left a moving platform. - return body_velocity + current_floor_velocity; + if (!on_floor) { + if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_floor_velocity.dot(up_direction) < 0) { + current_floor_velocity = current_floor_velocity.slide(up_direction); + } + return body_velocity + current_floor_velocity; + } } return body_velocity; @@ -1307,6 +1312,14 @@ Vector2 KinematicBody2D::get_floor_velocity() const { return floor_velocity; } +void KinematicBody2D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { + moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +} + +KinematicBody2D::MovingPlatformApplyVelocityOnLeave KinematicBody2D::get_moving_platform_apply_velocity_on_leave() const { + return moving_platform_apply_velocity_on_leave; +} + bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia) { ERR_FAIL_COND_V(!is_inside_tree(), false); @@ -1440,6 +1453,9 @@ void KinematicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody2D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &KinematicBody2D::get_safe_margin); + ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &KinematicBody2D::set_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &KinematicBody2D::get_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody2D::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody2D::_get_slide_collision); ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &KinematicBody2D::_get_last_slide_collision); @@ -1451,6 +1467,12 @@ void KinematicBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); + ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); + + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); } KinematicBody2D::KinematicBody2D() : diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index d4eca7ee9129..b356fe58eefd 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -267,6 +267,11 @@ class KinematicBody2D : public PhysicsBody2D { GDCLASS(KinematicBody2D, PhysicsBody2D); public: + enum MovingPlatformApplyVelocityOnLeave { + PLATFORM_VEL_ON_LEAVE_ALWAYS, + PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, + PLATFORM_VEL_ON_LEAVE_NEVER, + }; struct Collision { Vector2 collision; Vector2 normal; @@ -294,6 +299,7 @@ class KinematicBody2D : public PhysicsBody2D { bool on_ceiling; bool on_wall; bool sync_to_physics; + MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; Vector colliders; Vector> slide_colliders; @@ -307,6 +313,8 @@ class KinematicBody2D : public PhysicsBody2D { void _direct_state_changed(Object *p_state); Vector2 _move_and_slide_internal(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_up_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); void _set_collision_direction(const Collision &p_collision, const Vector2 &p_up_direction, float p_floor_max_angle); + void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); + MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; protected: void _notification(int p_what); @@ -341,6 +349,8 @@ class KinematicBody2D : public PhysicsBody2D { ~KinematicBody2D(); }; +VARIANT_ENUM_CAST(KinematicBody2D::MovingPlatformApplyVelocityOnLeave); + class KinematicCollision2D : public Reference { GDCLASS(KinematicCollision2D, Reference); diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 487cd7206483..ec73947c1132 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1198,9 +1198,14 @@ Vector3 KinematicBody::_move_and_slide_internal(const Vector3 &p_linear_velocity } } - if (!on_floor) { + if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { // Add last platform velocity when just left a moving platform. - return body_velocity + current_floor_velocity; + if (!on_floor) { + if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_floor_velocity.dot(up_direction) < 0) { + current_floor_velocity = current_floor_velocity.slide(up_direction); + } + return body_velocity + current_floor_velocity; + } } return body_velocity; @@ -1256,6 +1261,14 @@ Vector3 KinematicBody::get_floor_velocity() const { return floor_velocity; } +void KinematicBody::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { + moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +} + +KinematicBody::MovingPlatformApplyVelocityOnLeave KinematicBody::get_moving_platform_apply_velocity_on_leave() const { + return moving_platform_apply_velocity_on_leave; +} + bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia) { ERR_FAIL_COND_V(!is_inside_tree(), false); @@ -1445,6 +1458,9 @@ void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &KinematicBody::get_safe_margin); + ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &KinematicBody::set_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &KinematicBody::get_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody::_get_slide_collision); ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &KinematicBody::_get_last_slide_collision); @@ -1464,6 +1480,12 @@ void KinematicBody::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); + ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); + + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); } KinematicBody::KinematicBody() : diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 09d816ff47f3..2ba77b6e1cc8 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -262,6 +262,11 @@ class KinematicBody : public PhysicsBody { GDCLASS(KinematicBody, PhysicsBody); public: + enum MovingPlatformApplyVelocityOnLeave { + PLATFORM_VEL_ON_LEAVE_ALWAYS, + PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, + PLATFORM_VEL_ON_LEAVE_NEVER, + }; struct Collision { Vector3 collision; Vector3 normal; @@ -291,6 +296,8 @@ class KinematicBody : public PhysicsBody { bool on_ceiling; bool on_wall; bool sync_to_physics = false; + MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; + Vector colliders; Vector> slide_colliders; Ref motion_cache; @@ -304,6 +311,8 @@ class KinematicBody : public PhysicsBody { Vector3 _move_and_slide_internal(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_up_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); void _set_collision_direction(const Collision &p_collision, const Vector3 &p_up_direction, float p_floor_max_angle); + void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); + MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; protected: void _notification(int p_what); @@ -340,6 +349,8 @@ class KinematicBody : public PhysicsBody { ~KinematicBody(); }; +VARIANT_ENUM_CAST(KinematicBody::MovingPlatformApplyVelocityOnLeave); + class KinematicCollision : public Reference { GDCLASS(KinematicCollision, Reference);