diff options
Diffstat (limited to 'scene/2d/physics_body_2d.cpp')
-rw-r--r-- | scene/2d/physics_body_2d.cpp | 420 |
1 files changed, 360 insertions, 60 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index feb11089d0..9879f43877 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -30,10 +30,11 @@ #include "physics_body_2d.h" +#include "core/core_string_names.h" #include "core/method_bind_ext.gen.inc" #include "engine.h" +#include "math_funcs.h" #include "scene/scene_string_names.h" - void PhysicsBody2D::_notification(int p_what) { /* @@ -186,28 +187,75 @@ real_t StaticBody2D::get_constant_angular_velocity() const { return constant_angular_velocity; } +#ifndef DISABLE_DEPRECATED void StaticBody2D::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_friction(p_friction); + _reload_physics_characteristics(); } + real_t StaticBody2D::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void StaticBody2D::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_bounce(p_bounce); + _reload_physics_characteristics(); } + real_t StaticBody2D::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif + +void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const { + return physics_material_override; } void StaticBody2D::_bind_methods() { @@ -222,23 +270,41 @@ void StaticBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody2D::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody2D::get_bounce); + 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("_reload_physics_characteristics"), &StaticBody2D::_reload_physics_characteristics); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); } StaticBody2D::StaticBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) { constant_angular_velocity = 0; - bounce = 0; - friction = 1; } StaticBody2D::~StaticBody2D() { } +void StaticBody2D::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, Physics2DServer::COMBINE_MODE_INHERIT); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, Physics2DServer::COMBINE_MODE_INHERIT); + } else { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce()); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->get_friction()); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, (Physics2DServer::CombineMode)physics_material_override->get_bounce_combine_mode()); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, (Physics2DServer::CombineMode)physics_material_override->get_friction_combine_mode()); + } +} + void RigidBody2D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); @@ -545,28 +611,72 @@ real_t RigidBody2D::get_weight() const { return mass * real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10; } +#ifndef DISABLE_DEPRECATED void RigidBody2D::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_friction(p_friction); + _reload_physics_characteristics(); } real_t RigidBody2D::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void RigidBody2D::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_bounce(p_bounce); + _reload_physics_characteristics(); } real_t RigidBody2D::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif + +void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const { + return physics_material_override; } void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) { @@ -690,11 +800,19 @@ int RigidBody2D::get_max_contacts_reported() const { return max_contacts_reported; } +void RigidBody2D::apply_central_impulse(const Vector2 &p_impulse) { + Physics2DServer::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); +} + void RigidBody2D::apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse) { Physics2DServer::get_singleton()->body_apply_impulse(get_rid(), p_offset, p_impulse); } +void RigidBody2D::apply_torque_impulse(float p_torque) { + Physics2DServer::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque); +} + void RigidBody2D::set_applied_force(const Vector2 &p_force) { Physics2DServer::get_singleton()->body_set_applied_force(get_rid(), p_force); @@ -715,11 +833,19 @@ float RigidBody2D::get_applied_torque() const { return Physics2DServer::get_singleton()->body_get_applied_torque(get_rid()); }; +void RigidBody2D::add_central_force(const Vector2 &p_force) { + Physics2DServer::get_singleton()->body_add_central_force(get_rid(), p_force); +} + void RigidBody2D::add_force(const Vector2 &p_offset, const Vector2 &p_force) { Physics2DServer::get_singleton()->body_add_force(get_rid(), p_offset, p_force); } +void RigidBody2D::add_torque(const float p_torque) { + Physics2DServer::get_singleton()->body_add_torque(get_rid(), p_torque); +} + void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { ccd_mode = p_mode; @@ -843,6 +969,11 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody2D::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody2D::get_bounce); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody2D::_reload_physics_characteristics); + ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale); ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale); @@ -871,7 +1002,9 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidBody2D::get_continuous_collision_detection_mode); ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody2D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody2D::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "offset", "impulse"), &RigidBody2D::apply_impulse); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidBody2D::apply_torque_impulse); ClassDB::bind_method(D_METHOD("set_applied_force", "force"), &RigidBody2D::set_applied_force); ClassDB::bind_method(D_METHOD("get_applied_force"), &RigidBody2D::get_applied_force); @@ -879,7 +1012,9 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_applied_torque", "torque"), &RigidBody2D::set_applied_torque); ClassDB::bind_method(D_METHOD("get_applied_torque"), &RigidBody2D::get_applied_torque); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody2D::add_central_force); ClassDB::bind_method(D_METHOD("add_force", "offset", "force"), &RigidBody2D::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody2D::add_torque); ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody2D::set_sleeping); ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody2D::is_sleeping); @@ -903,6 +1038,7 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); + 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::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode"); @@ -941,9 +1077,7 @@ RigidBody2D::RigidBody2D() : mode = MODE_RIGID; - bounce = 0; mass = 1; - friction = 1; gravity_scale = 1; linear_damp = -1; @@ -969,13 +1103,27 @@ RigidBody2D::~RigidBody2D() { memdelete(contact_monitor); } +void RigidBody2D::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, Physics2DServer::COMBINE_MODE_INHERIT); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, Physics2DServer::COMBINE_MODE_INHERIT); + } else { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce()); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->get_friction()); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, (Physics2DServer::CombineMode)physics_material_override->get_bounce_combine_mode()); + Physics2DServer::get_singleton()->body_set_combine_mode(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, (Physics2DServer::CombineMode)physics_material_override->get_friction_combine_mode()); + } +} + ////////////////////////// -Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia) { +Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) { Collision col; - if (move_and_collide(p_motion, p_infinite_inertia, col)) { + if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -989,11 +1137,48 @@ Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p return Ref<KinematicCollision2D>(); } -bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision) { +bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) { + + Physics2DServer::SeparationResult sep_res[8]; //max 8 rays + + Transform2D gt = get_global_transform(); + + Vector2 recover; + int hits = Physics2DServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin); + int deepest = -1; + float deepest_depth; + for (int i = 0; i < hits; i++) { + if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) { + deepest = i; + deepest_depth = sep_res[i].collision_depth; + } + } + + gt.elements[2] += recover; + set_global_transform(gt); + + if (deepest != -1) { + r_collision.collider = sep_res[deepest].collider_id; + r_collision.collider_metadata = sep_res[deepest].collider_metadata; + r_collision.collider_shape = sep_res[deepest].collider_shape; + r_collision.collider_vel = sep_res[deepest].collider_velocity; + r_collision.collision = sep_res[deepest].collision_point; + r_collision.normal = sep_res[deepest].collision_normal; + r_collision.local_shape = sep_res[deepest].collision_local_shape; + r_collision.travel = recover; + r_collision.remainder = Vector2(); + + return true; + } else { + return false; + } +} + +bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) { Transform2D gt = get_global_transform(); Physics2DServer::MotionResult result; - bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result); + bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes); if (colliding) { r_collision.collider_metadata = result.collider_metadata; @@ -1002,23 +1187,36 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_ r_collision.collision = result.collision_point; r_collision.normal = result.collision_normal; r_collision.collider = result.collider_id; + r_collision.collider_rid = result.collider; r_collision.travel = result.motion; r_collision.remainder = result.remainder; r_collision.local_shape = result.collision_local_shape; } - gt.elements[2] += result.motion; - set_global_transform(gt); + if (!p_test_only) { + gt.elements[2] += result.motion; + set_global_transform(gt); + } return colliding; } Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { - Vector2 motion = (floor_velocity + p_linear_velocity) * get_physics_process_delta_time(); + Vector2 floor_motion = floor_velocity; + if (on_floor && on_floor_body.is_valid()) { + //this approach makes sure there is less delay between the actual body velocity and the one we saved + Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body); + if (bs) { + floor_motion = bs->get_linear_velocity(); + } + } + + Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time(); Vector2 lv = p_linear_velocity; on_floor = false; + on_floor_body = RID(); on_ceiling = false; on_wall = false; colliders.clear(); @@ -1027,48 +1225,68 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const while (p_max_slides) { Collision collision; + bool found_collision = false; + + for (int i = 0; i < 2; i++) { + bool collided; + if (i == 0) { //collide + collided = move_and_collide(motion, p_infinite_inertia, collision); + if (!collided) { + motion = Vector2(); //clear because no collision happened and motion completed + } + } else { //separate raycasts (if any) + collided = separate_raycast_shapes(p_infinite_inertia, collision); + if (collided) { + collision.remainder = motion; //keep + collision.travel = Vector2(); + } + } - bool collided = move_and_collide(motion, p_infinite_inertia, collision); - - if (collided) { - - motion = collision.remainder; - - if (p_floor_direction == Vector2()) { - //all is a wall - on_wall = true; - } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + if (collided) { + found_collision = true; + } - on_floor = true; - floor_velocity = collision.collider_vel; + if (collided) { - Vector2 rel_v = lv - floor_velocity; - Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + motion = collision.remainder; - if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { - Transform2D gt = get_global_transform(); - gt.elements[2] -= collision.travel; - set_global_transform(gt); - return Vector2(); - } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling - on_ceiling = true; - } else { + if (p_floor_direction == Vector2()) { + //all is a wall on_wall = true; + } else { + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + + on_floor = true; + on_floor_body = collision.collider_rid; + floor_velocity = collision.collider_vel; + + Vector2 rel_v = lv - floor_velocity; + Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + + if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { + Transform2D gt = get_global_transform(); + gt.elements[2] -= collision.travel; + set_global_transform(gt); + return Vector2(); + } + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + } } - } - Vector2 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); + Vector2 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); - colliders.push_back(collision); + colliders.push_back(collision); + } + } - } else { + if (!found_collision) { break; } - p_max_slides--; if (motion == Vector2()) break; @@ -1077,6 +1295,31 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const return lv; } +Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { + + bool was_on_floor = on_floor; + + Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_slope_stop_min_velocity, p_max_slides, p_floor_max_angle); + if (!was_on_floor || p_snap == Vector2()) { + return ret; + } + + Collision col; + Transform2D gt = get_global_transform(); + + if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) { + gt.elements[2] += col.travel; + if (p_floor_direction != Vector2() && Math::acos(p_floor_direction.normalized().dot(col.normal)) < p_floor_max_angle) { + on_floor = true; + on_floor_body = col.collider_rid; + floor_velocity = col.collider_vel; + } + set_global_transform(gt); + } + + return ret; +} + bool KinematicBody2D::is_on_floor() const { return on_floor; @@ -1130,18 +1373,68 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders[p_bounce].instance(); - slide_colliders[p_bounce]->owner = this; + slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce]->owner = this; } - slide_colliders[p_bounce]->collision = colliders[p_bounce]; + slide_colliders.write[p_bounce]->collision = colliders[p_bounce]; return slide_colliders[p_bounce]; } +void KinematicBody2D::set_sync_to_physics(bool p_enable) { + + if (sync_to_physics == p_enable) { + return; + } + sync_to_physics = p_enable; + if (p_enable) { + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, ""); + set_only_update_transform_changes(false); + set_notify_local_transform(false); + } +} + +bool KinematicBody2D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void KinematicBody2D::_direct_state_changed(Object *p_state) { + + if (!sync_to_physics) + return; + + Physics2DDirectBodyState *state = Object::cast_to<Physics2DDirectBodyState>(p_state); + + last_valid_transform = state->get_transform(); + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); +} + +void KinematicBody2D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + last_valid_transform = get_global_transform(); + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + //used by sync to physics, send the new transform to the physics + Transform2D new_transform = get_global_transform(); + Physics2DServer::get_singleton()->body_set_state(get_rid(), Physics2DServer::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); + } +} void KinematicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody2D::_move, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move); @@ -1156,7 +1449,13 @@ void KinematicBody2D::_bind_methods() { 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("set_sync_to_physics", "enable"), &KinematicBody2D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &KinematicBody2D::is_sync_to_physics_enabled); + + ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody2D::_direct_state_changed); + 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"); } KinematicBody2D::KinematicBody2D() : @@ -1167,6 +1466,7 @@ KinematicBody2D::KinematicBody2D() : on_floor = false; on_ceiling = false; on_wall = false; + sync_to_physics = false; } KinematicBody2D::~KinematicBody2D() { if (motion_cache.is_valid()) { @@ -1175,7 +1475,7 @@ KinematicBody2D::~KinematicBody2D() { for (int i = 0; i < slide_colliders.size(); i++) { if (slide_colliders[i].is_valid()) { - slide_colliders[i]->owner = NULL; + slide_colliders.write[i]->owner = NULL; } } } |