diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/2d/physics_body_2d.cpp | 48 | ||||
-rw-r--r-- | scene/2d/physics_body_2d.h | 4 | ||||
-rw-r--r-- | scene/3d/physics_body.cpp | 183 | ||||
-rw-r--r-- | scene/3d/physics_body.h | 11 |
4 files changed, 181 insertions, 65 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8758ffef9f..02213e07d0 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1207,7 +1207,7 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_ //so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 -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 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) { Vector2 floor_motion = floor_velocity; if (on_floor && on_floor_body.is_valid()) { @@ -1228,6 +1228,8 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const colliders.clear(); floor_velocity = Vector2(); + Vector2 lv_n = p_linear_velocity.normalized(); + while (p_max_slides) { Collision collision; @@ -1254,8 +1256,10 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const if (collided) { + colliders.push_back(collision); motion = collision.remainder; + bool is_on_slope = false; if (p_floor_direction == Vector2()) { //all is a wall on_wall = true; @@ -1266,15 +1270,17 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const 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(); + if (p_stop_on_slope) { + if (Vector2() == lv_n + p_floor_direction) { + Transform2D gt = get_global_transform(); + gt.elements[2] -= collision.travel; + set_global_transform(gt); + return Vector2(); + } } + + is_on_slope = true; + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling on_ceiling = true; } else { @@ -1282,12 +1288,18 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const } } - Vector2 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); - - colliders.push_back(collision); + if (p_stop_on_slope && is_on_slope) { + motion = motion.slide(p_floor_direction); + lv = lv.slide(p_floor_direction); + } else { + Vector2 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); + } } + + if (p_stop_on_slope) + break; } if (!found_collision) { @@ -1301,11 +1313,11 @@ 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) { +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, bool p_stop_on_slope, 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); + Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_stop_on_slope, p_max_slides, p_floor_max_angle); if (!was_on_floor || p_snap == Vector2()) { return ret; } @@ -1439,8 +1451,8 @@ void KinematicBody2D::_notification(int p_what) { void KinematicBody2D::_bind_methods() { 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("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 0a2ce0918b..852963a721 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -338,8 +338,8 @@ public: void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); - Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index e53ccb4cf4..d56ba5d403 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1078,10 +1078,10 @@ void RigidBody::_reload_physics_characteristics() { ////////////////////////////////////////////////////// ////////////////////////// -Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia) { +Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia, 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_test_only)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -1095,7 +1095,7 @@ Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_inf return Ref<KinematicCollision>(); } -bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision) { +bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_test_only) { Transform gt = get_global_transform(); PhysicsServer::MotionResult result; @@ -1108,6 +1108,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in 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; @@ -1119,8 +1120,10 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in } } - gt.origin += result.motion; - set_global_transform(gt); + if (!p_test_only) { + gt.origin += result.motion; + set_global_transform(gt); + } return colliding; } @@ -1128,7 +1131,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in //so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 -Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { +Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { Vector3 lv = p_linear_velocity; @@ -1146,69 +1149,127 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve colliders.clear(); floor_velocity = Vector3(); + Vector3 lv_n = p_linear_velocity.normalized(); + while (p_max_slides) { Collision collision; - bool collided = move_and_collide(motion, p_infinite_inertia, collision); - - if (collided) { + bool found_collision = false; - motion = collision.remainder; + int test_type = 0; - if (p_floor_direction == Vector3()) { - //all is a wall - on_wall = true; + do { + bool collided; + if (test_type == 0) { //collide + collided = move_and_collide(motion, p_infinite_inertia, collision); + if (!collided) { + motion = Vector3(); //clear because no collision happened and motion completed + } } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor + collided = separate_raycast_shapes(p_infinite_inertia, collision); + if (collided) { + collision.remainder = motion; //keep + collision.travel = Vector3(); + } + } - on_floor = true; - floor_velocity = collision.collider_vel; + if (collided) { + found_collision = true; + } - Vector3 rel_v = lv - floor_velocity; - Vector3 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + if (collided) { - if (collision.travel.length() < 0.05 && hv.length() < p_slope_stop_min_velocity) { - Transform gt = get_global_transform(); - gt.origin -= collision.travel; - set_global_transform(gt); - return floor_velocity - p_floor_direction * p_floor_direction.dot(floor_velocity); - } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling - on_ceiling = true; - } else { + colliders.push_back(collision); + motion = collision.remainder; + + bool is_on_slope = false; + if (p_floor_direction == Vector3()) { + //all is a wall on_wall = true; + } else { + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor + + on_floor = true; + on_floor_body = collision.collider_rid; + floor_velocity = collision.collider_vel; + + if (p_stop_on_slope) { + if (Vector3() == lv_n + p_floor_direction) { + Transform gt = get_global_transform(); + gt.origin -= collision.travel; + set_global_transform(gt); + return Vector3(); + } + } + + is_on_slope = true; + + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + } } - } - Vector3 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); + if (p_stop_on_slope && is_on_slope) { + motion = motion.slide(p_floor_direction); + lv = lv.slide(p_floor_direction); + } else { + Vector3 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); + } - for (int i = 0; i < 3; i++) { - if (locked_axis & (1 << i)) { - lv[i] = 0; + for (int i = 0; i < 3; i++) { + if (locked_axis & (1 << i)) { + lv[i] = 0; + } } } - colliders.push_back(collision); + ++test_type; + } while (!p_stop_on_slope && test_type < 2); - } else { + if (!found_collision || motion == Vector3()) break; - } - p_max_slides--; - if (motion == Vector3()) - break; + --p_max_slides; } return lv; } +Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) { + + bool was_on_floor = on_floor; + + Vector3 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia); + if (!was_on_floor || p_snap == Vector3()) { + return ret; + } + + Collision col; + Transform gt = get_global_transform(); + + if (move_and_collide(p_snap, p_infinite_inertia, col, true)) { + gt.origin += col.travel; + if (p_floor_direction != Vector3() && 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 KinematicBody::is_on_floor() const { return on_floor; } + bool KinematicBody::is_on_wall() const { return on_wall; @@ -1230,6 +1291,43 @@ bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion, return PhysicsServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia); } +bool KinematicBody::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) { + + PhysicsServer::SeparationResult sep_res[8]; //max 8 rays + + Transform gt = get_global_transform(); + + Vector3 recover; + int hits = PhysicsServer::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.origin += 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 = Vector3(); + + return true; + } else { + return false; + } +} + void KinematicBody::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock) { PhysicsServer::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock); } @@ -1276,8 +1374,9 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { void KinematicBody::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 80bf422c98..c4db41f577 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -285,6 +285,7 @@ public: Vector3 normal; Vector3 collider_vel; ObjectID collider; + RID collider_rid; int collider_shape; Variant collider_metadata; Vector3 remainder; @@ -298,6 +299,7 @@ private: float margin; Vector3 floor_velocity; + RID on_floor_body; bool on_floor; bool on_ceiling; bool on_wall; @@ -307,23 +309,26 @@ private: _FORCE_INLINE_ bool _ignores_mode(PhysicsServer::BodyMode) const; - Ref<KinematicCollision> _move(const Vector3 &p_motion, bool p_infinite_inertia = true); + Ref<KinematicCollision> _move(const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_test_only = false); Ref<KinematicCollision> _get_slide_collision(int p_bounce); protected: static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz, bool p_test_only = false); bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); + bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision); + void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer::BodyAxis p_axis) const; void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); + Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_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); + Vector3 move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; |